django-cfg 1.2.31__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 (256) 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/manage_currencies.py +303 -151
  39. django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
  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 +342 -152
  43. django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
  44. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  45. django_cfg/apps/payments/models/__init__.py +13 -18
  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 +172 -148
  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 -285
  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 +346 -467
  67. django_cfg/apps/payments/services/core/subscription_service.py +425 -481
  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 +234 -174
  74. django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
  75. django_cfg/apps/payments/services/providers/registry.py +367 -301
  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 +210 -129
  83. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  84. django_cfg/apps/payments/signals/payment_signals.py +128 -103
  85. django_cfg/apps/payments/signals/subscription_signals.py +194 -142
  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 +45 -48
  93. django_cfg/apps/payments/urls_admin.py +33 -42
  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/config.py +1 -1
  110. django_cfg/core/config.py +40 -4
  111. django_cfg/core/generation.py +25 -4
  112. django_cfg/core/integration/README.md +363 -0
  113. django_cfg/core/integration/__init__.py +47 -0
  114. django_cfg/core/integration/commands_collector.py +239 -0
  115. django_cfg/core/integration/display/__init__.py +15 -0
  116. django_cfg/core/integration/display/base.py +157 -0
  117. django_cfg/core/integration/display/ngrok.py +164 -0
  118. django_cfg/core/integration/display/startup.py +815 -0
  119. django_cfg/core/integration/url_integration.py +123 -0
  120. django_cfg/core/integration/version_checker.py +160 -0
  121. django_cfg/management/commands/auto_generate.py +4 -0
  122. django_cfg/management/commands/check_settings.py +6 -0
  123. django_cfg/management/commands/clear_constance.py +5 -2
  124. django_cfg/management/commands/create_token.py +6 -0
  125. django_cfg/management/commands/list_urls.py +6 -0
  126. django_cfg/management/commands/migrate_all.py +6 -0
  127. django_cfg/management/commands/migrator.py +3 -0
  128. django_cfg/management/commands/rundramatiq.py +6 -0
  129. django_cfg/management/commands/runserver_ngrok.py +51 -29
  130. django_cfg/management/commands/script.py +6 -0
  131. django_cfg/management/commands/show_config.py +12 -2
  132. django_cfg/management/commands/show_urls.py +4 -0
  133. django_cfg/management/commands/superuser.py +6 -0
  134. django_cfg/management/commands/task_clear.py +4 -1
  135. django_cfg/management/commands/task_status.py +3 -1
  136. django_cfg/management/commands/test_email.py +3 -0
  137. django_cfg/management/commands/test_telegram.py +6 -0
  138. django_cfg/management/commands/test_twilio.py +6 -0
  139. django_cfg/management/commands/tree.py +6 -0
  140. django_cfg/management/commands/validate_config.py +155 -149
  141. django_cfg/models/constance.py +31 -11
  142. django_cfg/models/payments.py +175 -492
  143. django_cfg/modules/django_logger.py +160 -146
  144. django_cfg/modules/django_unfold/dashboard.py +64 -16
  145. django_cfg/registry/core.py +1 -0
  146. django_cfg/template_archive/django_sample.zip +0 -0
  147. django_cfg/utils/smart_defaults.py +222 -571
  148. django_cfg/utils/toolkit.py +51 -11
  149. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/METADATA +4 -1
  150. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/RECORD +153 -185
  151. django_cfg/apps/payments/__init__.py +0 -8
  152. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  153. django_cfg/apps/payments/config/module.py +0 -70
  154. django_cfg/apps/payments/config/providers.py +0 -105
  155. django_cfg/apps/payments/config/settings.py +0 -96
  156. django_cfg/apps/payments/config/utils.py +0 -52
  157. django_cfg/apps/payments/decorators.py +0 -291
  158. django_cfg/apps/payments/management/commands/README.md +0 -146
  159. django_cfg/apps/payments/management/commands/currency_stats.py +0 -304
  160. django_cfg/apps/payments/managers/__init__.py +0 -23
  161. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  162. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  163. django_cfg/apps/payments/managers/currency_manager.py +0 -306
  164. django_cfg/apps/payments/managers/payment_manager.py +0 -192
  165. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  166. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  167. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
  168. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
  169. django_cfg/apps/payments/models/events.py +0 -73
  170. django_cfg/apps/payments/serializers/__init__.py +0 -57
  171. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  172. django_cfg/apps/payments/serializers/balance.py +0 -59
  173. django_cfg/apps/payments/serializers/currencies.py +0 -63
  174. django_cfg/apps/payments/serializers/payments.py +0 -62
  175. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  176. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  177. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  178. django_cfg/apps/payments/services/cache/base.py +0 -30
  179. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  180. django_cfg/apps/payments/services/internal_types.py +0 -461
  181. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  182. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  183. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
  184. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  185. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
  186. django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
  187. django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
  188. django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
  189. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
  190. django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
  191. django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
  192. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
  193. django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
  194. django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
  195. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
  196. django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
  197. django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
  198. django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
  199. django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
  200. django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
  201. django_cfg/apps/payments/services/security/__init__.py +0 -34
  202. django_cfg/apps/payments/services/security/error_handler.py +0 -635
  203. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  204. django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
  205. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  206. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  207. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  208. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  209. django_cfg/apps/payments/tasks/__init__.py +0 -12
  210. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  211. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
  212. django_cfg/apps/payments/templates/payments/base.html +0 -182
  213. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  214. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  215. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
  216. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  217. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
  218. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
  219. django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
  220. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
  221. django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
  222. django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
  223. django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
  224. django_cfg/apps/payments/templates/payments/stats.html +0 -261
  225. django_cfg/apps/payments/templates/payments/test.html +0 -213
  226. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  227. django_cfg/apps/payments/utils/__init__.py +0 -43
  228. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  229. django_cfg/apps/payments/utils/config_utils.py +0 -239
  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 -63
  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 -122
  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 -451
  241. django_cfg/apps/payments/views/templates/base.py +0 -212
  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 -158
  245. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  246. django_cfg/apps/payments/views/templates/stats.py +0 -244
  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 -66
  250. django_cfg/core/integration.py +0 -160
  251. django_cfg/template_archive/.gitignore +0 -1
  252. django_cfg/template_archive/__init__.py +0 -0
  253. django_cfg/urls.py +0 -33
  254. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
  255. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
  256. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,466 @@
1
+ """
2
+ Universal Payment System v2.0 - Template Tags
3
+
4
+ Custom Django template tags for payment system components.
5
+ """
6
+
7
+ from django import template
8
+ from django.utils.html import format_html
9
+ from django.utils.safestring import mark_safe
10
+ from decimal import Decimal
11
+ import json
12
+
13
+ register = template.Library()
14
+
15
+
16
+ @register.simple_tag
17
+ def material_icon(icon_name, variant='outlined', size='md', css_class=''):
18
+ """
19
+ Render a Material Icon with proper styling.
20
+
21
+ Usage:
22
+ {% material_icon 'payment' %}
23
+ {% material_icon 'check_circle' variant='round' size='lg' css_class='text-green-600' %}
24
+ """
25
+ size_classes = {
26
+ 'xs': 'text-xs',
27
+ 'sm': 'text-sm',
28
+ 'md': 'text-base',
29
+ 'lg': 'text-lg',
30
+ 'xl': 'text-xl',
31
+ '2xl': 'text-2xl'
32
+ }
33
+
34
+ variant_class = f'material-icons-{variant}' if variant != 'filled' else 'material-icons'
35
+ size_class = size_classes.get(size, 'text-base')
36
+
37
+ classes = f'{variant_class} {size_class} {css_class}'.strip()
38
+
39
+ return format_html(
40
+ '<span class="{}">{}</span>',
41
+ classes,
42
+ icon_name
43
+ )
44
+
45
+
46
+ @register.simple_tag
47
+ def status_badge(status, show_icon=True):
48
+ """
49
+ Render a status badge with appropriate styling and icon.
50
+
51
+ Usage:
52
+ {% status_badge payment.status %}
53
+ {% status_badge 'completed' show_icon=False %}
54
+ """
55
+ status_config = {
56
+ 'completed': {
57
+ 'class': 'status-badge success',
58
+ 'icon': 'check_circle',
59
+ 'text': 'Completed'
60
+ },
61
+ 'confirmed': {
62
+ 'class': 'status-badge success',
63
+ 'icon': 'verified',
64
+ 'text': 'Confirmed'
65
+ },
66
+ 'pending': {
67
+ 'class': 'status-badge warning',
68
+ 'icon': 'schedule',
69
+ 'text': 'Pending'
70
+ },
71
+ 'processing': {
72
+ 'class': 'status-badge info',
73
+ 'icon': 'sync',
74
+ 'text': 'Processing'
75
+ },
76
+ 'failed': {
77
+ 'class': 'status-badge error',
78
+ 'icon': 'error',
79
+ 'text': 'Failed'
80
+ },
81
+ 'cancelled': {
82
+ 'class': 'status-badge error',
83
+ 'icon': 'cancel',
84
+ 'text': 'Cancelled'
85
+ },
86
+ 'active': {
87
+ 'class': 'status-badge success',
88
+ 'icon': 'check_circle',
89
+ 'text': 'Active'
90
+ },
91
+ 'inactive': {
92
+ 'class': 'status-badge error',
93
+ 'icon': 'radio_button_unchecked',
94
+ 'text': 'Inactive'
95
+ }
96
+ }
97
+
98
+ config = status_config.get(status.lower(), {
99
+ 'class': 'status-badge info',
100
+ 'icon': 'help',
101
+ 'text': status.title()
102
+ })
103
+
104
+ icon_html = ''
105
+ if show_icon:
106
+ icon_html = format_html(
107
+ '<span class="material-icons-outlined mr-1" style="font-size: 14px;">{}</span>',
108
+ config['icon']
109
+ )
110
+
111
+ return format_html(
112
+ '<span class="{}">{}{}</span>',
113
+ config['class'],
114
+ icon_html,
115
+ config['text']
116
+ )
117
+
118
+
119
+ @register.simple_tag
120
+ def status_indicator(status):
121
+ """
122
+ Render a small status indicator dot.
123
+
124
+ Usage:
125
+ {% status_indicator payment.status %}
126
+ """
127
+ status_classes = {
128
+ 'completed': 'status-active',
129
+ 'confirmed': 'status-active',
130
+ 'active': 'status-active',
131
+ 'pending': 'status-warning',
132
+ 'processing': 'status-warning',
133
+ 'failed': 'status-inactive',
134
+ 'cancelled': 'status-inactive',
135
+ 'inactive': 'status-inactive'
136
+ }
137
+
138
+ css_class = status_classes.get(status.lower(), 'status-warning')
139
+
140
+ return format_html(
141
+ '<span class="status-indicator {}"></span>',
142
+ css_class
143
+ )
144
+
145
+
146
+ @register.simple_tag
147
+ def format_currency(amount, currency='USD', show_symbol=True):
148
+ """
149
+ Format currency amount with proper locale formatting.
150
+
151
+ Usage:
152
+ {% format_currency payment.amount_usd %}
153
+ {% format_currency payment.amount_crypto 'BTC' %}
154
+ """
155
+ try:
156
+ amount = float(amount) if amount else 0
157
+
158
+ if currency == 'USD' and show_symbol:
159
+ return f'${amount:,.2f}'
160
+ elif currency == 'USD':
161
+ return f'{amount:,.2f}'
162
+ else:
163
+ # For crypto currencies, show more decimal places
164
+ if amount >= 1:
165
+ formatted = f'{amount:,.8f}'.rstrip('0').rstrip('.')
166
+ else:
167
+ formatted = f'{amount:.8f}'.rstrip('0').rstrip('.')
168
+
169
+ return f'{formatted} {currency}' if show_symbol else formatted
170
+
171
+ except (ValueError, TypeError):
172
+ return f'0 {currency}' if show_symbol else '0'
173
+
174
+
175
+ @register.simple_tag
176
+ def payment_card(title, icon='payment', content='', css_class=''):
177
+ """
178
+ Render a payment card container.
179
+
180
+ Usage:
181
+ {% payment_card 'Payment Details' 'receipt_long' %}
182
+ """
183
+ return format_html(
184
+ '''
185
+ <div class="payment-card {}">
186
+ <div class="payment-card-header">
187
+ <h2 class="payment-card-title">
188
+ <span class="material-icons-outlined">{}</span>
189
+ {}
190
+ </h2>
191
+ </div>
192
+ <div class="payment-card-content">
193
+ {}
194
+ </div>
195
+ </div>
196
+ ''',
197
+ css_class,
198
+ icon,
199
+ title,
200
+ content
201
+ )
202
+
203
+
204
+ @register.simple_tag
205
+ def copy_button(text, tooltip='Copy to clipboard'):
206
+ """
207
+ Render a copy-to-clipboard button.
208
+
209
+ Usage:
210
+ {% copy_button payment.id %}
211
+ {% copy_button webhook_url 'Copy webhook URL' %}
212
+ """
213
+ return format_html(
214
+ '''
215
+ <button onclick="PaymentSystem.Utils.copyToClipboard('{}')"
216
+ class="btn-icon text-gray-400 hover:text-gray-600 dark:hover:text-gray-200"
217
+ title="{}">
218
+ <span class="material-icons-outlined text-sm">content_copy</span>
219
+ </button>
220
+ ''',
221
+ text,
222
+ tooltip
223
+ )
224
+
225
+
226
+ @register.simple_tag
227
+ def loading_spinner(size='md', text=''):
228
+ """
229
+ Render a loading spinner.
230
+
231
+ Usage:
232
+ {% loading_spinner %}
233
+ {% loading_spinner 'lg' 'Loading...' %}
234
+ """
235
+ spinner_html = format_html(
236
+ '<div class="loading-spinner {}"></div>',
237
+ size
238
+ )
239
+
240
+ if text:
241
+ return format_html(
242
+ '<div class="flex items-center space-x-2">{}<span>{}</span></div>',
243
+ spinner_html,
244
+ text
245
+ )
246
+
247
+ return spinner_html
248
+
249
+
250
+ @register.simple_tag
251
+ def action_button(text, icon, action='', css_class='btn-primary', disabled=False):
252
+ """
253
+ Render an action button with icon.
254
+
255
+ Usage:
256
+ {% action_button 'Create Payment' 'add' 'createPayment()' %}
257
+ {% action_button 'Refresh' 'refresh' css_class='btn-secondary' %}
258
+ """
259
+ disabled_attr = 'disabled' if disabled else ''
260
+ disabled_class = 'opacity-50 cursor-not-allowed' if disabled else ''
261
+
262
+ onclick_attr = f'onclick="{action}"' if action else ''
263
+
264
+ return format_html(
265
+ '''
266
+ <button {} {} class="{} {}">
267
+ <span class="material-icons-outlined">{}</span>
268
+ {}
269
+ </button>
270
+ ''',
271
+ onclick_attr,
272
+ disabled_attr,
273
+ css_class,
274
+ disabled_class,
275
+ icon,
276
+ text
277
+ )
278
+
279
+
280
+ @register.filter
281
+ def json_encode(value):
282
+ """
283
+ JSON encode a value for use in JavaScript.
284
+
285
+ Usage:
286
+ {{ payment_data|json_encode }}
287
+ """
288
+ return mark_safe(json.dumps(value))
289
+
290
+
291
+ @register.filter
292
+ def truncate_id(value, length=8):
293
+ """
294
+ Truncate an ID for display.
295
+
296
+ Usage:
297
+ {{ payment.id|truncate_id }}
298
+ {{ payment.id|truncate_id:12 }}
299
+ """
300
+ if not value:
301
+ return ''
302
+
303
+ str_value = str(value)
304
+ if len(str_value) <= length:
305
+ return str_value
306
+
307
+ return f'{str_value[:length]}...'
308
+
309
+
310
+ @register.filter
311
+ def provider_icon(provider_name):
312
+ """
313
+ Get Material Icon for payment provider.
314
+
315
+ Usage:
316
+ {{ payment.provider|provider_icon }}
317
+ """
318
+ provider_icons = {
319
+ 'nowpayments': 'account_balance',
320
+ 'stripe': 'credit_card',
321
+ 'paypal': 'payment',
322
+ 'cryptapi': 'currency_bitcoin',
323
+ 'cryptomus': 'currency_exchange'
324
+ }
325
+
326
+ return provider_icons.get(provider_name.lower(), 'account_balance')
327
+
328
+
329
+ @register.filter
330
+ def currency_icon(currency_code):
331
+ """
332
+ Get Material Icon for currency.
333
+
334
+ Usage:
335
+ {{ payment.currency.code|currency_icon }}
336
+ """
337
+ currency_icons = {
338
+ 'USD': 'attach_money',
339
+ 'EUR': 'euro',
340
+ 'GBP': 'currency_pound',
341
+ 'BTC': 'currency_bitcoin',
342
+ 'ETH': 'currency_exchange',
343
+ 'LTC': 'currency_exchange',
344
+ 'XRP': 'currency_exchange'
345
+ }
346
+
347
+ return currency_icons.get(currency_code.upper(), 'currency_exchange')
348
+
349
+
350
+ @register.inclusion_tag('payments/components/status_card.html')
351
+ def status_card(title, value, icon='analytics', color_class='text-gray-900 dark:text-white',
352
+ description='', icon_bg_color='blue', status_icon='', action_url='', action_icon='open_in_new'):
353
+ """
354
+ Render a status card component.
355
+
356
+ Usage:
357
+ {% status_card 'Active Payments' payment_count 'payments' 'text-blue-600' 'Currently processing' %}
358
+ """
359
+ return {
360
+ 'title': title,
361
+ 'value': value,
362
+ 'icon': icon,
363
+ 'color_class': color_class,
364
+ 'description': description,
365
+ 'icon_bg_color': icon_bg_color,
366
+ 'status_icon': status_icon,
367
+ 'action_url': action_url,
368
+ 'action_icon': action_icon
369
+ }
370
+
371
+
372
+ @register.inclusion_tag('payments/components/provider_card.html')
373
+ def provider_card(provider):
374
+ """
375
+ Render a provider card component.
376
+
377
+ Usage:
378
+ {% provider_card provider_data %}
379
+ """
380
+ return {'provider': provider}
381
+
382
+
383
+ @register.inclusion_tag('payments/components/loading_spinner.html')
384
+ def loading_component(size='md', text='Loading...'):
385
+ """
386
+ Render a loading spinner component.
387
+
388
+ Usage:
389
+ {% loading_component %}
390
+ {% loading_component 'lg' 'Processing payment...' %}
391
+ """
392
+ return {
393
+ 'size': size,
394
+ 'text': text
395
+ }
396
+
397
+
398
+ @register.simple_tag(takes_context=True)
399
+ def active_nav(context, url_name):
400
+ """
401
+ Check if current URL matches the given URL name.
402
+
403
+ Usage:
404
+ <a href="{% url 'payments:dashboard' %}" class="{% active_nav 'payments:dashboard' %}">
405
+ """
406
+ request = context.get('request')
407
+ if not request:
408
+ return ''
409
+
410
+ try:
411
+ from django.urls import reverse, resolve
412
+ current_url = resolve(request.path_info).url_name
413
+ target_url = url_name.split(':')[-1] # Get the last part after ':'
414
+
415
+ if current_url == target_url:
416
+ return 'text-blue-600 bg-blue-50 dark:bg-blue-900 dark:text-blue-200'
417
+ else:
418
+ return 'text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white'
419
+ except:
420
+ return 'text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white'
421
+
422
+
423
+ @register.simple_tag
424
+ def percentage(value, total):
425
+ """
426
+ Calculate percentage.
427
+
428
+ Usage:
429
+ {% percentage successful_payments total_payments %}%
430
+ """
431
+ try:
432
+ if not total or total == 0:
433
+ return 0
434
+ return round((float(value) / float(total)) * 100, 1)
435
+ except (ValueError, TypeError, ZeroDivisionError):
436
+ return 0
437
+
438
+
439
+ @register.filter
440
+ def multiply(value, arg):
441
+ """
442
+ Multiply two values.
443
+
444
+ Usage:
445
+ {{ amount|multiply:exchange_rate }}
446
+ """
447
+ try:
448
+ return float(value) * float(arg)
449
+ except (ValueError, TypeError):
450
+ return 0
451
+
452
+
453
+ @register.filter
454
+ def divide(value, arg):
455
+ """
456
+ Divide two values.
457
+
458
+ Usage:
459
+ {{ total_amount|divide:payment_count }}
460
+ """
461
+ try:
462
+ if float(arg) == 0:
463
+ return 0
464
+ return float(value) / float(arg)
465
+ except (ValueError, TypeError, ZeroDivisionError):
466
+ return 0
@@ -1,80 +1,77 @@
1
1
  """
2
- URL routing for universal payments with nested routers.
2
+ API URL routing for the Universal Payment System v2.0.
3
+
4
+ DRF routing with nested routers and custom endpoints.
3
5
  """
4
6
 
5
7
  from django.urls import path, include
6
8
  from rest_framework.routers import DefaultRouter
7
9
  from rest_framework_nested import routers
8
10
 
9
- from . import views
11
+ from .views.api import (
12
+ PaymentViewSet, UserPaymentViewSet, PaymentCreateView, PaymentStatusView,
13
+ UserBalanceViewSet, TransactionViewSet, UserTransactionViewSet,
14
+ CurrencyViewSet, NetworkViewSet, ProviderCurrencyViewSet, CurrencyConversionView, CurrencyRatesView, SupportedCurrenciesView,
15
+ SubscriptionViewSet, UserSubscriptionViewSet, EndpointGroupViewSet, TariffViewSet,
16
+ APIKeyViewSet, UserAPIKeyViewSet, APIKeyCreateView, APIKeyValidateView,
17
+ UniversalWebhookView, webhook_health_check, webhook_stats, supported_providers,
18
+ )
10
19
 
11
- app_name = 'payments'
20
+ app_name = 'cfg_payments'
12
21
 
13
22
  # Main router for global endpoints
14
23
  router = DefaultRouter()
15
-
16
- # Global ViewSets (without user nesting)
17
- router.register(r'payments', views.UniversalPaymentViewSet, basename='payment')
18
- router.register(r'subscriptions', views.SubscriptionViewSet, basename='subscription')
19
- router.register(r'api-keys', views.APIKeyViewSet, basename='apikey')
20
- router.register(r'balances', views.UserBalanceViewSet, basename='balance')
21
- router.register(r'transactions', views.TransactionViewSet, basename='transaction')
22
- router.register(r'currencies', views.CurrencyViewSet, basename='currency')
23
- router.register(r'networks', views.NetworkViewSet, basename='network')
24
- router.register(r'provider-currencies', views.ProviderCurrencyViewSet, basename='providercurrency')
25
- router.register(r'endpoint-groups', views.EndpointGroupViewSet, basename='endpointgroup')
26
- router.register(r'tariffs', views.TariffViewSet, basename='tariff')
27
- router.register(r'tariff-endpoint-groups', views.TariffEndpointGroupViewSet, basename='tariffendpointgroup')
24
+ router.register(r'payments', PaymentViewSet, basename='payment')
25
+ router.register(r'balances', UserBalanceViewSet, basename='balance')
26
+ router.register(r'transactions', TransactionViewSet, basename='transaction')
27
+ router.register(r'currencies', CurrencyViewSet, basename='currency')
28
+ router.register(r'networks', NetworkViewSet, basename='network')
29
+ router.register(r'provider-currencies', ProviderCurrencyViewSet, basename='provider-currency')
30
+ router.register(r'subscriptions', SubscriptionViewSet, basename='subscription')
31
+ router.register(r'endpoint-groups', EndpointGroupViewSet, basename='endpoint-group')
32
+ router.register(r'tariffs', TariffViewSet, basename='tariff')
33
+ router.register(r'api-keys', APIKeyViewSet, basename='api-key')
28
34
 
29
35
  # Nested routers for user-specific resources
30
- # /users/{user_id}/payments/
31
36
  users_router = routers.SimpleRouter()
32
- users_router.register(r'users', views.UserPaymentViewSet, basename='user')
37
+ users_router.register(r'users', UserPaymentViewSet, basename='user') # Base for nesting
33
38
 
34
39
  payments_router = routers.NestedSimpleRouter(users_router, r'users', lookup='user')
35
- payments_router.register(r'payments', views.UserPaymentViewSet, basename='user-payment')
40
+ payments_router.register(r'payments', UserPaymentViewSet, basename='user-payment')
36
41
 
37
- # /users/{user_id}/subscriptions/
38
42
  subscriptions_router = routers.NestedSimpleRouter(users_router, r'users', lookup='user')
39
- subscriptions_router.register(r'subscriptions', views.UserSubscriptionViewSet, basename='user-subscription')
43
+ subscriptions_router.register(r'subscriptions', UserSubscriptionViewSet, basename='user-subscription')
40
44
 
41
- # /users/{user_id}/api-keys/
42
45
  apikeys_router = routers.NestedSimpleRouter(users_router, r'users', lookup='user')
43
- apikeys_router.register(r'api-keys', views.UserAPIKeyViewSet, basename='user-apikey')
44
-
45
- # Generic API endpoints
46
- generic_patterns = [
47
- # Payment endpoints
48
- path('payment/create/', views.PaymentCreateView.as_view(), name='payment-create'),
49
- path('payment/status/<str:internal_payment_id>/', views.PaymentStatusView.as_view(), name='payment-status'),
50
-
51
- # Subscription endpoints
52
- path('subscription/create/', views.SubscriptionCreateView.as_view(), name='subscription-create'),
53
- path('subscriptions/active/', views.ActiveSubscriptionsView.as_view(), name='subscriptions-active'),
54
-
55
- # API Key endpoints
56
- path('api-key/create/', views.APIKeyCreateView.as_view(), name='apikey-create'),
57
- path('api-key/validate/', views.APIKeyValidateView.as_view(), name='apikey-validate'),
58
-
59
- # Currency endpoints
60
- path('currencies/supported/', views.SupportedCurrenciesView.as_view(), name='currencies-supported'),
61
- path('currencies/rates/', views.CurrencyRatesView.as_view(), name='currency-rates'),
62
-
63
- # Tariff endpoints
64
- path('tariffs/available/', views.AvailableTariffsView.as_view(), name='tariffs-available'),
65
- path('tariffs/comparison/', views.TariffComparisonView.as_view(), name='tariff-comparison'),
66
- ]
46
+ apikeys_router.register(r'api-keys', UserAPIKeyViewSet, basename='user-api-key')
67
47
 
68
48
  urlpatterns = [
69
49
  # Include all router URLs
70
50
  path('', include(router.urls)),
71
51
 
72
52
  # Include nested router URLs
53
+ path('', include(users_router.urls)),
73
54
  path('', include(payments_router.urls)),
74
55
  path('', include(subscriptions_router.urls)),
75
56
  path('', include(apikeys_router.urls)),
76
57
 
77
- # Include generic API endpoints
78
- path('', include(generic_patterns)),
58
+ # Custom API endpoints
59
+ path('payments/create/', PaymentCreateView.as_view(), name='payment-create'),
60
+ path('payments/status/<uuid:pk>/', PaymentStatusView.as_view(), name='payment-status'),
61
+
62
+ path('currencies/convert/', CurrencyConversionView.as_view(), name='currency-convert'),
63
+ path('currencies/rates/', CurrencyRatesView.as_view(), name='currency-rates'),
64
+ path('currencies/supported/', SupportedCurrenciesView.as_view(), name='currencies-supported'),
65
+
66
+ path('api-keys/create/', APIKeyCreateView.as_view(), name='apikey-create'),
67
+ path('api-keys/validate/', APIKeyValidateView.as_view(), name='apikey-validate'),
68
+
69
+ # Webhook endpoints - specific endpoints MUST come before generic <provider> pattern
70
+ path('webhooks/health/', webhook_health_check, name='webhook-health'),
71
+ path('webhooks/stats/', webhook_stats, name='webhook-stats'),
72
+ path('webhooks/providers/', supported_providers, name='webhook-providers'),
73
+ path('webhooks/<str:provider>/', UniversalWebhookView.as_view(), name='webhook-handler'),
79
74
 
75
+ # Health check endpoint
76
+ path('health/', PaymentViewSet.as_view({'get': 'health'}), name='health-check'),
80
77
  ]
@@ -1,58 +1,49 @@
1
1
  """
2
- Template URLs for Payment Dashboard.
2
+ Admin URLs for Universal Payment System v2.0.
3
3
 
4
- All URLs require superuser access as this is an internal admin tool.
4
+ Internal dashboard and management interfaces.
5
+ All URLs require staff/superuser access.
5
6
  """
6
7
 
7
- from django.urls import path
8
- from .views.templates import (
9
- PaymentDashboardView,
10
- PaymentDetailView,
11
- PaymentCreateView,
12
- PaymentStatsView,
8
+ from django.urls import path, include
9
+ from django.contrib.admin.views.decorators import staff_member_required
10
+
11
+ from .admin_interface.views import (
12
+ WebhookDashboardView,
13
+ PaymentFormView,
14
+ PaymentStatusView,
13
15
  PaymentListView,
14
- PaymentQRCodeView,
15
- PaymentTestView,
16
- payment_status_ajax,
17
- payment_events_ajax,
18
- )
19
- from .views.templates.ajax import (
20
- payment_stats_ajax,
21
- payment_search_ajax,
22
- payment_action_ajax,
23
- provider_currencies_ajax,
24
- all_providers_data_ajax,
16
+ PaymentDashboardView,
17
+ CurrencyConverterView,
25
18
  )
26
- from .views.templates.qr_code import qr_code_data_ajax
27
19
 
28
- app_name = 'payments_dashboard'
20
+ app_name = 'cfg_payments_admin'
29
21
 
30
22
  urlpatterns = [
31
23
  # Main dashboard
32
- path('', PaymentDashboardView.as_view(), name='dashboard'),
33
- path('dashboard/', PaymentDashboardView.as_view(), name='dashboard_alt'),
24
+ path('', staff_member_required(PaymentDashboardView.as_view()), name='dashboard'),
25
+ path('dashboard/', staff_member_required(PaymentDashboardView.as_view()), name='dashboard_alt'),
34
26
 
35
27
  # Payment management
36
- path('list/', PaymentListView.as_view(), name='list'),
37
- path('create/', PaymentCreateView.as_view(), name='create'),
38
- path('stats/', PaymentStatsView.as_view(), name='stats'),
39
-
40
- # Payment details
41
- path('payment/<uuid:pk>/', PaymentDetailView.as_view(), name='detail'),
42
- path('payment/<uuid:pk>/qr/', PaymentQRCodeView.as_view(), name='qr_code'),
28
+ path('payments/', include([
29
+ path('', staff_member_required(PaymentListView.as_view()), name='payment-list'),
30
+ path('create/', staff_member_required(PaymentFormView.as_view()), name='payment-create'),
31
+ path('<uuid:pk>/', staff_member_required(PaymentStatusView.as_view()), name='payment-detail'),
32
+ path('status/<uuid:pk>/', staff_member_required(PaymentStatusView.as_view()), name='payment-status'),
33
+ ])),
43
34
 
44
- # AJAX endpoints
45
- path('ajax/payment/<uuid:payment_id>/status/', payment_status_ajax, name='payment_status_ajax'),
46
- path('ajax/payment/<uuid:payment_id>/events/', payment_events_ajax, name='payment_events_ajax'),
47
- path('ajax/payment/<uuid:payment_id>/qr-data/', qr_code_data_ajax, name='qr_data_ajax'),
48
- path('ajax/payment/<uuid:payment_id>/action/', payment_action_ajax, name='payment_action_ajax'),
49
- path('ajax/stats/', payment_stats_ajax, name='payment_stats_ajax'),
50
- path('ajax/search/', payment_search_ajax, name='payment_search_ajax'),
35
+ # Webhook management
36
+ path('webhooks/', include([
37
+ path('', staff_member_required(WebhookDashboardView.as_view()), name='webhook-dashboard'),
38
+ path('dashboard/', staff_member_required(WebhookDashboardView.as_view()), name='webhook-dashboard-alt'),
39
+ ])),
51
40
 
52
- # Provider and Currency AJAX endpoints
53
- path('ajax/provider/currencies/', provider_currencies_ajax, name='provider_currencies_ajax'),
54
- path('ajax/providers/all/', all_providers_data_ajax, name='all_providers_data_ajax'),
41
+ # Tools and utilities
42
+ path('tools/', include([
43
+ path('converter/', staff_member_required(CurrencyConverterView.as_view()), name='currency-converter'),
44
+ ])),
55
45
 
56
- # Development/testing
57
- path('test/', PaymentTestView.as_view(), name='test'),
46
+ # Development/testing tools (only in DEBUG mode)
47
+ # path('test/', PaymentTestView.as_view(), name='test'),
48
+ # path('debug/', PaymentDebugView.as_view(), name='debug'),
58
49
  ]