django-cfg 1.3.7__py3-none-any.whl → 1.3.11__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 (246) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/__init__.py +24 -8
  3. django_cfg/apps/accounts/admin/activity_admin.py +146 -0
  4. django_cfg/apps/accounts/admin/filters.py +98 -22
  5. django_cfg/apps/accounts/admin/group_admin.py +86 -0
  6. django_cfg/apps/accounts/admin/inlines.py +42 -13
  7. django_cfg/apps/accounts/admin/otp_admin.py +115 -0
  8. django_cfg/apps/accounts/admin/registration_admin.py +173 -0
  9. django_cfg/apps/accounts/admin/resources.py +123 -19
  10. django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
  11. django_cfg/apps/accounts/admin/user_admin.py +362 -0
  12. django_cfg/apps/agents/admin/__init__.py +17 -4
  13. django_cfg/apps/agents/admin/execution_admin.py +204 -183
  14. django_cfg/apps/agents/admin/registry_admin.py +230 -255
  15. django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
  16. django_cfg/apps/agents/core/__init__.py +1 -1
  17. django_cfg/apps/agents/core/django_agent.py +221 -0
  18. django_cfg/apps/agents/core/exceptions.py +14 -0
  19. django_cfg/apps/agents/core/orchestrator.py +18 -3
  20. django_cfg/apps/knowbase/admin/__init__.py +1 -1
  21. django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
  22. django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
  23. django_cfg/apps/knowbase/admin/document_admin.py +269 -262
  24. django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
  25. django_cfg/apps/knowbase/config/settings.py +21 -4
  26. django_cfg/apps/knowbase/views/chat_views.py +3 -0
  27. django_cfg/apps/leads/admin/__init__.py +3 -1
  28. django_cfg/apps/leads/admin/leads_admin.py +235 -35
  29. django_cfg/apps/maintenance/admin/__init__.py +2 -2
  30. django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
  31. django_cfg/apps/maintenance/admin/log_admin.py +143 -61
  32. django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
  33. django_cfg/apps/maintenance/admin/site_admin.py +213 -352
  34. django_cfg/apps/newsletter/admin/__init__.py +29 -2
  35. django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
  36. django_cfg/apps/payments/admin/__init__.py +18 -27
  37. django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
  38. django_cfg/apps/payments/admin/balance_admin.py +166 -632
  39. django_cfg/apps/payments/admin/currencies_admin.py +235 -607
  40. django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
  41. django_cfg/apps/payments/admin/filters.py +83 -3
  42. django_cfg/apps/payments/admin/networks_admin.py +269 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +183 -460
  44. django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
  45. django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
  46. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +153 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  48. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  49. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  50. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  51. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  52. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  53. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  54. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +43 -17
  55. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  56. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  57. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +109 -63
  58. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  59. django_cfg/apps/payments/config/__init__.py +14 -15
  60. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  61. django_cfg/apps/payments/config/helpers.py +8 -13
  62. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  63. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  64. django_cfg/apps/payments/middleware/api_access.py +32 -6
  65. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  66. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  67. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  68. django_cfg/apps/payments/models/balance.py +12 -0
  69. django_cfg/apps/payments/models/currencies.py +106 -32
  70. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  71. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  72. django_cfg/apps/payments/models/payments.py +94 -0
  73. django_cfg/apps/payments/services/core/base.py +4 -4
  74. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  75. django_cfg/apps/payments/services/core/payment_service.py +266 -39
  76. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  77. django_cfg/apps/payments/services/providers/base.py +303 -41
  78. django_cfg/apps/payments/services/providers/models/__init__.py +42 -0
  79. django_cfg/apps/payments/services/providers/models/base.py +145 -0
  80. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  81. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  82. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  83. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  84. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  85. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  86. django_cfg/apps/payments/services/providers/nowpayments/provider.py +557 -0
  87. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  88. django_cfg/apps/payments/services/providers/registry.py +9 -37
  89. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  90. django_cfg/apps/payments/services/types/requests.py +19 -7
  91. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  92. django_cfg/apps/payments/static/payments/js/api-client.js +29 -6
  93. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  94. django_cfg/apps/payments/static/payments/js/payment-form.js +98 -32
  95. django_cfg/apps/payments/tasks/__init__.py +39 -0
  96. django_cfg/apps/payments/tasks/types.py +73 -0
  97. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  98. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  99. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  100. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  101. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  102. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  103. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  104. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  105. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  106. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  107. django_cfg/apps/payments/urls.py +3 -2
  108. django_cfg/apps/payments/urls_admin.py +1 -1
  109. django_cfg/apps/payments/views/api/currencies.py +8 -5
  110. django_cfg/apps/payments/views/overview/services.py +2 -2
  111. django_cfg/apps/payments/views/serializers/currencies.py +22 -8
  112. django_cfg/apps/support/admin/__init__.py +10 -1
  113. django_cfg/apps/support/admin/support_admin.py +338 -141
  114. django_cfg/apps/tasks/admin/__init__.py +11 -0
  115. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  116. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  117. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  118. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  119. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  120. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  121. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  122. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  123. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  124. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  125. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  126. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  127. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  128. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  129. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  130. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  131. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  132. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  133. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  134. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  135. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  136. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  137. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  138. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  139. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  140. django_cfg/apps/tasks/urls.py +2 -2
  141. django_cfg/apps/tasks/urls_admin.py +2 -2
  142. django_cfg/apps/tasks/utils/__init__.py +1 -0
  143. django_cfg/apps/tasks/utils/simulator.py +356 -0
  144. django_cfg/apps/tasks/views/__init__.py +16 -0
  145. django_cfg/apps/tasks/views/api.py +569 -0
  146. django_cfg/apps/tasks/views/dashboard.py +58 -0
  147. django_cfg/config.py +1 -1
  148. django_cfg/core/config.py +10 -5
  149. django_cfg/core/generation.py +1 -1
  150. django_cfg/core/integration/__init__.py +21 -0
  151. django_cfg/management/commands/__init__.py +13 -1
  152. django_cfg/management/commands/migrate_all.py +9 -3
  153. django_cfg/management/commands/migrator.py +11 -6
  154. django_cfg/management/commands/rundramatiq.py +3 -2
  155. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  156. django_cfg/middleware/__init__.py +0 -2
  157. django_cfg/models/api_keys.py +115 -0
  158. django_cfg/models/constance.py +0 -11
  159. django_cfg/models/payments.py +137 -3
  160. django_cfg/modules/django_admin/__init__.py +64 -0
  161. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  162. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  163. django_cfg/modules/django_admin/decorators/display.py +106 -0
  164. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  165. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  166. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  167. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  168. django_cfg/modules/django_admin/models/__init__.py +20 -0
  169. django_cfg/modules/django_admin/models/action_models.py +33 -0
  170. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  171. django_cfg/modules/django_admin/models/base.py +26 -0
  172. django_cfg/modules/django_admin/models/display_models.py +31 -0
  173. django_cfg/modules/django_admin/utils/badges.py +159 -0
  174. django_cfg/modules/django_admin/utils/displays.py +247 -0
  175. django_cfg/modules/django_currency/__init__.py +2 -2
  176. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  177. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  178. django_cfg/modules/django_currency/core/converter.py +12 -12
  179. django_cfg/modules/django_currency/database/__init__.py +2 -2
  180. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  181. django_cfg/modules/django_llm/llm/client.py +10 -2
  182. django_cfg/modules/django_tasks.py +54 -21
  183. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  184. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  185. django_cfg/modules/django_unfold/dashboard.py +14 -13
  186. django_cfg/modules/django_unfold/models/config.py +1 -1
  187. django_cfg/registry/core.py +7 -9
  188. django_cfg/registry/third_party.py +2 -2
  189. django_cfg/template_archive/django_sample.zip +0 -0
  190. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -1
  191. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/RECORD +198 -160
  192. django_cfg/apps/accounts/admin/activity.py +0 -96
  193. django_cfg/apps/accounts/admin/group.py +0 -17
  194. django_cfg/apps/accounts/admin/otp.py +0 -59
  195. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  196. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  197. django_cfg/apps/accounts/admin/user.py +0 -300
  198. django_cfg/apps/agents/core/agent.py +0 -281
  199. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  200. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  201. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  202. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  203. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  204. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  205. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  206. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  207. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  208. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  209. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  210. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  211. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  212. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  213. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  214. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  215. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  216. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  217. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  218. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  219. django_cfg/apps/payments/config/constance/fields.py +0 -69
  220. django_cfg/apps/payments/config/constance/settings.py +0 -160
  221. django_cfg/apps/payments/services/providers/nowpayments.py +0 -478
  222. django_cfg/apps/tasks/admin.py +0 -320
  223. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  224. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  225. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  226. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  227. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  228. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  229. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  230. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  231. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  232. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  233. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  234. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  235. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  236. django_cfg/apps/tasks/views.py +0 -461
  237. django_cfg/management/commands/auto_generate.py +0 -486
  238. django_cfg/middleware/static_nocache.py +0 -55
  239. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  240. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  241. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  242. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  243. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  244. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  245. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  246. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -10,8 +10,22 @@ class PaymentAPIClient {
10
10
 
11
11
  // Helper method to get CSRF token
12
12
  getCSRFToken() {
13
- const token = document.querySelector('[name=csrfmiddlewaretoken]');
14
- return token ? token.value : '';
13
+ // Try to get token from form input first
14
+ const tokenInput = document.querySelector('[name=csrfmiddlewaretoken]');
15
+ if (tokenInput && tokenInput.value) {
16
+ return tokenInput.value;
17
+ }
18
+
19
+ // Fallback to cookie
20
+ const cookieString = document.cookie;
21
+ if (cookieString.includes('csrftoken=')) {
22
+ const match = cookieString.match(/csrftoken=([^;]+)/);
23
+ if (match) {
24
+ return match[1];
25
+ }
26
+ }
27
+
28
+ return '';
15
29
  }
16
30
 
17
31
  // Generic request method
@@ -129,16 +143,20 @@ class PaymentAPIClient {
129
143
  supported: (params = {}) => this.get(`${this.baseURL}/currencies/supported/`, params),
130
144
 
131
145
  // Get currencies by provider
132
- byProvider: (provider) => this.get(`${this.baseURL}/currencies/supported/`, { provider }),
146
+ byProvider: (provider) => this.get(`${this.baseURL}/provider-currencies/`, { provider, page_size: 1000 }),
133
147
 
134
148
  // Get exchange rates
135
149
  rates: (params = {}) => this.get(`${this.baseURL}/currencies/rates/`, params),
136
150
 
137
151
  // Convert currency
138
- convert: (from, to, amount) => this.post(`${this.baseURL}/currencies/convert/`, { from, to, amount }),
152
+ convert: (from, to, amount) => this.post(`${this.baseURL}/currencies/convert/`, {
153
+ from_currency: from,
154
+ to_currency: to,
155
+ amount: amount
156
+ }),
139
157
 
140
158
  // Get provider-specific currency configurations
141
- providerConfigs: (provider) => this.get(`${this.baseURL}/provider-currencies/`, { provider }),
159
+ providerConfigs: (provider) => this.get(`${this.baseURL}/provider-currencies/`, { provider, page_size: 1000 }),
142
160
 
143
161
  // Get currencies grouped by provider
144
162
  byProviderGrouped: () => this.get(`${this.baseURL}/provider-currencies/by_provider/`)
@@ -283,6 +301,7 @@ class PaymentAPIClient {
283
301
  delete: (id) => this.delete(`${this.adminURL}/api/payments/${id}/`),
284
302
  cancel: (id) => this.post(`${this.adminURL}/api/payments/${id}/cancel/`),
285
303
  refund: (id) => this.post(`${this.adminURL}/api/payments/${id}/refund/`),
304
+ refreshStatus: (id) => this.post(`${this.adminURL}/api/payments/${id}/refresh_status/`),
286
305
  stats: () => this.get(`${this.adminURL}/api/payments/stats/`)
287
306
  },
288
307
 
@@ -302,7 +321,7 @@ class PaymentAPIClient {
302
321
 
303
322
  // Webhook test method
304
323
  webhookTest: {
305
- send: (url, eventType) => this.post(`${this.adminURL}/api/webhooks/test/`, {
324
+ send: (url, eventType) => this.post(`${this.adminURL}/api/webhook-test/test/`, {
306
325
  webhook_url: url,
307
326
  event_type: eventType
308
327
  })
@@ -377,7 +396,11 @@ class PaymentAPIClient {
377
396
  }
378
397
 
379
398
  // Create global instance
399
+ console.log('🔧 PaymentAPIClient: Creating global instance...');
380
400
  window.PaymentAPI = new PaymentAPIClient();
401
+ console.log('✅ PaymentAPIClient: Global instance created');
402
+ console.log('🔍 PaymentAPI.currencies:', window.PaymentAPI.currencies);
403
+ console.log('🔍 PaymentAPI.admin:', window.PaymentAPI.admin);
381
404
 
382
405
  // Export for modules
383
406
  if (typeof module !== 'undefined' && module.exports) {
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Payment Detail Page JavaScript
3
+ * Handles payment detail page functionality including AJAX status refresh
4
+ */
5
+
6
+ function paymentDetail() {
7
+ return {
8
+ loading: false,
9
+ showQRCode: false,
10
+ paymentId: null,
11
+
12
+ init(paymentId) {
13
+ this.paymentId = paymentId;
14
+ // Show QR code by default if payment address exists
15
+ const payAddress = document.querySelector('[data-field="pay_address"]');
16
+ if (payAddress && payAddress.textContent.trim()) {
17
+ this.showQRCode = true;
18
+ }
19
+ },
20
+
21
+ async refreshPaymentStatus() {
22
+ this.loading = true;
23
+ try {
24
+ // Use the existing PaymentAPI client
25
+ const data = await PaymentAPI.admin.payments.refreshStatus(this.paymentId);
26
+
27
+ if (data.success) {
28
+ // Show success notification
29
+ PaymentAPI.utils.showNotification(data.message, 'success');
30
+
31
+ // Update payment data on page without full reload
32
+ this.updatePaymentData(data.payment);
33
+ } else {
34
+ // Show error notification
35
+ PaymentAPI.utils.showNotification(data.message || 'Failed to refresh payment status', 'error');
36
+ }
37
+ } catch (error) {
38
+ console.error('Failed to refresh payment status:', error);
39
+ PaymentAPI.utils.showNotification('Network error while refreshing status', 'error');
40
+ } finally {
41
+ this.loading = false;
42
+ }
43
+ },
44
+
45
+ updatePaymentData(paymentData) {
46
+ // Update status badge
47
+ const statusBadge = document.querySelector('.payment-status-badge');
48
+ if (statusBadge && paymentData.status) {
49
+ statusBadge.textContent = paymentData.status.toUpperCase();
50
+ statusBadge.className = `payment-status-badge inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${this.getStatusBadgeClass(paymentData.status)}`;
51
+ }
52
+
53
+ // Update other fields that might have changed
54
+ const fields = {
55
+ 'pay_amount': paymentData.pay_amount,
56
+ 'transaction_hash': paymentData.transaction_hash,
57
+ 'pay_address': paymentData.pay_address,
58
+ 'payment_url': paymentData.payment_url,
59
+ 'expires_at': paymentData.expires_at,
60
+ 'confirmed_at': paymentData.confirmed_at,
61
+ 'updated_at': paymentData.updated_at,
62
+ 'status_changed_at': paymentData.status_changed_at
63
+ };
64
+
65
+ Object.entries(fields).forEach(([field, value]) => {
66
+ const element = document.querySelector(`[data-field="${field}"]`);
67
+ if (element && value !== null && value !== undefined) {
68
+ if (field.includes('_at') && value) {
69
+ // Format datetime fields
70
+ element.textContent = new Date(value).toLocaleString();
71
+ } else if (field === 'pay_amount' && paymentData.currency) {
72
+ // Format pay_amount with currency
73
+ element.textContent = `${parseFloat(value).toFixed(8)} ${paymentData.currency.code}`;
74
+ } else {
75
+ element.textContent = value;
76
+ }
77
+ }
78
+ });
79
+
80
+ // Update error info if present
81
+ if (paymentData.error_info) {
82
+ const errorElement = document.querySelector('[data-field="error_info"]');
83
+ if (errorElement) {
84
+ errorElement.textContent = JSON.stringify(paymentData.error_info, null, 2);
85
+ }
86
+ }
87
+
88
+ // Update QR code visibility if pay_address changed
89
+ if (paymentData.pay_address && !this.showQRCode) {
90
+ this.showQRCode = true;
91
+ }
92
+ },
93
+
94
+ getStatusBadgeClass(status) {
95
+ const statusClasses = {
96
+ 'pending': 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300',
97
+ 'confirming': 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300',
98
+ 'completed': 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300',
99
+ 'confirmed': 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300',
100
+ 'failed': 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300',
101
+ 'cancelled': 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300',
102
+ 'expired': 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-300',
103
+ 'refunded': 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300'
104
+ };
105
+ return statusClasses[status] || 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300';
106
+ },
107
+
108
+ async cancelPayment() {
109
+ if (!confirm('Are you sure you want to cancel this payment?')) {
110
+ return;
111
+ }
112
+
113
+ this.loading = true;
114
+ try {
115
+ const data = await PaymentAPI.admin.payments.cancel(this.paymentId);
116
+ PaymentAPI.utils.showNotification('Payment cancelled successfully', 'success');
117
+
118
+ // Update payment data
119
+ this.updatePaymentData(data);
120
+ } catch (error) {
121
+ console.error('Failed to cancel payment:', error);
122
+ PaymentAPI.utils.showNotification('Failed to cancel payment', 'error');
123
+ } finally {
124
+ this.loading = false;
125
+ }
126
+ },
127
+
128
+ exportDetails() {
129
+ // Get payment data from the page
130
+ const paymentData = {
131
+ id: this.paymentId,
132
+ status: document.querySelector('.payment-status-badge')?.textContent || 'Unknown',
133
+ amount_usd: document.querySelector('[data-field="amount_usd"]')?.textContent || '',
134
+ pay_amount: document.querySelector('[data-field="pay_amount"]')?.textContent || '',
135
+ currency: document.querySelector('[data-field="currency"]')?.textContent || '',
136
+ provider: document.querySelector('[data-field="provider"]')?.textContent || '',
137
+ pay_address: document.querySelector('[data-field="pay_address"]')?.textContent || '',
138
+ transaction_hash: document.querySelector('[data-field="transaction_hash"]')?.textContent || '',
139
+ created_at: document.querySelector('[data-field="created_at"]')?.textContent || '',
140
+ updated_at: document.querySelector('[data-field="updated_at"]')?.textContent || ''
141
+ };
142
+
143
+ // Create CSV content
144
+ const csvContent = Object.entries(paymentData)
145
+ .map(([key, value]) => `${key},${value}`)
146
+ .join('\n');
147
+
148
+ // Download CSV
149
+ const blob = new Blob([csvContent], { type: 'text/csv' });
150
+ const url = window.URL.createObjectURL(blob);
151
+ const a = document.createElement('a');
152
+ a.href = url;
153
+ a.download = `payment_${this.paymentId}_details.csv`;
154
+ document.body.appendChild(a);
155
+ a.click();
156
+ document.body.removeChild(a);
157
+ window.URL.revokeObjectURL(url);
158
+ }
159
+ };
160
+ }
161
+
162
+ // Auto-initialize if PaymentAPI is available
163
+ document.addEventListener('DOMContentLoaded', function() {
164
+ if (typeof PaymentAPI === 'undefined') {
165
+ console.warn('PaymentAPI not found. Make sure api-client.js is loaded before payment-detail.js');
166
+ }
167
+ });
@@ -22,12 +22,18 @@ function paymentForm() {
22
22
  ],
23
23
  conversionResult: null,
24
24
  users: [],
25
+ errorMessage: '',
25
26
 
26
27
  async init() {
28
+ console.log('🚀 PaymentForm: Initializing...');
29
+ console.log('🔍 PaymentAPI object:', window.PaymentAPI);
30
+ console.log('🔍 PaymentAPI.currencies:', window.PaymentAPI?.currencies);
31
+ console.log('🔍 PaymentAPI.admin:', window.PaymentAPI?.admin);
27
32
  await this.loadInitialData();
28
33
  },
29
34
 
30
35
  async loadInitialData() {
36
+ console.log('📊 PaymentForm: Loading initial data...');
31
37
  this.loading = true;
32
38
  try {
33
39
  // Load all currencies and provider-specific currencies
@@ -36,8 +42,9 @@ function paymentForm() {
36
42
  this.loadProviderCurrencies(),
37
43
  this.loadUsers()
38
44
  ]);
45
+ console.log('✅ PaymentForm: Initial data loaded successfully');
39
46
  } catch (error) {
40
- console.error('Failed to load initial data:', error);
47
+ console.error('❌ PaymentForm: Failed to load initial data:', error);
41
48
  PaymentAPI.utils.showNotification('Failed to load form data', 'error');
42
49
  } finally {
43
50
  this.loading = false;
@@ -45,44 +52,60 @@ function paymentForm() {
45
52
  },
46
53
 
47
54
  async loadAllCurrencies() {
55
+ console.log('💰 PaymentForm: Loading all currencies...');
56
+ console.log('🔍 PaymentAPI.currencies.supported type:', typeof PaymentAPI.currencies.supported);
48
57
  try {
58
+ if (typeof PaymentAPI.currencies.supported !== 'function') {
59
+ console.error('❌ PaymentAPI.currencies.supported is not a function:', PaymentAPI.currencies.supported);
60
+ console.log('🔍 Available currencies methods:', Object.keys(PaymentAPI.currencies));
61
+ return;
62
+ }
49
63
  const data = await PaymentAPI.currencies.supported();
50
- this.allCurrencies = data.currencies || [];
64
+ console.log('📊 Currencies API response:', data);
65
+ this.allCurrencies = data.currencies?.currencies || data.currencies || [];
66
+ console.log('✅ Loaded currencies:', this.allCurrencies.length);
51
67
  } catch (error) {
52
- console.error('Failed to load currencies:', error);
68
+ console.error('Failed to load currencies:', error);
53
69
  }
54
70
  },
55
71
 
56
72
  async loadProviderCurrencies() {
57
- if (!this.form.provider) return;
73
+ if (!this.form.provider) {
74
+ console.log('⚠️ PaymentForm: No provider selected, skipping currency load');
75
+ return;
76
+ }
77
+
78
+ console.log('🏦 PaymentForm: Loading provider currencies for:', this.form.provider);
79
+ console.log('🔍 PaymentAPI.currencies.byProvider type:', typeof PaymentAPI.currencies.byProvider);
58
80
 
59
81
  this.loadingCurrencies = true;
60
82
  try {
61
- const data = await PaymentAPI.currencies.providerConfigs(this.form.provider);
62
- this.currencies = data.results || data.currencies || [];
83
+ if (typeof PaymentAPI.currencies.byProvider !== 'function') {
84
+ console.error('❌ PaymentAPI.currencies.byProvider is not a function:', PaymentAPI.currencies.byProvider);
85
+ console.log('🔍 Available currencies methods:', Object.keys(PaymentAPI.currencies));
86
+ return;
87
+ }
88
+
89
+ // Use the provider-currencies API endpoint directly
90
+ const response = await PaymentAPI.request(`/api/payments/provider-currencies/by_provider/?provider=${this.form.provider}`);
91
+ console.log('📊 Provider currencies API response:', response);
63
92
 
64
- // Transform provider currency data for display
65
- this.currencies = this.currencies.map(pc => ({
66
- code: pc.currency?.code || pc.provider_currency_code,
67
- name: pc.currency?.name || pc.provider_currency_code,
68
- type: pc.currency?.currency_type || 'unknown',
69
- symbol: pc.currency?.symbol || '',
70
- network: pc.network?.code || null,
71
- network_name: pc.network?.name || null,
72
- min_amount: pc.min_amount,
73
- max_amount: pc.max_amount,
74
- fee_percentage: pc.fee_percentage,
75
- fixed_fee: pc.fixed_fee,
76
- provider_code: pc.provider_currency_code
77
- }));
93
+ // Extract currencies for the specific provider
94
+ const providerData = response.currencies_by_provider?.[this.form.provider];
95
+ this.currencies = providerData?.currencies || [];
96
+
97
+ console.log('🔍 Sample currency structure:', this.currencies[0]);
98
+
99
+ console.log('✅ Loaded provider currencies:', this.currencies.length);
78
100
 
79
101
  // If current currency is not supported by provider, reset it
80
- if (this.form.currency_code && !this.currencies.find(c => c.code === this.form.currency_code)) {
102
+ if (this.form.currency_code && !this.currencies.find(c => c.provider_currency_code === this.form.currency_code)) {
103
+ console.log('⚠️ Current currency not supported by provider, resetting');
81
104
  this.form.currency_code = '';
82
105
  this.conversionResult = null;
83
106
  }
84
107
  } catch (error) {
85
- console.error('Failed to load provider currencies:', error);
108
+ console.error('Failed to load provider currencies:', error);
86
109
  this.currencies = [];
87
110
  } finally {
88
111
  this.loadingCurrencies = false;
@@ -90,17 +113,43 @@ function paymentForm() {
90
113
  },
91
114
 
92
115
  async loadUsers() {
116
+ console.log('👥 PaymentForm: Loading users...');
117
+ console.log('🔍 PaymentAPI.admin:', PaymentAPI.admin);
118
+ console.log('🔍 PaymentAPI.admin?.users:', PaymentAPI.admin?.users);
119
+ console.log('🔍 PaymentAPI.admin?.users?.list type:', typeof PaymentAPI.admin?.users?.list);
120
+
93
121
  try {
122
+ if (!PaymentAPI.admin) {
123
+ console.error('❌ PaymentAPI.admin is undefined');
124
+ this.users = [{ id: '', username: 'Select User', email: '' }];
125
+ return;
126
+ }
127
+
128
+ if (!PaymentAPI.admin.users) {
129
+ console.error('❌ PaymentAPI.admin.users is undefined');
130
+ this.users = [{ id: '', username: 'Select User', email: '' }];
131
+ return;
132
+ }
133
+
134
+ if (typeof PaymentAPI.admin.users.list !== 'function') {
135
+ console.error('❌ PaymentAPI.admin.users.list is not a function:', PaymentAPI.admin.users.list);
136
+ this.users = [{ id: '', username: 'Select User', email: '' }];
137
+ return;
138
+ }
139
+
94
140
  const data = await PaymentAPI.admin.users.list();
141
+ console.log('📊 Users API response:', data);
95
142
  this.users = data.results || data || [];
96
143
 
97
144
  // If no users loaded, try to get current user info
98
145
  if (this.users.length === 0) {
99
- console.warn('No users loaded from admin API');
146
+ console.warn('⚠️ No users loaded from admin API');
100
147
  this.users = [{ id: '', username: 'Select User', email: '' }];
148
+ } else {
149
+ console.log('✅ Loaded users:', this.users.length);
101
150
  }
102
151
  } catch (error) {
103
- console.error('Failed to load users:', error);
152
+ console.error('Failed to load users:', error);
104
153
  // Set empty option for user selection
105
154
  this.users = [{ id: '', username: 'Select User', email: '' }];
106
155
  }
@@ -122,11 +171,15 @@ function paymentForm() {
122
171
  if (!this.form.amount_usd || !this.form.currency_code) return;
123
172
 
124
173
  try {
125
- const result = await PaymentAPI.currencies.convert('USD', this.form.currency_code, this.form.amount_usd);
174
+ // Get the original currency code for conversion (not provider code)
175
+ const currencyInfo = this.getCurrencyInfo(this.form.currency_code);
176
+ const originalCurrencyCode = currencyInfo.currency?.code || this.form.currency_code;
177
+
178
+ const result = await PaymentAPI.currencies.convert('USD', originalCurrencyCode, this.form.amount_usd);
126
179
  this.conversionResult = {
127
180
  amount: result.converted_amount,
128
181
  rate: result.rate,
129
- currency: this.form.currency_code
182
+ currency: originalCurrencyCode
130
183
  };
131
184
  } catch (error) {
132
185
  console.error('Currency conversion failed:', error);
@@ -134,10 +187,10 @@ function paymentForm() {
134
187
  }
135
188
  },
136
189
 
137
- getCurrencyInfo(code) {
138
- return this.currencies.find(c => c.code === code) ||
139
- this.allCurrencies.find(c => c.code === code) ||
140
- { code, name: code, type: 'unknown' };
190
+ getCurrencyInfo(providerCurrencyCode) {
191
+ return this.currencies.find(c => c.provider_currency_code === providerCurrencyCode) ||
192
+ this.allCurrencies.find(c => c.code === providerCurrencyCode) ||
193
+ { provider_currency_code: providerCurrencyCode, currency: { code: providerCurrencyCode, name: providerCurrencyCode, currency_type: 'unknown' } };
141
194
  },
142
195
 
143
196
  validateForm() {
@@ -151,10 +204,18 @@ function paymentForm() {
151
204
  return errors;
152
205
  },
153
206
 
207
+ clearError() {
208
+ this.errorMessage = '';
209
+ },
210
+
154
211
  async submitForm() {
212
+ // Clear previous error
213
+ this.clearError();
214
+
155
215
  const errors = this.validateForm();
156
216
  if (errors.length > 0) {
157
- PaymentAPI.utils.showNotification(errors.join(', '), 'error');
217
+ this.errorMessage = errors.join(', ');
218
+ PaymentAPI.utils.showNotification('Please fix form errors', 'error');
158
219
  return;
159
220
  }
160
221
 
@@ -166,7 +227,12 @@ function paymentForm() {
166
227
  window.location.href = `/cfg/admin/django_cfg_payments/admin/payments/${data.id}/`;
167
228
  } catch (error) {
168
229
  console.error('Error:', error);
169
- PaymentAPI.utils.showNotification(error.message || 'Failed to create payment', 'error');
230
+
231
+ // Show detailed error in form
232
+ this.errorMessage = error.message || 'Failed to create payment';
233
+
234
+ // Show brief notification
235
+ PaymentAPI.utils.showNotification('Payment creation failed', 'error');
170
236
  } finally {
171
237
  this.loading = false;
172
238
  }
@@ -0,0 +1,39 @@
1
+ """
2
+ Payments Background Tasks
3
+
4
+ Dramatiq tasks for usage tracking, statistics, and payment processing.
5
+ """
6
+
7
+ from .usage_tracking import (
8
+ update_api_key_usage_async,
9
+ update_subscription_usage_async,
10
+ batch_update_usage_counters,
11
+ cleanup_stale_usage_cache
12
+ )
13
+
14
+ from .types import (
15
+ TaskResult,
16
+ UsageUpdateRequest,
17
+ UsageUpdateResult,
18
+ BatchUpdateRequest,
19
+ BatchUpdateResult,
20
+ CleanupResult,
21
+ CacheStats
22
+ )
23
+
24
+ __all__ = [
25
+ # Usage tracking tasks
26
+ 'update_api_key_usage_async',
27
+ 'update_subscription_usage_async',
28
+ 'batch_update_usage_counters',
29
+ 'cleanup_stale_usage_cache',
30
+
31
+ # Pydantic types
32
+ 'TaskResult',
33
+ 'UsageUpdateRequest',
34
+ 'UsageUpdateResult',
35
+ 'BatchUpdateRequest',
36
+ 'BatchUpdateResult',
37
+ 'CleanupResult',
38
+ 'CacheStats',
39
+ ]
@@ -0,0 +1,73 @@
1
+ """
2
+ Pydantic types for background tasks.
3
+ """
4
+
5
+ from typing import Optional, Dict, Any, List
6
+ from pydantic import BaseModel, Field, ConfigDict
7
+ from datetime import datetime
8
+
9
+
10
+ class TaskResult(BaseModel):
11
+ """Base result type for all background tasks."""
12
+ model_config = ConfigDict(validate_assignment=True)
13
+
14
+ status: str = Field(description="Task execution status")
15
+ message: Optional[str] = Field(None, description="Human-readable message")
16
+ error: Optional[str] = Field(None, description="Error message if failed")
17
+ processing_time_ms: Optional[float] = Field(None, description="Processing time in milliseconds")
18
+ timestamp: datetime = Field(default_factory=datetime.utcnow, description="Task completion timestamp")
19
+
20
+
21
+ class UsageUpdateRequest(BaseModel):
22
+ """Request for updating usage counters."""
23
+ model_config = ConfigDict(validate_assignment=True)
24
+
25
+ resource_id: str = Field(description="Resource ID (API key or subscription)")
26
+ increment: int = Field(default=1, description="Amount to increment")
27
+ ip_address: Optional[str] = Field(None, description="Client IP address")
28
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
29
+
30
+
31
+ class UsageUpdateResult(TaskResult):
32
+ """Result of usage update operation."""
33
+
34
+ resource_id: str = Field(description="Updated resource ID")
35
+ total_requests: Optional[int] = Field(None, description="Total requests after update")
36
+ increment: int = Field(description="Amount incremented")
37
+ user_id: Optional[int] = Field(None, description="Associated user ID")
38
+
39
+
40
+ class BatchUpdateRequest(BaseModel):
41
+ """Request for batch usage updates."""
42
+ model_config = ConfigDict(validate_assignment=True)
43
+
44
+ api_key_updates: List[UsageUpdateRequest] = Field(default_factory=list, description="API key updates")
45
+ subscription_updates: List[UsageUpdateRequest] = Field(default_factory=list, description="Subscription updates")
46
+ force_flush: bool = Field(default=False, description="Force immediate processing")
47
+
48
+
49
+ class BatchUpdateResult(TaskResult):
50
+ """Result of batch update operation."""
51
+
52
+ api_keys_updated: int = Field(default=0, description="Number of API keys updated")
53
+ subscriptions_updated: int = Field(default=0, description="Number of subscriptions updated")
54
+ errors: List[Dict[str, Any]] = Field(default_factory=list, description="Processing errors")
55
+ total_items: int = Field(description="Total items processed")
56
+
57
+
58
+ class CleanupResult(TaskResult):
59
+ """Result of cleanup operation."""
60
+
61
+ cleaned_entries: int = Field(default=0, description="Number of entries cleaned")
62
+ cleanup_type: str = Field(description="Type of cleanup performed")
63
+ cutoff_date: Optional[datetime] = Field(None, description="Cleanup cutoff date")
64
+
65
+
66
+ class CacheStats(BaseModel):
67
+ """Cache statistics."""
68
+ model_config = ConfigDict(validate_assignment=True)
69
+
70
+ total_keys: int = Field(description="Total cache keys")
71
+ expired_keys: int = Field(description="Expired cache keys")
72
+ memory_usage_mb: Optional[float] = Field(None, description="Memory usage in MB")
73
+ hit_rate: Optional[float] = Field(None, description="Cache hit rate percentage")