Files
MathApp/affiliate/views.py
Zareian 663252a260 Remove the guest system entirely: no is_guest/device_id, no guest throttles, phone login is the only way in
accounts.0008 drops the columns and refuses to run while guest rows exist.
Docs describe the current state; tests that only proved guest behaviour are gone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-05 18:20:10 +03:30

101 lines
4.4 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from django.shortcuts import get_object_or_404
from drf_spectacular.utils import extend_schema
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.throttling import ScopedRateThrottle
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)
paid = account.conversions.filter(status='paid')
data = {
'referralCode': account.referral_code,
'commissionRate': account.commission_rate,
'referredCount': account.conversions.count(),
'paidCount': paid.count(),
'totalCommission': sum(paid.values_list('commission_amount', flat=True)),
}
return Response(AffiliateMeResponseSerializer(data).data)
class TrackConversionView(APIView):
"""
POST /api/affiliate/track — ثبت کلیک/ارجاع
طبق سند، این اپ فقط با referralCode به AffiliateAccount وصل می‌شه.
referredUserId دیگه ورودیِ کلاینت نیست: چون این درخواست باید توسطِ
خودِ کاربرِ تازه‌ارجاع‌شده (بعد از لاگین/ثبت‌نام) زده بشه، هویتش رو
از توکن احرازشده (request.user) می‌گیریم، نه از بدنه‌ی درخواست.
# هویت از request.user گرفته می‌شه (نه بدنه‌ی درخواست) چون یک
# referredUserIdِ کلاینت‌دار یعنی هر کلاینتِ ناشناس می‌تونست برای هر
# کاربرِ دیگه‌ای Conversion بسازه یا با اشغالِ همه‌ی userId ها (قیدِ
# unique رو دورزدن) مسیرِ ارجاعِ واقعی رو مسدود کنه.
"""
permission_classes = [IsAuthenticated]
# referralCode نامعتبر ۴۰۴ می‌گیره (یک oracle قابل‌probe)، پس throttle
# مجزا probe رو کند می‌کنه.
throttle_scope = 'affiliate_track'
throttle_classes = [ScopedRateThrottle]
@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)