- 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>
93 lines
5.0 KiB
Python
93 lines
5.0 KiB
Python
from rest_framework import serializers
|
||
|
||
|
||
class CreateOrderRequestSerializer(serializers.Serializer):
|
||
# این فراخوانی سرور-به-سرور از اپ Subscriptionه، نه از کلاینت؛ به همین
|
||
# دلیل userId اینجا مستقیماً در بدنه ارسال میشه (نه از توکن استخراج).
|
||
userId = serializers.CharField()
|
||
amount = serializers.IntegerField()
|
||
currency = serializers.CharField(required=False, default='IRT')
|
||
subscriptionId = serializers.CharField(required=False, allow_null=True, allow_blank=True)
|
||
# provider='bazaar' وقتی کاربر از همون ابتدا پرداخت درونبرنامهای بازار
|
||
# رو انتخاب کرده؛ در این حالت amount باید دقیقاً برابر قیمتِ محصولِ
|
||
# تعریفشده در پنل بازار برای همون bazaarProductSku باشه (بازار خودش
|
||
# قیمت رو کنترل میکنه، نه ما).
|
||
provider = serializers.ChoiceField(choices=['gateway', 'bazaar'], required=False, default='gateway')
|
||
bazaarProductSku = serializers.CharField(required=False, allow_blank=True)
|
||
# بستهی سوال (subscription.Plan با kind='pack'): بدونِ این، سفارشِ بسته بیplan_id ساخته میشد و
|
||
# پرداختش هیچ اعتباری نمیداد (بازبینیِ ۲۰۲۶-۱۰-۰۲).
|
||
planId = serializers.CharField(required=False, allow_null=True, allow_blank=True)
|
||
|
||
|
||
class BazaarVerifyRequestSerializer(serializers.Serializer):
|
||
orderId = serializers.CharField()
|
||
productSku = serializers.CharField(required=False, allow_blank=True)
|
||
purchaseToken = serializers.CharField()
|
||
|
||
|
||
class BazaarStartResponseSerializer(serializers.Serializer):
|
||
orderId = serializers.CharField()
|
||
sku = serializers.CharField(help_text='productIdِ PurchaseRequestِ Poolakey.')
|
||
productType = serializers.ChoiceField(
|
||
choices=['inapp', 'subscription'],
|
||
help_text='inapp: purchaseProduct و بعد از تأییدِ سرور consume. subscription: subscribeProduct، بیconsume.')
|
||
amount = serializers.IntegerField(help_text='مبلغِ سفارش (تومان)، پس از کدِ تخفیف.')
|
||
dynamicPriceToken = serializers.CharField(
|
||
allow_null=True, help_text='JWTِ «تخفیف پویا» برای PurchaseRequest.dynamicPriceToken؛ null یعنی قیمتِ پیشخان.')
|
||
|
||
|
||
class BazaarVerifyResponseSerializer(serializers.Serializer):
|
||
orderId = serializers.CharField()
|
||
status = serializers.CharField()
|
||
valid = serializers.BooleanField()
|
||
|
||
|
||
class CreateOrderResponseSerializer(serializers.Serializer):
|
||
orderId = serializers.CharField()
|
||
status = serializers.CharField()
|
||
# provider='bazaar' یعنی paymentUrl نداریم (کلاینت با SDK بازار خودش
|
||
# خرید میکنه، نه با ریدایرکت به یک URL).
|
||
paymentUrl = serializers.CharField(allow_null=True)
|
||
provider = serializers.CharField()
|
||
|
||
|
||
class ZarinpalCallbackResponseSerializer(serializers.Serializer):
|
||
"""فقط وقتی settings.ZARINPAL_REDIRECT_URL خالی باشه برگردونده میشه؛
|
||
وگرنه کاربر به همون URL ریدایرکت میشه، نه به این JSON."""
|
||
orderId = serializers.CharField()
|
||
status = serializers.CharField()
|
||
|
||
|
||
class TransactionSerializer(serializers.Serializer):
|
||
id = serializers.CharField()
|
||
gateway = serializers.CharField()
|
||
status = serializers.CharField()
|
||
paidAt = serializers.DateTimeField(allow_null=True)
|
||
|
||
|
||
class OrderDetailResponseSerializer(serializers.Serializer):
|
||
orderId = serializers.CharField()
|
||
status = serializers.CharField()
|
||
amount = serializers.IntegerField()
|
||
transactions = TransactionSerializer(many=True)
|
||
|
||
|
||
class InvoiceSerializer(serializers.Serializer):
|
||
orderId = serializers.CharField()
|
||
invoiceNumber = serializers.CharField(help_text='شمارهی فاکتور: «TD-» و شناسهی سفارش، شش رقمی.')
|
||
status = serializers.ChoiceField(choices=['pending', 'paid', 'failed', 'canceled', 'refunded'])
|
||
provider = serializers.CharField(help_text='gateway (زرینپال) یا bazaar')
|
||
planTitle = serializers.CharField(allow_blank=True)
|
||
listPrice = serializers.IntegerField(allow_null=True, help_text='قیمتِ پایه پیش از تخفیف؛ null برای سفارشهای پیش از ۱۴۰۵-۰۷-۰۹.')
|
||
discountAmount = serializers.IntegerField(allow_null=True)
|
||
discountCode = serializers.CharField(allow_blank=True)
|
||
amount = serializers.IntegerField(help_text='مبلغِ پرداختی (پس از تخفیف).')
|
||
currency = serializers.CharField()
|
||
createdAt = serializers.DateTimeField()
|
||
paidAt = serializers.DateTimeField(allow_null=True)
|
||
refId = serializers.CharField(allow_blank=True, help_text='کدِ پیگیریِ پرداخت (شمارهی مرجعِ زرینپال/بازار).')
|
||
|
||
|
||
class InvoiceListResponseSerializer(serializers.Serializer):
|
||
invoices = InvoiceSerializer(many=True)
|