Files
vontor-cz/backend/thirdparty/gopay/views.py
2025-11-05 02:05:35 +01:00

190 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
# Tato aplikace poskytuje samostatné HTTP API pro GoPay, připravené k připojení do eshopu.
# 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ý eshop 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)