73 lines
3.1 KiB
Python
73 lines
3.1 KiB
Python
"""Validate-promo endpoint — gives the frontend a preview of price + discount."""
|
|
|
|
from django.shortcuts import get_object_or_404
|
|
from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer
|
|
from rest_framework import serializers
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from rest_framework.response import Response
|
|
from rest_framework.views import APIView
|
|
|
|
from apps.courses.models import Course, CourseStatus
|
|
from apps.enrollments.api.permissions import IsStudent
|
|
from apps.payments.services.orders import OrderError, _resolve_discount
|
|
|
|
|
|
class PromoPreviewRequestSerializer(serializers.Serializer):
|
|
promo_code = serializers.CharField(required=False, allow_blank=True)
|
|
referral_code = serializers.CharField(required=False, allow_blank=True)
|
|
|
|
|
|
class PromoPreviewResponseSerializer(serializers.Serializer):
|
|
course_slug = serializers.CharField()
|
|
base_amount_toman = serializers.IntegerField()
|
|
discount_amount_toman = serializers.IntegerField()
|
|
final_amount_toman = serializers.IntegerField()
|
|
promo_code_applied = serializers.CharField(allow_null=True)
|
|
referrer_code_applied = serializers.CharField(allow_null=True)
|
|
|
|
|
|
class CheckoutPreviewView(APIView):
|
|
"""`POST /api/courses/<slug>/checkout/preview/` — dry-run price computation."""
|
|
|
|
permission_classes = [IsAuthenticated, IsStudent]
|
|
|
|
@extend_schema(
|
|
tags=["payments"],
|
|
summary="Preview checkout price (apply promo / referral code without committing)",
|
|
request=PromoPreviewRequestSerializer,
|
|
responses={
|
|
200: PromoPreviewResponseSerializer,
|
|
400: OpenApiResponse(description="Code invalid / not applicable / both supplied / course is free."),
|
|
404: OpenApiResponse(description="Course not found or not published."),
|
|
},
|
|
)
|
|
def post(self, request, slug):
|
|
course = get_object_or_404(Course, slug=slug, status=CourseStatus.PUBLISHED)
|
|
if course.is_free:
|
|
return Response(
|
|
{"detail": "Course is free; no checkout needed."},
|
|
status=400,
|
|
)
|
|
ser = PromoPreviewRequestSerializer(data=request.data)
|
|
ser.is_valid(raise_exception=True)
|
|
base = int(course.price_toman or 0)
|
|
try:
|
|
discount, pc_obj, referrer, _credit = _resolve_discount(
|
|
course, base,
|
|
promo_code=ser.validated_data.get("promo_code") or None,
|
|
referral_code=ser.validated_data.get("referral_code") or None,
|
|
buyer=request.user,
|
|
)
|
|
except OrderError as e:
|
|
return Response({"detail": str(e)}, status=400)
|
|
final = max(0, base - discount)
|
|
body = {
|
|
"course_slug": course.slug,
|
|
"base_amount_toman": base,
|
|
"discount_amount_toman": discount,
|
|
"final_amount_toman": final,
|
|
"promo_code_applied": pc_obj.code if pc_obj else None,
|
|
"referrer_code_applied": referrer.referral_code if referrer else None,
|
|
}
|
|
return Response(PromoPreviewResponseSerializer(body).data)
|