96 lines
4.3 KiB
Python
96 lines
4.3 KiB
Python
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)
|