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:
@@ -21,12 +21,12 @@ from .serializers import (
|
||||
PurchaseResponseSerializer,
|
||||
SubscriptionStatusResponseSerializer,
|
||||
)
|
||||
from .services import ai_daily_quota, credit_balance, has_active_subscription, hearts_pass_until
|
||||
from .services import ai_daily_quota, has_active_subscription
|
||||
|
||||
|
||||
# پلنها تقریباً هیچوقت عوض نمیشوند و روی صفحهی اشتراک هر بار خوانده میشوند.
|
||||
# با هر save/delete روی Plan کش باطل میشود (subscription/receivers.py).
|
||||
PLANS_CACHE_KEY = 'subscription:plans:v3' # v3: بستههای سوال
|
||||
PLANS_CACHE_KEY = 'subscription:plans:v4' # v4: بی بستهی سوال (subscription.0017)
|
||||
PLANS_CACHE_TTL = 3600
|
||||
|
||||
|
||||
@@ -53,12 +53,9 @@ class PlansListView(APIView):
|
||||
'features': p.features or [],
|
||||
'badge': p.badge,
|
||||
'aiDailyQuota': p.ai_daily_quota,
|
||||
'kind': p.kind,
|
||||
'questionCredits': p.question_credits,
|
||||
'heartsDays': p.hearts_days,
|
||||
'bazaarSku': p.bazaar_sku,
|
||||
}
|
||||
for p in Plan.objects.filter(on_sale=True).order_by('kind', 'price')
|
||||
for p in Plan.objects.filter(on_sale=True).order_by('price')
|
||||
],
|
||||
'freeAiDailyQuota': settings.AI_DAILY_QUOTA_FREE,
|
||||
}
|
||||
@@ -99,7 +96,7 @@ class PurchaseView(APIView):
|
||||
v = serializer.validated_data
|
||||
user = request.user
|
||||
|
||||
# پلنی که دیگر فروخته نمیشود (اشتراکهای ماهانه/سالانه پس از آمدنِ بستهها) خریدنی نیست.
|
||||
# پلنی که دیگر فروخته نمیشود (on_sale=False، مثلِ اشتراکهای قدیمیِ ماهانه/سالانه) خریدنی نیست.
|
||||
plan = get_object_or_404(Plan, pk=v['planId'], on_sale=True)
|
||||
idempotency_key = request.headers.get('Idempotency-Key')
|
||||
|
||||
@@ -112,16 +109,6 @@ class PurchaseView(APIView):
|
||||
data = {'orderId': str(order.id), 'paymentUrl': payment_url, **provider.purchase_extras(order, plan)}
|
||||
return Response(PurchaseResponseSerializer(data).data)
|
||||
|
||||
if plan.kind == 'pack':
|
||||
# بسته به Subscription دست نمیزند: اعتبار و روزهای قلب بعد از پرداخت از روی
|
||||
# خودِ سفارش داده میشوند (payment.services → subscription.services.grant_pack).
|
||||
order, payment_url = create_order(
|
||||
user_id=user.id, amount=plan.price, currency=plan.currency,
|
||||
idempotency_key=idempotency_key, plan_id=plan.id, plan_title=plan.title,
|
||||
provider=provider.key, bazaar_product_sku=provider.sku_for(plan),
|
||||
)
|
||||
return response(order, payment_url)
|
||||
|
||||
# Three-phase flow so create_order never runs inside a DB transaction/lock.
|
||||
# (create_order itself no longer calls Zarinpal — PaymentRedirectView
|
||||
# does, on first visit — so this split is now defensive.) Phase 1 creates/updates
|
||||
@@ -238,7 +225,5 @@ class SubscriptionStatusView(APIView):
|
||||
'aiDailyQuota': ai_daily_quota(request.user.id),
|
||||
'planId': str(subscription.plan_id) if subscription and subscription.plan_id else None,
|
||||
'expiresAt': subscription.expires_at if subscription else None,
|
||||
'questionCredits': credit_balance(request.user.id),
|
||||
'unlimitedHeartsUntil': hearts_pass_until(request.user.id),
|
||||
}
|
||||
return Response(SubscriptionStatusResponseSerializer(data).data)
|
||||
|
||||
Reference in New Issue
Block a user