django-cfg 1.3.1__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 +429 -0
- django_cfg/apps/payments/management/commands/currency_stats.py +443 -0
- 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/management/commands/process_pending_payments.py +357 -0
- django_cfg/apps/payments/management/commands/test_providers.py +434 -0
- 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/balance.py +5 -2
- django_cfg/apps/payments/models/managers/api_key_managers.py +6 -2
- django_cfg/apps/payments/models/managers/balance_managers.py +3 -3
- django_cfg/apps/payments/models/managers/payment_managers.py +5 -0
- django_cfg/apps/payments/models/managers/subscription_managers.py +3 -3
- django_cfg/apps/payments/models/subscriptions.py +0 -24
- django_cfg/apps/payments/services/cache/__init__.py +1 -1
- django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
- django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
- django_cfg/apps/payments/services/cache_service/interfaces.py +32 -0
- django_cfg/apps/payments/services/cache_service/keys.py +49 -0
- django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
- django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
- django_cfg/apps/payments/services/core/balance_service.py +13 -2
- django_cfg/apps/payments/services/core/payment_service.py +49 -22
- 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/api_key_signals.py +2 -2
- django_cfg/apps/payments/signals/balance_signals.py +8 -5
- 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 +51 -5
- {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/METADATA +1 -1
- {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/RECORD +111 -69
- 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/services/cache/cache_service.py +0 -235
- /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.1.dist-info → django_cfg-1.3.5.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,257 @@
|
|
1
|
+
"""
|
2
|
+
Admin Webhook ViewSets.
|
3
|
+
|
4
|
+
DRF ViewSets for webhook management in admin interface.
|
5
|
+
Requires admin permissions.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from rest_framework import viewsets, status
|
9
|
+
from rest_framework.decorators import action
|
10
|
+
from rest_framework.response import Response
|
11
|
+
from rest_framework.generics import ListAPIView
|
12
|
+
from django_filters.rest_framework import DjangoFilterBackend
|
13
|
+
from rest_framework.filters import SearchFilter, OrderingFilter
|
14
|
+
from django.utils import timezone
|
15
|
+
from django.db import models
|
16
|
+
from datetime import datetime, timedelta
|
17
|
+
|
18
|
+
from django_cfg.apps.payments.admin_interface.views.base import AdminBaseViewSet, AdminReadOnlyViewSet
|
19
|
+
from django_cfg.apps.payments.admin_interface.serializers import (
|
20
|
+
WebhookEventSerializer,
|
21
|
+
WebhookEventListSerializer,
|
22
|
+
WebhookStatsSerializer,
|
23
|
+
WebhookActionSerializer,
|
24
|
+
WebhookActionResultSerializer,
|
25
|
+
)
|
26
|
+
from django_cfg.apps.payments.services.core.webhook_service import WebhookService
|
27
|
+
from django_cfg.apps.payments.models import UniversalPayment
|
28
|
+
from django_cfg.modules.django_logger import get_logger
|
29
|
+
|
30
|
+
logger = get_logger("admin_webhook_api")
|
31
|
+
|
32
|
+
|
33
|
+
class AdminWebhookViewSet(AdminReadOnlyViewSet):
|
34
|
+
"""
|
35
|
+
Admin ViewSet for webhook configuration management.
|
36
|
+
|
37
|
+
Read-only view for webhook configurations and provider info.
|
38
|
+
Requires admin permissions.
|
39
|
+
"""
|
40
|
+
|
41
|
+
# No model - this is for webhook configuration data
|
42
|
+
serializer_class = WebhookStatsSerializer
|
43
|
+
|
44
|
+
def list(self, request):
|
45
|
+
"""List webhook providers and configurations."""
|
46
|
+
# Mock webhook provider data - replace with real configuration
|
47
|
+
providers_data = [
|
48
|
+
{
|
49
|
+
'name': 'nowpayments',
|
50
|
+
'display_name': 'NowPayments',
|
51
|
+
'enabled': True,
|
52
|
+
'webhook_url': 'https://api.nowpayments.io/v1/webhooks',
|
53
|
+
'supported_events': ['payment.created', 'payment.completed', 'payment.failed'],
|
54
|
+
'last_ping': timezone.now() - timedelta(minutes=5),
|
55
|
+
'status': 'active'
|
56
|
+
},
|
57
|
+
{
|
58
|
+
'name': 'stripe',
|
59
|
+
'display_name': 'Stripe',
|
60
|
+
'enabled': False,
|
61
|
+
'webhook_url': 'https://api.stripe.com/v1/webhooks',
|
62
|
+
'supported_events': ['payment_intent.succeeded', 'payment_intent.payment_failed'],
|
63
|
+
'last_ping': None,
|
64
|
+
'status': 'inactive'
|
65
|
+
}
|
66
|
+
]
|
67
|
+
|
68
|
+
serializer = self.get_serializer(providers_data, many=True)
|
69
|
+
return Response(serializer.data)
|
70
|
+
|
71
|
+
@action(detail=False, methods=['get'])
|
72
|
+
def stats(self, request):
|
73
|
+
"""Get webhook statistics."""
|
74
|
+
# Get real payment data for stats
|
75
|
+
total_payments = UniversalPayment.objects.count()
|
76
|
+
recent_payments = UniversalPayment.objects.filter(
|
77
|
+
created_at__gte=timezone.now() - timedelta(days=7)
|
78
|
+
).count()
|
79
|
+
|
80
|
+
# Mock webhook stats based on real payment data
|
81
|
+
stats_data = {
|
82
|
+
'total_events': total_payments * 2, # Assume 2 events per payment on average
|
83
|
+
'successful_events': int(total_payments * 1.8), # 90% success rate
|
84
|
+
'failed_events': int(total_payments * 0.2), # 10% failure rate
|
85
|
+
'success_rate': 90.0,
|
86
|
+
'recent_events': recent_payments * 2,
|
87
|
+
'providers': {
|
88
|
+
'nowpayments': {
|
89
|
+
'total': int(total_payments * 0.7),
|
90
|
+
'successful': int(total_payments * 0.65),
|
91
|
+
'failed': int(total_payments * 0.05),
|
92
|
+
'success_rate': 92.8
|
93
|
+
},
|
94
|
+
'stripe': {
|
95
|
+
'total': int(total_payments * 0.3),
|
96
|
+
'successful': int(total_payments * 0.28),
|
97
|
+
'failed': int(total_payments * 0.02),
|
98
|
+
'success_rate': 93.3
|
99
|
+
}
|
100
|
+
},
|
101
|
+
'events_by_type': {
|
102
|
+
'payment.created': int(total_payments * 1.0),
|
103
|
+
'payment.completed': int(total_payments * 0.8),
|
104
|
+
'payment.failed': int(total_payments * 0.2),
|
105
|
+
},
|
106
|
+
'recent_activity': [
|
107
|
+
{
|
108
|
+
'timestamp': timezone.now() - timedelta(minutes=i*5),
|
109
|
+
'event_type': 'payment.created' if i % 3 == 0 else 'payment.completed',
|
110
|
+
'provider': 'nowpayments' if i % 2 == 0 else 'stripe',
|
111
|
+
'status': 'success' if i % 4 != 0 else 'failed'
|
112
|
+
}
|
113
|
+
for i in range(10)
|
114
|
+
]
|
115
|
+
}
|
116
|
+
|
117
|
+
serializer = self.get_serializer(stats_data)
|
118
|
+
return Response(serializer.data)
|
119
|
+
|
120
|
+
|
121
|
+
class AdminWebhookEventViewSet(AdminReadOnlyViewSet):
|
122
|
+
"""
|
123
|
+
Admin ViewSet for webhook events management.
|
124
|
+
|
125
|
+
Provides listing, filtering, and actions for webhook events.
|
126
|
+
Requires admin permissions.
|
127
|
+
"""
|
128
|
+
|
129
|
+
# No model - using mock data for now
|
130
|
+
serializer_class = WebhookEventListSerializer
|
131
|
+
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
132
|
+
filterset_fields = ['event_type', 'status', 'provider']
|
133
|
+
search_fields = ['event_type', 'webhook_url']
|
134
|
+
ordering_fields = ['created_at', 'event_type', 'status']
|
135
|
+
ordering = ['-created_at']
|
136
|
+
|
137
|
+
def get_queryset(self):
|
138
|
+
"""Get webhook events queryset."""
|
139
|
+
# For now, return empty queryset since we're using mock data
|
140
|
+
# In real implementation, this would return WebhookEvent.objects.all()
|
141
|
+
return UniversalPayment.objects.none()
|
142
|
+
|
143
|
+
def list(self, request):
|
144
|
+
"""List webhook events with filtering and pagination."""
|
145
|
+
# Get filter parameters
|
146
|
+
event_type = request.query_params.get('event_type')
|
147
|
+
status_filter = request.query_params.get('status')
|
148
|
+
provider = request.query_params.get('provider')
|
149
|
+
|
150
|
+
# Get real payment data to generate realistic mock events
|
151
|
+
payments = UniversalPayment.objects.all()[:50] # Limit for performance
|
152
|
+
|
153
|
+
# Generate mock webhook events based on real payments
|
154
|
+
events = []
|
155
|
+
for i, payment in enumerate(payments):
|
156
|
+
# Create multiple events per payment
|
157
|
+
event_types = ['payment.created', 'payment.completed'] if payment.status == 'completed' else ['payment.created']
|
158
|
+
|
159
|
+
for event_type_name in event_types:
|
160
|
+
event = {
|
161
|
+
'id': f"evt_{payment.id}_{event_type_name.split('.')[1]}",
|
162
|
+
'event_type': event_type_name,
|
163
|
+
'webhook_url': f'https://example.com/webhook/{payment.id}',
|
164
|
+
'status': 'success' if i % 5 != 0 else 'failed',
|
165
|
+
'provider': 'nowpayments' if i % 2 == 0 else 'stripe',
|
166
|
+
'created_at': payment.created_at,
|
167
|
+
'response_code': 200 if i % 5 != 0 else 500,
|
168
|
+
'response_time': f"{50 + (i % 200)}ms",
|
169
|
+
'attempts': 1 if i % 5 != 0 else 3,
|
170
|
+
'payload': {
|
171
|
+
'payment_id': str(payment.id),
|
172
|
+
'amount': str(payment.amount),
|
173
|
+
'currency': payment.currency,
|
174
|
+
'status': payment.status,
|
175
|
+
'timestamp': payment.created_at.isoformat()
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
# Apply filters
|
180
|
+
if event_type and event['event_type'] != event_type:
|
181
|
+
continue
|
182
|
+
if status_filter and event['status'] != status_filter:
|
183
|
+
continue
|
184
|
+
if provider and event['provider'] != provider:
|
185
|
+
continue
|
186
|
+
|
187
|
+
events.append(event)
|
188
|
+
|
189
|
+
# Sort by created_at descending
|
190
|
+
events.sort(key=lambda x: x['created_at'], reverse=True)
|
191
|
+
|
192
|
+
# Pagination
|
193
|
+
page_size = 20
|
194
|
+
page = int(request.query_params.get('page', 1))
|
195
|
+
start = (page - 1) * page_size
|
196
|
+
end = start + page_size
|
197
|
+
paginated_events = events[start:end]
|
198
|
+
|
199
|
+
response_data = {
|
200
|
+
'events': paginated_events,
|
201
|
+
'total': len(events),
|
202
|
+
'page': page,
|
203
|
+
'per_page': page_size,
|
204
|
+
'has_next': end < len(events),
|
205
|
+
'has_previous': page > 1
|
206
|
+
}
|
207
|
+
|
208
|
+
serializer = self.get_serializer(response_data)
|
209
|
+
return Response(serializer.data)
|
210
|
+
|
211
|
+
@action(detail=True, methods=['post'])
|
212
|
+
def retry(self, request, pk=None):
|
213
|
+
"""Retry a failed webhook event."""
|
214
|
+
# Mock retry logic
|
215
|
+
result_data = {
|
216
|
+
'success': True,
|
217
|
+
'message': f'Webhook event {pk} retry initiated',
|
218
|
+
'event_id': pk,
|
219
|
+
'retry_count': 2,
|
220
|
+
'next_retry': timezone.now() + timedelta(minutes=5)
|
221
|
+
}
|
222
|
+
|
223
|
+
serializer = WebhookActionResultSerializer(result_data)
|
224
|
+
logger.info(f"Webhook event {pk} retry initiated by admin {request.user.id}")
|
225
|
+
return Response(serializer.data)
|
226
|
+
|
227
|
+
@action(detail=False, methods=['post'])
|
228
|
+
def clear_all(self, request):
|
229
|
+
"""Clear all webhook events."""
|
230
|
+
# Mock clear all logic
|
231
|
+
result_data = {
|
232
|
+
'success': True,
|
233
|
+
'message': 'All webhook events cleared',
|
234
|
+
'cleared_count': 150, # Mock count
|
235
|
+
}
|
236
|
+
|
237
|
+
serializer = WebhookActionResultSerializer(result_data)
|
238
|
+
logger.info(f"All webhook events cleared by admin {request.user.id}")
|
239
|
+
return Response(serializer.data)
|
240
|
+
|
241
|
+
@action(detail=False, methods=['post'])
|
242
|
+
def retry_failed(self, request):
|
243
|
+
"""Retry all failed webhook events."""
|
244
|
+
# Mock retry failed logic
|
245
|
+
# In real implementation:
|
246
|
+
# failed_events = WebhookEvent.objects.filter(status='failed')
|
247
|
+
# results = [retry_webhook_event(event) for event in failed_events]
|
248
|
+
|
249
|
+
result_data = {
|
250
|
+
'success': True,
|
251
|
+
'message': 'All failed webhook events retry initiated',
|
252
|
+
'processed_count': 0, # Mock count
|
253
|
+
}
|
254
|
+
|
255
|
+
serializer = WebhookActionResultSerializer(result_data)
|
256
|
+
logger.info(f"All failed webhook events retry initiated by admin {request.user.id}")
|
257
|
+
return Response(serializer.data)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
"""
|
2
|
+
Public Webhook ViewSets.
|
3
|
+
|
4
|
+
DRF ViewSets for public webhook functionality.
|
5
|
+
No authentication required.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from rest_framework import viewsets, status
|
9
|
+
from rest_framework.decorators import action
|
10
|
+
from rest_framework.response import Response
|
11
|
+
from rest_framework.permissions import AllowAny
|
12
|
+
from django.utils import timezone
|
13
|
+
|
14
|
+
from django_cfg.apps.payments.admin_interface.serializers import WebhookStatsSerializer
|
15
|
+
from django_cfg.modules.django_logger import get_logger
|
16
|
+
|
17
|
+
logger = get_logger("webhook_public_api")
|
18
|
+
|
19
|
+
|
20
|
+
class WebhookTestViewSet(viewsets.ViewSet):
|
21
|
+
"""
|
22
|
+
Public ViewSet for webhook testing functionality.
|
23
|
+
|
24
|
+
Allows testing webhook endpoints without admin permissions.
|
25
|
+
Perfect for development and integration testing.
|
26
|
+
"""
|
27
|
+
|
28
|
+
permission_classes = [AllowAny] # Explicitly allow any user
|
29
|
+
serializer_class = WebhookStatsSerializer # For schema generation
|
30
|
+
|
31
|
+
@action(detail=False, methods=['post'])
|
32
|
+
def test(self, request):
|
33
|
+
"""
|
34
|
+
Test webhook endpoint.
|
35
|
+
|
36
|
+
Sends a test webhook to the specified URL with the given event type.
|
37
|
+
Useful for developers to test their webhook implementations.
|
38
|
+
"""
|
39
|
+
webhook_url = request.data.get('webhook_url')
|
40
|
+
event_type = request.data.get('event_type')
|
41
|
+
|
42
|
+
if not webhook_url or not event_type:
|
43
|
+
return Response({
|
44
|
+
'success': False,
|
45
|
+
'error': 'webhook_url and event_type are required'
|
46
|
+
}, status=status.HTTP_400_BAD_REQUEST)
|
47
|
+
|
48
|
+
# TODO: In real implementation, send actual HTTP request to webhook_url
|
49
|
+
# For now, return mock success response
|
50
|
+
|
51
|
+
logger.info(f"Test webhook sent to {webhook_url} with event type {event_type}")
|
52
|
+
|
53
|
+
return Response({
|
54
|
+
'success': True,
|
55
|
+
'message': f'Test webhook sent to {webhook_url} with event type {event_type}',
|
56
|
+
'webhook_url': webhook_url,
|
57
|
+
'event_type': event_type,
|
58
|
+
'timestamp': timezone.now().isoformat(),
|
59
|
+
'test_payload': {
|
60
|
+
'event': event_type,
|
61
|
+
'data': {
|
62
|
+
'id': 'test_payment_123',
|
63
|
+
'amount': '100.00',
|
64
|
+
'currency': 'USD',
|
65
|
+
'status': 'completed',
|
66
|
+
'created_at': timezone.now().isoformat()
|
67
|
+
},
|
68
|
+
'timestamp': timezone.now().isoformat()
|
69
|
+
}
|
70
|
+
})
|
@@ -0,0 +1,114 @@
|
|
1
|
+
"""
|
2
|
+
Base ViewSet classes for Admin Interface API.
|
3
|
+
|
4
|
+
Common functionality for all admin interface ViewSets.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from rest_framework import viewsets, permissions, status
|
8
|
+
from rest_framework.decorators import action
|
9
|
+
from rest_framework.response import Response
|
10
|
+
from rest_framework.permissions import IsAdminUser
|
11
|
+
from django_filters.rest_framework import DjangoFilterBackend
|
12
|
+
from rest_framework.filters import SearchFilter, OrderingFilter
|
13
|
+
from django.contrib.admin.views.decorators import staff_member_required
|
14
|
+
from django.utils.decorators import method_decorator
|
15
|
+
from django.utils import timezone
|
16
|
+
from datetime import timedelta
|
17
|
+
|
18
|
+
|
19
|
+
from django_cfg.modules.django_logger import get_logger
|
20
|
+
|
21
|
+
logger = get_logger("admin_api")
|
22
|
+
|
23
|
+
|
24
|
+
class AdminBaseViewSet(viewsets.ModelViewSet):
|
25
|
+
"""
|
26
|
+
Base ViewSet for admin interface with staff permissions.
|
27
|
+
|
28
|
+
Provides standard CRUD operations with admin-only access.
|
29
|
+
"""
|
30
|
+
|
31
|
+
permission_classes = [IsAdminUser]
|
32
|
+
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
33
|
+
ordering = ['-created_at']
|
34
|
+
|
35
|
+
# Serializer classes mapping for different actions
|
36
|
+
serializer_classes = {}
|
37
|
+
|
38
|
+
def get_queryset(self):
|
39
|
+
"""
|
40
|
+
Optimized queryset for admin interface.
|
41
|
+
|
42
|
+
Override in subclasses to add specific optimizations.
|
43
|
+
"""
|
44
|
+
queryset = super().get_queryset()
|
45
|
+
|
46
|
+
# Add common optimizations for admin
|
47
|
+
if hasattr(self.queryset.model, 'user'):
|
48
|
+
queryset = queryset.select_related('user')
|
49
|
+
|
50
|
+
return queryset
|
51
|
+
|
52
|
+
def get_serializer_class(self):
|
53
|
+
"""
|
54
|
+
Dynamic serializer selection based on action.
|
55
|
+
"""
|
56
|
+
serializer_classes = getattr(self, 'serializer_classes', {})
|
57
|
+
return serializer_classes.get(self.action, self.serializer_class)
|
58
|
+
|
59
|
+
def get_serializer_context(self):
|
60
|
+
"""Enhanced context for admin serializers."""
|
61
|
+
context = super().get_serializer_context()
|
62
|
+
context.update({
|
63
|
+
'is_admin': True,
|
64
|
+
'admin_user': self.request.user,
|
65
|
+
})
|
66
|
+
return context
|
67
|
+
|
68
|
+
@action(detail=False, methods=['get'])
|
69
|
+
def stats(self, request):
|
70
|
+
"""Get statistics for this resource."""
|
71
|
+
queryset = self.get_queryset()
|
72
|
+
stats = {
|
73
|
+
'total': queryset.count(),
|
74
|
+
'recent': queryset.filter(
|
75
|
+
created_at__gte=timezone.now() - timedelta(days=7)
|
76
|
+
).count(),
|
77
|
+
}
|
78
|
+
return Response(stats)
|
79
|
+
|
80
|
+
|
81
|
+
class AdminReadOnlyViewSet(viewsets.ReadOnlyModelViewSet):
|
82
|
+
"""
|
83
|
+
Read-only ViewSet for admin interface.
|
84
|
+
|
85
|
+
For resources that should only be viewed, not modified.
|
86
|
+
"""
|
87
|
+
|
88
|
+
permission_classes = [IsAdminUser]
|
89
|
+
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
90
|
+
ordering = ['-created_at']
|
91
|
+
|
92
|
+
def get_serializer_context(self):
|
93
|
+
"""Enhanced context for admin serializers."""
|
94
|
+
context = super().get_serializer_context()
|
95
|
+
context.update({
|
96
|
+
'is_admin': True,
|
97
|
+
'admin_user': self.request.user,
|
98
|
+
})
|
99
|
+
return context
|
100
|
+
|
101
|
+
|
102
|
+
class AdminTemplateViewMixin:
|
103
|
+
"""
|
104
|
+
Mixin for template views requiring staff access.
|
105
|
+
"""
|
106
|
+
|
107
|
+
def get_context_data(self, **kwargs):
|
108
|
+
"""Add admin-specific context."""
|
109
|
+
context = super().get_context_data(**kwargs)
|
110
|
+
context.update({
|
111
|
+
'is_admin_interface': True,
|
112
|
+
'admin_user': self.request.user,
|
113
|
+
})
|
114
|
+
return context
|
@@ -0,0 +1,60 @@
|
|
1
|
+
"""
|
2
|
+
Admin Dashboard Template Views.
|
3
|
+
|
4
|
+
Django template views for admin dashboard interface.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from django.views.generic import TemplateView
|
8
|
+
from django.contrib.auth.mixins import LoginRequiredMixin
|
9
|
+
from django.contrib.admin.views.decorators import staff_member_required
|
10
|
+
from django.utils.decorators import method_decorator
|
11
|
+
|
12
|
+
from .base import AdminTemplateViewMixin
|
13
|
+
|
14
|
+
|
15
|
+
@method_decorator(staff_member_required, name='dispatch')
|
16
|
+
class PaymentDashboardView(AdminTemplateViewMixin, LoginRequiredMixin, TemplateView):
|
17
|
+
"""
|
18
|
+
Main admin dashboard view.
|
19
|
+
|
20
|
+
Displays overview of payments, webhooks, and system statistics.
|
21
|
+
"""
|
22
|
+
|
23
|
+
template_name = 'payments/payment_dashboard.html'
|
24
|
+
|
25
|
+
def get_context_data(self, **kwargs):
|
26
|
+
"""Add dashboard context data."""
|
27
|
+
context = super().get_context_data(**kwargs)
|
28
|
+
|
29
|
+
context.update({
|
30
|
+
'page_title': 'Payment Dashboard',
|
31
|
+
'page_subtitle': 'Overview of payment system activity',
|
32
|
+
'show_stats': True,
|
33
|
+
'auto_refresh': True,
|
34
|
+
})
|
35
|
+
|
36
|
+
return context
|
37
|
+
|
38
|
+
|
39
|
+
@method_decorator(staff_member_required, name='dispatch')
|
40
|
+
class WebhookDashboardView(AdminTemplateViewMixin, LoginRequiredMixin, TemplateView):
|
41
|
+
"""
|
42
|
+
Webhook dashboard view.
|
43
|
+
|
44
|
+
Displays webhook events, provider status, and ngrok configuration.
|
45
|
+
"""
|
46
|
+
|
47
|
+
template_name = 'payments/webhook_dashboard.html'
|
48
|
+
|
49
|
+
def get_context_data(self, **kwargs):
|
50
|
+
"""Add webhook dashboard context data."""
|
51
|
+
context = super().get_context_data(**kwargs)
|
52
|
+
|
53
|
+
context.update({
|
54
|
+
'page_title': 'Webhook Dashboard',
|
55
|
+
'page_subtitle': 'Monitor and test webhook endpoints',
|
56
|
+
'show_ngrok_status': True,
|
57
|
+
'auto_refresh': True,
|
58
|
+
})
|
59
|
+
|
60
|
+
return context
|
@@ -0,0 +1,94 @@
|
|
1
|
+
"""
|
2
|
+
Admin Form Template Views.
|
3
|
+
|
4
|
+
Django template views for payment forms and detail pages.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from django.views.generic import TemplateView, DetailView
|
8
|
+
from django.contrib.auth.mixins import LoginRequiredMixin
|
9
|
+
from django.contrib.admin.views.decorators import staff_member_required
|
10
|
+
from django.utils.decorators import method_decorator
|
11
|
+
from django.shortcuts import get_object_or_404
|
12
|
+
|
13
|
+
from .base import AdminTemplateViewMixin
|
14
|
+
from django_cfg.apps.payments.models import UniversalPayment
|
15
|
+
|
16
|
+
|
17
|
+
@method_decorator(staff_member_required, name='dispatch')
|
18
|
+
class PaymentFormView(AdminTemplateViewMixin, LoginRequiredMixin, TemplateView):
|
19
|
+
"""
|
20
|
+
Payment creation form view.
|
21
|
+
|
22
|
+
Displays form for creating new payments with provider selection.
|
23
|
+
"""
|
24
|
+
|
25
|
+
template_name = 'payments/payment_form.html'
|
26
|
+
|
27
|
+
def get_context_data(self, **kwargs):
|
28
|
+
"""Add form context data."""
|
29
|
+
context = super().get_context_data(**kwargs)
|
30
|
+
|
31
|
+
context.update({
|
32
|
+
'page_title': 'Create Payment',
|
33
|
+
'page_subtitle': 'Process a payment through the universal payment system',
|
34
|
+
'form_mode': 'create',
|
35
|
+
})
|
36
|
+
|
37
|
+
return context
|
38
|
+
|
39
|
+
|
40
|
+
@method_decorator(staff_member_required, name='dispatch')
|
41
|
+
class PaymentDetailView(AdminTemplateViewMixin, LoginRequiredMixin, DetailView):
|
42
|
+
"""
|
43
|
+
Payment detail view.
|
44
|
+
|
45
|
+
Displays detailed information about a specific payment.
|
46
|
+
"""
|
47
|
+
|
48
|
+
model = UniversalPayment
|
49
|
+
template_name = 'payments/payment_detail.html'
|
50
|
+
context_object_name = 'payment'
|
51
|
+
|
52
|
+
def get_queryset(self):
|
53
|
+
"""Optimized queryset with related objects."""
|
54
|
+
return UniversalPayment.objects.select_related('user')
|
55
|
+
|
56
|
+
def get_context_data(self, **kwargs):
|
57
|
+
"""Add detail context data."""
|
58
|
+
context = super().get_context_data(**kwargs)
|
59
|
+
|
60
|
+
payment = self.get_object()
|
61
|
+
|
62
|
+
context.update({
|
63
|
+
'page_title': f'Payment {payment.internal_payment_id or payment.id}',
|
64
|
+
'page_subtitle': f'Payment details and transaction history',
|
65
|
+
'show_actions': True,
|
66
|
+
'can_cancel': payment.status in ['pending', 'confirming'],
|
67
|
+
'can_refund': payment.status == 'completed',
|
68
|
+
})
|
69
|
+
|
70
|
+
return context
|
71
|
+
|
72
|
+
|
73
|
+
@method_decorator(staff_member_required, name='dispatch')
|
74
|
+
class PaymentListView(AdminTemplateViewMixin, LoginRequiredMixin, TemplateView):
|
75
|
+
"""
|
76
|
+
Payment list view.
|
77
|
+
|
78
|
+
Displays paginated list of payments with filtering options.
|
79
|
+
"""
|
80
|
+
|
81
|
+
template_name = 'payments/payment_list.html'
|
82
|
+
|
83
|
+
def get_context_data(self, **kwargs):
|
84
|
+
"""Add list context data."""
|
85
|
+
context = super().get_context_data(**kwargs)
|
86
|
+
|
87
|
+
context.update({
|
88
|
+
'page_title': 'Payment List',
|
89
|
+
'page_subtitle': 'Manage and monitor all payments',
|
90
|
+
'show_filters': True,
|
91
|
+
'show_bulk_actions': True,
|
92
|
+
})
|
93
|
+
|
94
|
+
return context
|
@@ -33,8 +33,8 @@ class MiddlewareConfigHelper(PaymentsConfigMixin):
|
|
33
33
|
return {
|
34
34
|
# Static settings from django-cfg
|
35
35
|
'enabled': config.enabled and config.middleware_enabled,
|
36
|
-
'
|
37
|
-
'
|
36
|
+
'protected_paths': config.protected_paths,
|
37
|
+
'protected_patterns': config.protected_patterns,
|
38
38
|
'rate_limiting_enabled': config.rate_limiting_enabled,
|
39
39
|
'default_rate_limits': config.default_rate_limits,
|
40
40
|
'usage_tracking_enabled': config.usage_tracking_enabled,
|