from django.shortcuts import render # Create your views here. from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated import gopay from gopay.enums import TokenScope, Language import os from drf_spectacular.utils import extend_schema, OpenApiResponse, OpenApiExample, OpenApiParameter from .serializers import ( GoPayCreatePaymentRequestSerializer, GoPayPaymentCreatedResponseSerializer, GoPayStatusResponseSerializer, GoPayRefundRequestSerializer, GoPayCaptureRequestSerializer, GoPayCreateRecurrenceRequestSerializer, ) class GoPayClientMixin: """Shared helpers for configuring GoPay client and formatting responses.""" def get_gopay_client(self): gateway_url = os.getenv("GOPAY_GATEWAY_URL", "https://gw.sandbox.gopay.com/api") return gopay.payments({ "goid": os.getenv("GOPAY_GOID"), "client_id": os.getenv("GOPAY_CLIENT_ID"), "client_secret": os.getenv("GOPAY_CLIENT_SECRET"), "gateway_url": gateway_url, "scope": TokenScope.ALL, "language": Language.CZECH, }) def _to_response(self, sdk_response): # The GoPay SDK returns a response object with has_succeed(), json, errors, status_code try: if hasattr(sdk_response, "has_succeed") and sdk_response.has_succeed(): return Response(getattr(sdk_response, "json", {})) status = getattr(sdk_response, "status_code", 400) errors = getattr(sdk_response, "errors", None) if errors is None and hasattr(sdk_response, "json"): errors = sdk_response.json if errors is None: errors = {"detail": "GoPay request failed"} return Response({"errors": errors}, status=status) except Exception as e: return Response({"errors": str(e)}, status=500) class GoPayPaymentView(GoPayClientMixin, APIView): permission_classes = [IsAuthenticated] @extend_schema( tags=["GoPay"], summary="Create GoPay payment", description="Creates a GoPay payment and returns gateway URL and payment info.", request=GoPayCreatePaymentRequestSerializer, responses={ 200: OpenApiResponse(response=GoPayPaymentCreatedResponseSerializer, description="Payment created"), 400: OpenApiResponse(description="Validation error or SDK error"), }, examples=[ OpenApiExample( "Create payment", value={ "amount": 123.45, "currency": "CZK", "order_number": "order-001", "order_description": "Example GoPay payment", "return_url": "https://yourfrontend.com/success", "notify_url": "https://yourbackend.com/gopay/notify", "preauthorize": False, }, request_only=True, ) ] ) def post(self, request): amount = request.data.get("amount") currency = request.data.get("currency", "CZK") order_number = request.data.get("order_number", "order-001") order_description = request.data.get("order_description", "Example GoPay payment") return_url = request.data.get("return_url", "https://yourfrontend.com/success") notify_url = request.data.get("notify_url", "https://yourbackend.com/gopay/notify") preauthorize = bool(request.data.get("preauthorize", False)) if not amount: return Response({"error": "Amount is required"}, status=400) payments = self.get_gopay_client() payment_data = { "payer": { "allowed_payment_instruments": ["PAYMENT_CARD"], "default_payment_instrument": "PAYMENT_CARD", "allowed_swifts": ["FIOB"], "contact": { "first_name": getattr(request.user, "first_name", ""), "last_name": getattr(request.user, "last_name", ""), "email": getattr(request.user, "email", ""), }, }, "amount": int(float(amount) * 100), # GoPay expects amount in cents "currency": currency, "order_number": order_number, "order_description": order_description, "items": [ {"name": "Example Item", "amount": int(float(amount) * 100)} ], "callback": {"return_url": return_url, "notify_url": notify_url}, "preauthorize": preauthorize, } resp = payments.create_payment(payment_data) return self._to_response(resp) class GoPayPaymentStatusView(GoPayClientMixin, APIView): permission_classes = [IsAuthenticated] @extend_schema( tags=["GoPay"], summary="Get GoPay payment status", parameters=[OpenApiParameter(name="payment_id", required=True, type=int, location=OpenApiParameter.PATH)], responses={200: OpenApiResponse(response=GoPayStatusResponseSerializer, description="Payment status")}, ) def get(self, request, payment_id: int): payments = self.get_gopay_client() resp = payments.get_status(payment_id) return self._to_response(resp) class GoPayRefundPaymentView(GoPayClientMixin, APIView): permission_classes = [IsAuthenticated] @extend_schema( tags=["GoPay"], summary="Refund GoPay payment", parameters=[OpenApiParameter(name="payment_id", required=True, type=int, location=OpenApiParameter.PATH)], request=GoPayRefundRequestSerializer, responses={200: OpenApiResponse(description="Refund processed")}, ) def post(self, request, payment_id: int): amount = request.data.get("amount") # optional for full refund payments = self.get_gopay_client() if amount is None or amount == "": # Full refund resp = payments.refund_payment(payment_id) else: resp = payments.refund_payment(payment_id, int(float(amount) * 100)) return self._to_response(resp) class GoPayCaptureAuthorizationView(GoPayClientMixin, APIView): permission_classes = [IsAuthenticated] @extend_schema( tags=["GoPay"], summary="Capture GoPay authorization", parameters=[OpenApiParameter(name="payment_id", required=True, type=int, location=OpenApiParameter.PATH)], request=GoPayCaptureRequestSerializer, responses={200: OpenApiResponse(description="Capture processed")}, ) def post(self, request, payment_id: int): amount = request.data.get("amount") # optional for partial capture payments = self.get_gopay_client() if amount is None or amount == "": resp = payments.capture_authorization(payment_id) else: resp = payments.capture_authorization(payment_id, int(float(amount) * 100)) return self._to_response(resp) class GoPayVoidAuthorizationView(GoPayClientMixin, APIView): permission_classes = [IsAuthenticated] @extend_schema( tags=["GoPay"], summary="Void GoPay authorization", parameters=[OpenApiParameter(name="payment_id", required=True, type=int, location=OpenApiParameter.PATH)], responses={200: OpenApiResponse(description="Authorization voided")}, ) def post(self, request, payment_id: int): payments = self.get_gopay_client() resp = payments.void_authorization(payment_id) return self._to_response(resp) class GoPayCreateRecurrenceView(GoPayClientMixin, APIView): permission_classes = [IsAuthenticated] @extend_schema( tags=["GoPay"], summary="Create GoPay recurrence", parameters=[OpenApiParameter(name="payment_id", required=True, type=int, location=OpenApiParameter.PATH)], request=GoPayCreateRecurrenceRequestSerializer, responses={200: OpenApiResponse(description="Recurrence created")}, ) def post(self, request, payment_id: int): amount = request.data.get("amount") currency = request.data.get("currency", "CZK") order_number = request.data.get("order_number", "recur-001") order_description = request.data.get("order_description", "Recurring payment") if not amount: return Response({"error": "Amount is required"}, status=400) payments = self.get_gopay_client() recurrence_payload = { "amount": int(float(amount) * 100), "currency": currency, "order_number": order_number, "order_description": order_description, } resp = payments.create_recurrence(payment_id, recurrence_payload) return self._to_response(resp) class GoPayPaymentInstrumentsView(GoPayClientMixin, APIView): permission_classes = [IsAuthenticated] @extend_schema( tags=["GoPay"], summary="Get GoPay payment instruments", parameters=[OpenApiParameter(name="currency", required=False, type=str, location=OpenApiParameter.QUERY)], responses={200: OpenApiResponse(description="Available payment instruments returned")}, ) def get(self, request): currency = request.query_params.get("currency", "CZK") goid = os.getenv("GOPAY_GOID") if not goid: return Response({"error": "GOPAY_GOID is not configured"}, status=500) payments = self.get_gopay_client() resp = payments.get_payment_instruments(goid, currency) return self._to_response(resp)