Some checks failed
System Monitoring / Health Checks (push) Has been cancelled
System Monitoring / Performance Monitoring (push) Has been cancelled
System Monitoring / Database Monitoring (push) Has been cancelled
System Monitoring / Cache Monitoring (push) Has been cancelled
System Monitoring / Log Monitoring (push) Has been cancelled
System Monitoring / Resource Monitoring (push) Has been cancelled
System Monitoring / Uptime Monitoring (push) Has been cancelled
System Monitoring / Backup Monitoring (push) Has been cancelled
System Monitoring / Security Monitoring (push) Has been cancelled
System Monitoring / Monitoring Dashboard (push) Has been cancelled
System Monitoring / Alerting (push) Has been cancelled
Security Scanning / Dependency Scanning (push) Has been cancelled
Security Scanning / Code Security Scanning (push) Has been cancelled
Security Scanning / Secrets Scanning (push) Has been cancelled
Security Scanning / Container Security Scanning (push) Has been cancelled
Security Scanning / Compliance Checking (push) Has been cancelled
Security Scanning / Security Dashboard (push) Has been cancelled
Security Scanning / Security Remediation (push) Has been cancelled
617 lines
20 KiB
Python
617 lines
20 KiB
Python
"""
|
|
Payment Management API Views
|
|
Handles payment processing, transactions, and financial management endpoints
|
|
"""
|
|
from rest_framework import viewsets, status, permissions
|
|
from rest_framework.decorators import action
|
|
from rest_framework.response import Response
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from django.utils import timezone
|
|
from drf_spectacular.utils import extend_schema, OpenApiParameter
|
|
from drf_spectacular.types import OpenApiTypes
|
|
|
|
from core.models.payment import (
|
|
PaymentTransaction, PaymentStatus, PaymentMethod, PaymentProvider,
|
|
RefundTransaction, DisputeTransaction, PaymentWebhook
|
|
)
|
|
from core.models.subscription import Subscription
|
|
from core.services.payment_service import PaymentService
|
|
from core.services.subscription_service import SubscriptionService
|
|
from core.auth.permissions import TenantPermission, IsTenantAdmin
|
|
from core.serializers.payment import (
|
|
PaymentTransactionSerializer,
|
|
PaymentCreateSerializer,
|
|
PaymentUpdateSerializer,
|
|
RefundTransactionSerializer,
|
|
DisputeTransactionSerializer,
|
|
PaymentMethodSerializer,
|
|
PaymentStatsSerializer,
|
|
PaymentWebhookSerializer
|
|
)
|
|
|
|
|
|
class PaymentTransactionViewSet(viewsets.ModelViewSet):
|
|
"""
|
|
Payment Transaction ViewSet
|
|
Provides CRUD operations for payment transactions with comprehensive financial logic
|
|
"""
|
|
|
|
permission_classes = [IsAuthenticated, TenantPermission]
|
|
serializer_class = PaymentTransactionSerializer
|
|
lookup_field = 'id'
|
|
|
|
def get_queryset(self):
|
|
"""
|
|
Filter payment transactions based on tenant and permissions
|
|
"""
|
|
user = self.request.user
|
|
tenant = user.tenant
|
|
|
|
if not tenant:
|
|
return PaymentTransaction.objects.none()
|
|
|
|
# Superusers see all transactions
|
|
if user.is_superuser:
|
|
return PaymentTransaction.objects.all()
|
|
|
|
# Tenant admins/managers see their tenant's transactions
|
|
if user.role in ['admin', 'manager']:
|
|
return PaymentTransaction.objects.filter(tenant=tenant)
|
|
|
|
# Regular users see only their own transactions
|
|
return PaymentTransaction.objects.filter(tenant=tenant, user=user)
|
|
|
|
def get_serializer_class(self):
|
|
"""
|
|
Return appropriate serializer based on action
|
|
"""
|
|
if self.action == 'create':
|
|
return PaymentCreateSerializer
|
|
elif self.action in ['update', 'partial_update']:
|
|
return PaymentUpdateSerializer
|
|
elif self.action == 'refund':
|
|
return RefundTransactionSerializer
|
|
elif self.action == 'dispute':
|
|
return DisputeTransactionSerializer
|
|
elif self.action == 'stats':
|
|
return PaymentStatsSerializer
|
|
elif self.action == 'methods':
|
|
return PaymentMethodSerializer
|
|
elif self.action == 'webhook':
|
|
return PaymentWebhookSerializer
|
|
return PaymentTransactionSerializer
|
|
|
|
def perform_create(self, serializer):
|
|
"""
|
|
Create payment transaction with proper initialization
|
|
"""
|
|
payment_service = PaymentService()
|
|
transaction = payment_service.create_payment(serializer.validated_data)
|
|
serializer.instance = transaction
|
|
|
|
def perform_update(self, serializer):
|
|
"""
|
|
Update payment transaction with validation
|
|
"""
|
|
payment_service = PaymentService()
|
|
transaction = payment_service.update_payment(
|
|
self.get_object(),
|
|
serializer.validated_data
|
|
)
|
|
serializer.instance = transaction
|
|
|
|
@extend_schema(
|
|
summary="Process Payment",
|
|
description="Process payment with various payment methods",
|
|
request=OpenApiTypes.OBJECT,
|
|
responses={200: PaymentTransactionSerializer}
|
|
)
|
|
@action(detail=True, methods=['post'])
|
|
def process(self, request, *args, **kwargs):
|
|
"""
|
|
Process payment transaction
|
|
"""
|
|
transaction = self.get_object()
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
result = payment_service.process_payment(transaction, request.data)
|
|
serializer = PaymentTransactionSerializer(result)
|
|
return Response(serializer.data)
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Payment processing failed: {str(e)}'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
@extend_schema(
|
|
summary="Refund Payment",
|
|
description="Refund payment transaction",
|
|
request=RefundTransactionSerializer,
|
|
responses={200: RefundTransactionSerializer}
|
|
)
|
|
@action(detail=True, methods=['post'])
|
|
def refund(self, request, *args, **kwargs):
|
|
"""
|
|
Refund payment transaction
|
|
"""
|
|
transaction = self.get_object()
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
refund = payment_service.refund_payment(transaction, request.data)
|
|
serializer = RefundTransactionSerializer(refund)
|
|
return Response(serializer.data)
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Refund failed: {str(e)}'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
@extend_schema(
|
|
summary="Dispute Payment",
|
|
description="Dispute payment transaction",
|
|
request=DisputeTransactionSerializer,
|
|
responses={200: DisputeTransactionSerializer}
|
|
)
|
|
@action(detail=True, methods=['post'])
|
|
def dispute(self, request, *args, **kwargs):
|
|
"""
|
|
Dispute payment transaction
|
|
"""
|
|
transaction = self.get_object()
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
dispute = payment_service.dispute_payment(transaction, request.data)
|
|
serializer = DisputeTransactionSerializer(dispute)
|
|
return Response(serializer.data)
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Dispute failed: {str(e)}'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
@extend_schema(
|
|
summary="Get Payment Receipt",
|
|
description="Generate payment receipt",
|
|
responses={200: OpenApiTypes.OBJECT}
|
|
)
|
|
@action(detail=True, methods=['get'])
|
|
def receipt(self, request, *args, **kwargs):
|
|
"""
|
|
Get payment receipt
|
|
"""
|
|
transaction = self.get_object()
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
receipt = payment_service.generate_receipt(transaction)
|
|
return Response(receipt)
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Receipt generation failed: {str(e)}'},
|
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
)
|
|
|
|
@extend_schema(
|
|
summary="Get Payment Stats",
|
|
description="Retrieve payment statistics",
|
|
responses={200: PaymentStatsSerializer}
|
|
)
|
|
@action(detail=False, methods=['get'])
|
|
def stats(self, request):
|
|
"""
|
|
Get payment statistics
|
|
"""
|
|
tenant = request.user.tenant
|
|
if not tenant:
|
|
return Response(
|
|
{'detail': 'Tenant not found'},
|
|
status=status.HTTP_404_NOT_FOUND
|
|
)
|
|
|
|
payment_service = PaymentService()
|
|
stats = payment_service.get_payment_stats(tenant)
|
|
serializer = PaymentStatsSerializer(stats)
|
|
return Response(serializer.data)
|
|
|
|
@extend_schema(
|
|
summary="Get Payment Methods",
|
|
description="Retrieve available payment methods",
|
|
responses={200: PaymentMethodSerializer(many=True)}
|
|
)
|
|
@action(detail=False, methods=['get'])
|
|
def methods(self, request):
|
|
"""
|
|
Get available payment methods
|
|
"""
|
|
payment_service = PaymentService()
|
|
methods = payment_service.get_available_payment_methods()
|
|
serializer = PaymentMethodSerializer(methods, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@extend_schema(
|
|
summary="Validate Payment Method",
|
|
description="Validate payment method details",
|
|
request=OpenApiTypes.OBJECT,
|
|
responses={200: OpenApiTypes.OBJECT}
|
|
)
|
|
@action(detail=False, methods=['post'])
|
|
def validate_method(self, request):
|
|
"""
|
|
Validate payment method
|
|
"""
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
validation_result = payment_service.validate_payment_method(request.data)
|
|
return Response(validation_result)
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Validation failed: {str(e)}'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
@extend_schema(
|
|
summary="Handle Payment Webhook",
|
|
description="Process payment provider webhook",
|
|
request=PaymentWebhookSerializer,
|
|
responses={200: OpenApiTypes.OBJECT}
|
|
)
|
|
@action(detail=False, methods=['post'])
|
|
def webhook(self, request):
|
|
"""
|
|
Handle payment webhook
|
|
"""
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
result = payment_service.handle_webhook(request.data)
|
|
return Response(result)
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Webhook processing failed: {str(e)}'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
@extend_schema(
|
|
summary="Get Transaction History",
|
|
description="Retrieve transaction history with filters",
|
|
parameters=[
|
|
OpenApiParameter(
|
|
name='start_date',
|
|
description='Start date filter',
|
|
type=OpenApiTypes.DATE
|
|
),
|
|
OpenApiParameter(
|
|
name='end_date',
|
|
description='End date filter',
|
|
type=OpenApiTypes.DATE
|
|
),
|
|
OpenApiParameter(
|
|
name='status',
|
|
description='Status filter',
|
|
type=OpenApiTypes.STR
|
|
),
|
|
OpenApiParameter(
|
|
name='provider',
|
|
description='Provider filter',
|
|
type=OpenApiTypes.STR
|
|
)
|
|
],
|
|
responses={200: PaymentTransactionSerializer(many=True)}
|
|
)
|
|
@action(detail=False, methods=['get'])
|
|
def history(self, request):
|
|
"""
|
|
Get transaction history
|
|
"""
|
|
tenant = request.user.tenant
|
|
if not tenant:
|
|
return Response(
|
|
{'detail': 'Tenant not found'},
|
|
status=status.HTTP_404_NOT_FOUND
|
|
)
|
|
|
|
# Parse filters
|
|
start_date = request.query_params.get('start_date')
|
|
end_date = request.query_params.get('end_date')
|
|
status_filter = request.query_params.get('status')
|
|
provider_filter = request.query_params.get('provider')
|
|
|
|
payment_service = PaymentService()
|
|
transactions = payment_service.get_transaction_history(
|
|
tenant=tenant,
|
|
start_date=start_date,
|
|
end_date=end_date,
|
|
status=status_filter,
|
|
provider=provider_filter
|
|
)
|
|
serializer = PaymentTransactionSerializer(transactions, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@extend_schema(
|
|
summary="Get Payment Analytics",
|
|
description="Retrieve payment analytics and insights",
|
|
parameters=[
|
|
OpenApiParameter(
|
|
name='period',
|
|
description='Analysis period (day, week, month, year)',
|
|
type=OpenApiTypes.STR,
|
|
default='month'
|
|
)
|
|
],
|
|
responses={200: OpenApiTypes.OBJECT}
|
|
)
|
|
@action(detail=False, methods=['get'])
|
|
def analytics(self, request):
|
|
"""
|
|
Get payment analytics
|
|
"""
|
|
tenant = request.user.tenant
|
|
if not tenant:
|
|
return Response(
|
|
{'detail': 'Tenant not found'},
|
|
status=status.HTTP_404_NOT_FOUND
|
|
)
|
|
|
|
period = request.query_params.get('period', 'month')
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
analytics = payment_service.get_payment_analytics(tenant, period)
|
|
return Response(analytics)
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Analytics retrieval failed: {str(e)}'},
|
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
)
|
|
|
|
def get_permissions(self):
|
|
"""
|
|
Override permissions based on action
|
|
"""
|
|
if self.action in ['refund', 'dispute', 'stats', 'analytics', 'webhook']:
|
|
permission_classes = [IsAuthenticated, IsTenantAdmin]
|
|
elif self.action in ['create', 'update', 'partial_update', 'destroy']:
|
|
permission_classes = [IsAuthenticated, IsTenantAdmin]
|
|
else:
|
|
permission_classes = [IsAuthenticated, TenantPermission]
|
|
|
|
return [permission() for permission in permission_classes]
|
|
|
|
|
|
class RefundTransactionViewSet(viewsets.ModelViewSet):
|
|
"""
|
|
Refund Transaction ViewSet
|
|
Manages refund transactions
|
|
"""
|
|
|
|
permission_classes = [IsAuthenticated, IsTenantAdmin]
|
|
serializer_class = RefundTransactionSerializer
|
|
lookup_field = 'id'
|
|
|
|
def get_queryset(self):
|
|
"""
|
|
Filter refund transactions based on tenant
|
|
"""
|
|
user = self.request.user
|
|
tenant = user.tenant
|
|
|
|
if not tenant:
|
|
return RefundTransaction.objects.none()
|
|
|
|
# Superusers see all refunds
|
|
if user.is_superuser:
|
|
return RefundTransaction.objects.all()
|
|
|
|
return RefundTransaction.objects.filter(payment_transaction__tenant=tenant)
|
|
|
|
@extend_schema(
|
|
summary="Process Refund",
|
|
description="Process refund transaction",
|
|
responses={200: RefundTransactionSerializer}
|
|
)
|
|
@action(detail=True, methods=['post'])
|
|
def process(self, request, *args, **kwargs):
|
|
"""
|
|
Process refund transaction
|
|
"""
|
|
refund = self.get_object()
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
result = payment_service.process_refund(refund)
|
|
serializer = RefundTransactionSerializer(result)
|
|
return Response(serializer.data)
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Refund processing failed: {str(e)}'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
@extend_schema(
|
|
summary="Cancel Refund",
|
|
description="Cancel refund transaction",
|
|
responses={200: OpenApiTypes.OBJECT}
|
|
)
|
|
@action(detail=True, methods=['post'])
|
|
def cancel(self, request, *args, **kwargs):
|
|
"""
|
|
Cancel refund transaction
|
|
"""
|
|
refund = self.get_object()
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
payment_service.cancel_refund(refund)
|
|
return Response({'detail': 'Refund cancelled successfully'})
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Refund cancellation failed: {str(e)}'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
|
|
class DisputeTransactionViewSet(viewsets.ModelViewSet):
|
|
"""
|
|
Dispute Transaction ViewSet
|
|
Manages dispute transactions
|
|
"""
|
|
|
|
permission_classes = [IsAuthenticated, IsTenantAdmin]
|
|
serializer_class = DisputeTransactionSerializer
|
|
lookup_field = 'id'
|
|
|
|
def get_queryset(self):
|
|
"""
|
|
Filter dispute transactions based on tenant
|
|
"""
|
|
user = self.request.user
|
|
tenant = user.tenant
|
|
|
|
if not tenant:
|
|
return DisputeTransaction.objects.none()
|
|
|
|
# Superusers see all disputes
|
|
if user.is_superuser:
|
|
return DisputeTransaction.objects.all()
|
|
|
|
return DisputeTransaction.objects.filter(payment_transaction__tenant=tenant)
|
|
|
|
@extend_schema(
|
|
summary="Submit Evidence",
|
|
description="Submit evidence for dispute",
|
|
request=OpenApiTypes.OBJECT,
|
|
responses={200: OpenApiTypes.OBJECT}
|
|
)
|
|
@action(detail=True, methods=['post'])
|
|
def submit_evidence(self, request, *args, **kwargs):
|
|
"""
|
|
Submit dispute evidence
|
|
"""
|
|
dispute = self.get_object()
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
payment_service.submit_dispute_evidence(dispute, request.data)
|
|
return Response({'detail': 'Evidence submitted successfully'})
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Evidence submission failed: {str(e)}'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
@extend_schema(
|
|
summary="Escalate Dispute",
|
|
description="Escalate dispute to payment provider",
|
|
responses={200: OpenApiTypes.OBJECT}
|
|
)
|
|
@action(detail=True, methods=['post'])
|
|
def escalate(self, request, *args, **kwargs):
|
|
"""
|
|
Escalate dispute
|
|
"""
|
|
dispute = self.get_object()
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
payment_service.escalate_dispute(dispute)
|
|
return Response({'detail': 'Dispute escalated successfully'})
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Dispute escalation failed: {str(e)}'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
|
|
class PaymentMethodViewSet(viewsets.ReadOnlyModelViewSet):
|
|
"""
|
|
Payment Method ViewSet
|
|
Provides read-only access to payment methods
|
|
"""
|
|
|
|
permission_classes = [IsAuthenticated, TenantPermission]
|
|
serializer_class = PaymentMethodSerializer
|
|
lookup_field = 'id'
|
|
|
|
def get_queryset(self):
|
|
"""
|
|
Return available payment methods
|
|
"""
|
|
return PaymentMethod.objects.filter(is_active=True)
|
|
|
|
@extend_schema(
|
|
summary="Get Payment Method Fees",
|
|
description="Retrieve payment method fees",
|
|
responses={200: OpenApiTypes.OBJECT}
|
|
)
|
|
@action(detail=True, methods=['get'])
|
|
def fees(self, request, *args, **kwargs):
|
|
"""
|
|
Get payment method fees
|
|
"""
|
|
payment_method = self.get_object()
|
|
payment_service = PaymentService()
|
|
|
|
try:
|
|
fees = payment_service.get_payment_method_fees(payment_method)
|
|
return Response(fees)
|
|
except Exception as e:
|
|
return Response(
|
|
{'detail': f'Fee retrieval failed: {str(e)}'},
|
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
)
|
|
|
|
|
|
class PublicPaymentViewSet(viewsets.ViewSet):
|
|
"""
|
|
Public Payment ViewSet
|
|
Provides public access to payment information
|
|
"""
|
|
|
|
permission_classes = [permissions.AllowAny]
|
|
|
|
@extend_schema(
|
|
summary="Get Available Payment Methods",
|
|
description="Retrieve publicly available payment methods",
|
|
responses={200: PaymentMethodSerializer(many=True)}
|
|
)
|
|
@action(detail=False, methods=['get'])
|
|
def available_methods(self, request):
|
|
"""
|
|
Get available payment methods
|
|
"""
|
|
payment_service = PaymentService()
|
|
methods = payment_service.get_public_payment_methods()
|
|
serializer = PaymentMethodSerializer(methods, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@extend_schema(
|
|
summary="Get Payment Providers",
|
|
description="Retrieve supported payment providers",
|
|
responses={200: OpenApiTypes.OBJECT}
|
|
)
|
|
@action(detail=False, methods=['get'])
|
|
def providers(self, request):
|
|
"""
|
|
Get payment providers
|
|
"""
|
|
payment_service = PaymentService()
|
|
providers = payment_service.get_payment_providers()
|
|
return Response(providers)
|
|
|
|
@extend_schema(
|
|
summary="Get Currency Information",
|
|
description="Retrieve supported currencies and exchange rates",
|
|
responses={200: OpenApiTypes.OBJECT}
|
|
)
|
|
@action(detail=False, methods=['get'])
|
|
def currencies(self, request):
|
|
"""
|
|
Get currency information
|
|
"""
|
|
payment_service = PaymentService()
|
|
currencies = payment_service.get_currency_info()
|
|
return Response(currencies) |