- 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>
50 lines
3.0 KiB
Python
50 lines
3.0 KiB
Python
from rest_framework import serializers
|
||
|
||
|
||
class PlanSerializer(serializers.Serializer):
|
||
id = serializers.CharField()
|
||
title = serializers.CharField()
|
||
price = serializers.IntegerField()
|
||
currency = serializers.CharField(required=False)
|
||
popular = serializers.BooleanField()
|
||
discountPercent = serializers.IntegerField(required=False)
|
||
durationDays = serializers.IntegerField(required=False)
|
||
features = serializers.ListField(child=serializers.CharField())
|
||
badge = serializers.CharField(allow_blank=True)
|
||
aiDailyQuota = serializers.IntegerField(help_text='سوال در روز از دستیار برای مشترکِ این پلن.')
|
||
bazaarSku = serializers.CharField(
|
||
allow_blank=True, help_text='SKUِ همین اشتراک در کافهبازار (محصولِ «اشتراکی»)؛ خالی یعنی در بازار خریدنی نیست.')
|
||
|
||
|
||
class PlansListResponseSerializer(serializers.Serializer):
|
||
plans = PlanSerializer(many=True)
|
||
# تا کارتِ پلن بتواند بگوید «۵ سوال در روز (رایگان: ۳)» بیآنکه عدد در فرانت هاردکد شود.
|
||
freeAiDailyQuota = serializers.IntegerField()
|
||
|
||
|
||
class PurchaseRequestSerializer(serializers.Serializer):
|
||
# هدر Idempotency-Key هم روی همین درخواست لازمه (بخش ۴.۳ سند API)؛
|
||
# از request.headers خونده میشه، نه از بدنه.
|
||
planId = serializers.CharField()
|
||
# تا ۲۰۲۶-۱۰-۰۳ هر مقداری پذیرفته و نادیده گرفته میشد، پس «bazaar» بیصدا به زرینپال میرفت.
|
||
paymentMethod = serializers.ChoiceField(choices=['zarinpal', 'bazaar'], required=False, default='zarinpal',
|
||
help_text='payment/providers.py: هر مقدار یک کلاسِ PaymentProvider.')
|
||
|
||
|
||
class PurchaseResponseSerializer(serializers.Serializer):
|
||
orderId = serializers.CharField()
|
||
paymentUrl = serializers.CharField(
|
||
allow_null=True, help_text='زرینپال: آدرسی که کاربر به آن برود. بازار: null.')
|
||
bazaarSku = serializers.CharField(
|
||
required=False, help_text='فقط بازار: SKUی که اپِ اندروید با Poolakey میخرد؛ '
|
||
'developerPayload همان orderId باشد.')
|
||
|
||
|
||
class SubscriptionStatusResponseSerializer(serializers.Serializer):
|
||
active = serializers.BooleanField(help_text='اشتراکِ زمانی فعال (یک/سه/ششماهه).')
|
||
planTitle = serializers.CharField(allow_null=True, help_text='نامِ پلنِ اشتراکِ فعال؛ null بیاشتراک.')
|
||
planDurationDays = serializers.IntegerField(allow_null=True, help_text='طولِ دورهی پلنِ فعال به روز.')
|
||
aiDailyQuota = serializers.IntegerField(help_text='سقفِ سوالِ روزانهی همین کاربر از دستیار.')
|
||
planId = serializers.CharField(allow_null=True)
|
||
expiresAt = serializers.DateTimeField(allow_null=True)
|