Ilo/apps/payments/api/preview.py
2026-05-02 20:01:30 +03:30

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)