import logging import stripe from django.conf import settings from rest_framework import permissions, status from rest_framework.response import Response from rest_framework.views import APIView from finance.models import Invoice, Payment from finance.serializers import ( CheckoutSessionSerializer, InvoiceSerializer, PaymentSerializer, ) from finance.services.stripe_service import ( StripeNotConfiguredError, create_checkout_session, ) from finance.services.webhooks import dispatch_stripe_event logger = logging.getLogger(__name__) class CreateCheckoutSessionView(APIView): """Create a Stripe Checkout Session and return the hosted redirect URL.""" def post(self, request): serializer = CheckoutSessionSerializer(data=request.data) serializer.is_valid(raise_exception=True) try: session = create_checkout_session( user=request.user, success_url=serializer.validated_data.get("success_url"), cancel_url=serializer.validated_data.get("cancel_url"), ) except StripeNotConfiguredError as exc: return Response( {"detail": str(exc)}, status=status.HTTP_503_SERVICE_UNAVAILABLE, ) except stripe.StripeError as exc: logger.exception("Stripe Checkout Session creation failed") return Response( {"detail": str(getattr(exc, "user_message", None) or exc)}, status=status.HTTP_502_BAD_GATEWAY, ) # Persist a draft invoice keyed by checkout session for admin visibility # before the webhook fires. Invoice.objects.update_or_create( stripe_checkout_session_id=session.id, defaults={ "user": request.user, "company": request.user.company, "provider": Invoice.Provider.STRIPE, "status": Invoice.Status.OPEN, "currency": settings.SUBSCRIPTION_PRICE_CURRENCY, "amount_due": settings.SUBSCRIPTION_PRICE_AMOUNT_CENTS, "amount_paid": 0, "stripe_customer_id": getattr(session, "customer", None) or "", "description": settings.SUBSCRIPTION_PRODUCT_NAME, }, ) return Response( { "checkout_url": session.url, "session_id": session.id, }, status=status.HTTP_201_CREATED, ) class InvoiceListView(APIView): def get(self, request): invoices = Invoice.objects.filter(user=request.user) return Response(InvoiceSerializer(invoices, many=True).data) class PaymentListView(APIView): def get(self, request): payments = Payment.objects.filter(user=request.user) return Response(PaymentSerializer(payments, many=True).data) class StripeWebhookView(APIView): """Verify Stripe signatures and upsert local invoice/payment rows.""" permission_classes = (permissions.AllowAny,) authentication_classes = () def post(self, request): payload = request.body sig_header = request.META.get("HTTP_STRIPE_SIGNATURE", "") webhook_secret = settings.STRIPE_WEBHOOK_SECRET if not webhook_secret: logger.error("STRIPE_WEBHOOK_SECRET is not configured") return Response( {"detail": "Webhook secret not configured"}, status=status.HTTP_503_SERVICE_UNAVAILABLE, ) try: event = stripe.Webhook.construct_event( payload=payload, sig_header=sig_header, secret=webhook_secret, ) except ValueError: return Response( {"detail": "Invalid payload"}, status=status.HTTP_400_BAD_REQUEST, ) except stripe.SignatureVerificationError: return Response( {"detail": "Invalid signature"}, status=status.HTTP_400_BAD_REQUEST, ) if hasattr(event, "to_dict"): event = event.to_dict() try: dispatch_stripe_event(event) except Exception: logger.exception("Error handling Stripe event %s", event.get("id")) return Response( {"detail": "Webhook handler error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) return Response({"received": True}, status=status.HTTP_200_OK)