188 lines
6.4 KiB
Python
188 lines
6.4 KiB
Python
from rest_framework import mixins, status, viewsets
|
|
from rest_framework.decorators import action
|
|
from rest_framework.permissions import AllowAny, IsAuthenticated
|
|
from rest_framework.response import Response
|
|
|
|
from .filters import (
|
|
BillingPlanFilter,
|
|
InvoiceFilter,
|
|
PaymentGatewayFilter,
|
|
PaymentTransactionFilter,
|
|
SubscriptionFilter,
|
|
)
|
|
from .models import (
|
|
BillingPlan,
|
|
Invoice,
|
|
PaymentGateway,
|
|
PaymentTransaction,
|
|
Subscription,
|
|
)
|
|
from .serializers import (
|
|
BillingPlanSerializer,
|
|
InvoiceSerializer,
|
|
PaymentGatewaySerializer,
|
|
PaymentTransactionSerializer,
|
|
SubscribeSerializer,
|
|
SubscriptionSerializer,
|
|
)
|
|
|
|
|
|
class PaymentGatewayViewSet(viewsets.ModelViewSet):
|
|
queryset = PaymentGateway.objects.all()
|
|
serializer_class = PaymentGatewaySerializer
|
|
permission_classes = [IsAuthenticated]
|
|
filterset_class = PaymentGatewayFilter
|
|
|
|
def initial(self, request, *args, **kwargs):
|
|
from infrasynth.features.services import FeatureService
|
|
|
|
if not FeatureService().is_enabled("billing", user=request.user):
|
|
from django.http import Http404
|
|
|
|
raise Http404()
|
|
super().initial(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return PaymentGateway.objects.all()
|
|
|
|
|
|
class BillingPlanViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
|
|
queryset = BillingPlan.objects.filter(is_active=True)
|
|
serializer_class = BillingPlanSerializer
|
|
filterset_class = BillingPlanFilter
|
|
|
|
def get_permissions(self):
|
|
if self.action in ("list", "retrieve"):
|
|
return [AllowAny()]
|
|
return [IsAuthenticated()]
|
|
|
|
def initial(self, request, *args, **kwargs):
|
|
from infrasynth.features.services import FeatureService
|
|
|
|
if not FeatureService().is_enabled("billing", user=request.user):
|
|
from django.http import Http404
|
|
|
|
raise Http404()
|
|
super().initial(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return BillingPlan.objects.filter(is_active=True).select_related("gateway")
|
|
|
|
|
|
class SubscriptionViewSet(viewsets.ModelViewSet):
|
|
queryset = Subscription.objects.all()
|
|
serializer_class = SubscriptionSerializer
|
|
permission_classes = [IsAuthenticated]
|
|
filterset_class = SubscriptionFilter
|
|
|
|
def initial(self, request, *args, **kwargs):
|
|
from infrasynth.features.services import FeatureService
|
|
|
|
if not FeatureService().is_enabled("billing", user=request.user):
|
|
from django.http import Http404
|
|
|
|
raise Http404()
|
|
super().initial(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return Subscription.objects.select_related("plan", "gateway", "user").all()
|
|
|
|
@action(detail=False, methods=["post"])
|
|
def subscribe(self, request):
|
|
ser = SubscribeSerializer(data=request.data)
|
|
ser.is_valid(raise_exception=True)
|
|
from .services import BillingService
|
|
|
|
try:
|
|
result, gateway = BillingService().create_checkout_session(
|
|
ser.validated_data["plan_slug"],
|
|
request.user,
|
|
success_url=ser.validated_data.get("success_url"),
|
|
cancel_url=ser.validated_data.get("cancel_url"),
|
|
)
|
|
except ValueError as exc:
|
|
return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
|
return Response(
|
|
{
|
|
"checkout_url": result.checkout_url,
|
|
"session_id": result.session_id,
|
|
"client_secret": result.client_secret,
|
|
"gateway": gateway.slug,
|
|
"plan_slug": ser.validated_data["plan_slug"],
|
|
}
|
|
)
|
|
|
|
|
|
class InvoiceViewSet(viewsets.ModelViewSet):
|
|
queryset = Invoice.objects.all()
|
|
serializer_class = InvoiceSerializer
|
|
permission_classes = [IsAuthenticated]
|
|
filterset_class = InvoiceFilter
|
|
|
|
def initial(self, request, *args, **kwargs):
|
|
from infrasynth.features.services import FeatureService
|
|
|
|
if not FeatureService().is_enabled("billing", user=request.user):
|
|
from django.http import Http404
|
|
|
|
raise Http404()
|
|
super().initial(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return Invoice.objects.select_related("subscription", "gateway", "user", "pdf_file").all()
|
|
|
|
|
|
class PaymentTransactionViewSet(viewsets.ReadOnlyModelViewSet):
|
|
queryset = PaymentTransaction.objects.all()
|
|
serializer_class = PaymentTransactionSerializer
|
|
permission_classes = [IsAuthenticated]
|
|
filterset_class = PaymentTransactionFilter
|
|
|
|
def initial(self, request, *args, **kwargs):
|
|
from infrasynth.features.services import FeatureService
|
|
|
|
if not FeatureService().is_enabled("billing", user=request.user):
|
|
from django.http import Http404
|
|
|
|
raise Http404()
|
|
super().initial(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return PaymentTransaction.objects.select_related("invoice", "gateway").all()
|
|
|
|
|
|
class WebhookViewSet(viewsets.GenericViewSet):
|
|
permission_classes = [AllowAny]
|
|
|
|
@action(detail=False, methods=["post"])
|
|
def receive(self, request):
|
|
gateway_slug = request.data.get("gateway_slug") or request.query_params.get("gateway_slug")
|
|
gateway = (
|
|
PaymentGateway.objects.filter(slug=gateway_slug, is_active=True).first()
|
|
if gateway_slug
|
|
else PaymentGateway.objects.filter(is_active=True).first()
|
|
)
|
|
if gateway is None:
|
|
return Response(
|
|
{"detail": "No active payment gateway found."},
|
|
status=status.HTTP_404_NOT_FOUND,
|
|
)
|
|
|
|
from django.utils.module_loading import import_string
|
|
|
|
try:
|
|
gateway_cls = import_string(gateway.gateway_class)
|
|
gateway_instance = gateway_cls(gateway.config)
|
|
result = gateway_instance.handle_webhook(payload=request.data, headers=dict(request.headers))
|
|
except Exception as exc: # noqa: BLE001
|
|
return Response(
|
|
{"detail": f"Webhook processing failed: {exc}"},
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
)
|
|
|
|
if not result.is_handled:
|
|
return Response(
|
|
{"status": "ignored", "event_type": result.event_type},
|
|
status=status.HTTP_200_OK,
|
|
)
|
|
return Response({"status": "processed", "event_type": result.event_type, "data": result.data})
|