project initialization
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

This commit is contained in:
2025-10-05 02:37:33 +08:00
parent 2cbb6d5fa1
commit b3fff546e9
226 changed files with 97805 additions and 35 deletions

View File

@@ -0,0 +1,617 @@
"""
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)