@ -14,6 +14,7 @@ from utils.exceptions import AppAPIException
from apps.account.models import User
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from apps.transaction.models import TransactionParticipant
from apps.transaction.doc import (
doc_upload_transaction_receipts ,
doc_list_transaction_receipts ,
@ -31,31 +32,111 @@ class TransactionParticipantCreateView(generics.CreateAPIView):
permission_classes = [ IsAuthenticated ]
@swagger_auto_schema (
operation_description = doc_create_transaction ( )
operation_description = doc_create_transaction ( ) ,
responses = {
status . HTTP_201_CREATED : openapi . Response (
description = " Transaction participant created successfully " ,
examples = {
" application/json " : {
" message " : " Transaction Participant created successfully. " ,
" transaction_id " : 374 ,
" payment_method " : " Payment_Gateway " ,
" payment_link " : " https://russia-payment.com/pay/374 " ,
" participant_infos " : [
{
" fullname " : " string " ,
" email " : " admin@gmail.com " ,
" phone_number " : " string " ,
" gender " : " male " ,
" birthdate " : " 2025-12-28 "
}
]
}
} ,
schema = openapi . Schema (
type = openapi . TYPE_OBJECT ,
properties = {
' message ' : openapi . Schema ( type = openapi . TYPE_STRING , description = " Success message " ) ,
' transaction_id ' : openapi . Schema ( type = openapi . TYPE_INTEGER , description = " Unique transaction identifier " ) ,
' payment_method ' : openapi . Schema (
type = openapi . TYPE_STRING ,
enum = [ ' Payment_Gateway ' , ' receipt ' ] ,
description = " Payment method: ' Payment_Gateway ' for online payment, ' receipt ' for WhatsApp upload "
) ,
' payment_link ' : openapi . Schema (
type = openapi . TYPE_STRING ,
nullable = True ,
description = " Payment gateway URL (only present when payment_method is ' Payment_Gateway ' ) "
) ,
' participant_infos ' : openapi . Schema (
type = openapi . TYPE_ARRAY ,
items = openapi . Schema (
type = openapi . TYPE_OBJECT ,
properties = {
' fullname ' : openapi . Schema ( type = openapi . TYPE_STRING ) ,
' email ' : openapi . Schema ( type = openapi . TYPE_STRING ) ,
' phone_number ' : openapi . Schema ( type = openapi . TYPE_STRING ) ,
' gender ' : openapi . Schema ( type = openapi . TYPE_STRING , enum = [ ' male ' , ' female ' ] ) ,
' birthdate ' : openapi . Schema ( type = openapi . TYPE_STRING , format = ' date ' ) ,
}
) ,
description = " List of participant information "
) ,
} ,
required = [ ' message ' , ' transaction_id ' , ' participant_infos ' ]
)
) ,
status . HTTP_400_BAD_REQUEST : openapi . Response (
description = " Invalid data provided " ,
schema = openapi . Schema (
type = openapi . TYPE_OBJECT ,
properties = {
' detail ' : openapi . Schema ( type = openapi . TYPE_STRING ) ,
}
)
) ,
status . HTTP_404_NOT_FOUND : openapi . Response (
description = " Course not found " ,
schema = openapi . Schema (
type = openapi . TYPE_OBJECT ,
properties = {
' message ' : openapi . Schema ( type = openapi . TYPE_STRING ) ,
}
)
) ,
} ,
tags = [ ' transaction ' ]
)
def create ( self , request , * args , * * kwargs ) :
def post ( self , request , * args , * * kwargs ) :
# Simply call the create method
return self . create ( request , * args , * * kwargs )
def create ( self , request , * args , * * kwargs ) :
user = request . user
course_slug = self . kwargs . get ( ' slug ' ) # Get the slug from the URL
course_slug = self . kwargs . get ( ' slug ' )
# 1. Retrieve Course
try :
course = Course . objects . get ( slug = course_slug ) # Retrieve the Course object
course = Course . objects . get ( slug = course_slug )
except Course . DoesNotExist :
raise AppAPIException ( { ' message ' : " Course not found " } ) # Handle course not found
raise AppAPIException ( { ' message ' : " Course not found " } )
participant_infos = request . data . get ( ' participant_infos ' , [ ] )
# 2. Validate and Initialize
serializer = self . get_serializer ( data = request . data )
serializer . is_valid ( raise_exception = True )
statis = TransactionParticipant . TransactionStatus . PENDING
# 3. Handle Free/Self-Enrollment Logic
if len ( participant_infos ) == 1 and ( course . final_price == 0 or course . is_free ) :
participant = participant_infos [ 0 ]
if participant . get ( ' email ' ) != user . email :
raise AppAPIException ( { ' message ' : " The email must be for the requesting user " } )
# به جای تغییر user_type، فقط نقش student را اضافه میکنیم
if not user . has_role ( ' student ' ) :
user . add_role ( ' student ' )
# فیلتر کردن برای چک کردن وجود participant
existing_participant = Participant . objects . filter ( student = user , course = course ) . first ( )
if existing_participant :
participant = existing_participant
@ -63,37 +144,57 @@ class TransactionParticipantCreateView(generics.CreateAPIView):
participant = Participant . objects . create ( student = user , course = course )
statis = TransactionParticipant . TransactionStatus . SUCCESS
transaction_participant = serializer . save ( user = user , course = course , price = course . final_price , status = statis )
print ( f ' ---> {type(transaction_participant)}/ {transaction_participant} ' )
# 4. Save Transaction
transaction_participant = serializer . save (
user = user ,
course = course ,
price = course . final_price ,
status = statis
)
print ( f ' ---> {type(transaction_participant)}/ {transaction_participant} ' )
# =======================================================
# NEW LOGIC: GEOLOCATION CHECK
# =======================================================
# NEW LOGIC: HYBRID GEOLOCATION CHECK (Cloudflare + Local DB)
# =======================================================
payment_link = None
payment_link = None # Default link
# Only check IP if transaction is PENDING (needs payment)
payment_method = TransactionParticipant . PaymentMethods . FREE
if statis == TransactionParticipant . TransactionStatus . PENDING :
client_ip = get_client_ip ( request )
# "188.93.104.1"
#get_client_ip(request)
country_code = get_country_code ( client_ip )
if country_code == ' RU ' :
# Generate Russia-specific link (e.g., YooMoney, Mir card, etc.)
# Step A: Fast Path - Check Cloudflare Header
# Cloudflare sends the 2-letter code (e.g., 'RU', 'US') in this header
country_code = request . META . get ( ' HTTP_CF_IPCOUNTRY ' )
# Step B: Slow Path - Fallback to Local DB
# If header is missing (e.g., Localhost, direct connection, or CF failed)
if not country_code :
try :
client_ip = get_client_ip ( request )
# "188.93.104.1"
# get_client_ip(request)
# Assuming your helper handles errors gracefully and returns None
country_code = get_country_code ( client_ip )
except Exception as e :
print ( f " GeoIP Lookup Failed: {e} " )
country_code = None
payment_method = TransactionParticipant . PaymentMethods . RECEIPT
# Step C: Apply Logic
if country_code != ' RU ' :
payment_method = TransactionParticipant . PaymentMethods . PAYMENT_GATEWAY
payment_link = f " https://russia-payment.com/pay/{transaction_participant.id} "
# Uncomment if you want a global fallback link
# else:
# # Standard Global Link (e.g., PayPal, Stripe)
# payment_link = f"https://global-payment.com/pay/{transaction_participant.id}"
# =======================================================
# =======================================================
return Response ( {
' message ' : ' Transaction Participant created successfully. ' ,
' transaction_id ' : transaction_participant . id ,
' payment_link ' : payment_link , # <--- Return the dynamic link
' payment_method ' : payment_method ,
' payment_link ' : payment_link ,
' participant_infos ' : serializer . data [ ' participant_infos ' ]
} , status = status . HTTP_201_CREATED )