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
@@ -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,78 +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'currency-networks', views.CurrencyNetworkViewSet, basename='currencynetwork')
24
- router.register(r'endpoint-groups', views.EndpointGroupViewSet, basename='endpointgroup')
25
- router.register(r'tariffs', views.TariffViewSet, basename='tariff')
26
- 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')
27
34
 
28
35
  # Nested routers for user-specific resources
29
- # /users/{user_id}/payments/
30
36
  users_router = routers.SimpleRouter()
31
- users_router.register(r'users', views.UserPaymentViewSet, basename='user')
37
+ users_router.register(r'users', UserPaymentViewSet, basename='user') # Base for nesting
32
38
 
33
39
  payments_router = routers.NestedSimpleRouter(users_router, r'users', lookup='user')
34
- payments_router.register(r'payments', views.UserPaymentViewSet, basename='user-payment')
40
+ payments_router.register(r'payments', UserPaymentViewSet, basename='user-payment')
35
41
 
36
- # /users/{user_id}/subscriptions/
37
42
  subscriptions_router = routers.NestedSimpleRouter(users_router, r'users', lookup='user')
38
- subscriptions_router.register(r'subscriptions', views.UserSubscriptionViewSet, basename='user-subscription')
43
+ subscriptions_router.register(r'subscriptions', UserSubscriptionViewSet, basename='user-subscription')
39
44
 
40
- # /users/{user_id}/api-keys/
41
45
  apikeys_router = routers.NestedSimpleRouter(users_router, r'users', lookup='user')
42
- apikeys_router.register(r'api-keys', views.UserAPIKeyViewSet, basename='user-apikey')
43
-
44
- # Generic API endpoints
45
- generic_patterns = [
46
- # Payment endpoints
47
- path('payment/create/', views.PaymentCreateView.as_view(), name='payment-create'),
48
- path('payment/status/<str:internal_payment_id>/', views.PaymentStatusView.as_view(), name='payment-status'),
49
-
50
- # Subscription endpoints
51
- path('subscription/create/', views.SubscriptionCreateView.as_view(), name='subscription-create'),
52
- path('subscriptions/active/', views.ActiveSubscriptionsView.as_view(), name='subscriptions-active'),
53
-
54
- # API Key endpoints
55
- path('api-key/create/', views.APIKeyCreateView.as_view(), name='apikey-create'),
56
- path('api-key/validate/', views.APIKeyValidateView.as_view(), name='apikey-validate'),
57
-
58
- # Currency endpoints
59
- path('currencies/supported/', views.SupportedCurrenciesView.as_view(), name='currencies-supported'),
60
- path('currencies/rates/', views.CurrencyRatesView.as_view(), name='currency-rates'),
61
-
62
- # Tariff endpoints
63
- path('tariffs/available/', views.AvailableTariffsView.as_view(), name='tariffs-available'),
64
- path('tariffs/comparison/', views.TariffComparisonView.as_view(), name='tariff-comparison'),
65
- ]
46
+ apikeys_router.register(r'api-keys', UserAPIKeyViewSet, basename='user-api-key')
66
47
 
67
48
  urlpatterns = [
68
49
  # Include all router URLs
69
50
  path('', include(router.urls)),
70
51
 
71
52
  # Include nested router URLs
53
+ path('', include(users_router.urls)),
72
54
  path('', include(payments_router.urls)),
73
55
  path('', include(subscriptions_router.urls)),
74
56
  path('', include(apikeys_router.urls)),
75
57
 
76
- # Include generic API endpoints
77
- 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'),
74
+
75
+ # Health check endpoint
76
+ path('health/', PaymentViewSet.as_view({'get': 'health'}), name='health-check'),
78
77
  ]
@@ -0,0 +1,49 @@
1
+ """
2
+ Admin URLs for Universal Payment System v2.0.
3
+
4
+ Internal dashboard and management interfaces.
5
+ All URLs require staff/superuser access.
6
+ """
7
+
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,
15
+ PaymentListView,
16
+ PaymentDashboardView,
17
+ CurrencyConverterView,
18
+ )
19
+
20
+ app_name = 'cfg_payments_admin'
21
+
22
+ urlpatterns = [
23
+ # Main dashboard
24
+ path('', staff_member_required(PaymentDashboardView.as_view()), name='dashboard'),
25
+ path('dashboard/', staff_member_required(PaymentDashboardView.as_view()), name='dashboard_alt'),
26
+
27
+ # Payment management
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
+ ])),
34
+
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
+ ])),
40
+
41
+ # Tools and utilities
42
+ path('tools/', include([
43
+ path('converter/', staff_member_required(CurrencyConverterView.as_view()), name='currency-converter'),
44
+ ])),
45
+
46
+ # Development/testing tools (only in DEBUG mode)
47
+ # path('test/', PaymentTestView.as_view(), name='test'),
48
+ # path('debug/', PaymentDebugView.as_view(), name='debug'),
49
+ ]