from django.db import transaction from django.shortcuts import get_object_or_404 from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiParameter, extend_schema from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from payment.services import create_order from .models import Plan, Subscription from .serializers import ( PlansListResponseSerializer, PurchaseRequestSerializer, PurchaseResponseSerializer, SubscriptionStatusResponseSerializer, ) class PlansListView(APIView): """GET /api/subscription/plans — لیست پلن‌ها""" @extend_schema(responses=PlansListResponseSerializer, tags=['Subscription (subscription)']) def get(self, request): plans = Plan.objects.all() data = { 'plans': [ { 'id': str(p.id), 'title': p.title, 'price': p.price, 'currency': p.currency, 'popular': p.popular, 'discountPercent': p.discount_percent, 'durationDays': p.duration_days, } for p in plans ] } return Response(PlansListResponseSerializer(data).data) class PurchaseView(APIView): """ POST /api/subscription/purchase — شروع خرید (با هدر Idempotency-Key) یک Subscription در وضعیت pending می‌سازه/به‌روز می‌کنه، بعد مستقیماً تابع سرویس اپ Payment رو صدا می‌زنه تا Order بسازه (همون «فراخوانی سرور-به-سرور» سند API که در این مونولیت به شکل import مستقیم پیاده‌سازی شده، نه یک HTTP call جدا به خودمون). """ permission_classes = [IsAuthenticated] # ⚠️ افزوده شده: مستندسازی هدر Idempotency-Key که قبلاً در Swagger UI # قابل تنظیم/تست نبود. @extend_schema( request=PurchaseRequestSerializer, responses=PurchaseResponseSerializer, parameters=[ OpenApiParameter( 'Idempotency-Key', OpenApiTypes.STR, OpenApiParameter.HEADER, required=False, description=( 'کلید یکتای idempotency برای جلوگیری از ساخت سفارش تکراری هنگام retry. ' 'در صورت عدم ارسال، یک کلید تصادفی ساخته می‌شود.' ), ), ], tags=['Subscription (subscription)'], ) def post(self, request): serializer = PurchaseRequestSerializer(data=request.data) serializer.is_valid(raise_exception=True) v = serializer.validated_data user = request.user plan = get_object_or_404(Plan, pk=v['planId']) # ⚠️ اصلاح: قبلاً اگه create_order (مثلاً به‌خاطر IdempotencyKeyConflict) # خطا می‌داد، Subscription که یک خط بالاتر update_or_create شده بود # نیمه‌کاره (status='pending' ولی بدون order_id معتبر) در دیتابیس # باقی می‌موند. حالا کل عملیات در یک تراکنش اتمیک انجام می‌شه. with transaction.atomic(): subscription, _created = Subscription.objects.update_or_create( user_id=user.id, defaults={'plan': plan, 'status': 'pending'}, ) order, payment_url = create_order( user_id=user.id, amount=plan.price, currency=plan.currency, subscription_id=subscription.id, idempotency_key=request.headers.get('Idempotency-Key'), ) subscription.order_id = order.id subscription.save(update_fields=['order_id']) data = {'orderId': str(order.id), 'paymentUrl': payment_url} return Response(PurchaseResponseSerializer(data).data) class SubscriptionStatusView(APIView): """GET /api/subscription/status — وضعیت فعلی اشتراک""" permission_classes = [IsAuthenticated] @extend_schema(responses=SubscriptionStatusResponseSerializer, tags=['Subscription (subscription)']) def get(self, request): subscription = Subscription.objects.filter(user_id=request.user.id).first() data = { 'active': bool(subscription and subscription.status == 'active'), 'planId': str(subscription.plan_id) if subscription and subscription.plan_id else None, 'expiresAt': subscription.expires_at if subscription else None, } return Response(SubscriptionStatusResponseSerializer(data).data)