django-cfg 1.3.3__py3-none-any.whl → 1.3.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/payments/admin_interface/old/payments/base.html +175 -0
- django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +125 -0
- django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +113 -0
- django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +35 -0
- django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +309 -0
- django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +303 -0
- django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +382 -0
- django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +518 -0
- django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/components.css +248 -9
- django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +163 -0
- django_cfg/apps/payments/admin_interface/serializers/__init__.py +39 -0
- django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +149 -0
- django_cfg/apps/payments/admin_interface/serializers/webhook_serializers.py +114 -0
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +55 -90
- django_cfg/apps/payments/admin_interface/templates/payments/components/dialog.html +81 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_help_dialog.html +112 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_status.html +175 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +21 -17
- django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +123 -250
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +170 -269
- django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +152 -355
- django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +202 -551
- django_cfg/apps/payments/admin_interface/views/__init__.py +25 -14
- django_cfg/apps/payments/admin_interface/views/api/__init__.py +20 -0
- django_cfg/apps/payments/admin_interface/views/api/payments.py +191 -0
- django_cfg/apps/payments/admin_interface/views/api/stats.py +206 -0
- django_cfg/apps/payments/admin_interface/views/api/users.py +60 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +257 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +70 -0
- django_cfg/apps/payments/admin_interface/views/base.py +114 -0
- django_cfg/apps/payments/admin_interface/views/dashboard.py +60 -0
- django_cfg/apps/payments/admin_interface/views/forms.py +94 -0
- django_cfg/apps/payments/config/helpers.py +2 -2
- django_cfg/apps/payments/management/commands/cleanup_expired_data.py +16 -6
- django_cfg/apps/payments/management/commands/currency_stats.py +72 -5
- django_cfg/apps/payments/management/commands/manage_currencies.py +9 -20
- django_cfg/apps/payments/management/commands/manage_providers.py +5 -5
- django_cfg/apps/payments/middleware/api_access.py +35 -34
- django_cfg/apps/payments/migrations/0001_initial.py +1 -1
- django_cfg/apps/payments/models/managers/api_key_managers.py +4 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +5 -0
- django_cfg/apps/payments/models/subscriptions.py +0 -24
- django_cfg/apps/payments/services/cache/__init__.py +1 -1
- django_cfg/apps/payments/services/core/balance_service.py +13 -2
- django_cfg/apps/payments/services/integrations/ngrok_service.py +3 -3
- django_cfg/apps/payments/services/providers/registry.py +20 -0
- django_cfg/apps/payments/signals/balance_signals.py +7 -4
- django_cfg/apps/payments/static/payments/js/api-client.js +385 -0
- django_cfg/apps/payments/static/payments/js/ngrok-status.js +58 -0
- django_cfg/apps/payments/static/payments/js/payment-dashboard.js +50 -0
- django_cfg/apps/payments/static/payments/js/payment-form.js +175 -0
- django_cfg/apps/payments/static/payments/js/payment-list.js +95 -0
- django_cfg/apps/payments/static/payments/js/webhook-dashboard.js +154 -0
- django_cfg/apps/payments/urls.py +4 -0
- django_cfg/apps/payments/urls_admin.py +37 -18
- django_cfg/apps/payments/views/api/api_keys.py +14 -0
- django_cfg/apps/payments/views/api/base.py +1 -0
- django_cfg/apps/payments/views/api/currencies.py +2 -2
- django_cfg/apps/payments/views/api/payments.py +11 -5
- django_cfg/apps/payments/views/api/subscriptions.py +36 -31
- django_cfg/apps/payments/views/overview/__init__.py +40 -0
- django_cfg/apps/payments/views/overview/serializers.py +205 -0
- django_cfg/apps/payments/views/overview/services.py +439 -0
- django_cfg/apps/payments/views/overview/urls.py +27 -0
- django_cfg/apps/payments/views/overview/views.py +231 -0
- django_cfg/apps/payments/views/serializers/api_keys.py +20 -6
- django_cfg/apps/payments/views/serializers/balances.py +5 -8
- django_cfg/apps/payments/views/serializers/currencies.py +2 -6
- django_cfg/apps/payments/views/serializers/payments.py +37 -32
- django_cfg/apps/payments/views/serializers/subscriptions.py +4 -26
- django_cfg/apps/urls.py +2 -1
- django_cfg/core/config.py +25 -15
- django_cfg/core/generation.py +12 -12
- django_cfg/core/integration/display/startup.py +1 -1
- django_cfg/core/validation.py +4 -4
- django_cfg/management/commands/show_config.py +2 -2
- django_cfg/management/commands/tree.py +1 -3
- django_cfg/middleware/__init__.py +2 -0
- django_cfg/middleware/static_nocache.py +55 -0
- django_cfg/models/payments.py +13 -15
- django_cfg/models/security.py +15 -0
- django_cfg/modules/django_ngrok.py +6 -0
- django_cfg/modules/django_unfold/dashboard.py +1 -3
- django_cfg/utils/smart_defaults.py +41 -1
- {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/METADATA +1 -1
- {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/RECORD +98 -65
- django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +0 -38
- django_cfg/apps/payments/admin_interface/views/payment_views.py +0 -259
- django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +0 -37
- /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/loading_spinner.html +0 -0
- /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/notification.html +0 -0
- /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/provider_card.html +0 -0
- /django_cfg/apps/payments/admin_interface/{templates → old}/payments/currency_converter.html +0 -0
- /django_cfg/apps/payments/admin_interface/{templates → old}/payments/payment_status.html +0 -0
- /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/dashboard.css +0 -0
- /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/components.js +0 -0
- /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/utils.js +0 -0
- {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/licenses/LICENSE +0 -0
@@ -1,23 +1,34 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
Admin Interface Views for Universal Payment System v2.0.
|
3
3
|
|
4
|
-
|
4
|
+
DRF ViewSets and template views for admin dashboard and management interfaces.
|
5
5
|
"""
|
6
6
|
|
7
|
-
|
8
|
-
from .
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
# Template Views
|
8
|
+
from .dashboard import PaymentDashboardView, WebhookDashboardView
|
9
|
+
from .forms import PaymentFormView, PaymentDetailView, PaymentListView
|
10
|
+
|
11
|
+
# API ViewSets
|
12
|
+
from .api import (
|
13
|
+
AdminPaymentViewSet,
|
14
|
+
AdminWebhookViewSet,
|
15
|
+
AdminWebhookEventViewSet,
|
16
|
+
AdminStatsViewSet,
|
17
|
+
AdminUserViewSet,
|
14
18
|
)
|
15
19
|
|
16
20
|
__all__ = [
|
21
|
+
# Template Views
|
22
|
+
'PaymentDashboardView',
|
17
23
|
'WebhookDashboardView',
|
18
|
-
'PaymentFormView',
|
19
|
-
'
|
24
|
+
'PaymentFormView',
|
25
|
+
'PaymentDetailView',
|
20
26
|
'PaymentListView',
|
21
|
-
|
22
|
-
|
23
|
-
|
27
|
+
|
28
|
+
# API ViewSets
|
29
|
+
'AdminPaymentViewSet',
|
30
|
+
'AdminWebhookViewSet',
|
31
|
+
'AdminWebhookEventViewSet',
|
32
|
+
'AdminStatsViewSet',
|
33
|
+
'AdminUserViewSet',
|
34
|
+
]
|
@@ -0,0 +1,20 @@
|
|
1
|
+
"""
|
2
|
+
Admin Interface API ViewSets.
|
3
|
+
|
4
|
+
DRF ViewSets for admin dashboard with nested routing.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from .payments import AdminPaymentViewSet
|
8
|
+
from .webhook_admin import AdminWebhookViewSet, AdminWebhookEventViewSet
|
9
|
+
from .webhook_public import WebhookTestViewSet
|
10
|
+
from .stats import AdminStatsViewSet
|
11
|
+
from .users import AdminUserViewSet
|
12
|
+
|
13
|
+
__all__ = [
|
14
|
+
'AdminPaymentViewSet',
|
15
|
+
'AdminWebhookViewSet',
|
16
|
+
'AdminWebhookEventViewSet',
|
17
|
+
'WebhookTestViewSet',
|
18
|
+
'AdminStatsViewSet',
|
19
|
+
'AdminUserViewSet',
|
20
|
+
]
|
@@ -0,0 +1,191 @@
|
|
1
|
+
"""
|
2
|
+
Admin Payment ViewSets.
|
3
|
+
|
4
|
+
DRF ViewSets for payment management in admin interface.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from rest_framework import viewsets, status
|
8
|
+
from rest_framework.decorators import action
|
9
|
+
from rest_framework.response import Response
|
10
|
+
from django_filters.rest_framework import DjangoFilterBackend
|
11
|
+
from rest_framework.filters import SearchFilter, OrderingFilter
|
12
|
+
from django.db.models import Count, Sum, Avg, Q
|
13
|
+
from django.utils import timezone
|
14
|
+
from datetime import timedelta
|
15
|
+
|
16
|
+
from django_cfg.apps.payments.admin_interface.views.base import AdminBaseViewSet
|
17
|
+
from django_cfg.apps.payments.models import UniversalPayment
|
18
|
+
from django_cfg.apps.payments.admin_interface.serializers import (
|
19
|
+
AdminPaymentListSerializer,
|
20
|
+
AdminPaymentDetailSerializer,
|
21
|
+
AdminPaymentCreateSerializer,
|
22
|
+
AdminPaymentUpdateSerializer,
|
23
|
+
AdminPaymentStatsSerializer,
|
24
|
+
)
|
25
|
+
from django_cfg.modules.django_logger import get_logger
|
26
|
+
|
27
|
+
logger = get_logger("admin_payment_api")
|
28
|
+
|
29
|
+
|
30
|
+
class AdminPaymentViewSet(AdminBaseViewSet):
|
31
|
+
"""
|
32
|
+
Admin ViewSet for payment management.
|
33
|
+
|
34
|
+
Provides full CRUD operations for payments with admin-specific features.
|
35
|
+
"""
|
36
|
+
|
37
|
+
queryset = UniversalPayment.objects.select_related('user').order_by('-created_at')
|
38
|
+
serializer_class = AdminPaymentDetailSerializer
|
39
|
+
|
40
|
+
serializer_classes = {
|
41
|
+
'list': AdminPaymentListSerializer,
|
42
|
+
'create': AdminPaymentCreateSerializer,
|
43
|
+
'update': AdminPaymentUpdateSerializer,
|
44
|
+
'partial_update': AdminPaymentUpdateSerializer,
|
45
|
+
'stats': AdminPaymentStatsSerializer,
|
46
|
+
}
|
47
|
+
|
48
|
+
filterset_fields = ['status', 'provider', 'currency__code', 'user']
|
49
|
+
search_fields = ['internal_payment_id', 'transaction_hash', 'description', 'user__username', 'user__email']
|
50
|
+
ordering_fields = ['created_at', 'amount_usd', 'status']
|
51
|
+
|
52
|
+
def get_queryset(self):
|
53
|
+
"""Optimized queryset for admin interface."""
|
54
|
+
queryset = super().get_queryset()
|
55
|
+
|
56
|
+
# Add filters based on query params
|
57
|
+
status_filter = self.request.query_params.get('status')
|
58
|
+
if status_filter:
|
59
|
+
queryset = queryset.filter(status=status_filter)
|
60
|
+
|
61
|
+
provider_filter = self.request.query_params.get('provider')
|
62
|
+
if provider_filter:
|
63
|
+
queryset = queryset.filter(provider=provider_filter)
|
64
|
+
|
65
|
+
# Date range filter
|
66
|
+
date_from = self.request.query_params.get('date_from')
|
67
|
+
date_to = self.request.query_params.get('date_to')
|
68
|
+
if date_from:
|
69
|
+
queryset = queryset.filter(created_at__gte=date_from)
|
70
|
+
if date_to:
|
71
|
+
queryset = queryset.filter(created_at__lte=date_to)
|
72
|
+
|
73
|
+
return queryset
|
74
|
+
|
75
|
+
@action(detail=False, methods=['get'])
|
76
|
+
def stats(self, request):
|
77
|
+
"""Get comprehensive payment statistics."""
|
78
|
+
queryset = self.get_queryset()
|
79
|
+
|
80
|
+
# Basic stats
|
81
|
+
total_payments = queryset.count()
|
82
|
+
total_amount = queryset.aggregate(Sum('amount_usd'))['amount_usd__sum'] or 0
|
83
|
+
|
84
|
+
# Status breakdown
|
85
|
+
status_stats = queryset.values('status').annotate(count=Count('id'))
|
86
|
+
successful = sum(s['count'] for s in status_stats if s['status'] in ['completed', 'confirmed'])
|
87
|
+
failed = sum(s['count'] for s in status_stats if s['status'] == 'failed')
|
88
|
+
pending = sum(s['count'] for s in status_stats if s['status'] in ['pending', 'confirming'])
|
89
|
+
|
90
|
+
success_rate = (successful / total_payments * 100) if total_payments > 0 else 0
|
91
|
+
|
92
|
+
# Provider breakdown
|
93
|
+
provider_stats = {}
|
94
|
+
for provider_data in queryset.values('provider').annotate(
|
95
|
+
count=Count('id'),
|
96
|
+
total_amount=Sum('amount_usd')
|
97
|
+
):
|
98
|
+
provider_stats[provider_data['provider']] = {
|
99
|
+
'count': provider_data['count'],
|
100
|
+
'total_amount': provider_data['total_amount'] or 0,
|
101
|
+
}
|
102
|
+
|
103
|
+
# Currency breakdown
|
104
|
+
currency_stats = {}
|
105
|
+
for currency_data in queryset.values('currency_code').annotate(
|
106
|
+
count=Count('id'),
|
107
|
+
total_amount=Sum('amount_usd')
|
108
|
+
):
|
109
|
+
currency_stats[currency_data['currency_code']] = {
|
110
|
+
'count': currency_data['count'],
|
111
|
+
'total_amount': currency_data['total_amount'] or 0,
|
112
|
+
}
|
113
|
+
|
114
|
+
# Time-based stats
|
115
|
+
now = timezone.now()
|
116
|
+
last_24h = queryset.filter(created_at__gte=now - timedelta(hours=24)).aggregate(
|
117
|
+
count=Count('id'),
|
118
|
+
amount=Sum('amount_usd')
|
119
|
+
)
|
120
|
+
last_7d = queryset.filter(created_at__gte=now - timedelta(days=7)).aggregate(
|
121
|
+
count=Count('id'),
|
122
|
+
amount=Sum('amount_usd')
|
123
|
+
)
|
124
|
+
last_30d = queryset.filter(created_at__gte=now - timedelta(days=30)).aggregate(
|
125
|
+
count=Count('id'),
|
126
|
+
amount=Sum('amount_usd')
|
127
|
+
)
|
128
|
+
|
129
|
+
stats_data = {
|
130
|
+
'total_payments': total_payments,
|
131
|
+
'total_amount_usd': total_amount,
|
132
|
+
'successful_payments': successful,
|
133
|
+
'failed_payments': failed,
|
134
|
+
'pending_payments': pending,
|
135
|
+
'success_rate': round(success_rate, 2),
|
136
|
+
'by_provider': provider_stats,
|
137
|
+
'by_currency': currency_stats,
|
138
|
+
'last_24h': {
|
139
|
+
'count': last_24h['count'] or 0,
|
140
|
+
'amount': last_24h['amount'] or 0,
|
141
|
+
},
|
142
|
+
'last_7d': {
|
143
|
+
'count': last_7d['count'] or 0,
|
144
|
+
'amount': last_7d['amount'] or 0,
|
145
|
+
},
|
146
|
+
'last_30d': {
|
147
|
+
'count': last_30d['count'] or 0,
|
148
|
+
'amount': last_30d['amount'] or 0,
|
149
|
+
},
|
150
|
+
}
|
151
|
+
|
152
|
+
serializer = self.get_serializer(stats_data)
|
153
|
+
return Response(serializer.data)
|
154
|
+
|
155
|
+
@action(detail=True, methods=['post'])
|
156
|
+
def cancel(self, request, pk=None):
|
157
|
+
"""Cancel a payment."""
|
158
|
+
payment = self.get_object()
|
159
|
+
|
160
|
+
if payment.status not in ['pending', 'confirming']:
|
161
|
+
return Response(
|
162
|
+
{'error': 'Payment cannot be cancelled in current status'},
|
163
|
+
status=status.HTTP_400_BAD_REQUEST
|
164
|
+
)
|
165
|
+
|
166
|
+
payment.status = UniversalPayment.PaymentStatus.CANCELLED
|
167
|
+
payment.save()
|
168
|
+
|
169
|
+
logger.info(f"Payment {payment.id} cancelled by admin {request.user.id}")
|
170
|
+
|
171
|
+
serializer = self.get_serializer(payment)
|
172
|
+
return Response(serializer.data)
|
173
|
+
|
174
|
+
@action(detail=True, methods=['post'])
|
175
|
+
def refund(self, request, pk=None):
|
176
|
+
"""Refund a payment."""
|
177
|
+
payment = self.get_object()
|
178
|
+
|
179
|
+
if payment.status != 'completed':
|
180
|
+
return Response(
|
181
|
+
{'error': 'Only completed payments can be refunded'},
|
182
|
+
status=status.HTTP_400_BAD_REQUEST
|
183
|
+
)
|
184
|
+
|
185
|
+
payment.status = UniversalPayment.PaymentStatus.REFUNDED
|
186
|
+
payment.save()
|
187
|
+
|
188
|
+
logger.info(f"Payment {payment.id} refunded by admin {request.user.id}")
|
189
|
+
|
190
|
+
serializer = self.get_serializer(payment)
|
191
|
+
return Response(serializer.data)
|
@@ -0,0 +1,206 @@
|
|
1
|
+
"""
|
2
|
+
Admin Statistics ViewSets.
|
3
|
+
|
4
|
+
DRF ViewSets for comprehensive statistics in admin interface.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from rest_framework import viewsets, status
|
8
|
+
from rest_framework.decorators import action
|
9
|
+
from rest_framework.response import Response
|
10
|
+
from django.db.models import Count, Sum, Avg, Q
|
11
|
+
from django.utils import timezone
|
12
|
+
from datetime import timedelta
|
13
|
+
|
14
|
+
|
15
|
+
from django_cfg.modules.django_logger import get_logger
|
16
|
+
from django_cfg.apps.payments.models import UniversalPayment
|
17
|
+
from ..base import AdminReadOnlyViewSet
|
18
|
+
from django_cfg.apps.payments.admin_interface.serializers import AdminPaymentStatsSerializer, WebhookStatsSerializer
|
19
|
+
|
20
|
+
logger = get_logger("admin_stats_api")
|
21
|
+
|
22
|
+
|
23
|
+
class AdminStatsViewSet(AdminReadOnlyViewSet):
|
24
|
+
"""
|
25
|
+
Admin ViewSet for comprehensive system statistics.
|
26
|
+
|
27
|
+
Provides aggregated statistics across all system components.
|
28
|
+
"""
|
29
|
+
|
30
|
+
# No model - this is for aggregated statistics
|
31
|
+
serializer_class = AdminPaymentStatsSerializer
|
32
|
+
|
33
|
+
def list(self, request):
|
34
|
+
"""Get overview statistics."""
|
35
|
+
now = timezone.now()
|
36
|
+
|
37
|
+
# Payment statistics
|
38
|
+
payments_queryset = UniversalPayment.objects.all()
|
39
|
+
|
40
|
+
payment_stats = {
|
41
|
+
'total_payments': payments_queryset.count(),
|
42
|
+
'total_amount_usd': payments_queryset.aggregate(Sum('amount_usd'))['amount_usd__sum'] or 0,
|
43
|
+
'successful_payments': payments_queryset.filter(status='completed').count(),
|
44
|
+
'failed_payments': payments_queryset.filter(status='failed').count(),
|
45
|
+
'pending_payments': payments_queryset.filter(status__in=['pending', 'confirming']).count(),
|
46
|
+
}
|
47
|
+
|
48
|
+
# Calculate success rate
|
49
|
+
total = payment_stats['total_payments']
|
50
|
+
if total > 0:
|
51
|
+
payment_stats['success_rate'] = (payment_stats['successful_payments'] / total) * 100
|
52
|
+
else:
|
53
|
+
payment_stats['success_rate'] = 0
|
54
|
+
|
55
|
+
# Time-based payment stats
|
56
|
+
payment_stats['last_24h'] = {
|
57
|
+
'count': payments_queryset.filter(created_at__gte=now - timedelta(hours=24)).count(),
|
58
|
+
'amount': payments_queryset.filter(created_at__gte=now - timedelta(hours=24)).aggregate(Sum('amount_usd'))['amount_usd__sum'] or 0,
|
59
|
+
}
|
60
|
+
|
61
|
+
# Mock webhook statistics (replace with real data)
|
62
|
+
webhook_stats = {
|
63
|
+
'total_events': 156,
|
64
|
+
'successful_events': 142,
|
65
|
+
'failed_events': 12,
|
66
|
+
'pending_events': 2,
|
67
|
+
'webhook_success_rate': 91.0,
|
68
|
+
}
|
69
|
+
|
70
|
+
# System health metrics
|
71
|
+
system_stats = {
|
72
|
+
'active_users_24h': 45, # Mock data
|
73
|
+
'api_requests_24h': 1250, # Mock data
|
74
|
+
'avg_response_time': 245.5, # Mock data
|
75
|
+
'uptime_percentage': 99.9, # Mock data
|
76
|
+
}
|
77
|
+
|
78
|
+
return Response({
|
79
|
+
'payments': payment_stats,
|
80
|
+
'webhooks': webhook_stats,
|
81
|
+
'system': system_stats,
|
82
|
+
'last_updated': now.isoformat(),
|
83
|
+
})
|
84
|
+
|
85
|
+
@action(detail=False, methods=['get'])
|
86
|
+
def payments(self, request):
|
87
|
+
"""Get detailed payment statistics."""
|
88
|
+
queryset = UniversalPayment.objects.all()
|
89
|
+
now = timezone.now()
|
90
|
+
|
91
|
+
# Basic stats
|
92
|
+
total_payments = queryset.count()
|
93
|
+
total_amount = queryset.aggregate(Sum('amount_usd'))['amount_usd__sum'] or 0
|
94
|
+
|
95
|
+
# Status breakdown
|
96
|
+
status_stats = queryset.values('status').annotate(count=Count('id'))
|
97
|
+
successful = sum(s['count'] for s in status_stats if s['status'] in ['completed', 'confirmed'])
|
98
|
+
failed = sum(s['count'] for s in status_stats if s['status'] == 'failed')
|
99
|
+
pending = sum(s['count'] for s in status_stats if s['status'] in ['pending', 'confirming'])
|
100
|
+
|
101
|
+
success_rate = (successful / total_payments * 100) if total_payments > 0 else 0
|
102
|
+
|
103
|
+
# Provider breakdown
|
104
|
+
provider_stats = {}
|
105
|
+
for provider_data in queryset.values('provider').annotate(
|
106
|
+
count=Count('id'),
|
107
|
+
total_amount=Sum('amount_usd'),
|
108
|
+
successful=Count('id', filter=Q(status='completed')),
|
109
|
+
failed=Count('id', filter=Q(status='failed'))
|
110
|
+
):
|
111
|
+
provider = provider_data['provider']
|
112
|
+
provider_stats[provider] = {
|
113
|
+
'count': provider_data['count'],
|
114
|
+
'total_amount': provider_data['total_amount'] or 0,
|
115
|
+
'successful': provider_data['successful'],
|
116
|
+
'failed': provider_data['failed'],
|
117
|
+
'success_rate': (provider_data['successful'] / provider_data['count'] * 100) if provider_data['count'] > 0 else 0,
|
118
|
+
}
|
119
|
+
|
120
|
+
# Currency breakdown
|
121
|
+
currency_stats = {}
|
122
|
+
for currency_data in queryset.values('currency__code').annotate(
|
123
|
+
count=Count('id'),
|
124
|
+
total_amount=Sum('amount_usd')
|
125
|
+
):
|
126
|
+
currency_stats[currency_data['currency__code']] = {
|
127
|
+
'count': currency_data['count'],
|
128
|
+
'total_amount': currency_data['total_amount'] or 0,
|
129
|
+
}
|
130
|
+
|
131
|
+
# Time-based stats
|
132
|
+
time_ranges = {
|
133
|
+
'last_24h': now - timedelta(hours=24),
|
134
|
+
'last_7d': now - timedelta(days=7),
|
135
|
+
'last_30d': now - timedelta(days=30),
|
136
|
+
}
|
137
|
+
|
138
|
+
time_stats = {}
|
139
|
+
for period, start_date in time_ranges.items():
|
140
|
+
period_queryset = queryset.filter(created_at__gte=start_date)
|
141
|
+
time_stats[period] = {
|
142
|
+
'count': period_queryset.count(),
|
143
|
+
'amount': period_queryset.aggregate(Sum('amount_usd'))['amount_usd__sum'] or 0,
|
144
|
+
'successful': period_queryset.filter(status='completed').count(),
|
145
|
+
'failed': period_queryset.filter(status='failed').count(),
|
146
|
+
}
|
147
|
+
|
148
|
+
stats_data = {
|
149
|
+
'total_payments': total_payments,
|
150
|
+
'total_amount_usd': total_amount,
|
151
|
+
'successful_payments': successful,
|
152
|
+
'failed_payments': failed,
|
153
|
+
'pending_payments': pending,
|
154
|
+
'success_rate': round(success_rate, 2),
|
155
|
+
'by_provider': provider_stats,
|
156
|
+
'by_currency': currency_stats,
|
157
|
+
**time_stats,
|
158
|
+
}
|
159
|
+
|
160
|
+
serializer = AdminPaymentStatsSerializer(stats_data)
|
161
|
+
return Response(serializer.data)
|
162
|
+
|
163
|
+
@action(detail=False, methods=['get'])
|
164
|
+
def webhooks(self, request):
|
165
|
+
"""Get detailed webhook statistics."""
|
166
|
+
# Mock webhook statistics - replace with real data from webhook event model
|
167
|
+
stats_data = {
|
168
|
+
'total': 156,
|
169
|
+
'successful': 142,
|
170
|
+
'failed': 12,
|
171
|
+
'pending': 2,
|
172
|
+
'success_rate': 91.0,
|
173
|
+
'providers': {
|
174
|
+
'nowpayments': {'total': 89, 'successful': 85, 'failed': 4, 'success_rate': 95.5},
|
175
|
+
'stripe': {'total': 45, 'successful': 42, 'failed': 3, 'success_rate': 93.3},
|
176
|
+
'cryptapi': {'total': 22, 'successful': 15, 'failed': 5, 'pending': 2, 'success_rate': 68.2},
|
177
|
+
},
|
178
|
+
'last_24h': {
|
179
|
+
'total': 23,
|
180
|
+
'successful': 21,
|
181
|
+
'failed': 2,
|
182
|
+
},
|
183
|
+
'avg_response_time': 245.5,
|
184
|
+
'max_response_time': 3000,
|
185
|
+
}
|
186
|
+
|
187
|
+
serializer = WebhookStatsSerializer(stats_data)
|
188
|
+
return Response(serializer.data)
|
189
|
+
|
190
|
+
@action(detail=False, methods=['get'])
|
191
|
+
def system(self, request):
|
192
|
+
"""Get system health and performance statistics."""
|
193
|
+
# Mock system statistics - replace with real metrics
|
194
|
+
system_data = {
|
195
|
+
'uptime': '99.9%',
|
196
|
+
'active_users_24h': 45,
|
197
|
+
'api_requests_24h': 1250,
|
198
|
+
'avg_response_time_ms': 245.5,
|
199
|
+
'error_rate_24h': 0.8,
|
200
|
+
'database_connections': 12,
|
201
|
+
'memory_usage': '68%',
|
202
|
+
'cpu_usage': '23%',
|
203
|
+
'disk_usage': '45%',
|
204
|
+
}
|
205
|
+
|
206
|
+
return Response(system_data)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
"""
|
2
|
+
Admin User ViewSet.
|
3
|
+
|
4
|
+
Simple ViewSet for user management in admin interface.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from rest_framework import viewsets
|
8
|
+
from rest_framework.permissions import IsAdminUser
|
9
|
+
from rest_framework.response import Response
|
10
|
+
from django.contrib.auth import get_user_model
|
11
|
+
from django_filters.rest_framework import DjangoFilterBackend
|
12
|
+
from rest_framework.filters import SearchFilter, OrderingFilter
|
13
|
+
|
14
|
+
from django_cfg.apps.payments.admin_interface.views.base import AdminReadOnlyViewSet
|
15
|
+
from django_cfg.apps.payments.admin_interface.serializers import AdminUserSerializer
|
16
|
+
from django_cfg.modules.django_logger import get_logger
|
17
|
+
|
18
|
+
logger = get_logger("admin_user_api")
|
19
|
+
|
20
|
+
User = get_user_model()
|
21
|
+
|
22
|
+
|
23
|
+
class AdminUserViewSet(AdminReadOnlyViewSet):
|
24
|
+
"""
|
25
|
+
Admin ViewSet for user management.
|
26
|
+
|
27
|
+
Provides read-only access to users for admin interface.
|
28
|
+
"""
|
29
|
+
|
30
|
+
queryset = User.objects.filter(is_active=True).order_by('username')
|
31
|
+
serializer_class = AdminUserSerializer
|
32
|
+
permission_classes = [IsAdminUser]
|
33
|
+
|
34
|
+
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
35
|
+
filterset_fields = ['is_active', 'is_staff', 'is_superuser']
|
36
|
+
search_fields = ['username', 'email', 'first_name', 'last_name']
|
37
|
+
ordering_fields = ['username', 'email', 'date_joined']
|
38
|
+
ordering = ['username'] # Override base class ordering
|
39
|
+
|
40
|
+
def get_queryset(self):
|
41
|
+
"""Optimized queryset for admin interface."""
|
42
|
+
# Don't slice here - let DRF handle pagination and ordering first
|
43
|
+
return super().get_queryset()
|
44
|
+
|
45
|
+
def list(self, request, *args, **kwargs):
|
46
|
+
"""Override list to limit results for dropdown."""
|
47
|
+
# Get the filtered and ordered queryset from DRF
|
48
|
+
queryset = self.filter_queryset(self.get_queryset())
|
49
|
+
|
50
|
+
# Now apply limit after filtering/ordering
|
51
|
+
queryset = queryset[:100] # Limit to first 100 users
|
52
|
+
|
53
|
+
# Use DRF pagination if needed
|
54
|
+
page = self.paginate_queryset(queryset)
|
55
|
+
if page is not None:
|
56
|
+
serializer = self.get_serializer(page, many=True)
|
57
|
+
return self.get_paginated_response(serializer.data)
|
58
|
+
|
59
|
+
serializer = self.get_serializer(queryset, many=True)
|
60
|
+
return Response(serializer.data)
|