django-cfg 1.2.29__py3-none-any.whl → 1.3.1__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 (258) 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 -9
  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 +600 -108
  9. django_cfg/apps/payments/admin/filters.py +306 -199
  10. django_cfg/apps/payments/admin/payments_admin.py +470 -64
  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/manage_currencies.py +381 -0
  39. django_cfg/apps/payments/management/commands/manage_providers.py +408 -0
  40. django_cfg/apps/payments/middleware/__init__.py +3 -1
  41. django_cfg/apps/payments/middleware/api_access.py +329 -222
  42. django_cfg/apps/payments/middleware/rate_limiting.py +343 -163
  43. django_cfg/apps/payments/middleware/usage_tracking.py +250 -238
  44. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  45. django_cfg/apps/payments/models/__init__.py +16 -20
  46. django_cfg/apps/payments/models/api_keys.py +121 -43
  47. django_cfg/apps/payments/models/balance.py +150 -115
  48. django_cfg/apps/payments/models/base.py +68 -15
  49. django_cfg/apps/payments/models/currencies.py +207 -67
  50. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  51. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  52. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  53. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  54. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  55. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  56. django_cfg/apps/payments/models/payments.py +235 -284
  57. django_cfg/apps/payments/models/subscriptions.py +257 -177
  58. django_cfg/apps/payments/models/tariffs.py +147 -40
  59. django_cfg/apps/payments/services/__init__.py +209 -56
  60. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  61. django_cfg/apps/payments/services/cache/{simple_cache.py → cache_service.py} +112 -12
  62. django_cfg/apps/payments/services/core/__init__.py +10 -6
  63. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  64. django_cfg/apps/payments/services/core/base.py +166 -0
  65. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  66. django_cfg/apps/payments/services/core/payment_service.py +344 -468
  67. django_cfg/apps/payments/services/core/subscription_service.py +425 -484
  68. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  69. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  70. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  71. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  72. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  73. django_cfg/apps/payments/services/providers/base.py +232 -71
  74. django_cfg/apps/payments/services/providers/nowpayments.py +404 -219
  75. django_cfg/apps/payments/services/providers/registry.py +429 -80
  76. django_cfg/apps/payments/services/types/__init__.py +78 -0
  77. django_cfg/apps/payments/services/types/data.py +177 -0
  78. django_cfg/apps/payments/services/types/requests.py +150 -0
  79. django_cfg/apps/payments/services/types/responses.py +156 -0
  80. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  81. django_cfg/apps/payments/signals/__init__.py +33 -8
  82. django_cfg/apps/payments/signals/api_key_signals.py +211 -130
  83. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  84. django_cfg/apps/payments/signals/payment_signals.py +129 -98
  85. django_cfg/apps/payments/signals/subscription_signals.py +195 -143
  86. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  87. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  88. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  89. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  90. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  91. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  92. django_cfg/apps/payments/urls.py +46 -47
  93. django_cfg/apps/payments/urls_admin.py +49 -0
  94. django_cfg/apps/payments/views/api/__init__.py +101 -0
  95. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  96. django_cfg/apps/payments/views/api/balances.py +381 -0
  97. django_cfg/apps/payments/views/api/base.py +298 -0
  98. django_cfg/apps/payments/views/api/currencies.py +402 -0
  99. django_cfg/apps/payments/views/api/payments.py +415 -0
  100. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  101. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  102. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  103. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  104. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  105. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  106. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  107. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  108. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  109. django_cfg/apps/tasks/urls.py +0 -2
  110. django_cfg/apps/tasks/urls_admin.py +14 -0
  111. django_cfg/apps/urls.py +4 -4
  112. django_cfg/config.py +1 -1
  113. django_cfg/core/config.py +75 -4
  114. django_cfg/core/generation.py +25 -4
  115. django_cfg/core/integration/README.md +363 -0
  116. django_cfg/core/integration/__init__.py +47 -0
  117. django_cfg/core/integration/commands_collector.py +239 -0
  118. django_cfg/core/integration/display/__init__.py +15 -0
  119. django_cfg/core/integration/display/base.py +157 -0
  120. django_cfg/core/integration/display/ngrok.py +164 -0
  121. django_cfg/core/integration/display/startup.py +815 -0
  122. django_cfg/core/integration/url_integration.py +123 -0
  123. django_cfg/core/integration/version_checker.py +160 -0
  124. django_cfg/management/commands/auto_generate.py +4 -0
  125. django_cfg/management/commands/check_settings.py +6 -0
  126. django_cfg/management/commands/clear_constance.py +5 -2
  127. django_cfg/management/commands/create_token.py +6 -0
  128. django_cfg/management/commands/list_urls.py +6 -0
  129. django_cfg/management/commands/migrate_all.py +6 -0
  130. django_cfg/management/commands/migrator.py +3 -0
  131. django_cfg/management/commands/rundramatiq.py +6 -0
  132. django_cfg/management/commands/runserver_ngrok.py +51 -29
  133. django_cfg/management/commands/script.py +6 -0
  134. django_cfg/management/commands/show_config.py +12 -2
  135. django_cfg/management/commands/show_urls.py +4 -0
  136. django_cfg/management/commands/superuser.py +6 -0
  137. django_cfg/management/commands/task_clear.py +4 -1
  138. django_cfg/management/commands/task_status.py +3 -1
  139. django_cfg/management/commands/test_email.py +3 -0
  140. django_cfg/management/commands/test_telegram.py +6 -0
  141. django_cfg/management/commands/test_twilio.py +6 -0
  142. django_cfg/management/commands/tree.py +6 -0
  143. django_cfg/management/commands/validate_config.py +155 -149
  144. django_cfg/models/constance.py +31 -11
  145. django_cfg/models/payments.py +175 -498
  146. django_cfg/modules/django_currency/__init__.py +16 -11
  147. django_cfg/modules/django_currency/clients/__init__.py +4 -4
  148. django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
  149. django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
  150. django_cfg/modules/django_currency/core/__init__.py +1 -7
  151. django_cfg/modules/django_currency/core/converter.py +18 -23
  152. django_cfg/modules/django_currency/core/models.py +122 -11
  153. django_cfg/modules/django_currency/database/__init__.py +4 -4
  154. django_cfg/modules/django_currency/database/database_loader.py +190 -309
  155. django_cfg/modules/django_logger.py +160 -146
  156. django_cfg/modules/django_unfold/dashboard.py +65 -12
  157. django_cfg/registry/core.py +1 -0
  158. django_cfg/template_archive/django_sample.zip +0 -0
  159. django_cfg/templates/admin/components/action_grid.html +9 -9
  160. django_cfg/templates/admin/components/metric_card.html +5 -5
  161. django_cfg/templates/admin/components/status_badge.html +2 -2
  162. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
  163. django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
  164. django_cfg/templates/admin/snippets/components/system_health.html +1 -1
  165. django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
  166. django_cfg/utils/smart_defaults.py +222 -571
  167. django_cfg/utils/toolkit.py +51 -11
  168. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/METADATA +5 -4
  169. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/RECORD +172 -182
  170. django_cfg/apps/payments/__init__.py +0 -8
  171. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  172. django_cfg/apps/payments/config/module.py +0 -70
  173. django_cfg/apps/payments/config/providers.py +0 -105
  174. django_cfg/apps/payments/config/settings.py +0 -96
  175. django_cfg/apps/payments/config/utils.py +0 -52
  176. django_cfg/apps/payments/decorators.py +0 -291
  177. django_cfg/apps/payments/management/commands/README.md +0 -178
  178. django_cfg/apps/payments/management/commands/currency_stats.py +0 -323
  179. django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
  180. django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
  181. django_cfg/apps/payments/managers/__init__.py +0 -22
  182. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  183. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  184. django_cfg/apps/payments/managers/currency_manager.py +0 -83
  185. django_cfg/apps/payments/managers/payment_manager.py +0 -44
  186. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  187. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  188. django_cfg/apps/payments/models/events.py +0 -73
  189. django_cfg/apps/payments/serializers/__init__.py +0 -56
  190. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  191. django_cfg/apps/payments/serializers/balance.py +0 -59
  192. django_cfg/apps/payments/serializers/currencies.py +0 -55
  193. django_cfg/apps/payments/serializers/payments.py +0 -62
  194. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  195. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  196. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  197. django_cfg/apps/payments/services/cache/base.py +0 -30
  198. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  199. django_cfg/apps/payments/services/internal_types.py +0 -297
  200. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  201. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  202. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -222
  203. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  204. django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
  205. django_cfg/apps/payments/services/providers/cryptomus.py +0 -311
  206. django_cfg/apps/payments/services/security/__init__.py +0 -34
  207. django_cfg/apps/payments/services/security/error_handler.py +0 -637
  208. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  209. django_cfg/apps/payments/services/security/webhook_validator.py +0 -475
  210. django_cfg/apps/payments/services/validators/__init__.py +0 -8
  211. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  212. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  213. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  214. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  215. django_cfg/apps/payments/tasks/__init__.py +0 -12
  216. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  217. django_cfg/apps/payments/templates/payments/base.html +0 -182
  218. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  219. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  220. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -36
  221. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  222. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -27
  223. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -144
  224. django_cfg/apps/payments/templates/payments/dashboard.html +0 -346
  225. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  226. django_cfg/apps/payments/urls_templates.py +0 -52
  227. django_cfg/apps/payments/utils/__init__.py +0 -45
  228. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  229. django_cfg/apps/payments/utils/config_utils.py +0 -245
  230. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  231. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  232. django_cfg/apps/payments/views/__init__.py +0 -62
  233. django_cfg/apps/payments/views/api_key_views.py +0 -164
  234. django_cfg/apps/payments/views/balance_views.py +0 -75
  235. django_cfg/apps/payments/views/currency_views.py +0 -111
  236. django_cfg/apps/payments/views/payment_views.py +0 -149
  237. django_cfg/apps/payments/views/subscription_views.py +0 -135
  238. django_cfg/apps/payments/views/tariff_views.py +0 -131
  239. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  240. django_cfg/apps/payments/views/templates/ajax.py +0 -312
  241. django_cfg/apps/payments/views/templates/base.py +0 -204
  242. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  243. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  244. django_cfg/apps/payments/views/templates/payment_management.py +0 -164
  245. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  246. django_cfg/apps/payments/views/templates/stats.py +0 -240
  247. django_cfg/apps/payments/views/templates/utils.py +0 -181
  248. django_cfg/apps/payments/views/webhook_views.py +0 -266
  249. django_cfg/apps/payments/viewsets.py +0 -65
  250. django_cfg/core/integration.py +0 -160
  251. django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
  252. django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
  253. django_cfg/template_archive/.gitignore +0 -1
  254. django_cfg/template_archive/__init__.py +0 -0
  255. django_cfg/urls.py +0 -33
  256. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
  257. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
  258. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,164 +0,0 @@
1
- """
2
- Payment management views.
3
-
4
- Provides list, create, and management functionality for payments.
5
- """
6
-
7
- from django.views.generic import TemplateView, ListView
8
- from .base import (
9
- SuperuserRequiredMixin,
10
- PaymentFilterMixin,
11
- PaymentContextMixin,
12
- log_view_access
13
- )
14
- from ...models import UniversalPayment
15
-
16
-
17
- class PaymentCreateView(
18
- SuperuserRequiredMixin,
19
- PaymentContextMixin,
20
- TemplateView
21
- ):
22
- """Form view for creating a new payment."""
23
-
24
- template_name = 'payments/payment_create.html'
25
- page_title = 'Create Payment'
26
-
27
- def get_breadcrumbs(self):
28
- return [
29
- {'name': 'Dashboard', 'url': '/payments/admin/'},
30
- {'name': 'Payments', 'url': '/payments/admin/list/'},
31
- {'name': 'Create Payment', 'url': ''},
32
- ]
33
-
34
- def get_context_data(self, **kwargs):
35
- context = super().get_context_data(**kwargs)
36
-
37
- # Log access for audit
38
- log_view_access('payment_create', self.request.user)
39
-
40
- # Get available providers
41
- providers = self._get_available_providers()
42
-
43
- # Get available currencies
44
- currencies = self._get_available_currencies()
45
-
46
- # Get common context
47
- common_context = self.get_common_context()
48
-
49
- context.update({
50
- 'providers': providers,
51
- 'currencies': currencies,
52
- 'default_amount': 10.0, # Default test amount
53
- **common_context
54
- })
55
-
56
- return context
57
-
58
- def _get_available_providers(self):
59
- """Get list of available payment providers."""
60
- try:
61
- from ...services.providers.registry import ProviderRegistry
62
- providers = []
63
- for provider_name, provider_class in ProviderRegistry.get_all_providers().items():
64
- providers.append({
65
- 'name': provider_name,
66
- 'display_name': provider_name.title(),
67
- 'is_crypto': provider_name in ['nowpayments', 'cryptapi', 'cryptomus'],
68
- 'description': getattr(provider_class, '__doc__', ''),
69
- })
70
- return providers
71
- except Exception:
72
- # Fallback if registry is not available
73
- return [
74
- {'name': 'nowpayments', 'display_name': 'NowPayments', 'is_crypto': True},
75
- {'name': 'cryptapi', 'display_name': 'CryptAPI', 'is_crypto': True},
76
- {'name': 'cryptomus', 'display_name': 'Cryptomus', 'is_crypto': True},
77
- {'name': 'stripe', 'display_name': 'Stripe', 'is_crypto': False},
78
- ]
79
-
80
- def _get_available_currencies(self):
81
- """Get list of available currencies."""
82
- from ...models import Currency
83
-
84
- try:
85
- # Get currencies from database
86
- currencies = Currency.objects.filter(is_active=True).order_by('code')
87
- return [{'code': c.code, 'name': c.name} for c in currencies]
88
- except Exception:
89
- # Fallback list
90
- return [
91
- {'code': 'USD', 'name': 'US Dollar'},
92
- {'code': 'EUR', 'name': 'Euro'},
93
- {'code': 'BTC', 'name': 'Bitcoin'},
94
- {'code': 'ETH', 'name': 'Ethereum'},
95
- {'code': 'LTC', 'name': 'Litecoin'},
96
- ]
97
-
98
-
99
- class PaymentListView(
100
- SuperuserRequiredMixin,
101
- PaymentFilterMixin,
102
- PaymentContextMixin,
103
- ListView
104
- ):
105
- """Paginated list view for all payments."""
106
-
107
- model = UniversalPayment
108
- template_name = 'payments/payment_list.html'
109
- context_object_name = 'payments'
110
- paginate_by = 20
111
- ordering = ['-created_at']
112
- page_title = 'All Payments'
113
-
114
- def get_breadcrumbs(self):
115
- return [
116
- {'name': 'Dashboard', 'url': '/payments/admin/'},
117
- {'name': 'All Payments', 'url': ''},
118
- ]
119
-
120
- def get_queryset(self):
121
- # Log access for audit
122
- log_view_access('payment_list', self.request.user)
123
-
124
- # Use filter mixin to get filtered queryset
125
- return self.get_filtered_payments().order_by(*self.ordering)
126
-
127
- def get_context_data(self, **kwargs):
128
- context = super().get_context_data(**kwargs)
129
-
130
- # Get filter context
131
- filter_context = self.get_filter_context()
132
-
133
- # Get available filter options
134
- filter_options = self._get_filter_options()
135
-
136
- # Get common context
137
- common_context = self.get_common_context()
138
-
139
- context.update({
140
- 'filters': filter_context,
141
- 'filter_options': filter_options,
142
- 'total_count': self.get_queryset().count(),
143
- **common_context
144
- })
145
-
146
- return context
147
-
148
- def _get_filter_options(self):
149
- """Get available options for filter dropdowns."""
150
- from django.db.models import Value
151
- from django.db.models.functions import Concat
152
-
153
- # Get unique statuses
154
- statuses = UniversalPayment.objects.values_list('status', flat=True).distinct()
155
- status_choices = [(status, status.title()) for status in statuses if status]
156
-
157
- # Get unique providers
158
- providers = UniversalPayment.objects.values_list('provider', flat=True).distinct()
159
- provider_choices = [(provider, provider.title()) for provider in providers if provider]
160
-
161
- return {
162
- 'statuses': status_choices,
163
- 'providers': provider_choices,
164
- }
@@ -1,174 +0,0 @@
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)
@@ -1,240 +0,0 @@
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
- for key in metrics:
211
- if metrics[key] > 0:
212
- metrics[f"{key}_formatted"] = self._format_duration(metrics[key])
213
- else:
214
- metrics[f"{key}_formatted"] = "N/A"
215
-
216
- return metrics
217
-
218
- def _calculate_growth_rates(self, current, previous):
219
- """Calculate growth rates between two periods."""
220
- growth = {}
221
-
222
- for key in ['total_count', 'total_volume', 'completed_count']:
223
- current_val = current.get(key, 0)
224
- previous_val = previous.get(key, 0)
225
-
226
- if previous_val > 0:
227
- growth[f"{key}_rate"] = ((current_val - previous_val) / previous_val) * 100
228
- else:
229
- growth[f"{key}_rate"] = 100 if current_val > 0 else 0
230
-
231
- return growth
232
-
233
- def _format_duration(self, seconds):
234
- """Format duration in seconds to human readable format."""
235
- if seconds < 60:
236
- return f"{seconds:.1f}s"
237
- elif seconds < 3600:
238
- return f"{seconds/60:.1f}m"
239
- else:
240
- return f"{seconds/3600:.1f}h"