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>
This commit is contained in:
39
subscription/migrations/0014_bazaar_skus_2026_10.py
Normal file
39
subscription/migrations/0014_bazaar_skus_2026_10.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
شناسهی کالای بازار برای اشتراکهای سهماهه و ششماهه (۲۰۲۶-۱۰-۱۲).
|
||||
|
||||
از پیشخانِ بازار (عکسِ سینیور): «اشتراکی ۹۰ روزه» = 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)]
|
||||
Reference in New Issue
Block a user