Files
vontor-cz/backend/thirdparty/gopay/views.py
2025-10-01 18:37:59 +02:00

233 lines
9.5 KiB
Python

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)