Files
MathApp/subscription/migrations/0014_bazaar_skus_2026_10.py
Zareian 8077ab4350 Bazaar: subscriptions (10343/10344), dynamic price (discount codes on Bazaar), auto-renewal sync
- 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>
2026-10-12 17:53:11 +03:30

40 lines
1.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
شناسه‌ی کالای بازار برای اشتراک‌های سه‌ماهه و شش‌ماهه (۲۰۲۶-۱۰-۱۲).
از پیشخانِ بازار (عکسِ سینیور): «اشتراکی ۹۰ روزه» = 10343، «اشتراکی ۱۸۰ روزه» = 10344. بازار هر دو را زیرِ
com.tadrisino.math.app شناخت (پرسشِ API با توکنِ ساختگی: «purchase not found»، نه «product not found»).
10342 هم در بازار هست ولی کسی نگفته چیست؛ عمداً به هیچ پلنی وصل نشده.
پلن‌ها با مدت پیدا می‌شوند، نه با id (idها بین دیتابیس‌ها فرق دارند). اگر دقیقاً یک پلنِ فروشیِ هم‌مدت
نباشد، migration می‌شکند تا دستی دیده شود؛ SKUی که ادمین از پیش گذاشته دست نمی‌خورد.
"""
from django.db import migrations
SKUS = {90: '10343', 180: '10344'}
def forward(apps, schema_editor):
Plan = apps.get_model('subscription', 'Plan')
for days, sku in SKUS.items():
if Plan.objects.filter(bazaar_sku=sku).exists():
continue
plans = list(Plan.objects.filter(kind='subscription', on_sale=True, duration_days=days))
if len(plans) != 1:
raise RuntimeError(
f'SKUِ بازار {sku}: باید دقیقاً یک اشتراکِ فروشیِ {days} روزه باشد، {len(plans)} تا پیدا شد.')
plan = plans[0]
if plan.bazaar_sku:
continue
plan.bazaar_sku = sku
plan.save(update_fields=['bazaar_sku'])
def backward(apps, schema_editor):
Plan = apps.get_model('subscription', 'Plan')
Plan.objects.filter(bazaar_sku__in=SKUS.values()).update(bazaar_sku='')
class Migration(migrations.Migration):
dependencies = [('subscription', '0013_monthly_plans_2026_10')]
operations = [migrations.RunPython(forward, backward)]