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,212 @@
1
+ """
2
+ Base mixins and decorators for payment template views.
3
+
4
+ Provides security and common functionality for all payment dashboard views.
5
+ """
6
+
7
+ from django.contrib.auth.decorators import user_passes_test
8
+ from django.utils.decorators import method_decorator
9
+ from django.db.models import Q, Count, Sum
10
+ from django.utils import timezone
11
+ from datetime import timedelta
12
+ from django_cfg.modules.django_logger import get_logger
13
+
14
+ logger = get_logger("view_base")
15
+
16
+
17
+ def superuser_required(function=None):
18
+ """Decorator that checks if user is superuser."""
19
+ actual_decorator = user_passes_test(
20
+ lambda u: u.is_authenticated and u.is_superuser,
21
+ login_url='/admin/login/'
22
+ )
23
+ if function:
24
+ return actual_decorator(function)
25
+ return actual_decorator
26
+
27
+
28
+ class SuperuserRequiredMixin:
29
+ """Mixin that requires superuser access for all views."""
30
+
31
+ @method_decorator(superuser_required)
32
+ def dispatch(self, request, *args, **kwargs):
33
+ return super().dispatch(request, *args, **kwargs)
34
+
35
+
36
+ class PaymentFilterMixin:
37
+ """Mixin that provides common payment filtering functionality."""
38
+
39
+ def get_filtered_payments(self, request=None):
40
+ """Get payments with common filters applied."""
41
+ if request is None:
42
+ request = self.request
43
+
44
+ from ...models import UniversalPayment
45
+
46
+ # Base queryset
47
+ payments_qs = UniversalPayment.objects.select_related('user')
48
+
49
+ # Apply filters from GET parameters
50
+ status_filter = request.GET.get('status', '')
51
+ provider_filter = request.GET.get('provider', '')
52
+ search_query = request.GET.get('search', '')
53
+ date_filter = request.GET.get('date', '')
54
+
55
+ if status_filter:
56
+ payments_qs = payments_qs.filter(status=status_filter)
57
+ if provider_filter:
58
+ payments_qs = payments_qs.filter(provider=provider_filter)
59
+ if search_query:
60
+ payments_qs = payments_qs.filter(
61
+ Q(internal_payment_id__icontains=search_query) |
62
+ Q(provider_payment_id__icontains=search_query) |
63
+ Q(amount_usd__icontains=search_query) |
64
+ Q(user__email__icontains=search_query)
65
+ )
66
+ if date_filter:
67
+ try:
68
+ filter_date = timezone.datetime.strptime(date_filter, '%Y-%m-%d').date()
69
+ payments_qs = payments_qs.filter(created_at__date=filter_date)
70
+ except ValueError:
71
+ pass # Invalid date format, ignore filter
72
+
73
+ return payments_qs
74
+
75
+ def get_filter_context(self, request=None):
76
+ """Get filter values for template context."""
77
+ if request is None:
78
+ request = self.request
79
+
80
+ return {
81
+ 'status': request.GET.get('status', ''),
82
+ 'provider': request.GET.get('provider', ''),
83
+ 'search': request.GET.get('search', ''),
84
+ 'date': request.GET.get('date', ''),
85
+ }
86
+
87
+
88
+ class PaymentStatsMixin:
89
+ """Mixin that provides payment statistics functionality."""
90
+
91
+ def get_payment_stats(self, queryset=None):
92
+ """Get payment statistics from queryset or all payments."""
93
+ if queryset is None:
94
+ from ...models import UniversalPayment
95
+ queryset = UniversalPayment.objects.all()
96
+
97
+ stats = queryset.aggregate(
98
+ total_count=Count('id'),
99
+ pending_count=Count('id', filter=Q(status='pending')),
100
+ confirming_count=Count('id', filter=Q(status='confirming')),
101
+ completed_count=Count('id', filter=Q(status='completed')),
102
+ failed_count=Count('id', filter=Q(status='failed')),
103
+ total_volume=Sum('amount_usd')
104
+ )
105
+
106
+ # Convert to template format
107
+ return {
108
+ 'total_payments_count': stats['total_count'] or 0,
109
+ 'pending_payments_count': stats['pending_count'] or 0,
110
+ 'confirming_payments_count': stats['confirming_count'] or 0,
111
+ 'completed_payments_count': stats['completed_count'] or 0,
112
+ 'failed_payments_count': stats['failed_count'] or 0,
113
+ 'total_volume': float(stats['total_volume'] or 0),
114
+ }
115
+
116
+ def get_provider_stats(self, queryset=None):
117
+ """Get provider-specific statistics."""
118
+ if queryset is None:
119
+ from ...models import UniversalPayment
120
+ queryset = UniversalPayment.objects.all()
121
+
122
+ provider_stats = queryset.values('provider').annotate(
123
+ count=Count('id'),
124
+ volume=Sum('amount_usd'),
125
+ completed_count=Count('id', filter=Q(status='completed')),
126
+ ).order_by('-volume')
127
+
128
+ # Calculate success rate and convert to list of dicts
129
+ stats_list = []
130
+ for stat in provider_stats:
131
+ if stat['count'] > 0:
132
+ stat['success_rate'] = (stat['completed_count'] / stat['count']) * 100
133
+ else:
134
+ stat['success_rate'] = 0
135
+
136
+ # Convert Decimal to float
137
+ if stat['volume']:
138
+ stat['volume'] = float(stat['volume'])
139
+ else:
140
+ stat['volume'] = 0.0
141
+
142
+ stats_list.append(stat)
143
+
144
+ return stats_list
145
+
146
+ def get_time_range_stats(self, days=30):
147
+ """Get statistics for a specific time range."""
148
+ from ...models import UniversalPayment
149
+
150
+ end_date = timezone.now()
151
+ start_date = end_date - timedelta(days=days)
152
+
153
+ queryset = UniversalPayment.objects.filter(
154
+ created_at__gte=start_date,
155
+ created_at__lte=end_date
156
+ )
157
+
158
+ return self.get_payment_stats(queryset)
159
+
160
+
161
+ class PaymentContextMixin:
162
+ """Mixin that provides common context data for payment views."""
163
+
164
+ def get_common_context(self):
165
+ """Get common context data used across multiple views."""
166
+ from ...models import PaymentEvent
167
+
168
+ # Get recent events for activity feed (if any exist)
169
+ try:
170
+ recent_events = PaymentEvent.objects.order_by('-created_at')[:10]
171
+ except Exception:
172
+ recent_events = []
173
+
174
+ return {
175
+ 'recent_events': recent_events,
176
+ 'page_title': self.get_page_title(),
177
+ 'breadcrumbs': self.get_breadcrumbs(),
178
+ }
179
+
180
+ def get_page_title(self):
181
+ """Get page title for the view."""
182
+ return getattr(self, 'page_title', 'Payment Dashboard')
183
+
184
+ def get_breadcrumbs(self):
185
+ """Get breadcrumb navigation for the view."""
186
+ return getattr(self, 'breadcrumbs', [
187
+ {'name': 'Payment Dashboard', 'url': '/payments/admin/'},
188
+ ])
189
+
190
+
191
+ def get_progress_percentage(status):
192
+ """Helper function to calculate progress percentage."""
193
+ progress_map = {
194
+ 'pending': 10,
195
+ 'confirming': 40,
196
+ 'confirmed': 70,
197
+ 'completed': 100,
198
+ 'failed': 0,
199
+ 'expired': 0,
200
+ 'cancelled': 0,
201
+ 'refunded': 50,
202
+ }
203
+ return progress_map.get(status, 0)
204
+
205
+
206
+ def log_view_access(view_name, user, **kwargs):
207
+ """Log access to payment views for audit purposes."""
208
+ extra_info = ', '.join([f"{k}={v}" for k, v in kwargs.items()])
209
+ logger.info(
210
+ f"Payment dashboard access: {view_name} by {user.email} "
211
+ f"(superuser={user.is_superuser}) {extra_info}"
212
+ )
@@ -0,0 +1,60 @@
1
+ """
2
+ Main payment dashboard view.
3
+
4
+ Provides overview, statistics, and recent payments for superuser access.
5
+ """
6
+
7
+ from django.views.generic import TemplateView
8
+ from .base import (
9
+ SuperuserRequiredMixin,
10
+ PaymentFilterMixin,
11
+ PaymentStatsMixin,
12
+ PaymentContextMixin,
13
+ log_view_access
14
+ )
15
+
16
+
17
+ class PaymentDashboardView(
18
+ SuperuserRequiredMixin,
19
+ PaymentFilterMixin,
20
+ PaymentStatsMixin,
21
+ PaymentContextMixin,
22
+ TemplateView
23
+ ):
24
+ """Main payment dashboard with overview and recent payments."""
25
+
26
+ template_name = 'payments/dashboard.html'
27
+ page_title = 'Payment Dashboard'
28
+
29
+ def get_context_data(self, **kwargs):
30
+ context = super().get_context_data(**kwargs)
31
+
32
+ # Log access for audit
33
+ log_view_access('dashboard', self.request.user)
34
+
35
+ # Get filtered payments
36
+ payments_qs = self.get_filtered_payments()
37
+
38
+ # Get recent payments (limit to 20 for performance)
39
+ recent_payments = payments_qs.order_by('-created_at')[:20]
40
+
41
+ # Check if there are more payments for pagination
42
+ has_more = payments_qs.count() > 20
43
+
44
+ # Get statistics
45
+ payment_stats = self.get_payment_stats()
46
+ provider_stats = self.get_provider_stats()
47
+
48
+ # Get common context
49
+ common_context = self.get_common_context()
50
+
51
+ context.update({
52
+ 'payments': recent_payments,
53
+ 'has_more_payments': has_more,
54
+ 'payment_stats': payment_stats,
55
+ 'provider_stats': provider_stats,
56
+ 'filters': self.get_filter_context(),
57
+ **common_context
58
+ })
59
+
60
+ return context
@@ -0,0 +1,102 @@
1
+ """
2
+ Payment detail view.
3
+
4
+ Provides detailed information about a single payment for superuser access.
5
+ """
6
+
7
+ from django.views.generic import DetailView
8
+ from .base import (
9
+ SuperuserRequiredMixin,
10
+ PaymentContextMixin,
11
+ log_view_access
12
+ )
13
+ from ...models import UniversalPayment, PaymentEvent
14
+
15
+
16
+ class PaymentDetailView(
17
+ SuperuserRequiredMixin,
18
+ PaymentContextMixin,
19
+ DetailView
20
+ ):
21
+ """Detailed view for a single payment."""
22
+
23
+ model = UniversalPayment
24
+ template_name = 'payments/payment_detail.html'
25
+ context_object_name = 'payment'
26
+ page_title = 'Payment Details'
27
+
28
+ def get_breadcrumbs(self):
29
+ payment = self.get_object()
30
+ return [
31
+ {'name': 'Dashboard', 'url': '/payments/admin/'},
32
+ {'name': 'Payments', 'url': '/payments/admin/list/'},
33
+ {'name': f'Payment #{payment.internal_payment_id or str(payment.id)[:8]}', 'url': ''},
34
+ ]
35
+
36
+ def get_context_data(self, **kwargs):
37
+ context = super().get_context_data(**kwargs)
38
+ payment = self.get_object()
39
+
40
+ # Log access for audit
41
+ log_view_access('payment_detail', self.request.user, payment_id=payment.id)
42
+
43
+ # Get payment events for this payment
44
+ events = PaymentEvent.objects.filter(payment_id=payment.id).order_by('-created_at')
45
+
46
+ # Get related payments (same user, similar amount range)
47
+ related_payments = UniversalPayment.objects.filter(
48
+ user=payment.user,
49
+ amount_usd__gte=payment.amount_usd * 0.8,
50
+ amount_usd__lte=payment.amount_usd * 1.2
51
+ ).exclude(id=payment.id).order_by('-created_at')[:5]
52
+
53
+ # Get provider-specific information
54
+ provider_info = self._get_provider_info(payment)
55
+
56
+ # Get common context
57
+ common_context = self.get_common_context()
58
+
59
+ context.update({
60
+ 'events': events,
61
+ 'related_payments': related_payments,
62
+ 'provider_info': provider_info,
63
+ 'can_retry': self._can_retry_payment(payment),
64
+ 'can_cancel': self._can_cancel_payment(payment),
65
+ 'can_refund': self._can_refund_payment(payment),
66
+ **common_context
67
+ })
68
+
69
+ return context
70
+
71
+ def _get_provider_info(self, payment):
72
+ """Get provider-specific information for the payment."""
73
+ info = {
74
+ 'display_name': payment.provider.title(),
75
+ 'is_crypto': payment.provider in ['nowpayments', 'cryptapi', 'cryptomus'],
76
+ 'supports_qr': payment.provider in ['nowpayments', 'cryptapi', 'cryptomus'],
77
+ 'supports_webhook': True,
78
+ }
79
+
80
+ # Add crypto-specific info
81
+ if info['is_crypto'] and payment.pay_address:
82
+ info.update({
83
+ 'crypto_address': payment.pay_address,
84
+ 'crypto_amount': payment.pay_amount,
85
+ 'crypto_currency': payment.currency_code,
86
+ 'network': getattr(payment, 'network', 'mainnet'),
87
+ 'confirmations': getattr(payment, 'confirmations_count', 0),
88
+ })
89
+
90
+ return info
91
+
92
+ def _can_retry_payment(self, payment):
93
+ """Check if payment can be retried."""
94
+ return payment.status in ['failed', 'expired']
95
+
96
+ def _can_cancel_payment(self, payment):
97
+ """Check if payment can be cancelled."""
98
+ return payment.status in ['pending', 'confirming']
99
+
100
+ def _can_refund_payment(self, payment):
101
+ """Check if payment can be refunded."""
102
+ return payment.status == 'completed'
@@ -0,0 +1,158 @@
1
+ """
2
+ Payment management views.
3
+
4
+ Provides list, create, and management functionality for payments.
5
+ """
6
+
7
+ import json
8
+ from django.views.generic import TemplateView, ListView
9
+ from .base import (
10
+ SuperuserRequiredMixin,
11
+ PaymentFilterMixin,
12
+ PaymentContextMixin,
13
+ log_view_access
14
+ )
15
+ from ...models import UniversalPayment, Currency, PaymentProvider
16
+ from ...services.providers.registry import ProviderRegistry
17
+
18
+
19
+ class PaymentCreateView(
20
+ SuperuserRequiredMixin,
21
+ PaymentContextMixin,
22
+ TemplateView
23
+ ):
24
+ """Form view for creating a new payment."""
25
+
26
+ template_name = 'payments/payment_create.html'
27
+ page_title = 'Create Payment'
28
+
29
+ def get_breadcrumbs(self):
30
+ return [
31
+ {'name': 'Dashboard', 'url': '/payments/admin/'},
32
+ {'name': 'Payments', 'url': '/payments/admin/list/'},
33
+ {'name': 'Create Payment', '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_create', self.request.user)
41
+
42
+ # Get available providers
43
+ providers = self._get_available_providers()
44
+
45
+ # Get available currencies
46
+ currencies = self._get_available_currencies()
47
+
48
+ # Get common context
49
+ common_context = self.get_common_context()
50
+
51
+ # Provider enum with defaults (serialize to JSON for JavaScript)
52
+ provider_defaults = {
53
+ PaymentProvider.NOWPAYMENTS.value: 'USDTTRC20',
54
+ PaymentProvider.CRYPTAPI.value: 'BTC',
55
+ PaymentProvider.CRYPTOMUS.value: 'USDTTRC20',
56
+ PaymentProvider.STRIPE.value: 'USD'
57
+ }
58
+ provider_defaults_json = json.dumps(provider_defaults)
59
+
60
+ context.update({
61
+ 'providers': providers,
62
+ 'currencies': currencies,
63
+ 'provider_enum': PaymentProvider,
64
+ 'provider_defaults_json': provider_defaults_json,
65
+ 'default_amount': 10.0, # Default test amount
66
+ **common_context
67
+ })
68
+
69
+ return context
70
+
71
+ def _get_available_providers(self):
72
+ """Get list of available payment providers from registry."""
73
+ registry = ProviderRegistry()
74
+ provider_names = registry.list_providers()
75
+
76
+ providers = []
77
+ for provider_name in provider_names:
78
+ provider_instance = registry.get_provider(provider_name)
79
+ providers.append({
80
+ 'name': provider_name,
81
+ 'display_name': provider_name.title(),
82
+ 'is_crypto': provider_name in ['nowpayments', 'cryptapi', 'cryptomus'],
83
+ 'description': getattr(provider_instance.__class__, '__doc__', '') if provider_instance else '',
84
+ })
85
+ return providers
86
+
87
+ def _get_available_currencies(self):
88
+ """Get list of available currencies from database."""
89
+ # Get all active currencies - both fiat and crypto
90
+ return Currency.objects.all().order_by('currency_type', 'code')
91
+
92
+
93
+
94
+
95
+ class PaymentListView(
96
+ SuperuserRequiredMixin,
97
+ PaymentFilterMixin,
98
+ PaymentContextMixin,
99
+ ListView
100
+ ):
101
+ """Paginated list view for all payments."""
102
+
103
+ model = UniversalPayment
104
+ template_name = 'payments/payment_list.html'
105
+ context_object_name = 'payments'
106
+ paginate_by = 20
107
+ ordering = ['-created_at']
108
+ page_title = 'All Payments'
109
+
110
+ def get_breadcrumbs(self):
111
+ return [
112
+ {'name': 'Dashboard', 'url': '/payments/admin/'},
113
+ {'name': 'All Payments', 'url': ''},
114
+ ]
115
+
116
+ def get_queryset(self):
117
+ # Log access for audit
118
+ log_view_access('payment_list', self.request.user)
119
+
120
+ # Use filter mixin to get filtered queryset
121
+ return self.get_filtered_payments().order_by(*self.ordering)
122
+
123
+ def get_context_data(self, **kwargs):
124
+ context = super().get_context_data(**kwargs)
125
+
126
+ # Get filter context
127
+ filter_context = self.get_filter_context()
128
+
129
+ # Get available filter options
130
+ filter_options = self._get_filter_options()
131
+
132
+ # Get common context
133
+ common_context = self.get_common_context()
134
+
135
+ context.update({
136
+ 'filters': filter_context,
137
+ 'filter_options': filter_options,
138
+ 'total_count': self.get_queryset().count(),
139
+ **common_context
140
+ })
141
+
142
+ return context
143
+
144
+ def _get_filter_options(self):
145
+ """Get available options for filter dropdowns."""
146
+
147
+ # Get unique statuses
148
+ statuses = UniversalPayment.objects.values_list('status', flat=True).distinct()
149
+ status_choices = [(status, status.title()) for status in statuses if status]
150
+
151
+ # Get unique providers
152
+ providers = UniversalPayment.objects.values_list('provider', flat=True).distinct()
153
+ provider_choices = [(provider, provider.title()) for provider in providers if provider]
154
+
155
+ return {
156
+ 'statuses': status_choices,
157
+ 'providers': provider_choices,
158
+ }