Paywall: first unit of each grade free; 1/3/6-month subscriptions; AI sold separately

- content: unit_is_free/has_unit_access; questions, node quiz, answer and lesson/node
  complete return 402 subscription_required outside the first unit without an active
  subscription. /api/home units carry requiresSubscription.
- subscription.0013: monthly 498k, quarterly 798k, six-month 1,298k toman, unlimited
  hearts, 3 AI questions/day; packs stay on sale for more questions.
- AI_DAILY_QUOTA_FREE default 3 -> 2.
- /api/subscription/status adds planTitle, planDurationDays, aiDailyQuota.
- tests: content/tests_paywall.py; existing unlock-order tests get a subscription.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 01:19:02 +03:30
parent 7d81d193bd
commit 64c4c09326
16 changed files with 214 additions and 28 deletions

View File

@@ -51,8 +51,9 @@ Message.objects.filter(role='assistant', created_at__gte=since).values('conversa
پلن‌های اشتراکی پاک نشدند (`on_sale=False`) تا مشترکِ فعلی سقفش را نگه دارد.
- **ترتیبِ خرج:** اول `AI_DAILY_QUOTA_FREE` سوالِ رایگانِ همان روز (پیش‌فرض ۳)، بعد اعتبارِ بسته؛ مشترکِ
قدیمی سقفِ `Plan.ai_daily_quota`ِ خودش را دارد.
- **ترتیبِ خرج:** اول سقفِ روزانه (بی‌اشتراک `AI_DAILY_QUOTA_FREE`، پیش‌فرض ۲؛ مشترک `Plan.ai_daily_quota`،
برای یک/سه/شش‌ماهه ۳)، بعد اعتبارِ بسته. اشتراک محتوا و قلب می‌فروشد؛ سوالِ بیشتر از دستیار فقط با بسته.
بدترین حالتِ مشترکِ شش‌ماهه با سقفِ ۳: ~۶۳ هزار تومان در ماه، ۲۹٪ِ سهمِ ماهانه‌ی قیمت.
- **چه چیزی خرج می‌شود:** هر سوالی که به API برسد، از جمله ردِ «غیرِ ریاضی» توسطِ مدل. ردِ صافیِ سرور،
جوابِ کش‌شده و جوابی که ساخته نشد چیزی کم نمی‌کنند. `Message.charge` روی هر پیامِ کاربر می‌گوید
سوال از کجا پرداخت شد (`free` / `credit` / `none`).

View File

@@ -27,7 +27,7 @@
| ۸ | سرور بدونِ ArvanCloud روی پورتِ ۸۰ هم جواب می‌دهد | متوسط | ادمینِ سرور |
| ۹ | کشِ مشترک بینِ workerها نیست (Redisِ اختصاصی)؛ سقف‌های مبتنی بر کش ≈ ۳ برابرند | متوسط | تصمیم + ساختِ Redis |
| ۱۱ | کدِ OTP بی‌هش در دیتابیس و در فهرستِ پنل | کم | کد |
| ۱۲ | هزینه‌ی دستیارِ AI با ساختِ شماره‌ی تازه (هر شماره ۳ سوالِ رایگان) قابلِ بالا بردن است | کم (مالی) | کد |
| ۱۲ | هزینه‌ی دستیارِ AI با ساختِ شماره‌ی تازه (هر شماره ۲ سوالِ رایگان) قابلِ بالا بردن است | کم (مالی) | کد |
| ۱۴ | SQLite روی دیسکِ مشترکِ پُر؛ بکاپِ پیش از migrate خودکار است ولی بیرون از ماشین نیست | کم (داده) | زیرساخت |
| ۱۵ | دسترسیِ سرور: حسابِ مشترک، رمز، sudoِ کامل | بالا | ادمینِ سرور |
| ۱۶ | فرانت توکن را در `localStorage` نگه می‌دارد | کم | فرانت |
@@ -106,8 +106,8 @@
## ۱۲. هزینه‌ی دستیارِ AI
- **چیست:** هر کاربر روزی ۳ سؤالِ رایگان دارد (`AI_DAILY_QUOTA_FREE`)
و بعد فقط با اعتبارِ خریده‌شده؛ ۲۰ در ساعت. هر شماره‌ی تازه ۳ سؤالِ رایگان می‌گیرد و ساختنِ شماره با سقفِ پیامک
- **چیست:** هر کاربر روزی ۲ سؤالِ رایگان دارد (`AI_DAILY_QUOTA_FREE`)
و بعد فقط با اعتبارِ خریده‌شده؛ ۲۰ در ساعت. هر شماره‌ی تازه ۲ سؤالِ رایگان می‌گیرد و ساختنِ شماره با سقفِ پیامک
(بند ۲۴) محدود است. سقفِ
روزانه‌ی کلِ سامانه (`AI_GLOBAL_DAILY_MAX`، ۳۰۰۰) خرجِ کل را محدود می‌کند. طولِ سؤال حداکثر ۲۰۰۰ نویسه است.
- **خطر:** با چند IP، اعتبارِ GapGPT در چند ساعت تمام می‌شود (اعتبار یک بار در ۲۰۲۶-۰۹-۲۵

View File

@@ -700,7 +700,7 @@ if (Math.sign(y) !== Math.sign(py)) { const r = bisect(f, px, x); if (Math.abs(f
| انتخابِ مدلِ جواب: ۱۰۲ مسئله‌ی تصحیح‌شده‌ی دستی | `gpt-4.1-mini` : ۹۵–۹۶ درست · `gpt-4o-mini` : ۸۵–۹۰ |
| شکل‌ها: ۶۴ مسئله‌ی تصادفیِ کتاب، وارسیِ چشمیِ تک‌تک | ۴۰ درست، ۱۲ قابلِ قبول، ۷ غلط، ۵ بی‌شکل — حدودِ یکی از پنج هنوز ایراد دارد؛ برای همین شکل همیشه کنارِ حلِ متنی می‌آید |
| سرعت | جوابِ کامل با شکل ~۳۰ ثانیه؛ اندازه‌گیری شد که در خودِ GapGPT است («فقط بگو سلام» = ۷ ثانیه) |
| سقف‌ها | ۳ سؤالِ رایگان در روز، بعد از اعتبارِ بسته؛ ۲۰ در ساعت؛ مهمان‌ها بر اساسِ IP (تا با ساختِ حسابِ مهمانِ تازه دور زده نشود) |
| سقف‌ها | ۲ سؤالِ رایگان در روز (مشترک ۳)، بعد از اعتبارِ بسته؛ ۲۰ در ساعت؛ مهمان‌ها بر اساسِ IP (تا با ساختِ حسابِ مهمانِ تازه دور زده نشود) |
### ۹-۷ فقط ریاضی، و چه چیزی هزینه دارد
@@ -708,7 +708,7 @@ if (Math.sign(y) !== Math.sign(py)) { const r = bisect(f, px, x); if (Math.abs(f
| قاعده‌ی خرج | |
|---|---|
| ترتیب | اول ۳ سوالِ رایگانِ همان روز، بعد اعتبارِ بسته ( `Message.charge` : `free` / `credit` / `none` ) |
| ترتیب | اول سقفِ روزانه (۲ رایگان، مشترک ۳)، بعد اعتبارِ بسته ( `Message.charge` : `free` / `credit` / `none` ) |
| خرج می‌شود | هر سوالی که به مدل برسد، از جمله ردِ «غیرِ ریاضی» توسطِ مدل |
| خرج نمی‌شود | ردِ صافیِ سرور، جوابِ کش‌شده، جوابی که ساخته نشد، و تکرارِ درخواست با همان `Idempotency-Key` |
| هم‌زمانی | دو سوالِ هم‌زمان با یک اعتبار فقط یک جواب می‌گیرند؛ با دو thread واقعی آزموده می‌شود ( `ai_tutor/tests_credit_concurrency.py` ) |
@@ -881,7 +881,7 @@ fetch ← `npm ci` ← بررسیِ نوع‌ها ← build در `dist.next` ←
| `ZARINPAL_MERCHANT_ID` · `BAZAAR_*` | پرداخت بسته می‌ماند (۵۰۳). |
| `CACHE_URL` | کشِ جدا برای هر worker؛ `/api/health` هشدار می‌دهد. |
| `HEARTS_ENFORCED` · `TEST_LOGIN_PHONES` | روشن (قلب واقعی؛ `0` خاموشش می‌کند) · هیچ حسابِ آزمایشی. |
| `AI_DAILY_QUOTA_FREE` | ۳ سوالِ رایگان در روز. |
| `AI_DAILY_QUOTA_FREE` | ۲ سوالِ رایگان در روز (پیش‌فرض). |
| `DJANGO_DEBUG` | خاموش — و **هرگز روی سرور روشن نشود** (کدِ ورودِ همگانیِ `0000` را فعال می‌کند). |
مقدارهای `.env` با پیش‌فرض‌های کد ادغام می‌شوند؛ یک `.env` ِ ناقص سرویس را نمی‌خواباند. شرحِ کامل در `.env.example` و `docs/operations.md`.