Question packs removed at the root; all subscriptions 4 assistant questions a day; Bazaar is subscription-only

- 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>
This commit is contained in:
2026-10-12 20:33:38 +03:30
parent 9c3adc3e60
commit 947f946909
39 changed files with 398 additions and 1174 deletions

View File

@@ -694,12 +694,9 @@ SPECTACULAR_SETTINGS = {
'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAdminUser'],
'SERVE_AUTHENTICATION': ['rest_framework.authentication.SessionAuthentication'],
'COMPONENT_SPLIT_REQUEST': True,
# دو فیلدِ «kind» با دو مجموعه‌ی مقدار: نوعِ درس و نوعِ پلن (بسته/اشتراک، از ۲۰۲۶-۱۰-۰۲).
# بدونِ نامِ صریح، drf-spectacular یکی را «KindEd8Enum» می‌نامید و هشدار می‌داد.
# نامِ صریحِ enumها؛ بدونِ آن drf-spectacular نامی مثلِ «KindEd8Enum» می‌سازد و هشدار می‌دهد.
'ENUM_NAME_OVERRIDES': {
'LessonKindEnum': [('quiz', 'quiz'), ('video', 'video')],
'PlanKindEnum': [('pack', 'pack'), ('subscription', 'subscription')],
'BazaarProductTypeEnum': [('inapp', 'inapp'), ('subscription', 'subscription')],
},
'SWAGGER_UI_SETTINGS': {
'deepLinking': True,
@@ -862,7 +859,7 @@ NODE_PROGRESS_ABANDON_DAYS = 14
# قلبِ صفر جلوی جواب دادن را در سرور بگیرد؟ (content.views.QuestionAnswerView)
# از ۲۰۲۶-۱۰-۰۲ روشن است (خواستِ صاحبِ پروژه): فرانت ۴۰۳ِ `no_hearts` را با صفحه‌ی «قلب‌هایت
# تمام شد» و زمانِ پر شدن نشان می‌دهد، و قلبِ نامحدودِ هدیه‌ی بسته‌ها بدونِ این اثری نداشت.
# تمام شد» و زمانِ پر شدن نشان می‌دهد؛ قلبِ نامحدود فقط با اشتراک است.
# HEARTS_ENFORCED=0 در .env آن را خاموش می‌کند.
HEARTS_ENFORCED = os.environ.get('HEARTS_ENFORCED', '1') == '1'
@@ -959,10 +956,10 @@ OPENAI_BASE_URL = os.environ.get('OPENAI_BASE_URL', 'https://api.gapgpt.app/v1')
# docs/architecture.md. پیش‌فرض همین‌جاست تا دیپلوی به ویرایشِ دستیِ .env نیاز نداشته باشد.
OPENAI_CHAT_MODEL = os.environ.get('OPENAI_CHAT_MODEL', 'gpt-4.1-mini')
# سقفِ سوالِ روزانه از دستیار برای کاربرِ بدونِ اشتراک (مشترکان: Plan.ai_daily_quota).
# ۲ از ۲۰۲۶-۱۰-۱۰ (تصمیمِ مالک): دستیار جدا از اشتراک فروخته می‌شود (بسته‌ی سوال)، پس سهمِ رایگان فقط
# برای آشنایی است. پیش از آن ۳، و تا ۲۰۲۶-۱۰-۰۱ برای همه ۱۵ بود.
# ۲ (تصمیمِ مالک): سهمِ رایگان فقط برای آشنایی است و هزینه‌ی خالص دارد (۲ × ~۷۰۰ تومان برای هر کاربرِ فعال در
# روز؛ docs/ai-cost-and-guardrails.md «اشتراک‌ها»). سوالِ بیشتر فقط با اشتراک.
AI_DAILY_QUOTA_FREE = int(os.environ.get('AI_DAILY_QUOTA_FREE', '2'))
# سقفِ روزانه‌ی *کلِ سامانه* برای سوال‌هایی که به مدل می‌رسند (رایگان یا با اعتبار)، همان الگوی
# سقفِ روزانه‌ی *کلِ سامانه* برای سوال‌هایی که به مدل می‌رسند، همان الگوی
# OTP_SMS_GLOBAL_DAILY_MAX. سقف‌های بالا برای هر کاربر و هر IP‌اند؛ کسی با چند IP و مهمان‌های
# تازه می‌توانست خرجِ GapGPT را بی‌سقف بالا ببرد. ۳۰۰۰ با ~۰٫۲ سنت برای هر سوال یعنی حدودِ ۶
# دلار در روز؛ رسیدن به آن ۵۰۳ و لاگِ ERROR می‌دهد. با رشدِ کاربرِ واقعی از .env بالا ببرید.