from __future__ import annotations # Tato aplikace poskytuje samostatné HTTP API pro GoPay, připravené k připojení do e‑shopu. # Pohledy volají GoPay SDK a ukládají minimální data o životním cyklu (platby/refundy). # Koncové body jsou dokumentovány pro Swagger/Redoc pomocí drf-spectacular. from rest_framework import permissions, status from rest_framework.response import Response from rest_framework.views import APIView from drf_spectacular.utils import ( extend_schema, OpenApiParameter, OpenApiResponse, OpenApiExample, OpenApiTypes, ) from . import client as goclient from .models import GoPayPayment, GoPayRefund from .serializers import ( PaymentCreateSerializer, RefundSerializer, ) class CreatePaymentView(APIView): """Vytvoří novou platbu v GoPay a uloží odpověď lokálně. Typický e‑shop flow: - Frontend zavolá tento endpoint s GoPay payloadem. - Backend předá payload GoPay SDK a vrátí upstream odpověď. - Vznikne lokální záznam GoPayPayment pro pozdější sledování stavu. """ permission_classes = [permissions.IsAuthenticated] @extend_schema( tags=["GoPay"], operation_id="gopay_create_payment", summary="Vytvořit platbu", description="Vytvoří platbu v GoPay. Objekt 'payment' je předán SDK beze změn.", request=PaymentCreateSerializer, responses={ 201: OpenApiResponse( response=OpenApiTypes.OBJECT, description="Platba vytvořena (odpověď z GoPay)." ), 502: OpenApiResponse(description="Chyba upstream GoPay"), }, examples=[ OpenApiExample( "Základní platba kartou", value={ "payment": { "amount": 19900, "currency": "CZK", "order_number": "ORDER-1001", "items": [{"name": "T-Shirt", "amount": 19900, "count": 1}], "callback": {"return_url": "https://shop.example.com/return"}, } }, ) ], extensions={ "x-codeSamples": [ { "lang": "typescript", "label": "Client.auth (frontend)", "source": 'await Client.auth.post("/api/payments/gopay/create/", { payment });', }, { "lang": "curl", "source": 'curl -X POST http://localhost:8000/api/payments/gopay/create/ -H "Content-Type: application/json" -d \'{"payment": {"amount":19900,"currency":"CZK"}}\'', }, ] }, ) def post(self, request): ser = PaymentCreateSerializer(data=request.data) ser.is_valid(raise_exception=True) payload = ser.validated_data["payment"] try: res = goclient.create_payment(payload) # Expecting dict-like response except Exception as e: return Response({"detail": str(e)}, status=status.HTTP_502_BAD_GATEWAY) # Map fields defensively gopay_id = str((res.get("id") if isinstance(res, dict) else getattr(res, "id", "")) or "") status_text = (res.get("state") if isinstance(res, dict) else getattr(res, "state", "")) or "" amount = int((res.get("amount") if isinstance(res, dict) else getattr(res, "amount", 0)) or payload.get("amount") or 0) currency = (res.get("currency") if isinstance(res, dict) else getattr(res, "currency", "")) or payload.get("currency", "") payment = GoPayPayment.objects.create( user=request.user if request.user and request.user.is_authenticated else None, gopay_id=gopay_id or payload.get("id", ""), order_number=str(payload.get("order_number", "")), amount=amount, currency=currency, status=status_text, preauthorized=bool(payload.get("preauthorize", False)), request_payload=payload, response_payload=res if isinstance(res, dict) else {"raw": str(res)}, ) return Response({"payment": payment.response_payload}, status=status.HTTP_201_CREATED) class PaymentStatusView(APIView): """Načte aktuální stav platby GoPay a případně synchronizuje lokální záznam.""" permission_classes = [permissions.IsAuthenticated] @extend_schema( tags=["GoPay"], operation_id="gopay_get_status", summary="Získat stav platby", parameters=[ OpenApiParameter( name="payment_id", type=OpenApiTypes.STR, location=OpenApiParameter.PATH, description="ID platby GoPay", ) ], responses={ 200: OpenApiResponse(OpenApiTypes.OBJECT, description="Aktuální stav platby"), 502: OpenApiResponse(description="Chyba upstream GoPay"), }, ) def get(self, request, payment_id: str | int): try: res = goclient.get_status(payment_id) except Exception as e: return Response({"detail": str(e)}, status=status.HTTP_502_BAD_GATEWAY) # Update local status if we have it try: local = GoPayPayment.objects.get(gopay_id=str(payment_id)) status_text = (res.get("state") if isinstance(res, dict) else getattr(res, "state", "")) or "" if status_text: local.status = status_text local.response_payload = res if isinstance(res, dict) else {"raw": str(res)} local.save(update_fields=["status", "response_payload", "updated_at"]) except GoPayPayment.DoesNotExist: pass return Response(res, status=status.HTTP_200_OK) class RefundPaymentView(APIView): """Provede refundaci platby v GoPay a uloží záznam refundace.""" permission_classes = [permissions.IsAuthenticated] @extend_schema( tags=["GoPay"], operation_id="gopay_refund_payment", summary="Refundovat platbu", parameters=[ OpenApiParameter( name="payment_id", type=OpenApiTypes.STR, location=OpenApiParameter.PATH, description="ID platby GoPay k refundaci", ) ], request=RefundSerializer, responses={ 200: OpenApiResponse(OpenApiTypes.OBJECT, description="Výsledek refundace"), 502: OpenApiResponse(description="Chyba upstream GoPay"), }, ) def post(self, request, payment_id: str | int): ser = RefundSerializer(data=request.data) ser.is_valid(raise_exception=True) amount = ser.validated_data["amount"] try: res = goclient.refund_payment(payment_id, amount) except Exception as e: return Response({"detail": str(e)}, status=status.HTTP_502_BAD_GATEWAY) payment = GoPayPayment.objects.filter(gopay_id=str(payment_id)).first() ref = GoPayRefund.objects.create( payment=payment, gopay_refund_id=str((res.get("id") if isinstance(res, dict) else getattr(res, "id", "")) or ""), amount=amount, status=(res.get("state") if isinstance(res, dict) else getattr(res, "state", "")) or "", payload=res if isinstance(res, dict) else {"raw": str(res)}, ) return Response({"refund": ref.payload}, status=status.HTTP_200_OK)