django-cfg 1.2.31__py3-none-any.whl → 1.3.3__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 (264) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/api/health/views.py +4 -2
  3. django_cfg/apps/knowbase/config/settings.py +16 -15
  4. django_cfg/apps/payments/README.md +326 -0
  5. django_cfg/apps/payments/admin/__init__.py +20 -10
  6. django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
  7. django_cfg/apps/payments/admin/balance_admin.py +592 -297
  8. django_cfg/apps/payments/admin/currencies_admin.py +526 -222
  9. django_cfg/apps/payments/admin/filters.py +306 -199
  10. django_cfg/apps/payments/admin/payments_admin.py +465 -70
  11. django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
  12. django_cfg/apps/payments/admin_interface/__init__.py +18 -0
  13. django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
  14. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
  23. django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
  24. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
  25. django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
  26. django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
  27. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
  28. django_cfg/apps/payments/apps.py +34 -9
  29. django_cfg/apps/payments/config/__init__.py +28 -51
  30. django_cfg/apps/payments/config/constance/__init__.py +22 -0
  31. django_cfg/apps/payments/config/constance/config_service.py +123 -0
  32. django_cfg/apps/payments/config/constance/fields.py +69 -0
  33. django_cfg/apps/payments/config/constance/settings.py +160 -0
  34. django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
  35. django_cfg/apps/payments/config/helpers.py +130 -0
  36. django_cfg/apps/payments/management/__init__.py +1 -3
  37. django_cfg/apps/payments/management/commands/__init__.py +1 -3
  38. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +419 -0
  39. django_cfg/apps/payments/management/commands/currency_stats.py +297 -225
  40. django_cfg/apps/payments/management/commands/manage_currencies.py +303 -151
  41. django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
  42. django_cfg/apps/payments/management/commands/process_pending_payments.py +357 -0
  43. django_cfg/apps/payments/management/commands/test_providers.py +434 -0
  44. django_cfg/apps/payments/middleware/__init__.py +3 -1
  45. django_cfg/apps/payments/middleware/api_access.py +329 -222
  46. django_cfg/apps/payments/middleware/rate_limiting.py +342 -152
  47. django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
  48. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  49. django_cfg/apps/payments/models/__init__.py +13 -18
  50. django_cfg/apps/payments/models/api_keys.py +121 -43
  51. django_cfg/apps/payments/models/balance.py +153 -115
  52. django_cfg/apps/payments/models/base.py +68 -15
  53. django_cfg/apps/payments/models/currencies.py +172 -148
  54. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  55. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  56. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  57. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  58. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  59. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  60. django_cfg/apps/payments/models/payments.py +235 -285
  61. django_cfg/apps/payments/models/subscriptions.py +257 -177
  62. django_cfg/apps/payments/models/tariffs.py +147 -40
  63. django_cfg/apps/payments/services/__init__.py +209 -56
  64. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  65. django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
  66. django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
  67. django_cfg/apps/payments/services/{cache/base.py → cache_service/interfaces.py} +3 -1
  68. django_cfg/apps/payments/services/cache_service/keys.py +49 -0
  69. django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
  70. django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
  71. django_cfg/apps/payments/services/core/__init__.py +10 -6
  72. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  73. django_cfg/apps/payments/services/core/base.py +166 -0
  74. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  75. django_cfg/apps/payments/services/core/payment_service.py +371 -465
  76. django_cfg/apps/payments/services/core/subscription_service.py +425 -481
  77. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  78. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  79. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  80. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  81. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  82. django_cfg/apps/payments/services/providers/base.py +234 -174
  83. django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
  84. django_cfg/apps/payments/services/providers/registry.py +367 -301
  85. django_cfg/apps/payments/services/types/__init__.py +78 -0
  86. django_cfg/apps/payments/services/types/data.py +177 -0
  87. django_cfg/apps/payments/services/types/requests.py +150 -0
  88. django_cfg/apps/payments/services/types/responses.py +156 -0
  89. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  90. django_cfg/apps/payments/signals/__init__.py +33 -8
  91. django_cfg/apps/payments/signals/api_key_signals.py +210 -129
  92. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  93. django_cfg/apps/payments/signals/payment_signals.py +128 -103
  94. django_cfg/apps/payments/signals/subscription_signals.py +194 -142
  95. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  96. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  97. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  98. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  99. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  100. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  101. django_cfg/apps/payments/urls.py +45 -48
  102. django_cfg/apps/payments/urls_admin.py +33 -42
  103. django_cfg/apps/payments/views/api/__init__.py +101 -0
  104. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  105. django_cfg/apps/payments/views/api/balances.py +381 -0
  106. django_cfg/apps/payments/views/api/base.py +298 -0
  107. django_cfg/apps/payments/views/api/currencies.py +402 -0
  108. django_cfg/apps/payments/views/api/payments.py +415 -0
  109. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  110. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  111. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  112. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  113. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  114. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  115. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  116. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  117. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  118. django_cfg/config.py +1 -1
  119. django_cfg/core/config.py +40 -4
  120. django_cfg/core/generation.py +25 -4
  121. django_cfg/core/integration/README.md +363 -0
  122. django_cfg/core/integration/__init__.py +47 -0
  123. django_cfg/core/integration/commands_collector.py +239 -0
  124. django_cfg/core/integration/display/__init__.py +15 -0
  125. django_cfg/core/integration/display/base.py +157 -0
  126. django_cfg/core/integration/display/ngrok.py +164 -0
  127. django_cfg/core/integration/display/startup.py +815 -0
  128. django_cfg/core/integration/url_integration.py +123 -0
  129. django_cfg/core/integration/version_checker.py +160 -0
  130. django_cfg/management/commands/auto_generate.py +4 -0
  131. django_cfg/management/commands/check_settings.py +6 -0
  132. django_cfg/management/commands/clear_constance.py +5 -2
  133. django_cfg/management/commands/create_token.py +6 -0
  134. django_cfg/management/commands/list_urls.py +6 -0
  135. django_cfg/management/commands/migrate_all.py +6 -0
  136. django_cfg/management/commands/migrator.py +3 -0
  137. django_cfg/management/commands/rundramatiq.py +6 -0
  138. django_cfg/management/commands/runserver_ngrok.py +51 -29
  139. django_cfg/management/commands/script.py +6 -0
  140. django_cfg/management/commands/show_config.py +12 -2
  141. django_cfg/management/commands/show_urls.py +4 -0
  142. django_cfg/management/commands/superuser.py +6 -0
  143. django_cfg/management/commands/task_clear.py +4 -1
  144. django_cfg/management/commands/task_status.py +3 -1
  145. django_cfg/management/commands/test_email.py +3 -0
  146. django_cfg/management/commands/test_telegram.py +6 -0
  147. django_cfg/management/commands/test_twilio.py +6 -0
  148. django_cfg/management/commands/tree.py +6 -0
  149. django_cfg/management/commands/validate_config.py +155 -149
  150. django_cfg/models/constance.py +31 -11
  151. django_cfg/models/payments.py +175 -492
  152. django_cfg/modules/django_logger.py +160 -146
  153. django_cfg/modules/django_unfold/dashboard.py +64 -16
  154. django_cfg/registry/core.py +1 -0
  155. django_cfg/template_archive/django_sample.zip +0 -0
  156. django_cfg/utils/smart_defaults.py +227 -570
  157. django_cfg/utils/toolkit.py +51 -11
  158. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/METADATA +4 -1
  159. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/RECORD +162 -185
  160. django_cfg/apps/payments/__init__.py +0 -8
  161. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  162. django_cfg/apps/payments/config/module.py +0 -70
  163. django_cfg/apps/payments/config/providers.py +0 -105
  164. django_cfg/apps/payments/config/settings.py +0 -96
  165. django_cfg/apps/payments/config/utils.py +0 -52
  166. django_cfg/apps/payments/decorators.py +0 -291
  167. django_cfg/apps/payments/management/commands/README.md +0 -146
  168. django_cfg/apps/payments/managers/__init__.py +0 -23
  169. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  170. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  171. django_cfg/apps/payments/managers/currency_manager.py +0 -306
  172. django_cfg/apps/payments/managers/payment_manager.py +0 -192
  173. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  174. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  175. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
  176. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
  177. django_cfg/apps/payments/models/events.py +0 -73
  178. django_cfg/apps/payments/serializers/__init__.py +0 -57
  179. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  180. django_cfg/apps/payments/serializers/balance.py +0 -59
  181. django_cfg/apps/payments/serializers/currencies.py +0 -63
  182. django_cfg/apps/payments/serializers/payments.py +0 -62
  183. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  184. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  185. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  186. django_cfg/apps/payments/services/cache/simple_cache.py +0 -135
  187. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  188. django_cfg/apps/payments/services/internal_types.py +0 -461
  189. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  190. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  191. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
  192. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  193. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
  194. django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
  195. django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
  196. django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
  197. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
  198. django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
  199. django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
  200. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
  201. django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
  202. django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
  203. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
  204. django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
  205. django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
  206. django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
  207. django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
  208. django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
  209. django_cfg/apps/payments/services/security/__init__.py +0 -34
  210. django_cfg/apps/payments/services/security/error_handler.py +0 -635
  211. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  212. django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
  213. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  214. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  215. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  216. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  217. django_cfg/apps/payments/tasks/__init__.py +0 -12
  218. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  219. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
  220. django_cfg/apps/payments/templates/payments/base.html +0 -182
  221. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  222. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  223. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
  224. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  225. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
  226. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
  227. django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
  228. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
  229. django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
  230. django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
  231. django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
  232. django_cfg/apps/payments/templates/payments/stats.html +0 -261
  233. django_cfg/apps/payments/templates/payments/test.html +0 -213
  234. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  235. django_cfg/apps/payments/utils/__init__.py +0 -43
  236. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  237. django_cfg/apps/payments/utils/config_utils.py +0 -239
  238. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  239. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  240. django_cfg/apps/payments/views/__init__.py +0 -63
  241. django_cfg/apps/payments/views/api_key_views.py +0 -164
  242. django_cfg/apps/payments/views/balance_views.py +0 -75
  243. django_cfg/apps/payments/views/currency_views.py +0 -122
  244. django_cfg/apps/payments/views/payment_views.py +0 -149
  245. django_cfg/apps/payments/views/subscription_views.py +0 -135
  246. django_cfg/apps/payments/views/tariff_views.py +0 -131
  247. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  248. django_cfg/apps/payments/views/templates/ajax.py +0 -451
  249. django_cfg/apps/payments/views/templates/base.py +0 -212
  250. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  251. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  252. django_cfg/apps/payments/views/templates/payment_management.py +0 -158
  253. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  254. django_cfg/apps/payments/views/templates/stats.py +0 -244
  255. django_cfg/apps/payments/views/templates/utils.py +0 -181
  256. django_cfg/apps/payments/views/webhook_views.py +0 -266
  257. django_cfg/apps/payments/viewsets.py +0 -66
  258. django_cfg/core/integration.py +0 -160
  259. django_cfg/template_archive/.gitignore +0 -1
  260. django_cfg/template_archive/__init__.py +0 -0
  261. django_cfg/urls.py +0 -33
  262. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/WHEEL +0 -0
  263. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/entry_points.txt +0 -0
  264. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,451 +0,0 @@
1
- """
2
- AJAX endpoints for payment dashboard.
3
-
4
- Provides real-time data updates and interactive functionality.
5
- """
6
-
7
- from django.http import JsonResponse
8
- from django.shortcuts import get_object_or_404
9
- from django.utils import timezone
10
- from .base import superuser_required, get_progress_percentage, log_view_access
11
- from ...models import UniversalPayment, PaymentEvent, Currency, Network, ProviderCurrency
12
- from ...services.providers.registry import get_provider_registry
13
- from ...services.internal_types import ProviderCurrencyOptionsResponse, CurrencyOptionModel
14
- from django_cfg.modules.django_logger import get_logger
15
-
16
- logger = get_logger("ajax_views")
17
-
18
-
19
- @superuser_required
20
- def payment_status_ajax(request, payment_id):
21
- """AJAX endpoint for real-time payment status."""
22
- try:
23
- payment = get_object_or_404(UniversalPayment, id=payment_id)
24
-
25
- # Log access for audit
26
- log_view_access('payment_status_ajax', request.user, payment_id=payment_id)
27
-
28
- data = {
29
- 'id': str(payment.id),
30
- 'status': payment.status,
31
- 'status_display': payment.get_status_display(),
32
- 'progress_percentage': get_progress_percentage(payment.status),
33
- 'amount_usd': float(payment.amount_usd),
34
- 'currency_code': payment.currency_code,
35
- 'provider': payment.provider,
36
- 'provider_display': payment.provider.title(),
37
- 'created_at': payment.created_at.isoformat(),
38
- 'updated_at': payment.updated_at.isoformat(),
39
- }
40
-
41
- # Add completion time if available
42
- if payment.completed_at:
43
- data['completed_at'] = payment.completed_at.isoformat()
44
-
45
- # Add crypto-specific data
46
- if payment.provider in ['nowpayments', 'cryptapi', 'cryptomus']:
47
- data.update({
48
- 'is_crypto': True,
49
- 'pay_address': payment.pay_address,
50
- 'pay_amount': str(payment.pay_amount) if payment.pay_amount else None,
51
- 'network': getattr(payment, 'network', None),
52
- 'confirmations': getattr(payment, 'confirmations_count', 0),
53
- })
54
- else:
55
- data['is_crypto'] = False
56
-
57
- return JsonResponse(data)
58
-
59
- except Exception as e:
60
- return JsonResponse({'error': str(e)}, status=400)
61
-
62
-
63
- @superuser_required
64
- def payment_events_ajax(request, payment_id):
65
- """AJAX endpoint for payment events."""
66
- try:
67
- payment = get_object_or_404(UniversalPayment, id=payment_id)
68
-
69
- # Log access for audit
70
- log_view_access('payment_events_ajax', request.user, payment_id=payment_id)
71
-
72
- events = PaymentEvent.objects.filter(payment_id=payment.id).order_by('-created_at')
73
-
74
- events_data = []
75
- for event in events:
76
- event_data = {
77
- 'id': event.id,
78
- 'event_type': event.event_type,
79
- 'created_at': event.created_at.isoformat(),
80
- 'metadata': event.metadata or {},
81
- }
82
-
83
- # Add human-readable description
84
- event_data['description'] = _get_event_description(event)
85
-
86
- events_data.append(event_data)
87
-
88
- return JsonResponse({
89
- 'events': events_data,
90
- 'count': len(events_data)
91
- })
92
-
93
- except Exception as e:
94
- return JsonResponse({'error': str(e)}, status=400)
95
-
96
-
97
- @superuser_required
98
- def payment_stats_ajax(request):
99
- """AJAX endpoint for dashboard statistics."""
100
- try:
101
- # Log access for audit
102
- log_view_access('payment_stats_ajax', request.user)
103
-
104
- from .base import PaymentStatsMixin
105
-
106
- # Create instance to use mixin methods
107
- stats_mixin = PaymentStatsMixin()
108
-
109
- # Get basic stats
110
- payment_stats = stats_mixin.get_payment_stats()
111
- provider_stats = stats_mixin.get_provider_stats()
112
-
113
- # Get time range stats if requested
114
- days = int(request.GET.get('days', 30))
115
- time_range_stats = stats_mixin.get_time_range_stats(days)
116
-
117
- data = {
118
- 'payment_stats': payment_stats,
119
- 'provider_stats': list(provider_stats),
120
- 'time_range_stats': time_range_stats,
121
- 'last_updated': timezone.now().isoformat(),
122
- }
123
-
124
- return JsonResponse(data)
125
-
126
- except Exception as e:
127
- return JsonResponse({'error': str(e)}, status=500)
128
-
129
-
130
- @superuser_required
131
- def payment_search_ajax(request):
132
- """AJAX endpoint for payment search."""
133
- try:
134
- query = request.GET.get('q', '').strip()
135
- if not query:
136
- return JsonResponse({'results': []})
137
-
138
- # Log access for audit
139
- log_view_access('payment_search_ajax', request.user, query=query)
140
-
141
- from django.db.models import Q
142
-
143
- # Search payments
144
- payments = UniversalPayment.objects.filter(
145
- Q(internal_payment_id__icontains=query) |
146
- Q(provider_payment_id__icontains=query) |
147
- Q(user__email__icontains=query) |
148
- Q(pay_address__icontains=query)
149
- ).order_by('-created_at')[:20]
150
-
151
- results = []
152
- for payment in payments:
153
- results.append({
154
- 'id': str(payment.id),
155
- 'internal_id': payment.internal_payment_id,
156
- 'provider_id': payment.provider_payment_id,
157
- 'amount_usd': float(payment.amount_usd),
158
- 'currency_code': payment.currency_code,
159
- 'status': payment.status,
160
- 'status_display': payment.get_status_display(),
161
- 'provider': payment.provider,
162
- 'provider_display': payment.provider.title(),
163
- 'user_email': payment.user.email,
164
- 'created_at': payment.created_at.isoformat(),
165
- 'url': f'/payments/payment/{payment.id}/',
166
- })
167
-
168
- return JsonResponse({
169
- 'results': results,
170
- 'count': len(results),
171
- 'query': query
172
- })
173
-
174
- except Exception as e:
175
- return JsonResponse({'error': str(e)}, status=500)
176
-
177
-
178
- @superuser_required
179
- def payment_action_ajax(request, payment_id):
180
- """AJAX endpoint for payment actions (cancel, retry, etc.)."""
181
- try:
182
- if request.method != 'POST':
183
- return JsonResponse({'error': 'POST method required'}, status=405)
184
-
185
- payment = get_object_or_404(UniversalPayment, id=payment_id)
186
- action = request.POST.get('action')
187
-
188
- # Log access for audit
189
- log_view_access('payment_action_ajax', request.user,
190
- payment_id=payment_id, action=action)
191
-
192
- if action == 'cancel':
193
- return _handle_cancel_payment(payment, request.user)
194
- elif action == 'retry':
195
- return _handle_retry_payment(payment, request.user)
196
- elif action == 'refresh':
197
- return _handle_refresh_payment(payment, request.user)
198
- else:
199
- return JsonResponse({'error': 'Invalid action'}, status=400)
200
-
201
- except Exception as e:
202
- return JsonResponse({'error': str(e)}, status=500)
203
-
204
-
205
- def _get_event_description(event):
206
- """Get human-readable description for payment event."""
207
- descriptions = {
208
- 'created': 'Payment was created',
209
- 'pending': 'Payment is pending confirmation',
210
- 'confirming': 'Payment is being confirmed',
211
- 'confirmed': 'Payment has been confirmed',
212
- 'completed': 'Payment was completed successfully',
213
- 'failed': 'Payment failed',
214
- 'cancelled': 'Payment was cancelled',
215
- 'expired': 'Payment expired',
216
- 'refunded': 'Payment was refunded',
217
- 'webhook_received': 'Webhook notification received',
218
- 'status_updated': 'Payment status was updated',
219
- }
220
-
221
- description = descriptions.get(event.event_type, f'Event: {event.event_type}')
222
-
223
- # Add metadata details if available
224
- if event.metadata:
225
- if 'reason' in event.metadata:
226
- description += f" - {event.metadata['reason']}"
227
- if 'amount' in event.metadata:
228
- description += f" (Amount: ${event.metadata['amount']})"
229
-
230
- return description
231
-
232
-
233
- def _handle_cancel_payment(payment, user):
234
- """Handle payment cancellation."""
235
- if payment.status not in ['pending', 'confirming']:
236
- return JsonResponse({
237
- 'error': 'Payment cannot be cancelled in current status'
238
- }, status=400)
239
-
240
- try:
241
- # Update payment status
242
- payment.status = 'cancelled'
243
- payment.save()
244
-
245
- # Create event
246
- PaymentEvent.objects.create(
247
- payment=payment,
248
- event_type='cancelled',
249
- metadata={'cancelled_by': user.email}
250
- )
251
-
252
- return JsonResponse({
253
- 'success': True,
254
- 'message': 'Payment cancelled successfully',
255
- 'new_status': payment.status
256
- })
257
-
258
- except Exception as e:
259
- return JsonResponse({'error': f'Failed to cancel payment: {str(e)}'}, status=500)
260
-
261
-
262
- def _handle_retry_payment(payment, user):
263
- """Handle payment retry."""
264
- if payment.status not in ['failed', 'expired']:
265
- return JsonResponse({
266
- 'error': 'Payment cannot be retried in current status'
267
- }, status=400)
268
-
269
- try:
270
- # Reset payment status
271
- payment.status = 'pending'
272
- payment.save()
273
-
274
- # Create event
275
- PaymentEvent.objects.create(
276
- payment=payment,
277
- event_type='retried',
278
- metadata={'retried_by': user.email}
279
- )
280
-
281
- return JsonResponse({
282
- 'success': True,
283
- 'message': 'Payment retry initiated',
284
- 'new_status': payment.status
285
- })
286
-
287
- except Exception as e:
288
- return JsonResponse({'error': f'Failed to retry payment: {str(e)}'}, status=500)
289
-
290
-
291
- def _handle_refresh_payment(payment, user):
292
- """Handle payment status refresh."""
293
- try:
294
- # Try to refresh payment status from provider
295
- from ...services.core.payment_service import PaymentService
296
-
297
- payment_service = PaymentService()
298
- updated_payment = payment_service.refresh_payment_status(payment.id)
299
-
300
- # Create event
301
- PaymentEvent.objects.create(
302
- payment=payment,
303
- event_type='refreshed',
304
- metadata={'refreshed_by': user.email}
305
- )
306
-
307
- return JsonResponse({
308
- 'success': True,
309
- 'message': 'Payment status refreshed',
310
- 'new_status': updated_payment.status
311
- })
312
-
313
- except Exception as e:
314
- return JsonResponse({
315
- 'success': False,
316
- 'message': f'Failed to refresh payment: {str(e)}'
317
- })
318
-
319
-
320
- @superuser_required
321
- def provider_currencies_ajax(request):
322
- """AJAX endpoint for getting supported currencies by provider using new ProviderCurrency model."""
323
- try:
324
- provider_name = request.GET.get('provider')
325
- if not provider_name:
326
- return JsonResponse({'error': 'Provider parameter required'}, status=400)
327
-
328
- # Log access for audit
329
- log_view_access('provider_currencies_ajax', request.user, provider=provider_name)
330
-
331
- # Get flat currency options using manager method
332
- try:
333
- currency_options_dicts = ProviderCurrency.objects.get_currency_options_for_provider(provider_name)
334
-
335
- # Convert dicts to Pydantic models for validation
336
- currency_options = [CurrencyOptionModel(**option) for option in currency_options_dicts]
337
-
338
- # Create typed response
339
- response_data = ProviderCurrencyOptionsResponse(
340
- success=True,
341
- provider=provider_name,
342
- currency_options=currency_options,
343
- count=len(currency_options)
344
- )
345
-
346
- return JsonResponse(response_data.model_dump())
347
-
348
- except Exception as e:
349
- logger.error(f"Error getting currencies for {provider_name}: {e}")
350
-
351
- # Create typed error response
352
- error_response = ProviderCurrencyOptionsResponse(
353
- success=False,
354
- provider=provider_name,
355
- currency_options=[],
356
- count=0,
357
- error=f'Failed to get currencies for {provider_name}: {str(e)}'
358
- )
359
- return JsonResponse(error_response.model_dump())
360
-
361
- except Exception as e:
362
- logger.error(f"Provider currencies AJAX error: {e}")
363
- return JsonResponse({'error': str(e)}, status=500)
364
-
365
-
366
- # currency_networks_ajax removed - networks now included in provider_currencies_ajax
367
-
368
-
369
- @superuser_required
370
- def all_providers_data_ajax(request):
371
- """AJAX endpoint for getting all providers with their currencies and capabilities."""
372
- try:
373
- # Log access for audit
374
- log_view_access('all_providers_data_ajax', request.user)
375
-
376
- # Get provider registry
377
- registry = get_provider_registry()
378
- all_providers = registry.list_providers()
379
-
380
- providers_data = {}
381
-
382
- for provider_name in all_providers:
383
- try:
384
- provider_instance = registry.get_provider(provider_name)
385
-
386
- if provider_instance:
387
- # Get provider info
388
- provider_info = {
389
- 'name': provider_name,
390
- 'display_name': provider_name.title(),
391
- 'enabled': provider_instance.enabled,
392
- 'is_crypto': provider_name in ['nowpayments', 'cryptapi', 'cryptomus'],
393
- 'supports_webhooks': hasattr(provider_instance, 'validate_webhook'),
394
- 'currencies': [],
395
- 'default_currency': None
396
- }
397
-
398
- # Get supported currencies from ProviderCurrency model
399
- try:
400
- provider_currencies = ProviderCurrency.objects.enabled_for_provider(provider_name).select_related(
401
- 'base_currency'
402
- ).distinct('base_currency')[:10] # Limit to first 10 for performance
403
-
404
- for pc in provider_currencies:
405
- provider_info['currencies'].append({
406
- 'code': pc.base_currency.code,
407
- 'name': pc.base_currency.name,
408
- 'type': pc.base_currency.currency_type
409
- })
410
-
411
- # Set default currency
412
- if provider_info['currencies']:
413
- defaults = {
414
- 'cryptapi': 'BTC',
415
- 'cryptomus': 'USDT',
416
- 'nowpayments': 'BTC',
417
- 'stripe': 'USD'
418
- }
419
- default_code = defaults.get(provider_name)
420
- if default_code and any(c['code'] == default_code for c in provider_info['currencies']):
421
- provider_info['default_currency'] = default_code
422
- else:
423
- provider_info['default_currency'] = provider_info['currencies'][0]['code']
424
-
425
- except Exception as e:
426
- provider_info['error'] = f'Failed to load currencies: {str(e)}'
427
-
428
- providers_data[provider_name] = provider_info
429
-
430
- else:
431
- providers_data[provider_name] = {
432
- 'name': provider_name,
433
- 'enabled': False,
434
- 'error': 'Provider instance not available'
435
- }
436
-
437
- except Exception as e:
438
- providers_data[provider_name] = {
439
- 'name': provider_name,
440
- 'enabled': False,
441
- 'error': str(e)
442
- }
443
-
444
- return JsonResponse({
445
- 'success': True,
446
- 'providers': providers_data,
447
- 'count': len(providers_data)
448
- })
449
-
450
- except Exception as e:
451
- return JsonResponse({'error': str(e)}, status=500)
@@ -1,212 +0,0 @@
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
- )