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.
Files changed (101) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin_interface/old/payments/base.html +175 -0
  3. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +125 -0
  4. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +113 -0
  5. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +35 -0
  6. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +309 -0
  7. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +303 -0
  8. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +382 -0
  9. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +518 -0
  10. django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/components.css +248 -9
  11. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +163 -0
  12. django_cfg/apps/payments/admin_interface/serializers/__init__.py +39 -0
  13. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +149 -0
  14. django_cfg/apps/payments/admin_interface/serializers/webhook_serializers.py +114 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/base.html +55 -90
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/dialog.html +81 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_help_dialog.html +112 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_status.html +175 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +21 -17
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +123 -250
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +170 -269
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +152 -355
  23. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +202 -551
  24. django_cfg/apps/payments/admin_interface/views/__init__.py +25 -14
  25. django_cfg/apps/payments/admin_interface/views/api/__init__.py +20 -0
  26. django_cfg/apps/payments/admin_interface/views/api/payments.py +191 -0
  27. django_cfg/apps/payments/admin_interface/views/api/stats.py +206 -0
  28. django_cfg/apps/payments/admin_interface/views/api/users.py +60 -0
  29. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +257 -0
  30. django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +70 -0
  31. django_cfg/apps/payments/admin_interface/views/base.py +114 -0
  32. django_cfg/apps/payments/admin_interface/views/dashboard.py +60 -0
  33. django_cfg/apps/payments/admin_interface/views/forms.py +94 -0
  34. django_cfg/apps/payments/config/helpers.py +2 -2
  35. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +16 -6
  36. django_cfg/apps/payments/management/commands/currency_stats.py +72 -5
  37. django_cfg/apps/payments/management/commands/manage_currencies.py +9 -20
  38. django_cfg/apps/payments/management/commands/manage_providers.py +5 -5
  39. django_cfg/apps/payments/middleware/api_access.py +35 -34
  40. django_cfg/apps/payments/migrations/0001_initial.py +1 -1
  41. django_cfg/apps/payments/models/managers/api_key_managers.py +4 -0
  42. django_cfg/apps/payments/models/managers/payment_managers.py +5 -0
  43. django_cfg/apps/payments/models/subscriptions.py +0 -24
  44. django_cfg/apps/payments/services/cache/__init__.py +1 -1
  45. django_cfg/apps/payments/services/core/balance_service.py +13 -2
  46. django_cfg/apps/payments/services/integrations/ngrok_service.py +3 -3
  47. django_cfg/apps/payments/services/providers/registry.py +20 -0
  48. django_cfg/apps/payments/signals/balance_signals.py +7 -4
  49. django_cfg/apps/payments/static/payments/js/api-client.js +385 -0
  50. django_cfg/apps/payments/static/payments/js/ngrok-status.js +58 -0
  51. django_cfg/apps/payments/static/payments/js/payment-dashboard.js +50 -0
  52. django_cfg/apps/payments/static/payments/js/payment-form.js +175 -0
  53. django_cfg/apps/payments/static/payments/js/payment-list.js +95 -0
  54. django_cfg/apps/payments/static/payments/js/webhook-dashboard.js +154 -0
  55. django_cfg/apps/payments/urls.py +4 -0
  56. django_cfg/apps/payments/urls_admin.py +37 -18
  57. django_cfg/apps/payments/views/api/api_keys.py +14 -0
  58. django_cfg/apps/payments/views/api/base.py +1 -0
  59. django_cfg/apps/payments/views/api/currencies.py +2 -2
  60. django_cfg/apps/payments/views/api/payments.py +11 -5
  61. django_cfg/apps/payments/views/api/subscriptions.py +36 -31
  62. django_cfg/apps/payments/views/overview/__init__.py +40 -0
  63. django_cfg/apps/payments/views/overview/serializers.py +205 -0
  64. django_cfg/apps/payments/views/overview/services.py +439 -0
  65. django_cfg/apps/payments/views/overview/urls.py +27 -0
  66. django_cfg/apps/payments/views/overview/views.py +231 -0
  67. django_cfg/apps/payments/views/serializers/api_keys.py +20 -6
  68. django_cfg/apps/payments/views/serializers/balances.py +5 -8
  69. django_cfg/apps/payments/views/serializers/currencies.py +2 -6
  70. django_cfg/apps/payments/views/serializers/payments.py +37 -32
  71. django_cfg/apps/payments/views/serializers/subscriptions.py +4 -26
  72. django_cfg/apps/urls.py +2 -1
  73. django_cfg/core/config.py +25 -15
  74. django_cfg/core/generation.py +12 -12
  75. django_cfg/core/integration/display/startup.py +1 -1
  76. django_cfg/core/validation.py +4 -4
  77. django_cfg/management/commands/show_config.py +2 -2
  78. django_cfg/management/commands/tree.py +1 -3
  79. django_cfg/middleware/__init__.py +2 -0
  80. django_cfg/middleware/static_nocache.py +55 -0
  81. django_cfg/models/payments.py +13 -15
  82. django_cfg/models/security.py +15 -0
  83. django_cfg/modules/django_ngrok.py +6 -0
  84. django_cfg/modules/django_unfold/dashboard.py +1 -3
  85. django_cfg/utils/smart_defaults.py +41 -1
  86. {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/METADATA +1 -1
  87. {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/RECORD +98 -65
  88. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +0 -38
  89. django_cfg/apps/payments/admin_interface/views/payment_views.py +0 -259
  90. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +0 -37
  91. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/loading_spinner.html +0 -0
  92. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/notification.html +0 -0
  93. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/provider_card.html +0 -0
  94. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/currency_converter.html +0 -0
  95. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/payment_status.html +0 -0
  96. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/dashboard.css +0 -0
  97. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/components.js +0 -0
  98. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/utils.js +0 -0
  99. {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/WHEEL +0 -0
  100. {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/entry_points.txt +0 -0
  101. {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/licenses/LICENSE +0 -0
@@ -128,13 +128,28 @@
128
128
 
129
129
  /* Card Components */
130
130
  .payment-card {
131
- @apply bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6;
131
+ background: white;
132
+ border: 1px solid #e5e7eb;
133
+ border-radius: 12px;
134
+ padding: 24px;
135
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
132
136
  transition: all 0.2s ease-in-out;
133
137
  }
134
138
 
135
139
  .payment-card:hover {
136
- @apply shadow-md;
137
- transform: translateY(-1px);
140
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
141
+ transform: translateY(-2px);
142
+ }
143
+
144
+ /* Dark theme cards */
145
+ .dark .payment-card {
146
+ background: #1f2937;
147
+ border-color: #374151;
148
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.3);
149
+ }
150
+
151
+ .dark .payment-card:hover {
152
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
138
153
  }
139
154
 
140
155
  .payment-card-header {
@@ -153,29 +168,253 @@
153
168
  @apply space-y-3;
154
169
  }
155
170
 
171
+ /* Status Cards */
172
+ .status-card {
173
+ background: white;
174
+ border: 1px solid #e5e7eb;
175
+ border-radius: 12px;
176
+ padding: 20px;
177
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
178
+ transition: all 0.2s ease-in-out;
179
+ }
180
+
181
+ .status-card:hover {
182
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
183
+ transform: translateY(-1px);
184
+ }
185
+
186
+ .status-card-content {
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 16px;
190
+ }
191
+
192
+ .status-card-icon {
193
+ width: 48px;
194
+ height: 48px;
195
+ border-radius: 12px;
196
+ display: flex;
197
+ align-items: center;
198
+ justify-content: center;
199
+ font-size: 24px;
200
+ }
201
+
202
+ .status-card-info {
203
+ flex: 1;
204
+ }
205
+
206
+ .status-card-title {
207
+ font-size: 14px;
208
+ font-weight: 500;
209
+ color: #6b7280;
210
+ margin-bottom: 4px;
211
+ }
212
+
213
+ .status-card-value {
214
+ font-size: 28px;
215
+ font-weight: 700;
216
+ display: flex;
217
+ align-items: center;
218
+ gap: 8px;
219
+ margin-bottom: 4px;
220
+ }
221
+
222
+ .status-card-description {
223
+ font-size: 12px;
224
+ color: #9ca3af;
225
+ margin: 0;
226
+ }
227
+
228
+ .status-card-action-btn {
229
+ padding: 8px;
230
+ border-radius: 8px;
231
+ color: #6b7280;
232
+ transition: all 0.2s;
233
+ }
234
+
235
+ .status-card-action-btn:hover {
236
+ background: #f3f4f6;
237
+ color: #374151;
238
+ }
239
+
240
+ /* Status Card Color Variants */
241
+ .status-card-blue .status-card-icon {
242
+ background: #dbeafe;
243
+ color: #2563eb;
244
+ }
245
+
246
+ .status-card-blue .status-card-value {
247
+ color: #2563eb;
248
+ }
249
+
250
+ .status-card-green .status-card-icon {
251
+ background: #dcfce7;
252
+ color: #16a34a;
253
+ }
254
+
255
+ .status-card-green .status-card-value {
256
+ color: #16a34a;
257
+ }
258
+
259
+ .status-card-red .status-card-icon {
260
+ background: #fee2e2;
261
+ color: #dc2626;
262
+ }
263
+
264
+ .status-card-red .status-card-value {
265
+ color: #dc2626;
266
+ }
267
+
268
+ .status-card-yellow .status-card-icon {
269
+ background: #fef3c7;
270
+ color: #d97706;
271
+ }
272
+
273
+ .status-card-yellow .status-card-value {
274
+ color: #d97706;
275
+ }
276
+
277
+ .status-card-purple .status-card-icon {
278
+ background: #f3e8ff;
279
+ color: #9333ea;
280
+ }
281
+
282
+ .status-card-purple .status-card-value {
283
+ color: #9333ea;
284
+ }
285
+
286
+ /* Dark Theme Status Cards */
287
+ .dark .status-card {
288
+ background: #1f2937;
289
+ border-color: #374151;
290
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.3);
291
+ }
292
+
293
+ .dark .status-card:hover {
294
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
295
+ }
296
+
297
+ .dark .status-card-title {
298
+ color: #d1d5db;
299
+ }
300
+
301
+ .dark .status-card-description {
302
+ color: #9ca3af;
303
+ }
304
+
305
+ .dark .status-card-action-btn {
306
+ color: #9ca3af;
307
+ }
308
+
309
+ .dark .status-card-action-btn:hover {
310
+ background: #374151;
311
+ color: #f3f4f6;
312
+ }
313
+
314
+ .dark .status-card-blue .status-card-icon {
315
+ background: #1e3a8a;
316
+ color: #60a5fa;
317
+ }
318
+
319
+ .dark .status-card-blue .status-card-value {
320
+ color: #60a5fa;
321
+ }
322
+
323
+ .dark .status-card-green .status-card-icon {
324
+ background: #14532d;
325
+ color: #4ade80;
326
+ }
327
+
328
+ .dark .status-card-green .status-card-value {
329
+ color: #4ade80;
330
+ }
331
+
332
+ .dark .status-card-red .status-card-icon {
333
+ background: #7f1d1d;
334
+ color: #f87171;
335
+ }
336
+
337
+ .dark .status-card-red .status-card-value {
338
+ color: #f87171;
339
+ }
340
+
341
+ .dark .status-card-yellow .status-card-icon {
342
+ background: #92400e;
343
+ color: #fbbf24;
344
+ }
345
+
346
+ .dark .status-card-yellow .status-card-value {
347
+ color: #fbbf24;
348
+ }
349
+
350
+ .dark .status-card-purple .status-card-icon {
351
+ background: #581c87;
352
+ color: #c084fc;
353
+ }
354
+
355
+ .dark .status-card-purple .status-card-value {
356
+ color: #c084fc;
357
+ }
358
+
156
359
  /* Status Badges */
157
360
  .status-badge {
158
- @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
361
+ display: inline-flex;
362
+ align-items: center;
363
+ padding: 4px 12px;
364
+ border-radius: 20px;
365
+ font-size: 12px;
366
+ font-weight: 500;
159
367
  }
160
368
 
161
369
  .status-badge.success {
162
- @apply bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200;
370
+ background: #dcfce7;
371
+ color: #166534;
163
372
  }
164
373
 
165
374
  .status-badge.error {
166
- @apply bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200;
375
+ background: #fee2e2;
376
+ color: #991b1b;
167
377
  }
168
378
 
169
379
  .status-badge.warning {
170
- @apply bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200;
380
+ background: #fef3c7;
381
+ color: #92400e;
171
382
  }
172
383
 
173
384
  .status-badge.info {
174
- @apply bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200;
385
+ background: #dbeafe;
386
+ color: #1d4ed8;
175
387
  }
176
388
 
177
389
  .status-badge.pending {
178
- @apply bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200;
390
+ background: #f3f4f6;
391
+ color: #374151;
392
+ }
393
+
394
+ /* Dark theme badges */
395
+ .dark .status-badge.success {
396
+ background: #14532d;
397
+ color: #4ade80;
398
+ }
399
+
400
+ .dark .status-badge.error {
401
+ background: #7f1d1d;
402
+ color: #f87171;
403
+ }
404
+
405
+ .dark .status-badge.warning {
406
+ background: #92400e;
407
+ color: #fbbf24;
408
+ }
409
+
410
+ .dark .status-badge.info {
411
+ background: #1e3a8a;
412
+ color: #60a5fa;
413
+ }
414
+
415
+ .dark .status-badge.pending {
416
+ background: #374151;
417
+ color: #d1d5db;
179
418
  }
180
419
 
181
420
  /* Button Components */
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Ngrok Status Component
3
+ *
4
+ * Alpine.js component for displaying and managing ngrok tunnel status.
5
+ */
6
+
7
+ // Ensure PaymentSystem namespace exists
8
+ window.PaymentSystem = window.PaymentSystem || {};
9
+ window.PaymentSystem.Components = window.PaymentSystem.Components || {};
10
+
11
+ /**
12
+ * Ngrok Status Card Component
13
+ */
14
+ window.PaymentSystem.Components.ngrokStatus = function() {
15
+ return {
16
+ status: {
17
+ active: false,
18
+ public_url: '',
19
+ webhook_url: '',
20
+ region: 'us',
21
+ proto: 'https',
22
+ error: null
23
+ },
24
+ refreshing: false,
25
+
26
+ async init() {
27
+ await this.loadStatus();
28
+ // Auto-refresh every 30 seconds
29
+ setInterval(() => this.loadStatus(), 30000);
30
+ },
31
+
32
+ async loadStatus() {
33
+ try {
34
+ // Use our health check endpoint instead of direct ngrok API
35
+ const response = await fetch('/api/payments/webhooks/health/');
36
+ if (response.ok) {
37
+ const data = await response.json();
38
+ const ngrokActive = data.details?.ngrok_available || false;
39
+ const apiUrl = data.details?.api_base_url || '';
40
+
41
+ if (ngrokActive) {
42
+ this.status = {
43
+ active: true,
44
+ public_url: apiUrl,
45
+ webhook_url: apiUrl + '/api/payments/webhooks/',
46
+ region: 'auto',
47
+ proto: apiUrl.startsWith('https') ? 'https' : 'http',
48
+ error: null
49
+ };
50
+ } else {
51
+ this.status = {
52
+ active: false,
53
+ public_url: '',
54
+ webhook_url: '',
55
+ region: 'us',
56
+ proto: 'https',
57
+ error: 'Ngrok tunnel not active'
58
+ };
59
+ }
60
+ } else {
61
+ this.status = {
62
+ active: false,
63
+ public_url: '',
64
+ webhook_url: '',
65
+ region: 'us',
66
+ proto: 'https',
67
+ error: 'Health check API not accessible'
68
+ };
69
+ }
70
+ } catch (error) {
71
+ console.error('Failed to fetch Ngrok status:', error);
72
+ this.status = {
73
+ active: false,
74
+ public_url: '',
75
+ webhook_url: '',
76
+ region: 'us',
77
+ proto: 'https',
78
+ error: error.message || 'Failed to check ngrok status'
79
+ };
80
+ }
81
+ },
82
+
83
+ async refreshStatus() {
84
+ this.refreshing = true;
85
+ try {
86
+ await this.loadStatus();
87
+ } finally {
88
+ this.refreshing = false;
89
+ }
90
+ },
91
+
92
+ async copyUrl() {
93
+ if (this.status.public_url) {
94
+ try {
95
+ await navigator.clipboard.writeText(this.status.public_url);
96
+ this.$dispatch('show-notification', {
97
+ type: 'success',
98
+ message: 'Public URL copied to clipboard'
99
+ });
100
+ } catch (error) {
101
+ console.error('Failed to copy URL:', error);
102
+ this.$dispatch('show-notification', {
103
+ type: 'error',
104
+ message: 'Failed to copy URL to clipboard'
105
+ });
106
+ }
107
+ }
108
+ },
109
+
110
+ async copyWebhookUrl() {
111
+ if (this.status.webhook_url) {
112
+ try {
113
+ await navigator.clipboard.writeText(this.status.webhook_url);
114
+ this.$dispatch('show-notification', {
115
+ type: 'success',
116
+ message: 'Webhook URL copied to clipboard'
117
+ });
118
+ } catch (error) {
119
+ console.error('Failed to copy webhook URL:', error);
120
+ this.$dispatch('show-notification', {
121
+ type: 'error',
122
+ message: 'Failed to copy webhook URL to clipboard'
123
+ });
124
+ }
125
+ }
126
+ },
127
+
128
+ openInBrowser() {
129
+ if (this.status.public_url) {
130
+ window.open(this.status.public_url, '_blank');
131
+ }
132
+ },
133
+
134
+ async startTunnel() {
135
+ // This would typically call a backend endpoint to start ngrok
136
+ this.$dispatch('show-notification', {
137
+ type: 'info',
138
+ message: 'Starting ngrok tunnel... (This feature requires backend implementation)'
139
+ });
140
+ },
141
+
142
+ async stopTunnel() {
143
+ // This would typically call a backend endpoint to stop ngrok
144
+ this.$dispatch('show-notification', {
145
+ type: 'info',
146
+ message: 'Stopping ngrok tunnel... (This feature requires backend implementation)'
147
+ });
148
+ }
149
+ };
150
+ };
151
+
152
+ // Global function for backward compatibility
153
+ window.ngrokStatus = window.PaymentSystem.Components.ngrokStatus;
154
+
155
+ // Also define it directly for immediate availability
156
+ if (!window.ngrokStatus) {
157
+ window.ngrokStatus = window.PaymentSystem.Components.ngrokStatus;
158
+ }
159
+
160
+ // Debug: Log that the component is loaded
161
+ console.log('Ngrok Status component loaded:', typeof window.ngrokStatus);
162
+ console.log('PaymentSystem namespace:', window.PaymentSystem);
163
+ console.log('Available functions:', Object.keys(window).filter(key => key.includes('ngrok')));
@@ -0,0 +1,39 @@
1
+ """
2
+ Admin Interface Serializers for Universal Payment System v2.0.
3
+
4
+ DRF serializers for admin dashboard API endpoints.
5
+ """
6
+
7
+ from .webhook_serializers import (
8
+ WebhookEventSerializer,
9
+ WebhookEventListSerializer,
10
+ WebhookStatsSerializer,
11
+ WebhookActionSerializer,
12
+ WebhookActionResultSerializer,
13
+ )
14
+
15
+ from .payment_serializers import (
16
+ AdminUserSerializer,
17
+ AdminPaymentListSerializer,
18
+ AdminPaymentDetailSerializer,
19
+ AdminPaymentCreateSerializer,
20
+ AdminPaymentUpdateSerializer,
21
+ AdminPaymentStatsSerializer,
22
+ )
23
+
24
+ __all__ = [
25
+ # Webhook serializers
26
+ 'WebhookEventSerializer',
27
+ 'WebhookEventListSerializer',
28
+ 'WebhookStatsSerializer',
29
+ 'WebhookActionSerializer',
30
+ 'WebhookActionResultSerializer',
31
+
32
+ # Payment serializers
33
+ 'AdminUserSerializer',
34
+ 'AdminPaymentListSerializer',
35
+ 'AdminPaymentDetailSerializer',
36
+ 'AdminPaymentCreateSerializer',
37
+ 'AdminPaymentUpdateSerializer',
38
+ 'AdminPaymentStatsSerializer',
39
+ ]
@@ -0,0 +1,149 @@
1
+ """
2
+ Payment Serializers for Admin Interface.
3
+
4
+ DRF serializers for payment management in admin dashboard.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+ from django.contrib.auth import get_user_model
9
+ from ...models import UniversalPayment
10
+
11
+ User = get_user_model()
12
+
13
+
14
+ class AdminUserSerializer(serializers.ModelSerializer):
15
+ """
16
+ Simplified user serializer for admin interface.
17
+ """
18
+ class Meta:
19
+ model = User
20
+ fields = ['id', 'username', 'email', 'first_name', 'last_name', 'is_active']
21
+ read_only_fields = fields
22
+
23
+
24
+ class AdminPaymentListSerializer(serializers.ModelSerializer):
25
+ """
26
+ Serializer for payment list in admin interface.
27
+ """
28
+ user = AdminUserSerializer(read_only=True)
29
+ status_display = serializers.CharField(source='get_status_display', read_only=True)
30
+ provider_display = serializers.CharField(source='get_provider_display', read_only=True)
31
+ age = serializers.SerializerMethodField()
32
+
33
+ class Meta:
34
+ model = UniversalPayment
35
+ fields = [
36
+ 'id', 'user', 'amount_usd', 'currency_code', 'provider', 'provider_display',
37
+ 'status', 'status_display', 'pay_amount', 'pay_address', 'transaction_hash',
38
+ 'created_at', 'updated_at', 'age', 'description'
39
+ ]
40
+ read_only_fields = fields
41
+
42
+ def get_age(self, obj):
43
+ """Get human-readable age of payment."""
44
+ return obj.age_display
45
+
46
+
47
+ class AdminPaymentDetailSerializer(serializers.ModelSerializer):
48
+ """
49
+ Detailed serializer for individual payment in admin interface.
50
+ """
51
+ user = AdminUserSerializer(read_only=True)
52
+ status_display = serializers.CharField(source='get_status_display', read_only=True)
53
+ provider_display = serializers.CharField(source='get_provider_display', read_only=True)
54
+ age = serializers.SerializerMethodField()
55
+
56
+ class Meta:
57
+ model = UniversalPayment
58
+ fields = [
59
+ 'id', 'user', 'internal_payment_id', 'amount_usd', 'actual_amount_usd',
60
+ 'fee_amount_usd', 'currency_code', 'provider', 'provider_display',
61
+ 'status', 'status_display', 'pay_amount', 'pay_address', 'payment_url',
62
+ 'transaction_hash', 'confirmations_count', 'security_nonce',
63
+ 'expires_at', 'completed_at', 'description', 'callback_url', 'cancel_url',
64
+ 'provider_data', 'webhook_data', 'created_at', 'updated_at', 'age'
65
+ ]
66
+ read_only_fields = fields
67
+
68
+ def get_age(self, obj):
69
+ """Get human-readable age of payment."""
70
+ return obj.age_display
71
+
72
+
73
+ class AdminPaymentCreateSerializer(serializers.ModelSerializer):
74
+ """
75
+ Serializer for creating payments in admin interface.
76
+ """
77
+ class Meta:
78
+ model = UniversalPayment
79
+ fields = [
80
+ 'user', 'amount_usd', 'currency_code', 'provider',
81
+ 'description', 'callback_url', 'cancel_url'
82
+ ]
83
+
84
+ def validate_amount_usd(self, value):
85
+ """Validate USD amount."""
86
+ if value <= 0:
87
+ raise serializers.ValidationError("Amount must be positive")
88
+ if value > 100000: # Max $100k per payment
89
+ raise serializers.ValidationError("Amount exceeds maximum limit")
90
+ return value
91
+
92
+
93
+ class AdminPaymentUpdateSerializer(serializers.ModelSerializer):
94
+ """
95
+ Serializer for updating payments in admin interface.
96
+ """
97
+ class Meta:
98
+ model = UniversalPayment
99
+ fields = [
100
+ 'status', 'description', 'callback_url', 'cancel_url',
101
+ 'provider_data', 'webhook_data'
102
+ ]
103
+
104
+ def validate_status(self, value):
105
+ """Validate status transitions."""
106
+ if self.instance and self.instance.status == UniversalPayment.PaymentStatus.COMPLETED:
107
+ if value != UniversalPayment.PaymentStatus.COMPLETED:
108
+ raise serializers.ValidationError("Cannot change status of completed payment")
109
+ return value
110
+
111
+
112
+ class AdminPaymentStatsSerializer(serializers.Serializer):
113
+ """
114
+ Serializer for payment statistics in admin interface.
115
+ """
116
+ total_payments = serializers.IntegerField()
117
+ total_amount_usd = serializers.FloatField()
118
+ successful_payments = serializers.IntegerField()
119
+ failed_payments = serializers.IntegerField()
120
+ pending_payments = serializers.IntegerField()
121
+ success_rate = serializers.FloatField()
122
+
123
+ # Provider breakdown
124
+ by_provider = serializers.DictField(
125
+ child=serializers.DictField(),
126
+ help_text="Statistics by provider"
127
+ )
128
+
129
+ # Currency breakdown
130
+ by_currency = serializers.DictField(
131
+ child=serializers.DictField(),
132
+ help_text="Statistics by currency"
133
+ )
134
+
135
+ # Time-based stats
136
+ last_24h = serializers.DictField(
137
+ child=serializers.IntegerField(),
138
+ help_text="Payments in last 24 hours"
139
+ )
140
+
141
+ last_7d = serializers.DictField(
142
+ child=serializers.IntegerField(),
143
+ help_text="Payments in last 7 days"
144
+ )
145
+
146
+ last_30d = serializers.DictField(
147
+ child=serializers.IntegerField(),
148
+ help_text="Payments in last 30 days"
149
+ )