from django.shortcuts import get_object_or_404 from drf_spectacular.utils import extend_schema from rest_framework.exceptions import ValidationError from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from .models import AffiliateAccount, Conversion from .serializers import ( AffiliateMeResponseSerializer, ConversionListResponseSerializer, TrackRequestSerializer, TrackResponseSerializer, ) class AffiliateMeView(APIView): """GET /api/affiliate/me — حساب و کد ارجاع کاربر""" permission_classes = [IsAuthenticated] @extend_schema(responses=AffiliateMeResponseSerializer, tags=['Affiliate (affiliate)']) def get(self, request): account, _ = AffiliateAccount.objects.get_or_create(user_id=request.user.id) data = {'referralCode': account.referral_code, 'commissionRate': account.commission_rate} return Response(AffiliateMeResponseSerializer(data).data) class TrackConversionView(APIView): """ POST /api/affiliate/track — ثبت کلیک/ارجاع طبق سند، این اپ فقط با referralCode به AffiliateAccount وصل می‌شه. referredUserId دیگه ورودیِ کلاینت نیست: چون این درخواست باید توسطِ خودِ کاربرِ تازه‌ارجاع‌شده (بعد از لاگین/ثبت‌نام) زده بشه، هویتش رو از توکن احرازشده (request.user) می‌گیریم، نه از بدنه‌ی درخواست. # ⚠️ اصلاح امنیتی: قبلاً این endpoint با AllowAny و یک # referredUserId دلخواه از سمت کلاینت کار می‌کرد؛ یعنی هر کلاینتِ # ناشناس می‌تونست برای هر کاربرِ واقعیِ دیگه‌ای (بدون اطلاع یا # مشارکت خودش) به‌جای او Conversion بسازه، یا حتی برای فرار از # قید unique روی referred_user_id، از قبل تمام userId های موجود # رو برای یک کد افیلیتِ جعلی «اشغال» کنه و مسیر ارجاع واقعی رو # برای همیشه مسدود کنه. با گرفتن هویت از request.user (نیازمند # احراز هویت)، دیگه امکان جعل/انتساب ارجاع به کاربر دیگری وجود # نداره؛ چک self-referral هم حفظ شده. """ permission_classes = [IsAuthenticated] @extend_schema( request=TrackRequestSerializer, responses=TrackResponseSerializer, tags=['Affiliate (affiliate)'] ) def post(self, request): serializer = TrackRequestSerializer(data=request.data) serializer.is_valid(raise_exception=True) v = serializer.validated_data account = get_object_or_404(AffiliateAccount, referral_code=v['referralCode']) referred_user_id = request.user.id if referred_user_id == account.user_id: raise ValidationError({'referredUserId': 'امکان ثبت ارجاع برای خودِ صاحب کد وجود ندارد.'}) conversion, _ = Conversion.objects.get_or_create( referred_user_id=referred_user_id, defaults={'affiliate_account': account}, ) data = {'success': True, 'conversionId': str(conversion.pk), 'status': conversion.status} return Response(TrackResponseSerializer(data).data) class ConversionsListView(APIView): """GET /api/affiliate/conversions — لیست تبدیل‌ها و کمیسیون""" permission_classes = [IsAuthenticated] @extend_schema(responses=ConversionListResponseSerializer, tags=['Affiliate (affiliate)']) def get(self, request): account = AffiliateAccount.objects.filter(user_id=request.user.id).first() conversions = account.conversions.all() if account else Conversion.objects.none() items = [ { 'id': str(c.pk), 'referredUserId': str(c.referred_user_id), 'status': c.status, 'commissionAmount': c.commission_amount, } for c in conversions ] data = {'conversions': items} return Response(ConversionListResponseSerializer(data).data)