gopay done
This commit is contained in:
131
backend/thirdparty/gopay/views.py
vendored
131
backend/thirdparty/gopay/views.py
vendored
@@ -13,12 +13,44 @@ from drf_spectacular.utils import (
|
||||
OpenApiExample,
|
||||
OpenApiTypes,
|
||||
)
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
import gopay
|
||||
from gopay.enums import TokenScope, Language
|
||||
|
||||
|
||||
def payments_client():
|
||||
"""Create a fresh GoPay payments client from settings.
|
||||
|
||||
Keeps it simple and explicit; no shared global state.
|
||||
"""
|
||||
required = [
|
||||
("GOPAY_GOID", settings.GOPAY_GOID),
|
||||
("GOPAY_CLIENT_ID", settings.GOPAY_CLIENT_ID),
|
||||
("GOPAY_CLIENT_SECRET", settings.GOPAY_CLIENT_SECRET),
|
||||
("GOPAY_GATEWAY_URL", settings.GOPAY_GATEWAY_URL),
|
||||
]
|
||||
missing = [name for name, val in required if not val]
|
||||
if missing:
|
||||
raise ImproperlyConfigured(f"Missing GoPay settings: {', '.join(missing)}")
|
||||
|
||||
cfg = {
|
||||
"goid": settings.GOPAY_GOID,
|
||||
"client_id": settings.GOPAY_CLIENT_ID,
|
||||
"client_secret": settings.GOPAY_CLIENT_SECRET,
|
||||
"gateway_url": settings.GOPAY_GATEWAY_URL,
|
||||
# reasonable defaults; can be changed later via settings if desired
|
||||
"scope": TokenScope.ALL,
|
||||
"language": Language.CZECH,
|
||||
}
|
||||
return gopay.payments(cfg)
|
||||
|
||||
|
||||
from . import client as goclient
|
||||
from .models import GoPayPayment, GoPayRefund
|
||||
from .serializers import (
|
||||
PaymentCreateSerializer,
|
||||
RefundSerializer,
|
||||
PaymentCreateSerializer,
|
||||
RefundSerializer,
|
||||
)
|
||||
|
||||
|
||||
@@ -35,43 +67,38 @@ class CreatePaymentView(APIView):
|
||||
@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.",
|
||||
summary="Vytvořit platbu (minimální vstup)",
|
||||
description=(
|
||||
"Vytvoří platbu v GoPay s minimálními povinnými poli. Citlivé údaje o kartě se neposílají,"
|
||||
" platbu obslouží GoPay stránka (gw_url). Pokud není zadán notification_url, použije se"
|
||||
" hodnota ze settings.GOPAY_NOTIFICATION_URL."
|
||||
),
|
||||
request=PaymentCreateSerializer,
|
||||
responses={
|
||||
201: OpenApiResponse(
|
||||
response=OpenApiTypes.OBJECT,
|
||||
description="Platba vytvořena (odpověď z GoPay)."
|
||||
description="Platba vytvořena. Vrací gw_url pro přesměrování na platební bránu."
|
||||
),
|
||||
502: OpenApiResponse(description="Chyba upstream GoPay"),
|
||||
},
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Základní platba kartou",
|
||||
"Minimální platba",
|
||||
value={
|
||||
"payment": {
|
||||
"amount": 19900,
|
||||
"amount": 10000,
|
||||
"currency": "CZK",
|
||||
"order_number": "ORDER-1001",
|
||||
"items": [{"name": "T-Shirt", "amount": 19900, "count": 1}],
|
||||
"callback": {"return_url": "https://shop.example.com/return"},
|
||||
"order_number": "123456",
|
||||
"payer": {"contact": {"email": "john.doe@example.com"}},
|
||||
"callback": {
|
||||
"return_url": "https://example.com/your-return-url",
|
||||
"notification_url": "https://example.com/your-notify-url"
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
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)
|
||||
@@ -79,29 +106,39 @@ class CreatePaymentView(APIView):
|
||||
payload = ser.validated_data["payment"]
|
||||
|
||||
try:
|
||||
res = goclient.create_payment(payload) # Expecting dict-like response
|
||||
res = payments_client().create_payment(payload) # Expecting dict-like/dict 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", "")
|
||||
as_dict = res if isinstance(res, dict) else getattr(res, "__dict__", {}) or {}
|
||||
gopay_id = str(as_dict.get("id", ""))
|
||||
status_text = str(as_dict.get("state", ""))
|
||||
amount = int(as_dict.get("amount", payload.get("amount", 0)) or 0)
|
||||
currency = as_dict.get("currency", payload.get("currency", ""))
|
||||
gw_url = as_dict.get("gw_url") or as_dict.get("gwUrl") or as_dict.get("gw-url")
|
||||
|
||||
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,
|
||||
currency=currency or "",
|
||||
status=status_text,
|
||||
preauthorized=bool(payload.get("preauthorize", False)),
|
||||
request_payload=payload,
|
||||
response_payload=res if isinstance(res, dict) else {"raw": str(res)},
|
||||
response_payload=as_dict or {"raw": str(res)},
|
||||
)
|
||||
|
||||
return Response({"payment": payment.response_payload}, status=status.HTTP_201_CREATED)
|
||||
return Response(
|
||||
{
|
||||
"payment_id": payment.gopay_id,
|
||||
"state": payment.status,
|
||||
"gw_url": gw_url,
|
||||
"raw": payment.response_payload,
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
|
||||
|
||||
class PaymentStatusView(APIView):
|
||||
@@ -127,22 +164,37 @@ class PaymentStatusView(APIView):
|
||||
)
|
||||
def get(self, request, payment_id: str | int):
|
||||
try:
|
||||
res = goclient.get_status(payment_id)
|
||||
res = payments_client().get_status(payment_id)
|
||||
except Exception as e:
|
||||
return Response({"detail": str(e)}, status=status.HTTP_502_BAD_GATEWAY)
|
||||
|
||||
# Normalize GoPay SDK response into a dict body
|
||||
body = None
|
||||
if hasattr(res, "success") and hasattr(res, "json"):
|
||||
# Official SDK-style: check success and extract JSON body
|
||||
ok = bool(getattr(res, "success", False))
|
||||
if not ok:
|
||||
return Response({"detail": "GoPay upstream error", "success": False}, status=status.HTTP_502_BAD_GATEWAY)
|
||||
json_attr = getattr(res, "json")
|
||||
body = json_attr() if callable(json_attr) else json_attr
|
||||
elif isinstance(res, dict):
|
||||
body = res
|
||||
else:
|
||||
body = getattr(res, "__dict__", {}) or {"raw": str(res)}
|
||||
|
||||
state_val = body.get("state") if isinstance(body, dict) else None
|
||||
|
||||
# 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)}
|
||||
if state_val:
|
||||
local.status = state_val
|
||||
local.response_payload = body if isinstance(body, dict) else {"raw": str(body)}
|
||||
local.save(update_fields=["status", "response_payload", "updated_at"])
|
||||
except GoPayPayment.DoesNotExist:
|
||||
pass
|
||||
|
||||
return Response(res, status=status.HTTP_200_OK)
|
||||
return Response(body, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class RefundPaymentView(APIView):
|
||||
@@ -173,11 +225,12 @@ class RefundPaymentView(APIView):
|
||||
amount = ser.validated_data["amount"]
|
||||
|
||||
try:
|
||||
res = goclient.refund_payment(payment_id, amount)
|
||||
res = payments_client().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 ""),
|
||||
|
||||
Reference in New Issue
Block a user