django-cfg 1.2.27__py3-none-any.whl → 1.2.31__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 (138) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin/__init__.py +3 -2
  3. django_cfg/apps/payments/admin/balance_admin.py +18 -18
  4. django_cfg/apps/payments/admin/currencies_admin.py +319 -131
  5. django_cfg/apps/payments/admin/payments_admin.py +15 -4
  6. django_cfg/apps/payments/config/module.py +2 -2
  7. django_cfg/apps/payments/config/utils.py +2 -2
  8. django_cfg/apps/payments/decorators.py +2 -2
  9. django_cfg/apps/payments/management/commands/README.md +95 -127
  10. django_cfg/apps/payments/management/commands/currency_stats.py +5 -24
  11. django_cfg/apps/payments/management/commands/manage_currencies.py +229 -0
  12. django_cfg/apps/payments/management/commands/manage_providers.py +235 -0
  13. django_cfg/apps/payments/managers/__init__.py +3 -2
  14. django_cfg/apps/payments/managers/balance_manager.py +2 -2
  15. django_cfg/apps/payments/managers/currency_manager.py +272 -49
  16. django_cfg/apps/payments/managers/payment_manager.py +161 -13
  17. django_cfg/apps/payments/middleware/api_access.py +2 -2
  18. django_cfg/apps/payments/middleware/rate_limiting.py +8 -18
  19. django_cfg/apps/payments/middleware/usage_tracking.py +20 -17
  20. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +241 -0
  21. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +30 -0
  22. django_cfg/apps/payments/models/__init__.py +3 -2
  23. django_cfg/apps/payments/models/currencies.py +187 -71
  24. django_cfg/apps/payments/models/payments.py +3 -2
  25. django_cfg/apps/payments/serializers/__init__.py +3 -2
  26. django_cfg/apps/payments/serializers/currencies.py +20 -12
  27. django_cfg/apps/payments/services/cache/simple_cache.py +2 -2
  28. django_cfg/apps/payments/services/core/balance_service.py +2 -2
  29. django_cfg/apps/payments/services/core/fallback_service.py +2 -2
  30. django_cfg/apps/payments/services/core/payment_service.py +3 -6
  31. django_cfg/apps/payments/services/core/subscription_service.py +4 -7
  32. django_cfg/apps/payments/services/internal_types.py +171 -7
  33. django_cfg/apps/payments/services/monitoring/api_schemas.py +58 -204
  34. django_cfg/apps/payments/services/monitoring/provider_health.py +2 -2
  35. django_cfg/apps/payments/services/providers/base.py +144 -43
  36. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +4 -0
  37. django_cfg/apps/payments/services/providers/cryptapi/config.py +8 -0
  38. django_cfg/apps/payments/services/providers/cryptapi/models.py +192 -0
  39. django_cfg/apps/payments/services/providers/cryptapi/provider.py +439 -0
  40. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +4 -0
  41. django_cfg/apps/payments/services/providers/cryptomus/models.py +176 -0
  42. django_cfg/apps/payments/services/providers/cryptomus/provider.py +429 -0
  43. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +564 -0
  44. django_cfg/apps/payments/services/providers/models/__init__.py +34 -0
  45. django_cfg/apps/payments/services/providers/models/currencies.py +190 -0
  46. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +4 -0
  47. django_cfg/apps/payments/services/providers/nowpayments/models.py +196 -0
  48. django_cfg/apps/payments/services/providers/nowpayments/provider.py +380 -0
  49. django_cfg/apps/payments/services/providers/registry.py +294 -11
  50. django_cfg/apps/payments/services/providers/stripe/__init__.py +4 -0
  51. django_cfg/apps/payments/services/providers/stripe/models.py +184 -0
  52. django_cfg/apps/payments/services/providers/stripe/provider.py +109 -0
  53. django_cfg/apps/payments/services/security/error_handler.py +6 -8
  54. django_cfg/apps/payments/services/security/payment_notifications.py +2 -2
  55. django_cfg/apps/payments/services/security/webhook_validator.py +3 -4
  56. django_cfg/apps/payments/signals/api_key_signals.py +2 -2
  57. django_cfg/apps/payments/signals/payment_signals.py +11 -5
  58. django_cfg/apps/payments/signals/subscription_signals.py +2 -2
  59. django_cfg/apps/payments/static/payments/css/payments.css +340 -0
  60. django_cfg/apps/payments/static/payments/js/notifications.js +202 -0
  61. django_cfg/apps/payments/static/payments/js/payment-utils.js +318 -0
  62. django_cfg/apps/payments/static/payments/js/theme.js +86 -0
  63. django_cfg/apps/payments/tasks/webhook_processing.py +2 -2
  64. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +50 -0
  65. django_cfg/apps/payments/templates/payments/base.html +182 -0
  66. django_cfg/apps/payments/templates/payments/components/payment_card.html +201 -0
  67. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +109 -0
  68. django_cfg/apps/payments/templates/payments/components/progress_bar.html +43 -0
  69. django_cfg/apps/payments/templates/payments/components/provider_stats.html +40 -0
  70. django_cfg/apps/payments/templates/payments/components/status_badge.html +34 -0
  71. django_cfg/apps/payments/templates/payments/components/status_overview.html +148 -0
  72. django_cfg/apps/payments/templates/payments/dashboard.html +258 -0
  73. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +35 -0
  74. django_cfg/apps/payments/templates/payments/payment_create.html +579 -0
  75. django_cfg/apps/payments/templates/payments/payment_detail.html +373 -0
  76. django_cfg/apps/payments/templates/payments/payment_list.html +354 -0
  77. django_cfg/apps/payments/templates/payments/stats.html +261 -0
  78. django_cfg/apps/payments/templates/payments/test.html +213 -0
  79. django_cfg/apps/payments/templatetags/__init__.py +1 -0
  80. django_cfg/apps/payments/templatetags/payments_tags.py +315 -0
  81. django_cfg/apps/payments/urls.py +3 -1
  82. django_cfg/apps/payments/urls_admin.py +58 -0
  83. django_cfg/apps/payments/utils/__init__.py +1 -3
  84. django_cfg/apps/payments/utils/billing_utils.py +2 -2
  85. django_cfg/apps/payments/utils/config_utils.py +2 -8
  86. django_cfg/apps/payments/utils/validation_utils.py +2 -2
  87. django_cfg/apps/payments/views/__init__.py +3 -2
  88. django_cfg/apps/payments/views/currency_views.py +31 -20
  89. django_cfg/apps/payments/views/payment_views.py +2 -2
  90. django_cfg/apps/payments/views/templates/__init__.py +25 -0
  91. django_cfg/apps/payments/views/templates/ajax.py +451 -0
  92. django_cfg/apps/payments/views/templates/base.py +212 -0
  93. django_cfg/apps/payments/views/templates/dashboard.py +60 -0
  94. django_cfg/apps/payments/views/templates/payment_detail.py +102 -0
  95. django_cfg/apps/payments/views/templates/payment_management.py +158 -0
  96. django_cfg/apps/payments/views/templates/qr_code.py +174 -0
  97. django_cfg/apps/payments/views/templates/stats.py +244 -0
  98. django_cfg/apps/payments/views/templates/utils.py +181 -0
  99. django_cfg/apps/payments/views/webhook_views.py +2 -2
  100. django_cfg/apps/payments/viewsets.py +3 -2
  101. django_cfg/apps/tasks/urls.py +0 -2
  102. django_cfg/apps/tasks/urls_admin.py +14 -0
  103. django_cfg/apps/urls.py +6 -3
  104. django_cfg/core/config.py +35 -0
  105. django_cfg/models/payments.py +2 -8
  106. django_cfg/modules/django_currency/__init__.py +16 -11
  107. django_cfg/modules/django_currency/clients/__init__.py +4 -4
  108. django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
  109. django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
  110. django_cfg/modules/django_currency/core/__init__.py +1 -7
  111. django_cfg/modules/django_currency/core/converter.py +18 -23
  112. django_cfg/modules/django_currency/core/models.py +122 -11
  113. django_cfg/modules/django_currency/database/__init__.py +4 -4
  114. django_cfg/modules/django_currency/database/database_loader.py +190 -309
  115. django_cfg/modules/django_unfold/dashboard.py +7 -2
  116. django_cfg/registry/core.py +1 -0
  117. django_cfg/template_archive/.gitignore +1 -0
  118. django_cfg/template_archive/django_sample.zip +0 -0
  119. django_cfg/templates/admin/components/action_grid.html +9 -9
  120. django_cfg/templates/admin/components/metric_card.html +5 -5
  121. django_cfg/templates/admin/components/status_badge.html +2 -2
  122. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
  123. django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
  124. django_cfg/templates/admin/snippets/components/system_health.html +1 -1
  125. django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
  126. {django_cfg-1.2.27.dist-info → django_cfg-1.2.31.dist-info}/METADATA +13 -18
  127. {django_cfg-1.2.27.dist-info → django_cfg-1.2.31.dist-info}/RECORD +130 -83
  128. django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
  129. django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
  130. django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
  131. django_cfg/apps/payments/services/providers/cryptomus.py +0 -310
  132. django_cfg/apps/payments/services/providers/nowpayments.py +0 -293
  133. django_cfg/apps/payments/services/validators/__init__.py +0 -8
  134. django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
  135. django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
  136. {django_cfg-1.2.27.dist-info → django_cfg-1.2.31.dist-info}/WHEEL +0 -0
  137. {django_cfg-1.2.27.dist-info → django_cfg-1.2.31.dist-info}/entry_points.txt +0 -0
  138. {django_cfg-1.2.27.dist-info → django_cfg-1.2.31.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,174 @@
1
+ """
2
+ QR code views for crypto payments.
3
+
4
+ Provides QR code generation and display for cryptocurrency payments.
5
+ """
6
+
7
+ from django.views.generic import DetailView
8
+ from django.http import JsonResponse
9
+ from .base import (
10
+ SuperuserRequiredMixin,
11
+ PaymentContextMixin,
12
+ superuser_required,
13
+ log_view_access
14
+ )
15
+ from ...models import UniversalPayment
16
+
17
+
18
+ class PaymentQRCodeView(
19
+ SuperuserRequiredMixin,
20
+ PaymentContextMixin,
21
+ DetailView
22
+ ):
23
+ """QR code view for crypto payments."""
24
+
25
+ model = UniversalPayment
26
+ template_name = 'payments/payment_qr.html'
27
+ context_object_name = 'payment'
28
+ page_title = 'Payment QR Code'
29
+
30
+ def get_breadcrumbs(self):
31
+ payment = self.get_object()
32
+ return [
33
+ {'name': 'Dashboard', 'url': '/payments/admin/'},
34
+ {'name': 'Payments', 'url': '/payments/admin/list/'},
35
+ {'name': f'Payment #{payment.internal_payment_id or str(payment.id)[:8]}',
36
+ 'url': f'/payments/admin/payment/{payment.id}/'},
37
+ {'name': 'QR Code', 'url': ''},
38
+ ]
39
+
40
+ def get_context_data(self, **kwargs):
41
+ context = super().get_context_data(**kwargs)
42
+ payment = self.get_object()
43
+
44
+ # Log access for audit
45
+ log_view_access('payment_qr', self.request.user, payment_id=payment.id)
46
+
47
+ # Check if payment supports QR codes
48
+ if not self._supports_qr_code(payment):
49
+ context['error'] = "QR codes are not supported for this payment method"
50
+ return context
51
+
52
+ # Generate QR code data
53
+ qr_data = self._generate_qr_data(payment)
54
+
55
+ # Get payment instructions
56
+ instructions = self._get_payment_instructions(payment)
57
+
58
+ # Get common context
59
+ common_context = self.get_common_context()
60
+
61
+ context.update({
62
+ 'qr_data': qr_data,
63
+ 'qr_size': self.request.GET.get('size', 256),
64
+ 'instructions': instructions,
65
+ 'is_crypto': self._is_crypto_payment(payment),
66
+ 'can_copy': True,
67
+ **common_context
68
+ })
69
+
70
+ return context
71
+
72
+ def _supports_qr_code(self, payment):
73
+ """Check if payment method supports QR codes."""
74
+ crypto_providers = ['nowpayments', 'cryptapi', 'cryptomus']
75
+ return payment.provider in crypto_providers and payment.pay_address
76
+
77
+ def _is_crypto_payment(self, payment):
78
+ """Check if payment is cryptocurrency-based."""
79
+ crypto_providers = ['nowpayments', 'cryptapi', 'cryptomus']
80
+ return payment.provider in crypto_providers
81
+
82
+ def _generate_qr_data(self, payment):
83
+ """Generate QR code data for the payment."""
84
+ if not payment.pay_address:
85
+ return None
86
+
87
+ # For crypto payments, use standard format
88
+ if self._is_crypto_payment(payment):
89
+ qr_data = payment.pay_address
90
+
91
+ # Add amount if available
92
+ if payment.pay_amount:
93
+ # Use appropriate URI scheme based on currency
94
+ uri_schemes = {
95
+ 'BTC': 'bitcoin',
96
+ 'LTC': 'litecoin',
97
+ 'ETH': 'ethereum',
98
+ 'BCH': 'bitcoincash',
99
+ }
100
+
101
+ scheme = uri_schemes.get(payment.currency_code.upper(), 'crypto')
102
+ qr_data = f"{scheme}:{payment.pay_address}?amount={payment.pay_amount}"
103
+
104
+ # Add label if available
105
+ if payment.internal_payment_id:
106
+ qr_data += f"&label=Payment%20{payment.internal_payment_id}"
107
+
108
+ return qr_data
109
+
110
+ return payment.pay_address
111
+
112
+ def _get_payment_instructions(self, payment):
113
+ """Get step-by-step payment instructions."""
114
+ if not self._is_crypto_payment(payment):
115
+ return []
116
+
117
+ instructions = [
118
+ "Scan the QR code with your crypto wallet app",
119
+ f"Send exactly {payment.pay_amount} {payment.currency_code} to the address",
120
+ "Wait for network confirmations",
121
+ "Payment will be automatically confirmed"
122
+ ]
123
+
124
+ # Add provider-specific instructions
125
+ if payment.provider == 'cryptapi':
126
+ instructions.append("Minimum 1 confirmation required")
127
+ elif payment.provider == 'nowpayments':
128
+ instructions.append("Minimum 2 confirmations required")
129
+ elif payment.provider == 'cryptomus':
130
+ instructions.append("Confirmations depend on selected network")
131
+
132
+ return instructions
133
+
134
+
135
+ @superuser_required
136
+ def qr_code_data_ajax(request, payment_id):
137
+ """AJAX endpoint to get QR code data."""
138
+ try:
139
+ payment = UniversalPayment.objects.get(id=payment_id)
140
+
141
+ # Log access for audit
142
+ log_view_access('qr_ajax', request.user, payment_id=payment_id)
143
+
144
+ view = PaymentQRCodeView()
145
+
146
+ # Check if payment supports QR codes
147
+ if not view._supports_qr_code(payment):
148
+ return JsonResponse({
149
+ 'error': 'QR codes not supported for this payment method'
150
+ }, status=400)
151
+
152
+ # Generate QR data
153
+ qr_data = view._generate_qr_data(payment)
154
+
155
+ if not qr_data:
156
+ return JsonResponse({
157
+ 'error': 'Unable to generate QR code data'
158
+ }, status=400)
159
+
160
+ response_data = {
161
+ 'qr_data': qr_data,
162
+ 'payment_address': payment.pay_address,
163
+ 'payment_amount': str(payment.pay_amount) if payment.pay_amount else None,
164
+ 'currency': payment.currency_code,
165
+ 'provider': payment.provider,
166
+ 'instructions': view._get_payment_instructions(payment),
167
+ }
168
+
169
+ return JsonResponse(response_data)
170
+
171
+ except UniversalPayment.DoesNotExist:
172
+ return JsonResponse({'error': 'Payment not found'}, status=404)
173
+ except Exception as e:
174
+ return JsonResponse({'error': str(e)}, status=500)
@@ -0,0 +1,244 @@
1
+ """
2
+ Payment statistics and analytics views.
3
+
4
+ Provides comprehensive analytics for payment performance and trends.
5
+ """
6
+
7
+ from django.views.generic import TemplateView
8
+ from django.utils import timezone
9
+ from django.db.models import Q
10
+ from datetime import timedelta
11
+ from .base import (
12
+ SuperuserRequiredMixin,
13
+ PaymentStatsMixin,
14
+ PaymentContextMixin,
15
+ log_view_access
16
+ )
17
+
18
+
19
+ class PaymentStatsView(
20
+ SuperuserRequiredMixin,
21
+ PaymentStatsMixin,
22
+ PaymentContextMixin,
23
+ TemplateView
24
+ ):
25
+ """Analytics and statistics view."""
26
+
27
+ template_name = 'payments/stats.html'
28
+ page_title = 'Payment Analytics'
29
+
30
+ def get_breadcrumbs(self):
31
+ return [
32
+ {'name': 'Dashboard', 'url': '/payments/admin/'},
33
+ {'name': 'Analytics', 'url': ''},
34
+ ]
35
+
36
+ def get_context_data(self, **kwargs):
37
+ context = super().get_context_data(**kwargs)
38
+
39
+ # Log access for audit
40
+ log_view_access('payment_stats', self.request.user)
41
+
42
+ # Get time period from request (default to 30 days)
43
+ days = int(self.request.GET.get('days', 30))
44
+
45
+ # Calculate date ranges
46
+ now = timezone.now()
47
+ ranges = self._get_date_ranges(now, days)
48
+
49
+ # Get comprehensive statistics
50
+ stats = {
51
+ 'overview': self._get_overview_stats(),
52
+ 'time_periods': self._get_time_period_stats(ranges),
53
+ 'providers': self._get_detailed_provider_stats(),
54
+ 'status_distribution': self._get_status_distribution(),
55
+ 'trends': self._get_trend_data(days),
56
+ 'performance': self._get_performance_metrics(),
57
+ }
58
+
59
+ # Get common context
60
+ common_context = self.get_common_context()
61
+
62
+ context.update({
63
+ 'stats': stats,
64
+ 'selected_days': days,
65
+ 'available_periods': [7, 30, 90, 365],
66
+ 'date_ranges': ranges,
67
+ **common_context
68
+ })
69
+
70
+ return context
71
+
72
+ def _get_date_ranges(self, now, days):
73
+ """Calculate various date ranges for statistics."""
74
+ return {
75
+ 'current_period_start': now - timedelta(days=days),
76
+ 'current_period_end': now,
77
+ 'previous_period_start': now - timedelta(days=days * 2),
78
+ 'previous_period_end': now - timedelta(days=days),
79
+ 'last_7_days': now - timedelta(days=7),
80
+ 'last_30_days': now - timedelta(days=30),
81
+ 'last_year': now - timedelta(days=365),
82
+ }
83
+
84
+ def _get_overview_stats(self):
85
+ """Get overall payment statistics."""
86
+ return self.get_payment_stats()
87
+
88
+ def _get_time_period_stats(self, ranges):
89
+ """Get statistics for different time periods."""
90
+ from ...models import UniversalPayment
91
+
92
+ periods = {}
93
+
94
+ # Current period
95
+ current_qs = UniversalPayment.objects.filter(
96
+ created_at__gte=ranges['current_period_start'],
97
+ created_at__lte=ranges['current_period_end']
98
+ )
99
+ periods['current'] = self.get_payment_stats(current_qs)
100
+
101
+ # Previous period for comparison
102
+ previous_qs = UniversalPayment.objects.filter(
103
+ created_at__gte=ranges['previous_period_start'],
104
+ created_at__lte=ranges['previous_period_end']
105
+ )
106
+ periods['previous'] = self.get_payment_stats(previous_qs)
107
+
108
+ # Calculate growth rates
109
+ periods['growth'] = self._calculate_growth_rates(
110
+ periods['current'],
111
+ periods['previous']
112
+ )
113
+
114
+ # Last 7 days
115
+ last_7_qs = UniversalPayment.objects.filter(created_at__gte=ranges['last_7_days'])
116
+ periods['last_7_days'] = self.get_payment_stats(last_7_qs)
117
+
118
+ # Last 30 days
119
+ last_30_qs = UniversalPayment.objects.filter(created_at__gte=ranges['last_30_days'])
120
+ periods['last_30_days'] = self.get_payment_stats(last_30_qs)
121
+
122
+ return periods
123
+
124
+ def _get_detailed_provider_stats(self):
125
+ """Get detailed provider statistics."""
126
+ provider_stats = self.get_provider_stats()
127
+
128
+ # Add additional metrics for each provider
129
+ for stat in provider_stats:
130
+ stat['avg_amount'] = stat['volume'] / stat['count'] if stat['count'] > 0 else 0
131
+ stat['failure_rate'] = 100 - stat['success_rate']
132
+
133
+ return provider_stats
134
+
135
+ def _get_status_distribution(self):
136
+ """Get payment status distribution."""
137
+ from ...models import UniversalPayment
138
+ from django.db.models import Count
139
+
140
+ distribution = UniversalPayment.objects.values('status').annotate(
141
+ count=Count('id')
142
+ ).order_by('-count')
143
+
144
+ total = sum(item['count'] for item in distribution)
145
+
146
+ # Add percentage
147
+ for item in distribution:
148
+ item['percentage'] = (item['count'] / total * 100) if total > 0 else 0
149
+
150
+ return distribution
151
+
152
+ def _get_trend_data(self, days):
153
+ """Get trend data for charts."""
154
+ from ...models import UniversalPayment
155
+ from django.db.models import Count, Sum
156
+ from django.db.models.functions import TruncDate
157
+
158
+ end_date = timezone.now()
159
+ start_date = end_date - timedelta(days=days)
160
+
161
+ # Daily trends
162
+ daily_trends = UniversalPayment.objects.filter(
163
+ created_at__gte=start_date,
164
+ created_at__lte=end_date
165
+ ).annotate(
166
+ date=TruncDate('created_at')
167
+ ).values('date').annotate(
168
+ count=Count('id'),
169
+ volume=Sum('amount_usd'),
170
+ completed=Count('id', filter=Q(status='completed'))
171
+ ).order_by('date')
172
+
173
+ # Convert to list and add calculated fields
174
+ trends = []
175
+ for item in daily_trends:
176
+ trends.append({
177
+ 'date': item['date'].isoformat(),
178
+ 'count': item['count'],
179
+ 'volume': float(item['volume'] or 0),
180
+ 'completed': item['completed'],
181
+ 'success_rate': (item['completed'] / item['count'] * 100) if item['count'] > 0 else 0
182
+ })
183
+
184
+ return trends
185
+
186
+ def _get_performance_metrics(self):
187
+ """Get performance metrics."""
188
+ from ...models import UniversalPayment, PaymentEvent
189
+ from django.db.models import Avg, Min, Max
190
+
191
+ # Average processing time (from created to completed)
192
+ completed_payments = UniversalPayment.objects.filter(
193
+ status='completed',
194
+ completed_at__isnull=False
195
+ )
196
+
197
+ processing_times = []
198
+ for payment in completed_payments[:100]: # Sample for performance
199
+ if payment.completed_at and payment.created_at:
200
+ duration = payment.completed_at - payment.created_at
201
+ processing_times.append(duration.total_seconds())
202
+
203
+ metrics = {
204
+ 'avg_processing_time': sum(processing_times) / len(processing_times) if processing_times else 0,
205
+ 'min_processing_time': min(processing_times) if processing_times else 0,
206
+ 'max_processing_time': max(processing_times) if processing_times else 0,
207
+ }
208
+
209
+ # Convert seconds to human readable format
210
+ formatted_metrics = {}
211
+ for key, value in metrics.items():
212
+ if value > 0:
213
+ formatted_metrics[f"{key}_formatted"] = self._format_duration(value)
214
+ else:
215
+ formatted_metrics[f"{key}_formatted"] = "N/A"
216
+
217
+ # Add formatted metrics to the original metrics
218
+ metrics.update(formatted_metrics)
219
+
220
+ return metrics
221
+
222
+ def _calculate_growth_rates(self, current, previous):
223
+ """Calculate growth rates between two periods."""
224
+ growth = {}
225
+
226
+ for key in ['total_count', 'total_volume', 'completed_count']:
227
+ current_val = current.get(key, 0)
228
+ previous_val = previous.get(key, 0)
229
+
230
+ if previous_val > 0:
231
+ growth[f"{key}_rate"] = ((current_val - previous_val) / previous_val) * 100
232
+ else:
233
+ growth[f"{key}_rate"] = 100 if current_val > 0 else 0
234
+
235
+ return growth
236
+
237
+ def _format_duration(self, seconds):
238
+ """Format duration in seconds to human readable format."""
239
+ if seconds < 60:
240
+ return f"{seconds:.1f}s"
241
+ elif seconds < 3600:
242
+ return f"{seconds/60:.1f}m"
243
+ else:
244
+ return f"{seconds/3600:.1f}h"
@@ -0,0 +1,181 @@
1
+ """
2
+ Utility views for payment dashboard.
3
+
4
+ Provides testing, debugging, and development functionality.
5
+ """
6
+
7
+ from django.views.generic import TemplateView
8
+ from django.utils import timezone
9
+ from datetime import timedelta
10
+ from .base import (
11
+ SuperuserRequiredMixin,
12
+ PaymentContextMixin,
13
+ log_view_access
14
+ )
15
+
16
+
17
+ class PaymentTestView(
18
+ SuperuserRequiredMixin,
19
+ PaymentContextMixin,
20
+ TemplateView
21
+ ):
22
+ """Test view for development and debugging purposes."""
23
+
24
+ template_name = 'payments/test.html'
25
+ page_title = 'Payment System Test'
26
+
27
+ def get_breadcrumbs(self):
28
+ return [
29
+ {'name': 'Dashboard', 'url': '/payments/admin/'},
30
+ {'name': 'System Test', 'url': ''},
31
+ ]
32
+
33
+ def get_context_data(self, **kwargs):
34
+ context = super().get_context_data(**kwargs)
35
+
36
+ # Log access for audit
37
+ log_view_access('payment_test', self.request.user)
38
+
39
+ # Create sample data for testing templates
40
+ sample_data = self._generate_sample_data()
41
+
42
+ # Get system information
43
+ system_info = self._get_system_info()
44
+
45
+ # Get test scenarios
46
+ test_scenarios = self._get_test_scenarios()
47
+
48
+ # Get common context
49
+ common_context = self.get_common_context()
50
+
51
+ context.update({
52
+ 'sample_data': sample_data,
53
+ 'system_info': system_info,
54
+ 'test_scenarios': test_scenarios,
55
+ 'test_mode': True,
56
+ **common_context
57
+ })
58
+
59
+ return context
60
+
61
+ def _generate_sample_data(self):
62
+ """Generate sample payment data for testing."""
63
+ sample_payments = []
64
+ statuses = ['pending', 'confirming', 'completed', 'failed']
65
+ providers = ['nowpayments', 'cryptapi', 'cryptomus', 'stripe']
66
+
67
+ for i in range(12):
68
+ sample_payments.append({
69
+ 'id': f'sample-{i}',
70
+ 'internal_payment_id': f'PAY-{1000 + i}',
71
+ 'provider_payment_id': f'PROV-{2000 + i}',
72
+ 'amount_usd': 50.0 + (i * 25),
73
+ 'currency_code': 'USD',
74
+ 'status': statuses[i % len(statuses)],
75
+ 'provider': providers[i % len(providers)],
76
+ 'user_email': f'user{i}@example.com',
77
+ 'created_at': timezone.now() - timedelta(hours=i),
78
+ 'updated_at': timezone.now() - timedelta(minutes=i * 10),
79
+ 'pay_address': f'1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa{i}' if i % 2 == 0 else None,
80
+ 'pay_amount': 0.001 + (i * 0.0001) if i % 2 == 0 else None,
81
+ })
82
+
83
+ return {
84
+ 'payments': sample_payments,
85
+ 'stats': {
86
+ 'total_count': len(sample_payments),
87
+ 'pending_count': len([p for p in sample_payments if p['status'] == 'pending']),
88
+ 'completed_count': len([p for p in sample_payments if p['status'] == 'completed']),
89
+ 'failed_count': len([p for p in sample_payments if p['status'] == 'failed']),
90
+ 'total_volume': sum(p['amount_usd'] for p in sample_payments),
91
+ }
92
+ }
93
+
94
+ def _get_system_info(self):
95
+ """Get system information for debugging."""
96
+ import django
97
+ import sys
98
+ from django.conf import settings
99
+
100
+ info = {
101
+ 'django_version': django.get_version(),
102
+ 'python_version': sys.version,
103
+ 'debug_mode': settings.DEBUG,
104
+ 'database_engine': settings.DATABASES['default']['ENGINE'],
105
+ 'installed_apps': len(settings.INSTALLED_APPS),
106
+ 'timezone': str(settings.TIME_ZONE),
107
+ 'language': settings.LANGUAGE_CODE,
108
+ }
109
+
110
+ # Add payment-specific info
111
+ try:
112
+ from ...models import UniversalPayment, PaymentEvent
113
+ info.update({
114
+ 'total_payments': UniversalPayment.objects.count(),
115
+ 'total_events': PaymentEvent.objects.count(),
116
+ 'providers_in_use': list(
117
+ UniversalPayment.objects.values_list('provider', flat=True).distinct()
118
+ ),
119
+ })
120
+ except Exception:
121
+ info.update({
122
+ 'total_payments': 'Unable to query',
123
+ 'total_events': 'Unable to query',
124
+ 'providers_in_use': [],
125
+ })
126
+
127
+ return info
128
+
129
+ def _get_test_scenarios(self):
130
+ """Get available test scenarios."""
131
+ scenarios = [
132
+ {
133
+ 'name': 'Template Component Test',
134
+ 'description': 'Test all payment template components with sample data',
135
+ 'endpoint': '/payments/test/?test=components',
136
+ 'available': True,
137
+ },
138
+ {
139
+ 'name': 'Status Badge Test',
140
+ 'description': 'Test payment status badges for all possible statuses',
141
+ 'endpoint': '/payments/test/?test=status_badges',
142
+ 'available': True,
143
+ },
144
+ {
145
+ 'name': 'Progress Bar Test',
146
+ 'description': 'Test payment progress bars with different percentages',
147
+ 'endpoint': '/payments/test/?test=progress_bars',
148
+ 'available': True,
149
+ },
150
+ {
151
+ 'name': 'Provider Statistics Test',
152
+ 'description': 'Test provider statistics with sample data',
153
+ 'endpoint': '/payments/test/?test=provider_stats',
154
+ 'available': True,
155
+ },
156
+ {
157
+ 'name': 'Real-time Updates Test',
158
+ 'description': 'Test WebSocket connections and real-time updates',
159
+ 'endpoint': '/payments/test/?test=realtime',
160
+ 'available': False, # Requires WebSocket setup
161
+ },
162
+ {
163
+ 'name': 'QR Code Generation Test',
164
+ 'description': 'Test QR code generation for crypto payments',
165
+ 'endpoint': '/payments/test/?test=qr_codes',
166
+ 'available': True,
167
+ },
168
+ {
169
+ 'name': 'API Integration Test',
170
+ 'description': 'Test payment provider API integrations',
171
+ 'endpoint': '/payments/test/?test=api_integration',
172
+ 'available': False, # Requires API keys
173
+ },
174
+ ]
175
+
176
+ # Add current test parameter
177
+ current_test = self.request.GET.get('test', 'overview')
178
+ for scenario in scenarios:
179
+ scenario['is_current'] = scenario['endpoint'].endswith(f'test={current_test}')
180
+
181
+ return scenarios
@@ -3,7 +3,7 @@ Webhook processing views with signature validation.
3
3
  """
4
4
 
5
5
  import json
6
- import logging
6
+ from django_cfg.modules.django_logger import get_logger
7
7
  from typing import Dict, Any
8
8
 
9
9
  from django.http import JsonResponse, HttpResponse
@@ -20,7 +20,7 @@ from ..tasks.webhook_processing import process_webhook_with_fallback
20
20
  from ..services.security.webhook_validator import webhook_validator
21
21
  from ..services.security.error_handler import error_handler, SecurityError, ValidationError
22
22
 
23
- logger = logging.getLogger(__name__)
23
+ logger = get_logger("webhook_views")
24
24
 
25
25
 
26
26
  @csrf_exempt
@@ -19,7 +19,7 @@ from .views import (
19
19
  UserAPIKeyViewSet, APIKeyViewSet,
20
20
 
21
21
  # Currency ViewSets
22
- CurrencyViewSet, CurrencyNetworkViewSet,
22
+ CurrencyViewSet, NetworkViewSet, ProviderCurrencyViewSet,
23
23
 
24
24
  # Tariff ViewSets
25
25
  TariffViewSet, TariffEndpointGroupViewSet,
@@ -49,7 +49,8 @@ class PaymentSystemRouter:
49
49
 
50
50
  # Currency and pricing
51
51
  self.router.register(r'currencies', CurrencyViewSet, basename='currency')
52
- self.router.register(r'currency-networks', CurrencyNetworkViewSet, basename='currency-network')
52
+ self.router.register(r'networks', NetworkViewSet, basename='network')
53
+ self.router.register(r'provider-currencies', ProviderCurrencyViewSet, basename='provider-currency')
53
54
  self.router.register(r'tariffs', TariffViewSet, basename='tariff')
54
55
  self.router.register(r'tariff-groups', TariffEndpointGroupViewSet, basename='tariff-group')
55
56
 
@@ -18,6 +18,4 @@ urlpatterns = [
18
18
  # RESTful API endpoints using ViewSets
19
19
  path('api/', include(router.urls)),
20
20
 
21
- # Dashboard view
22
- path('dashboard/', views.dashboard_view, name='dashboard'),
23
21
  ]
@@ -0,0 +1,14 @@
1
+ """
2
+ URLs for Django CFG Tasks app.
3
+
4
+ Provides RESTful endpoints for task queue management and monitoring using ViewSets and routers.
5
+ """
6
+
7
+ from django.urls import path
8
+ from . import views
9
+
10
+ urlpatterns = [
11
+
12
+ # Dashboard view
13
+ path('dashboard/', views.dashboard_view, name='dashboard'),
14
+ ]
django_cfg/apps/urls.py CHANGED
@@ -46,11 +46,14 @@ def get_django_cfg_urlpatterns() -> List[URLPattern]:
46
46
 
47
47
  # Tasks app - enabled when knowbase or agents are enabled
48
48
  if base_module.should_enable_tasks():
49
- patterns.append(path('tasks/', include('django_cfg.apps.tasks.urls')))
49
+ patterns.append(path('admin/django_cfg_tasks/admin/', include('django_cfg.apps.tasks.urls_admin')))
50
50
 
51
51
  # Maintenance app - multi-site maintenance mode with Cloudflare
52
- if base_module.is_maintenance_enabled():
53
- patterns.append(path('maintenance/', include('django_cfg.apps.maintenance.urls')))
52
+ # if base_module.is_maintenance_enabled():
53
+ # patterns.append(path('admin/django_cfg_maintenance/', include('django_cfg.apps.maintenance.urls_admin')))
54
+
55
+ if base_module.is_payments_enabled():
56
+ patterns.append(path('admin/django_cfg_payments/admin/', include('django_cfg.apps.payments.urls_admin')))
54
57
 
55
58
  except Exception:
56
59
  # Fallback: include all URLs if config is not available