- subscription.0017/0018, payment.0013: pack plans deleted, QuestionCredit/HeartsPass models and Plan.kind /
question_credits / hearts_days / Order.question_credits / hearts_days dropped. The only paid pack order was the
company owner's test account (user 26).
- Assistant: no credit spending; quota is the daily cap only (free 2, every plan 4, gift subscription 4).
- Bazaar: verify_purchase is subscription-only (no inapp guess), start/purchase responses lose productType,
verify refuses an order without a subscription. A paid order with no subscription now logs ERROR.
- Removed POST /api/payment/orders (internal; no caller). Its permission tests stay.
- Pack-based tests rewritten on subscriptions (refund, security, Bazaar token); pack-only test files deleted.
- docs: profit table for 4/day on both payment routes plus free users; handbook, operations, CLAUDE.md current.
949 tests green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Purchase with paymentMethod=bazaar now works for subscription plans with a bazaar_sku (not only packs);
response carries bazaarProductType. Migration subscription.0014 puts 10343/10344 on the 90/180-day plans.
- Verify asks Bazaar for the right product type (from the order) and stores validUntil (payment.0012).
- POST /api/payment/bazaar/orders/{id}/start: SKU, type, and an HS256 dynamic-price JWT when a discount
code lowered the price (BAZAAR_DYNAMIC_PRICE_KEY); discount on Bazaar orders allowed only with that key
and only before start.
- Auto-renewal: sync_bazaar_renewals re-asks Bazaar when the paid period is near its end (status page and
the paywall via has_subscription_access); each new period becomes a paid order.
- Default BAZAAR_PACKAGE_NAME = com.tadrisino.math.app; health shows bazaarDynamicPrice.
979 tests green. No real Bazaar purchase or renewal tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was no way to take an order back: staff could only edit the
status by hand, and turning 'paid' into 'failed' left the credits,
heart days or subscription days the order had given. Two OrderAdmin
actions now do it:
- cancel a pending order (a payment that does arrive later still counts);
- refund a paid order, after a confirmation page: takes back the pack's
unspent credits (never below zero), its heart days, or the plan's
subscription days, and undoes the referrer's commission.
The money itself is returned in the Zarinpal / Bazaar panel; the action
says so. Status is read-only in the order form. Steps in
docs/operations.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- packs (20 / 60 / 150 questions for 45,000 / 119,000 / 259,000 toman) never expire and
give 7 / 30 / 60 days of unlimited hearts; old subscription plans stay for existing
subscribers but are no longer on sale
- credit ledger (subscription.QuestionCredit): +N per paid pack (once per order), -1 per
tutor question that reaches the API; the free daily questions are spent first; questions
stopped by the server filter, cached answers and failed answers cost nothing
- unlimited hearts from packs live in HeartsPass, so buying a pack never rewrites a
running subscription
- Message.charge records how each question was paid; GET /api/ai/quota and the ask
responses report free-left-today and credits
- HEARTS_ENFORCED now defaults to on: zero hearts stops answering until refill or a pack
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Five a day was too close to the free three. Pricing keeps the tutor under ~40% of net revenue
at typical use (a third of the cap); full numbers in the cost report and docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- remove the family app from the root (separate parent accounts, invite codes, parental
settings); progress.0006 drops its tables, permissions and migration history, and
Profile.parent_phone, the two family notification types and ?userId= on
/api/progress/nodes go with it
- GET /api/user/weekly-report: last 7 days next to the 7 before (estimated minutes,
questions, accuracy, islands, tutor questions) for the "parent report" page
- profile birthDate in Jalali (accounts/jalali.py), ages 5-99, birthYear derived from it
- GET /api/payment/invoices; orders now snapshot list price and plan title
- tutor answers only math: server pre-filter, scope rule in the prompt (tutor-v11),
refusal marker replaced with fixed text; self-harm gets a help line instead
- streamed answers now record token usage (it was always 0)
- daily tutor quota: AI_DAILY_QUOTA_FREE (default 3) vs Plan.ai_daily_quota
- delete_users management command for wiping test accounts across every app
- docs/ai-cost-and-guardrails.md: measured cost per question and guardrail results
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
کارتِ اشتراک زیرِ هر پلن فهرستِ امکانات و یک برچسب («محبوبترین»، «بهترین
ارزش») نشان میدهد، و هر دو در کدِ فرانت هاردکد بودند — یعنی عوض کردنِ یک
بندِ متنی به دیپلویِ تازهی اپ نیاز داشت.
Plan حالا features و badge دارد. `popular` سرِ جایش ماند: بولین است و متنِ
برچسب را نمیدهد، پس این جایگزینش نیست، کنارش است. هیچکدام اجباری نیستند و
پلنهای موجود بدونشان همچنان سرو میشوند.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>