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,181 +0,0 @@
1
- """
2
- Utility views for payment dashboard.
3
-
4
- Provides testing, debugging, and development functionality.
5
- """
6
-
7
- from django.views.generic import TemplateView
8
- from django.utils import timezone
9
- from datetime import timedelta
10
- from .base import (
11
- SuperuserRequiredMixin,
12
- PaymentContextMixin,
13
- log_view_access
14
- )
15
-
16
-
17
- class PaymentTestView(
18
- SuperuserRequiredMixin,
19
- PaymentContextMixin,
20
- TemplateView
21
- ):
22
- """Test view for development and debugging purposes."""
23
-
24
- template_name = 'payments/test.html'
25
- page_title = 'Payment System Test'
26
-
27
- def get_breadcrumbs(self):
28
- return [
29
- {'name': 'Dashboard', 'url': '/payments/admin/'},
30
- {'name': 'System Test', 'url': ''},
31
- ]
32
-
33
- def get_context_data(self, **kwargs):
34
- context = super().get_context_data(**kwargs)
35
-
36
- # Log access for audit
37
- log_view_access('payment_test', self.request.user)
38
-
39
- # Create sample data for testing templates
40
- sample_data = self._generate_sample_data()
41
-
42
- # Get system information
43
- system_info = self._get_system_info()
44
-
45
- # Get test scenarios
46
- test_scenarios = self._get_test_scenarios()
47
-
48
- # Get common context
49
- common_context = self.get_common_context()
50
-
51
- context.update({
52
- 'sample_data': sample_data,
53
- 'system_info': system_info,
54
- 'test_scenarios': test_scenarios,
55
- 'test_mode': True,
56
- **common_context
57
- })
58
-
59
- return context
60
-
61
- def _generate_sample_data(self):
62
- """Generate sample payment data for testing."""
63
- sample_payments = []
64
- statuses = ['pending', 'confirming', 'completed', 'failed']
65
- providers = ['nowpayments', 'cryptapi', 'cryptomus', 'stripe']
66
-
67
- for i in range(12):
68
- sample_payments.append({
69
- 'id': f'sample-{i}',
70
- 'internal_payment_id': f'PAY-{1000 + i}',
71
- 'provider_payment_id': f'PROV-{2000 + i}',
72
- 'amount_usd': 50.0 + (i * 25),
73
- 'currency_code': 'USD',
74
- 'status': statuses[i % len(statuses)],
75
- 'provider': providers[i % len(providers)],
76
- 'user_email': f'user{i}@example.com',
77
- 'created_at': timezone.now() - timedelta(hours=i),
78
- 'updated_at': timezone.now() - timedelta(minutes=i * 10),
79
- 'pay_address': f'1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa{i}' if i % 2 == 0 else None,
80
- 'pay_amount': 0.001 + (i * 0.0001) if i % 2 == 0 else None,
81
- })
82
-
83
- return {
84
- 'payments': sample_payments,
85
- 'stats': {
86
- 'total_count': len(sample_payments),
87
- 'pending_count': len([p for p in sample_payments if p['status'] == 'pending']),
88
- 'completed_count': len([p for p in sample_payments if p['status'] == 'completed']),
89
- 'failed_count': len([p for p in sample_payments if p['status'] == 'failed']),
90
- 'total_volume': sum(p['amount_usd'] for p in sample_payments),
91
- }
92
- }
93
-
94
- def _get_system_info(self):
95
- """Get system information for debugging."""
96
- import django
97
- import sys
98
- from django.conf import settings
99
-
100
- info = {
101
- 'django_version': django.get_version(),
102
- 'python_version': sys.version,
103
- 'debug_mode': settings.DEBUG,
104
- 'database_engine': settings.DATABASES['default']['ENGINE'],
105
- 'installed_apps': len(settings.INSTALLED_APPS),
106
- 'timezone': str(settings.TIME_ZONE),
107
- 'language': settings.LANGUAGE_CODE,
108
- }
109
-
110
- # Add payment-specific info
111
- try:
112
- from ...models import UniversalPayment, PaymentEvent
113
- info.update({
114
- 'total_payments': UniversalPayment.objects.count(),
115
- 'total_events': PaymentEvent.objects.count(),
116
- 'providers_in_use': list(
117
- UniversalPayment.objects.values_list('provider', flat=True).distinct()
118
- ),
119
- })
120
- except Exception:
121
- info.update({
122
- 'total_payments': 'Unable to query',
123
- 'total_events': 'Unable to query',
124
- 'providers_in_use': [],
125
- })
126
-
127
- return info
128
-
129
- def _get_test_scenarios(self):
130
- """Get available test scenarios."""
131
- scenarios = [
132
- {
133
- 'name': 'Template Component Test',
134
- 'description': 'Test all payment template components with sample data',
135
- 'endpoint': '/payments/test/?test=components',
136
- 'available': True,
137
- },
138
- {
139
- 'name': 'Status Badge Test',
140
- 'description': 'Test payment status badges for all possible statuses',
141
- 'endpoint': '/payments/test/?test=status_badges',
142
- 'available': True,
143
- },
144
- {
145
- 'name': 'Progress Bar Test',
146
- 'description': 'Test payment progress bars with different percentages',
147
- 'endpoint': '/payments/test/?test=progress_bars',
148
- 'available': True,
149
- },
150
- {
151
- 'name': 'Provider Statistics Test',
152
- 'description': 'Test provider statistics with sample data',
153
- 'endpoint': '/payments/test/?test=provider_stats',
154
- 'available': True,
155
- },
156
- {
157
- 'name': 'Real-time Updates Test',
158
- 'description': 'Test WebSocket connections and real-time updates',
159
- 'endpoint': '/payments/test/?test=realtime',
160
- 'available': False, # Requires WebSocket setup
161
- },
162
- {
163
- 'name': 'QR Code Generation Test',
164
- 'description': 'Test QR code generation for crypto payments',
165
- 'endpoint': '/payments/test/?test=qr_codes',
166
- 'available': True,
167
- },
168
- {
169
- 'name': 'API Integration Test',
170
- 'description': 'Test payment provider API integrations',
171
- 'endpoint': '/payments/test/?test=api_integration',
172
- 'available': False, # Requires API keys
173
- },
174
- ]
175
-
176
- # Add current test parameter
177
- current_test = self.request.GET.get('test', 'overview')
178
- for scenario in scenarios:
179
- scenario['is_current'] = scenario['endpoint'].endswith(f'test={current_test}')
180
-
181
- return scenarios
@@ -1,266 +0,0 @@
1
- """
2
- Webhook processing views with signature validation.
3
- """
4
-
5
- import json
6
- import logging
7
- from typing import Dict, Any
8
-
9
- from django.http import JsonResponse, HttpResponse
10
- from django.views.decorators.csrf import csrf_exempt
11
- from django.views.decorators.http import require_http_methods
12
- from django.utils.decorators import method_decorator
13
- from rest_framework.decorators import api_view, permission_classes
14
- from rest_framework.permissions import AllowAny
15
- from rest_framework.response import Response
16
- from rest_framework import status
17
-
18
- from ..services.core.payment_service import PaymentService
19
- from ..tasks.webhook_processing import process_webhook_with_fallback
20
- from ..services.security.webhook_validator import webhook_validator
21
- from ..services.security.error_handler import error_handler, SecurityError, ValidationError
22
-
23
- logger = logging.getLogger(__name__)
24
-
25
-
26
- @csrf_exempt
27
- @require_http_methods(["POST"])
28
- def webhook_handler(request, provider: str):
29
- """
30
- Main webhook handler with signature validation.
31
-
32
- Accepts webhooks from payment providers and processes them
33
- with proper validation and fallback mechanisms.
34
- """
35
- try:
36
- # Parse webhook data
37
- webhook_data = json.loads(request.body.decode('utf-8'))
38
-
39
- # Extract request headers
40
- request_headers = {
41
- key: value for key, value in request.META.items()
42
- if key.startswith('HTTP_')
43
- }
44
-
45
- # Generate idempotency key for deduplication
46
- idempotency_key = _generate_idempotency_key(provider, webhook_data, request_headers)
47
-
48
- logger.info(f"📥 Received webhook from {provider}, key: {idempotency_key}")
49
-
50
- # Validate webhook with enhanced security
51
- is_valid, validation_error = webhook_validator.validate_webhook(
52
- provider=provider,
53
- webhook_data=webhook_data,
54
- request_headers=request_headers,
55
- raw_body=request.body
56
- )
57
-
58
- if not is_valid:
59
- security_error = SecurityError(
60
- f"Webhook validation failed: {validation_error}",
61
- details={'provider': provider, 'validation_error': validation_error}
62
- )
63
- error_handler.handle_error(security_error, {
64
- 'provider': provider,
65
- 'webhook_data_keys': list(webhook_data.keys()),
66
- 'headers_count': len(request_headers)
67
- }, request)
68
-
69
- return JsonResponse(
70
- {'error': 'Webhook validation failed', 'code': 'INVALID_WEBHOOK'},
71
- status=403
72
- )
73
-
74
- # Process webhook (async with fallback to sync)
75
- result = process_webhook_with_fallback(
76
- provider=provider,
77
- webhook_data=webhook_data,
78
- idempotency_key=idempotency_key,
79
- request_headers=request_headers
80
- )
81
-
82
- if result.get('success'):
83
- logger.info(f"✅ Webhook processed successfully: {idempotency_key}")
84
- return JsonResponse({
85
- 'status': 'success',
86
- 'idempotency_key': idempotency_key,
87
- 'processing_mode': result.get('mode', 'unknown')
88
- })
89
- else:
90
- logger.error(f"❌ Webhook processing failed: {result.get('error')}")
91
- return JsonResponse({
92
- 'status': 'error',
93
- 'error': result.get('error', 'Processing failed'),
94
- 'idempotency_key': idempotency_key
95
- }, status=400)
96
-
97
- except json.JSONDecodeError as e:
98
- validation_error = ValidationError(
99
- f"Invalid JSON in webhook from {provider}",
100
- details={'provider': provider, 'json_error': str(e)}
101
- )
102
- error_result = error_handler.handle_error(validation_error, {
103
- 'provider': provider,
104
- 'raw_body_length': len(request.body) if request.body else 0
105
- }, request)
106
-
107
- return JsonResponse({
108
- 'error': 'Invalid JSON',
109
- 'code': validation_error.error_code
110
- }, status=400)
111
-
112
- except Exception as e:
113
- # Handle unexpected errors with centralized error handler
114
- error_result = error_handler.handle_error(e, {
115
- 'provider': provider,
116
- 'operation': 'webhook_processing',
117
- 'webhook_data_available': 'webhook_data' in locals()
118
- }, request)
119
-
120
- return JsonResponse({
121
- 'error': 'Internal server error',
122
- 'code': error_result.error.error_code
123
- }, status=500)
124
-
125
-
126
- @api_view(['POST'])
127
- @permission_classes([AllowAny])
128
- def webhook_test(request):
129
- """
130
- Test webhook endpoint for development.
131
-
132
- Allows testing webhook processing without requiring
133
- actual payment provider signatures.
134
- """
135
- try:
136
- provider = request.data.get('provider', 'test')
137
- webhook_data = request.data.get('webhook_data', {})
138
-
139
- # Add test marker
140
- webhook_data['_test_webhook'] = True
141
-
142
- # Generate test idempotency key
143
- import uuid
144
- idempotency_key = f"test_{uuid.uuid4().hex[:8]}"
145
-
146
- logger.info(f"🧪 Processing test webhook: {provider}")
147
-
148
- # Process with PaymentService directly (sync)
149
- payment_service = PaymentService()
150
- result = payment_service.process_webhook(
151
- provider=provider,
152
- webhook_data=webhook_data,
153
- request_headers={'HTTP_X_TEST': 'true'}
154
- )
155
-
156
- return Response({
157
- 'status': 'success',
158
- 'test_mode': True,
159
- 'provider': provider,
160
- 'idempotency_key': idempotency_key,
161
- 'result': result.dict() if hasattr(result, 'dict') else result
162
- })
163
-
164
- except Exception as e:
165
- logger.error(f"❌ Test webhook error: {e}")
166
- return Response({
167
- 'status': 'error',
168
- 'error': str(e),
169
- 'test_mode': True
170
- }, status=status.HTTP_400_BAD_REQUEST)
171
-
172
-
173
- def _validate_webhook_signature(provider: str, webhook_data: Dict[str, Any],
174
- request_headers: Dict[str, str]) -> bool:
175
- """
176
- Validate webhook signature based on provider.
177
-
178
- Each provider has different signature validation methods.
179
- """
180
- try:
181
- if provider == 'nowpayments':
182
- return _validate_nowpayments_signature(webhook_data, request_headers)
183
- elif provider == 'cryptapi':
184
- return _validate_cryptapi_signature(webhook_data, request_headers)
185
- elif provider == 'test':
186
- return True # Allow test webhooks
187
- else:
188
- logger.warning(f"Unknown provider for signature validation: {provider}")
189
- return False
190
-
191
- except Exception as e:
192
- logger.error(f"Signature validation error for {provider}: {e}")
193
- return False
194
-
195
-
196
- def _validate_nowpayments_signature(webhook_data: Dict[str, Any],
197
- request_headers: Dict[str, str]) -> bool:
198
- """Validate NowPayments webhook signature."""
199
- import hmac
200
- import hashlib
201
- from ..utils.config_utils import get_payments_config
202
-
203
- # Get IPN secret from config
204
- config = get_payments_config()
205
- if not config or not hasattr(config, 'providers') or 'nowpayments' not in config.providers:
206
- logger.warning("NowPayments IPN secret not configured, skipping validation")
207
- return True # Allow if not configured (development mode)
208
-
209
- nowpayments_config = config.providers['nowpayments']
210
- ipn_secret = getattr(nowpayments_config, 'ipn_secret', None)
211
-
212
- if not ipn_secret:
213
- logger.warning("NowPayments IPN secret not configured, skipping validation")
214
- return True
215
-
216
- # Get signature from headers
217
- signature = request_headers.get('HTTP_X_NOWPAYMENTS_SIG')
218
- if not signature:
219
- logger.warning("No NowPayments signature found in headers")
220
- return False
221
-
222
- # Calculate expected signature
223
- payload = json.dumps(webhook_data, separators=(',', ':'), sort_keys=True)
224
- expected_signature = hmac.new(
225
- ipn_secret.encode(),
226
- payload.encode(),
227
- hashlib.sha512
228
- ).hexdigest()
229
-
230
- return hmac.compare_digest(signature, expected_signature)
231
-
232
-
233
- def _validate_cryptapi_signature(webhook_data: Dict[str, Any],
234
- request_headers: Dict[str, str]) -> bool:
235
- """Validate CryptAPI webhook signature."""
236
- # CryptAPI uses different validation method
237
- # For now, implement basic validation
238
-
239
- # Check if required fields are present
240
- required_fields = ['address_in', 'address_out', 'txid_in', 'value_coin', 'coin', 'confirmations']
241
- for field in required_fields:
242
- if field not in webhook_data:
243
- logger.warning(f"Missing required field in CryptAPI webhook: {field}")
244
- return False
245
-
246
- return True
247
-
248
-
249
- def _generate_idempotency_key(provider: str, webhook_data: Dict[str, Any],
250
- request_headers: Dict[str, str]) -> str:
251
- """Generate idempotency key for webhook deduplication."""
252
- import hashlib
253
-
254
- # Use provider + payment ID + timestamp for uniqueness
255
- payment_id = (
256
- webhook_data.get('payment_id') or
257
- webhook_data.get('order_id') or
258
- webhook_data.get('id') or
259
- 'unknown'
260
- )
261
-
262
- timestamp = webhook_data.get('created_at') or webhook_data.get('timestamp')
263
-
264
- # Create hash from key components
265
- key_data = f"{provider}:{payment_id}:{timestamp}"
266
- return hashlib.md5(key_data.encode()).hexdigest()[:16]
@@ -1,65 +0,0 @@
1
- """
2
- Payment system ViewSets router for easy integration.
3
- """
4
-
5
- from rest_framework.routers import DefaultRouter
6
- from rest_framework_nested import routers
7
-
8
- from .views import (
9
- # Balance ViewSets
10
- UserBalanceViewSet, TransactionViewSet,
11
-
12
- # Payment ViewSets
13
- UserPaymentViewSet, UniversalPaymentViewSet,
14
-
15
- # Subscription ViewSets
16
- UserSubscriptionViewSet, SubscriptionViewSet, EndpointGroupViewSet,
17
-
18
- # API Key ViewSets
19
- UserAPIKeyViewSet, APIKeyViewSet,
20
-
21
- # Currency ViewSets
22
- CurrencyViewSet, CurrencyNetworkViewSet,
23
-
24
- # Tariff ViewSets
25
- TariffViewSet, TariffEndpointGroupViewSet,
26
- )
27
-
28
-
29
- class PaymentSystemRouter:
30
- """Universal router with all payment endpoints"""
31
-
32
- def __init__(self):
33
- self.router = DefaultRouter()
34
- self._setup_main_routes()
35
-
36
- def _setup_main_routes(self):
37
- """Setup main resource routes"""
38
- # Core payment resources
39
- self.router.register(r'payments', UniversalPaymentViewSet, basename='payment')
40
- self.router.register(r'balances', UserBalanceViewSet, basename='balance')
41
- self.router.register(r'transactions', TransactionViewSet, basename='transaction')
42
-
43
- # Subscription management
44
- self.router.register(r'subscriptions', SubscriptionViewSet, basename='subscription')
45
- self.router.register(r'endpoint-groups', EndpointGroupViewSet, basename='endpoint-group')
46
-
47
- # API key management
48
- self.router.register(r'api-keys', APIKeyViewSet, basename='api-key')
49
-
50
- # Currency and pricing
51
- self.router.register(r'currencies', CurrencyViewSet, basename='currency')
52
- self.router.register(r'currency-networks', CurrencyNetworkViewSet, basename='currency-network')
53
- self.router.register(r'tariffs', TariffViewSet, basename='tariff')
54
- self.router.register(r'tariff-groups', TariffEndpointGroupViewSet, basename='tariff-group')
55
-
56
- @property
57
- def urls(self):
58
- """Get all URLs"""
59
- return self.router.urls
60
-
61
-
62
- # Create default router instance
63
- payment_router = PaymentSystemRouter()
64
-
65
- __all__ = ['PaymentSystemRouter', 'payment_router']
@@ -1,160 +0,0 @@
1
- """
2
- Django CFG URL integration utilities.
3
-
4
- Provides automatic URL registration for django_cfg endpoints and integrations.
5
- """
6
-
7
- from typing import List
8
- from django.urls import path, include, URLPattern
9
- from django_cfg.core.environment import EnvironmentDetector
10
- from django.conf import settings
11
-
12
-
13
- def add_django_cfg_urls(urlpatterns: List[URLPattern], cfg_prefix: str = "cfg/") -> List[URLPattern]:
14
- """
15
- Automatically add django_cfg URLs and all integrations to the main URL configuration.
16
-
17
- This function adds:
18
- - Django CFG management URLs (cfg/)
19
- - Django Revolution URLs (if available)
20
- - Debug output (if development environment)
21
-
22
- Args:
23
- urlpatterns: Existing URL patterns list
24
- cfg_prefix: URL prefix for django_cfg endpoints (default: "cfg/")
25
-
26
- Returns:
27
- Updated URL patterns list with all URLs added
28
-
29
- Example:
30
- # In your main urls.py
31
- from django_cfg import add_django_cfg_urls
32
-
33
- urlpatterns = [
34
- path("", home_view, name="home"),
35
- path("admin/", admin.site.urls),
36
- ]
37
-
38
- # Automatically adds:
39
- # - path("cfg/", include("django_cfg.apps.urls"))
40
- # - Django Revolution URLs (if available)
41
- # - Debug output (if development environment)
42
- urlpatterns = add_django_cfg_urls(urlpatterns)
43
- """
44
- # Add django_cfg API URLs
45
- new_patterns = urlpatterns + [
46
- path(cfg_prefix, include("django_cfg.apps.urls")),
47
- ]
48
-
49
- # Try to add Django Revolution URLs if available
50
- try:
51
- from django_revolution import add_revolution_urls
52
- new_patterns = add_revolution_urls(new_patterns)
53
- except ImportError:
54
- # Django Revolution not available - skip
55
- pass
56
-
57
- # Show debug output if in development environment
58
- try:
59
- if EnvironmentDetector.is_development():
60
- _print_url_integration_info()
61
- except ImportError:
62
- # Fallback to Django DEBUG setting
63
- try:
64
- if getattr(settings, 'DEBUG', False):
65
- _print_url_integration_info()
66
- except ImportError:
67
- pass
68
-
69
- return new_patterns
70
-
71
-
72
- def get_django_cfg_urls_info() -> dict:
73
- """
74
- Get information about django_cfg URL integration and all integrations.
75
-
76
- Returns:
77
- Dictionary with complete URL integration info
78
- """
79
- try:
80
- from django_cfg import __version__
81
- version = __version__
82
- except ImportError:
83
- version = "unknown"
84
-
85
- # Get enabled endpoints based on configuration
86
- endpoints = [
87
- "cfg/commands/",
88
- "cfg/health/",
89
- ]
90
-
91
- try:
92
- from django_cfg.modules.base import BaseCfgModule
93
- base_module = BaseCfgModule()
94
-
95
- # All business logic apps are handled by Django Revolution zones
96
- # if base_module.is_support_enabled():
97
- # endpoints.append("cfg/support/")
98
- # if base_module.is_accounts_enabled():
99
- # endpoints.append("cfg/accounts/")
100
- # Newsletter and Leads are handled by Django Revolution zones
101
- # if base_module.is_newsletter_enabled():
102
- # endpoints.append("cfg/newsletter/")
103
- # if base_module.is_leads_enabled():
104
- # endpoints.append("cfg/leads/")
105
- except Exception:
106
- # Fallback: show all possible endpoints
107
- endpoints.extend([
108
- "cfg/support/",
109
- "cfg/accounts/",
110
- "cfg/newsletter/",
111
- "cfg/leads/",
112
- ])
113
-
114
- info = {
115
- "django_cfg": {
116
- "version": version,
117
- "prefix": "cfg/",
118
- "endpoints": endpoints,
119
- "description": "Django CFG management endpoints",
120
- }
121
- }
122
-
123
- # Add Django Revolution info if available
124
- try:
125
- from django_revolution import get_revolution_urls_info
126
- revolution_info = get_revolution_urls_info()
127
- if revolution_info:
128
- info["django_revolution"] = revolution_info
129
- except ImportError:
130
- pass
131
-
132
- return info
133
-
134
-
135
- def _print_url_integration_info():
136
- """Print URL integration debug information."""
137
- integration_info = get_django_cfg_urls_info()
138
-
139
- print("=" * 60)
140
-
141
- # Django CFG info
142
- if "django_cfg" in integration_info:
143
- cfg_info = integration_info["django_cfg"]
144
- print("⚙️ Django CFG URL Integration")
145
- print(f"📦 Version: {cfg_info.get('version', 'unknown')}")
146
- print(f"🔗 Prefix: /{cfg_info.get('prefix', 'cfg/')}")
147
- print(f"📋 Endpoints: {len(cfg_info.get('endpoints', []))}")
148
- for endpoint in cfg_info.get('endpoints', []):
149
- print(f" • {endpoint}")
150
- print("=" * 60)
151
-
152
- # Django Revolution info
153
- if "django_revolution" in integration_info:
154
- revolution_info = integration_info["django_revolution"]
155
- print("🚀 Django Revolution URL Integration")
156
- print(f"📦 Version: {revolution_info.get('version', 'unknown')}")
157
- print(f"📊 Zones: {revolution_info.get('total_zones', 0)}")
158
- print(f"📱 Apps: {revolution_info.get('total_apps', 0)}")
159
- print(f"🔗 API Prefix: /{revolution_info.get('api_prefix', 'apix')}/")
160
- print("=" * 60)