Harden auth, AI tutor, payments, and subscriptions

This commit is contained in:
2026-08-22 04:42:36 +03:30
parent f945f960c9
commit cb18c93a3e
41 changed files with 2202 additions and 595 deletions

View File

@@ -8,7 +8,6 @@ class AffiliateMeResponseSerializer(serializers.Serializer):
class TrackRequestSerializer(serializers.Serializer):
referralCode = serializers.CharField()
referredUserId = serializers.CharField()
class TrackResponseSerializer(serializers.Serializer):

View File

@@ -1,3 +1 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -31,12 +31,24 @@ class TrackConversionView(APIView):
"""
POST /api/affiliate/track — ثبت کلیک/ارجاع
طبق سند، این اپ فقط با referralCode به AffiliateAccount وصل می‌شه
(نه توکن کاربر)، چون referredUserId خودش کاربر تازه‌ارجاع‌شده‌ست که
لزوماً صاحب حساب افیلیت نیست؛ پس اینجا permission پیش‌فرض (AllowAny)
نگه داشته شده، دقیقاً مثل ValidateDiscountView در اپ discount.
طبق سند، این اپ فقط با 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)']
)
@@ -46,16 +58,7 @@ class TrackConversionView(APIView):
v = serializer.validated_data
account = get_object_or_404(AffiliateAccount, referral_code=v['referralCode'])
referred_user_id = int(v['referredUserId'])
# ⚠️ اصلاح امنیتی: قبلاً referredUserId بدون هیچ اعتبارسنجی‌ای
# مستقیم مصرف می‌شد؛ یعنی می‌شد برای یک کاربر ناموجود Conversion
# ساخت یا با فرستادن userId خودِ صاحب حساب affiliate، به خودش
# ارجاع داد (self-referral). حالا هر دو حالت رد می‌شن.
from accounts.models import User as AccountUser
if not AccountUser.objects.filter(pk=referred_user_id).exists():
raise ValidationError({'referredUserId': 'چنین کاربری وجود ندارد.'})
referred_user_id = request.user.id
if referred_user_id == account.user_id:
raise ValidationError({'referredUserId': 'امکان ثبت ارجاع برای خودِ صاحب کد وجود ندارد.'})