django-cfg 1.3.9__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 (187) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin/networks_admin.py +12 -1
  3. django_cfg/apps/payments/admin/payments_admin.py +13 -0
  4. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +62 -14
  5. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  6. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  7. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  8. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  9. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  10. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  11. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  12. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +33 -3
  13. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  14. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +96 -45
  15. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  16. django_cfg/apps/payments/config/__init__.py +14 -15
  17. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  18. django_cfg/apps/payments/config/helpers.py +8 -13
  19. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  20. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  21. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  22. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  23. django_cfg/apps/payments/models/payments.py +94 -0
  24. django_cfg/apps/payments/services/core/base.py +4 -4
  25. django_cfg/apps/payments/services/core/payment_service.py +265 -38
  26. django_cfg/apps/payments/services/providers/base.py +209 -3
  27. django_cfg/apps/payments/services/providers/models/__init__.py +2 -0
  28. django_cfg/apps/payments/services/providers/models/base.py +25 -2
  29. django_cfg/apps/payments/services/providers/nowpayments/models.py +2 -2
  30. django_cfg/apps/payments/services/providers/nowpayments/provider.py +57 -9
  31. django_cfg/apps/payments/services/providers/registry.py +5 -5
  32. django_cfg/apps/payments/services/types/requests.py +19 -7
  33. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  34. django_cfg/apps/payments/static/payments/js/api-client.js +6 -1
  35. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  36. django_cfg/apps/payments/static/payments/js/payment-form.js +35 -26
  37. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  38. django_cfg/apps/payments/urls.py +3 -2
  39. django_cfg/apps/payments/views/api/currencies.py +3 -0
  40. django_cfg/apps/payments/views/serializers/currencies.py +18 -5
  41. django_cfg/apps/tasks/admin/tasks_admin.py +2 -2
  42. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  43. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  44. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  45. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  46. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  47. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  48. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  49. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  50. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  51. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  52. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  53. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  54. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  55. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  56. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  57. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  58. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  59. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  60. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  61. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  62. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  63. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  64. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  65. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  66. django_cfg/apps/tasks/urls.py +2 -2
  67. django_cfg/apps/tasks/urls_admin.py +2 -2
  68. django_cfg/apps/tasks/utils/__init__.py +1 -0
  69. django_cfg/apps/tasks/utils/simulator.py +356 -0
  70. django_cfg/apps/tasks/views/__init__.py +16 -0
  71. django_cfg/apps/tasks/views/api.py +569 -0
  72. django_cfg/apps/tasks/views/dashboard.py +58 -0
  73. django_cfg/core/integration/__init__.py +21 -0
  74. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  75. django_cfg/models/constance.py +0 -11
  76. django_cfg/models/payments.py +137 -3
  77. django_cfg/modules/django_tasks.py +54 -21
  78. django_cfg/registry/core.py +4 -9
  79. django_cfg/template_archive/django_sample.zip +0 -0
  80. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -2
  81. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/RECORD +84 -152
  82. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  83. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  84. django_cfg/apps/payments/config/constance/fields.py +0 -69
  85. django_cfg/apps/payments/config/constance/settings.py +0 -160
  86. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +0 -26
  87. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +0 -28
  88. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +0 -30
  89. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  90. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  91. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  92. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  93. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  94. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  95. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  96. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  97. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  98. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  99. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  100. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  101. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  102. django_cfg/apps/tasks/views.py +0 -461
  103. django_cfg/management/commands/app_agent_diagnose.py +0 -470
  104. django_cfg/management/commands/app_agent_generate.py +0 -342
  105. django_cfg/management/commands/app_agent_info.py +0 -308
  106. django_cfg/management/commands/auto_generate.py +0 -486
  107. django_cfg/modules/django_app_agent/__init__.py +0 -87
  108. django_cfg/modules/django_app_agent/agents/__init__.py +0 -40
  109. django_cfg/modules/django_app_agent/agents/base/__init__.py +0 -24
  110. django_cfg/modules/django_app_agent/agents/base/agent.py +0 -354
  111. django_cfg/modules/django_app_agent/agents/base/context.py +0 -236
  112. django_cfg/modules/django_app_agent/agents/base/executor.py +0 -430
  113. django_cfg/modules/django_app_agent/agents/generation/__init__.py +0 -12
  114. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +0 -15
  115. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +0 -147
  116. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +0 -99
  117. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +0 -32
  118. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +0 -290
  119. django_cfg/modules/django_app_agent/agents/interfaces.py +0 -376
  120. django_cfg/modules/django_app_agent/core/__init__.py +0 -33
  121. django_cfg/modules/django_app_agent/core/config.py +0 -300
  122. django_cfg/modules/django_app_agent/core/exceptions.py +0 -359
  123. django_cfg/modules/django_app_agent/models/__init__.py +0 -71
  124. django_cfg/modules/django_app_agent/models/base.py +0 -283
  125. django_cfg/modules/django_app_agent/models/context.py +0 -496
  126. django_cfg/modules/django_app_agent/models/enums.py +0 -481
  127. django_cfg/modules/django_app_agent/models/requests.py +0 -500
  128. django_cfg/modules/django_app_agent/models/responses.py +0 -585
  129. django_cfg/modules/django_app_agent/pytest.ini +0 -6
  130. django_cfg/modules/django_app_agent/services/__init__.py +0 -42
  131. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +0 -30
  132. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +0 -133
  133. django_cfg/modules/django_app_agent/services/app_generator/context.py +0 -40
  134. django_cfg/modules/django_app_agent/services/app_generator/main.py +0 -202
  135. django_cfg/modules/django_app_agent/services/app_generator/structure.py +0 -316
  136. django_cfg/modules/django_app_agent/services/app_generator/validation.py +0 -125
  137. django_cfg/modules/django_app_agent/services/base.py +0 -437
  138. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +0 -34
  139. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +0 -141
  140. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +0 -276
  141. django_cfg/modules/django_app_agent/services/context_builder/main.py +0 -272
  142. django_cfg/modules/django_app_agent/services/context_builder/models.py +0 -40
  143. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +0 -85
  144. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +0 -31
  145. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +0 -311
  146. django_cfg/modules/django_app_agent/services/project_scanner/main.py +0 -221
  147. django_cfg/modules/django_app_agent/services/project_scanner/models.py +0 -59
  148. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +0 -94
  149. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +0 -28
  150. django_cfg/modules/django_app_agent/services/questioning_service/main.py +0 -273
  151. django_cfg/modules/django_app_agent/services/questioning_service/models.py +0 -111
  152. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +0 -251
  153. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +0 -347
  154. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +0 -356
  155. django_cfg/modules/django_app_agent/services/report_service.py +0 -332
  156. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +0 -18
  157. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +0 -236
  158. django_cfg/modules/django_app_agent/services/template_manager/main.py +0 -159
  159. django_cfg/modules/django_app_agent/services/template_manager/models.py +0 -36
  160. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +0 -100
  161. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +0 -105
  162. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +0 -31
  163. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +0 -44
  164. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +0 -81
  165. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +0 -107
  166. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +0 -139
  167. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +0 -91
  168. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +0 -195
  169. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +0 -35
  170. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +0 -211
  171. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +0 -200
  172. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +0 -25
  173. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +0 -333
  174. django_cfg/modules/django_app_agent/services/validation_service/main.py +0 -242
  175. django_cfg/modules/django_app_agent/services/validation_service/models.py +0 -66
  176. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +0 -352
  177. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +0 -272
  178. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +0 -203
  179. django_cfg/modules/django_app_agent/ui/__init__.py +0 -25
  180. django_cfg/modules/django_app_agent/ui/cli.py +0 -419
  181. django_cfg/modules/django_app_agent/ui/rich_components.py +0 -622
  182. django_cfg/modules/django_app_agent/utils/__init__.py +0 -38
  183. django_cfg/modules/django_app_agent/utils/logging.py +0 -360
  184. django_cfg/modules/django_app_agent/utils/validation.py +0 -417
  185. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  186. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  187. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,363 @@
1
+ {% extends 'payments/base.html' %}
2
+ {% load static %}
3
+ {% load payment_tags %}
4
+
5
+ {% block title %}Payment Details - Payment Admin{% endblock %}
6
+
7
+ {% block page_title %}Payment Details{% endblock %}
8
+ {% block page_subtitle %}Payment {{ payment.internal_payment_id|default:payment.id|truncatechars:8 }}{% endblock %}
9
+
10
+ {% block content %}
11
+ <div x-data="paymentDetail()" x-init="init('{{ payment.id }}')" class="space-y-6">
12
+
13
+ <!-- Back Navigation -->
14
+ <div class="flex items-center space-x-4 mb-6">
15
+ <a href="{% url 'cfg_payments_admin:payment-list' %}"
16
+ class="inline-flex items-center px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-md text-sm font-medium transition-colors">
17
+ <span class="material-icons-outlined mr-2">arrow_back</span>
18
+ Back to Payments
19
+ </a>
20
+
21
+ <!-- Status Refresh Button -->
22
+ <button @click="refreshPaymentStatus()"
23
+ :disabled="loading"
24
+ class="inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white rounded-md text-sm font-medium transition-colors">
25
+ <span class="material-icons-outlined mr-2" :class="{ 'animate-spin': loading }">refresh</span>
26
+ <span x-show="!loading">Refresh Status</span>
27
+ <span x-show="loading">Refreshing...</span>
28
+ </button>
29
+ </div>
30
+
31
+ <!-- Payment Overview Card -->
32
+ <div class="bg-white dark:bg-gray-800 shadow rounded-lg">
33
+ <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
34
+ <div class="flex items-center justify-between">
35
+ <div>
36
+ <h3 class="text-lg font-medium text-gray-900 dark:text-white">Payment Overview</h3>
37
+ <p class="text-sm text-gray-600 dark:text-gray-400 mt-1">Complete payment information</p>
38
+ </div>
39
+ <div class="flex items-center space-x-3">
40
+ {% include 'payments/components/status_badge.html' with payment=payment %}
41
+ {% if payment.pay_address %}
42
+ <button @click="showQRCode = !showQRCode"
43
+ class="inline-flex items-center px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md text-sm font-medium transition-colors">
44
+ <span class="material-icons-outlined mr-2">qr_code</span>
45
+ <span x-text="showQRCode ? 'Hide QR' : 'Show QR'"></span>
46
+ </button>
47
+ {% endif %}
48
+ </div>
49
+ </div>
50
+ </div>
51
+
52
+ <div class="p-6">
53
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
54
+ <!-- Payment Information -->
55
+ <div>
56
+ <h4 class="text-md font-semibold text-gray-900 dark:text-white mb-4">Payment Information</h4>
57
+ <dl class="space-y-3">
58
+ <div class="flex justify-between">
59
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Internal ID:</dt>
60
+ <dd class="text-sm font-mono text-gray-900 dark:text-white">{{ payment.internal_payment_id|default:payment.id }}</dd>
61
+ </div>
62
+ <div class="flex justify-between">
63
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Provider ID:</dt>
64
+ <dd class="text-sm font-mono text-gray-900 dark:text-white">{{ payment.provider_payment_id|default:"N/A" }}</dd>
65
+ </div>
66
+ <div class="flex justify-between">
67
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Amount:</dt>
68
+ <dd class="text-sm font-semibold text-gray-900 dark:text-white">${{ payment.amount_usd|floatformat:2 }}</dd>
69
+ </div>
70
+ <div class="flex justify-between">
71
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Currency:</dt>
72
+ <dd class="text-sm text-gray-900 dark:text-white">{{ payment.currency.code }} - {{ payment.currency.name }}</dd>
73
+ </div>
74
+ <div class="flex justify-between">
75
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Provider:</dt>
76
+ <dd class="text-sm text-gray-900 dark:text-white">{{ payment.provider }}</dd>
77
+ </div>
78
+ {% if payment.description %}
79
+ <div class="flex justify-between">
80
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Description:</dt>
81
+ <dd class="text-sm text-gray-900 dark:text-white">{{ payment.description }}</dd>
82
+ </div>
83
+ {% endif %}
84
+ {% if payment.payment_url %}
85
+ <div class="flex justify-between">
86
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Payment URL:</dt>
87
+ <dd class="text-sm">
88
+ <a href="{{ payment.payment_url }}" target="_blank"
89
+ class="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300">
90
+ Open Payment Page
91
+ <span class="material-icons-outlined text-sm ml-1">open_in_new</span>
92
+ </a>
93
+ </dd>
94
+ </div>
95
+ {% endif %}
96
+ </dl>
97
+ </div>
98
+
99
+ <!-- User Information -->
100
+ <div>
101
+ <h4 class="text-md font-semibold text-gray-900 dark:text-white mb-4">User Information</h4>
102
+ <dl class="space-y-3">
103
+ <div class="flex justify-between">
104
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Name:</dt>
105
+ <dd class="text-sm text-gray-900 dark:text-white">{{ payment.user.get_full_name|default:"N/A" }}</dd>
106
+ </div>
107
+ <div class="flex justify-between">
108
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Email:</dt>
109
+ <dd class="text-sm text-gray-900 dark:text-white">{{ payment.user.email }}</dd>
110
+ </div>
111
+ <div class="flex justify-between">
112
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Username:</dt>
113
+ <dd class="text-sm text-gray-900 dark:text-white">{{ payment.user.username }}</dd>
114
+ </div>
115
+ <div class="flex justify-between">
116
+ <dt class="text-sm text-gray-600 dark:text-gray-400">User ID:</dt>
117
+ <dd class="text-sm font-mono text-gray-900 dark:text-white">{{ payment.user.id }}</dd>
118
+ </div>
119
+ </dl>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+
125
+
126
+ <!-- QR Code Section (Collapsible) -->
127
+ {% if payment.pay_address %}
128
+ <div x-show="showQRCode"
129
+ x-transition:enter="transition ease-out duration-300"
130
+ x-transition:enter-start="opacity-0 transform scale-95"
131
+ x-transition:enter-end="opacity-100 transform scale-100"
132
+ x-transition:leave="transition ease-in duration-200"
133
+ x-transition:leave-start="opacity-100 transform scale-100"
134
+ x-transition:leave-end="opacity-0 transform scale-95"
135
+ class="bg-white dark:bg-gray-800 shadow rounded-lg">
136
+ <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
137
+ <h3 class="text-lg font-medium text-gray-900 dark:text-white">Payment QR Code</h3>
138
+ <p class="text-sm text-gray-600 dark:text-gray-400 mt-1">Scan to make payment</p>
139
+ </div>
140
+
141
+ <div class="p-6">
142
+ {% include 'payments/components/payment_qr_code.html' %}
143
+ </div>
144
+ </div>
145
+ {% else %}
146
+ <div class="bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded mb-4">
147
+ <strong>QR Code not shown:</strong> payment.pay_address is empty or null
148
+ </div>
149
+ {% endif %}
150
+
151
+ <!-- Crypto Details (if applicable) -->
152
+ {% if payment.pay_address %}
153
+ <div class="bg-white dark:bg-gray-800 shadow rounded-lg">
154
+ <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
155
+ <h3 class="text-lg font-medium text-gray-900 dark:text-white">Cryptocurrency Details</h3>
156
+ <p class="text-sm text-gray-600 dark:text-gray-400 mt-1">Blockchain transaction information</p>
157
+ </div>
158
+
159
+ <div class="p-6">
160
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
161
+ <!-- Payment Addresses -->
162
+ <div>
163
+ <h4 class="text-md font-semibold text-gray-900 dark:text-white mb-4">Payment Addresses</h4>
164
+ <dl class="space-y-3">
165
+ <div>
166
+ <dt class="text-sm text-gray-600 dark:text-gray-400 mb-1">Pay Address:</dt>
167
+ <dd class="text-sm font-mono bg-gray-50 dark:bg-gray-700 p-2 rounded border border-gray-200 dark:border-gray-600 break-all" data-field="pay_address">
168
+ {{ payment.pay_address }}
169
+ </dd>
170
+ </div>
171
+ {% if payment.sender_address %}
172
+ <div>
173
+ <dt class="text-sm text-gray-600 dark:text-gray-400 mb-1">Sender Address:</dt>
174
+ <dd class="text-sm font-mono bg-gray-50 dark:bg-gray-700 p-2 rounded border border-gray-200 dark:border-gray-600 break-all">
175
+ {{ payment.sender_address }}
176
+ </dd>
177
+ </div>
178
+ {% endif %}
179
+ {% if payment.receiver_address %}
180
+ <div>
181
+ <dt class="text-sm text-gray-600 dark:text-gray-400 mb-1">Receiver Address:</dt>
182
+ <dd class="text-sm font-mono bg-gray-50 dark:bg-gray-700 p-2 rounded border border-gray-200 dark:border-gray-600 break-all">
183
+ {{ payment.receiver_address }}
184
+ </dd>
185
+ </div>
186
+ {% endif %}
187
+ </dl>
188
+ </div>
189
+
190
+ <!-- Transaction Details -->
191
+ <div>
192
+ <h4 class="text-md font-semibold text-gray-900 dark:text-white mb-4">Transaction Details</h4>
193
+ <dl class="space-y-3">
194
+ {% if payment.pay_amount %}
195
+ <div class="flex justify-between">
196
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Pay Amount:</dt>
197
+ <dd class="text-sm font-semibold text-gray-900 dark:text-white" data-field="pay_amount">
198
+ {{ payment.pay_amount|floatformat:8 }} {{ payment.currency.code }}
199
+ </dd>
200
+ </div>
201
+ {% endif %}
202
+ {% if payment.network %}
203
+ <div class="flex justify-between">
204
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Network:</dt>
205
+ <dd class="text-sm text-gray-900 dark:text-white">{{ payment.network.name }}</dd>
206
+ </div>
207
+ {% endif %}
208
+ {% if payment.expires_at %}
209
+ <div class="flex justify-between">
210
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Expires At:</dt>
211
+ <dd class="text-sm text-gray-900 dark:text-white">{{ payment.expires_at|date:"M d, Y H:i" }}</dd>
212
+ </div>
213
+ {% endif %}
214
+ {% if payment.completed_at %}
215
+ <div class="flex justify-between">
216
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Completed At:</dt>
217
+ <dd class="text-sm text-gray-900 dark:text-white">{{ payment.completed_at|date:"M d, Y H:i" }}</dd>
218
+ </div>
219
+ {% endif %}
220
+ {% if payment.transaction_hash %}
221
+ <div>
222
+ <dt class="text-sm text-gray-600 dark:text-gray-400 mb-1">Transaction Hash:</dt>
223
+ <dd class="text-sm font-mono bg-gray-50 dark:bg-gray-700 p-2 rounded border border-gray-200 dark:border-gray-600 break-all" data-field="transaction_hash">
224
+ {{ payment.transaction_hash }}
225
+ {% if payment.get_payment_explorer_link %}
226
+ <a href="{{ payment.get_payment_explorer_link }}" target="_blank"
227
+ class="ml-2 text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300">
228
+ <span class="material-icons-outlined text-sm">open_in_new</span>
229
+ </a>
230
+ {% endif %}
231
+ </dd>
232
+ </div>
233
+ {% endif %}
234
+ </dl>
235
+ </div>
236
+ </div>
237
+ </div>
238
+ </div>
239
+ {% endif %}
240
+
241
+ <!-- Timeline -->
242
+ <div class="bg-white dark:bg-gray-800 shadow rounded-lg">
243
+ <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
244
+ <h3 class="text-lg font-medium text-gray-900 dark:text-white">Timeline</h3>
245
+ <p class="text-sm text-gray-600 dark:text-gray-400 mt-1">Payment lifecycle timestamps</p>
246
+ </div>
247
+
248
+ <div class="p-6">
249
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-6">
250
+ <div class="text-center">
251
+ <div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Created</div>
252
+ <div class="text-sm font-medium text-gray-900 dark:text-white">{{ payment.created_at|date:"M d, Y" }}</div>
253
+ <div class="text-xs text-gray-500 dark:text-gray-400">{{ payment.created_at|time:"H:i:s" }}</div>
254
+ </div>
255
+ <div class="text-center">
256
+ <div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Status Changed</div>
257
+ {% if payment.status_changed_at %}
258
+ <div class="text-sm font-medium text-gray-900 dark:text-white" data-field="status_changed_at">{{ payment.status_changed_at|date:"M d, Y" }}</div>
259
+ <div class="text-xs text-gray-500 dark:text-gray-400">{{ payment.status_changed_at|time:"H:i:s" }}</div>
260
+ {% else %}
261
+ <div class="text-sm text-gray-500 dark:text-gray-400" data-field="status_changed_at">Not changed</div>
262
+ {% endif %}
263
+ </div>
264
+ <div class="text-center">
265
+ <div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Updated</div>
266
+ <div class="text-sm font-medium text-gray-900 dark:text-white">{{ payment.updated_at|date:"M d, Y" }}</div>
267
+ <div class="text-xs text-gray-500 dark:text-gray-400">{{ payment.updated_at|time:"H:i:s" }}</div>
268
+ </div>
269
+ <div class="text-center">
270
+ <div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Processed</div>
271
+ {% if payment.processed_at %}
272
+ <div class="text-sm font-medium text-gray-900 dark:text-white">{{ payment.processed_at|date:"M d, Y" }}</div>
273
+ <div class="text-xs text-gray-500 dark:text-gray-400">{{ payment.processed_at|time:"H:i:s" }}</div>
274
+ {% else %}
275
+ <div class="text-sm text-gray-500 dark:text-gray-400">Not processed</div>
276
+ {% endif %}
277
+ </div>
278
+ </div>
279
+ </div>
280
+ </div>
281
+
282
+ <!-- Provider Data (if available) -->
283
+ {% if payment.provider_data %}
284
+ <div class="bg-white dark:bg-gray-800 shadow rounded-lg">
285
+ <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
286
+ <h3 class="text-lg font-medium text-gray-900 dark:text-white">Provider Information</h3>
287
+ <p class="mt-1 text-sm text-gray-600 dark:text-gray-400">Data provided by {{ payment.provider|title }}</p>
288
+ </div>
289
+
290
+ <div class="p-6">
291
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
292
+ {% for key, value in payment.provider_data.items %}
293
+ <div>
294
+ <dt class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-1">{{ key|title }}:</dt>
295
+ <dd class="text-sm text-gray-900 dark:text-white">
296
+ {% if value|length > 50 %}
297
+ <div class="font-mono bg-gray-50 dark:bg-gray-700 p-2 rounded border border-gray-200 dark:border-gray-600 break-all text-xs">
298
+ {{ value }}
299
+ </div>
300
+ {% else %}
301
+ {{ value }}
302
+ {% endif %}
303
+ </dd>
304
+ </div>
305
+ {% endfor %}
306
+ </div>
307
+
308
+ <!-- Raw JSON (collapsible) -->
309
+ <div class="mt-6 pt-6 border-t border-gray-200 dark:border-gray-700">
310
+ <button onclick="document.getElementById('raw-provider-data').classList.toggle('hidden')"
311
+ class="text-sm text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300">
312
+ <span class="material-icons-outlined text-sm mr-1">code</span>
313
+ Toggle Raw JSON
314
+ </button>
315
+ <div id="raw-provider-data" class="hidden mt-3">
316
+ <pre class="text-xs font-mono bg-gray-50 dark:bg-gray-700 p-4 rounded border border-gray-200 dark:border-gray-600 overflow-x-auto">{{ payment.provider_data|pprint }}</pre>
317
+ </div>
318
+ </div>
319
+ </div>
320
+ </div>
321
+ {% endif %}
322
+
323
+ <!-- Actions -->
324
+ <div class="bg-white dark:bg-gray-800 shadow rounded-lg">
325
+ <div class="p-6">
326
+ <div class="flex flex-wrap gap-3">
327
+ {% if payment.status == 'pending' or payment.status == 'confirming' %}
328
+ <button @click="cancelPayment()"
329
+ :disabled="loading"
330
+ class="inline-flex items-center px-4 py-2 bg-red-600 hover:bg-red-700 disabled:bg-red-400 text-white rounded-md text-sm font-medium transition-colors">
331
+ <span class="material-icons-outlined mr-2">cancel</span>
332
+ Cancel Payment
333
+ </button>
334
+ {% endif %}
335
+
336
+ <button @click="exportDetails()"
337
+ class="inline-flex items-center px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md text-sm font-medium transition-colors">
338
+ <span class="material-icons-outlined mr-2">file_download</span>
339
+ Export Details
340
+ </button>
341
+
342
+ {% if payment.callback_url %}
343
+ <a href="{{ payment.callback_url }}"
344
+ target="_blank"
345
+ class="inline-flex items-center px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-md text-sm font-medium transition-colors">
346
+ <span class="material-icons-outlined mr-2">open_in_new</span>
347
+ Callback URL
348
+ </a>
349
+ {% endif %}
350
+ </div>
351
+ </div>
352
+ </div>
353
+ </div>
354
+ {% endblock %}
355
+
356
+ {% block extra_js %}
357
+ <!-- Payment API Client -->
358
+ <script src="{% static 'payments/js/api-client.js' %}"></script>
359
+ <!-- Payment Detail Page JavaScript -->
360
+ <script src="{% static 'payments/js/payment-detail.js' %}"></script>
361
+ <!-- QR Code Library -->
362
+ <script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js"></script>
363
+ {% endblock %}
@@ -18,6 +18,36 @@
18
18
  <form @submit.prevent="submitForm()" class="p-6 space-y-6">
19
19
  {% csrf_token %}
20
20
 
21
+ <!-- Error Display -->
22
+ <div x-show="errorMessage"
23
+ x-transition:enter="transition ease-out duration-300"
24
+ x-transition:enter-start="opacity-0 transform -translate-y-2"
25
+ x-transition:enter-end="opacity-100 transform translate-y-0"
26
+ x-transition:leave="transition ease-in duration-200"
27
+ x-transition:leave-start="opacity-100 transform translate-y-0"
28
+ x-transition:leave-end="opacity-0 transform -translate-y-2"
29
+ class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-md p-4">
30
+ <div class="flex">
31
+ <div class="flex-shrink-0">
32
+ <span class="material-icons-outlined text-red-400">error</span>
33
+ </div>
34
+ <div class="ml-3">
35
+ <h3 class="text-sm font-medium text-red-800 dark:text-red-200">
36
+ Payment Creation Failed
37
+ </h3>
38
+ <div class="mt-2 text-sm text-red-700 dark:text-red-300">
39
+ <p x-text="errorMessage"></p>
40
+ </div>
41
+ <div class="mt-3">
42
+ <button @click="clearError()"
43
+ class="text-sm font-medium text-red-800 dark:text-red-200 hover:text-red-600 dark:hover:text-red-100">
44
+ Dismiss
45
+ </button>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </div>
50
+
21
51
  <!-- Amount -->
22
52
  <div>
23
53
  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
@@ -96,8 +126,8 @@
96
126
  class="block w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white disabled:opacity-50"
97
127
  required>
98
128
  <option value="">Select currency...</option>
99
- <template x-for="currency in currencies" :key="`${currency.code}-${currency.provider_code || currency.network || Math.random()}`">
100
- <option :value="currency.code" x-text="`${currency.code} - ${currency.name}${currency.type === 'crypto' ? ' (Crypto)' : ''}${currency.network_name ? ' [' + currency.network_name + ']' : ''}`">
129
+ <template x-for="currency in currencies" :key="`${currency.provider_currency_code || currency.code}-${currency.network?.code || Math.random()}`">
130
+ <option :value="currency.provider_currency_code || currency.code" x-text="`${currency.currency?.code || currency.code} - ${currency.currency?.name || currency.name}${currency.currency?.currency_type === 'crypto' ? ' (Crypto)' : ''}${currency.network?.name ? ' [' + currency.network.name + ']' : ''}`">
101
131
  </option>
102
132
  </template>
103
133
  </select>
@@ -183,7 +213,7 @@
183
213
  Cancel
184
214
  </a>
185
215
  <button type="submit"
186
- :disabled="loading || !form.amount || !form.currency || !form.provider"
216
+ :disabled="loading || !form.amount_usd || !form.currency_code || !form.provider || !form.user"
187
217
  class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed">
188
218
  <span x-show="!loading">Create Payment</span>
189
219
  <span x-show="loading">Creating...</span>
@@ -7,6 +7,7 @@ DRF ViewSets for payment management in admin interface.
7
7
  from rest_framework import viewsets, status
8
8
  from rest_framework.decorators import action
9
9
  from rest_framework.response import Response
10
+ from rest_framework import serializers
10
11
  from django_filters.rest_framework import DjangoFilterBackend
11
12
  from rest_framework.filters import SearchFilter, OrderingFilter
12
13
  from django.db.models import Count, Sum, Avg, Q
@@ -72,6 +73,53 @@ class AdminPaymentViewSet(AdminBaseViewSet):
72
73
 
73
74
  return queryset
74
75
 
76
+ def create(self, request, *args, **kwargs):
77
+ """Create payment with enhanced error handling."""
78
+ serializer = self.get_serializer(data=request.data)
79
+
80
+ if serializer.is_valid():
81
+ try:
82
+ payment = serializer.save()
83
+ response_serializer = AdminPaymentDetailSerializer(payment, context={'request': request})
84
+ return Response(response_serializer.data, status=status.HTTP_201_CREATED)
85
+ except serializers.ValidationError as e:
86
+ # Extract the error message from ValidationError
87
+ error_message = str(e)
88
+ if hasattr(e, 'detail') and isinstance(e.detail, list) and len(e.detail) > 0:
89
+ error_message = str(e.detail[0])
90
+ elif hasattr(e, 'detail') and isinstance(e.detail, dict):
91
+ # Handle field-specific errors
92
+ error_message = '; '.join([f"{field}: {', '.join(errors) if isinstance(errors, list) else errors}"
93
+ for field, errors in e.detail.items()])
94
+
95
+ return Response(
96
+ {
97
+ 'success': False,
98
+ 'error': error_message,
99
+ 'message': error_message # Add message field for frontend compatibility
100
+ },
101
+ status=status.HTTP_400_BAD_REQUEST
102
+ )
103
+ else:
104
+ # Handle validation errors
105
+ error_messages = []
106
+ for field, errors in serializer.errors.items():
107
+ if isinstance(errors, list):
108
+ error_messages.extend([str(error) for error in errors])
109
+ else:
110
+ error_messages.append(str(errors))
111
+
112
+ error_message = '; '.join(error_messages)
113
+ return Response(
114
+ {
115
+ 'success': False,
116
+ 'error': error_message,
117
+ 'message': error_message, # Add message field for frontend compatibility
118
+ 'errors': serializer.errors
119
+ },
120
+ status=status.HTTP_400_BAD_REQUEST
121
+ )
122
+
75
123
  @action(detail=False, methods=['get'])
76
124
  def stats(self, request):
77
125
  """Get comprehensive payment statistics."""
@@ -189,3 +237,57 @@ class AdminPaymentViewSet(AdminBaseViewSet):
189
237
 
190
238
  serializer = self.get_serializer(payment)
191
239
  return Response(serializer.data)
240
+
241
+ @action(detail=True, methods=['post'])
242
+ def refresh_status(self, request, pk=None):
243
+ """Refresh payment status from provider via AJAX."""
244
+ payment = self.get_object()
245
+
246
+ try:
247
+ # Import here to avoid circular imports
248
+ from django_cfg.apps.payments.services.core.payment_service import PaymentService
249
+
250
+ # Create PaymentStatusRequest
251
+ from django_cfg.apps.payments.services.types import PaymentStatusRequest
252
+
253
+ status_request = PaymentStatusRequest(
254
+ payment_id=str(payment.id),
255
+ force_provider_check=True
256
+ )
257
+
258
+ # Create service instance and force refresh from provider
259
+ payment_service = PaymentService()
260
+ result = payment_service.get_payment_status(status_request)
261
+
262
+ if result.success:
263
+ # Reload payment from database to get updated data
264
+ payment.refresh_from_db()
265
+
266
+ # Serialize updated payment data
267
+ serializer = self.get_serializer(payment)
268
+
269
+ return Response({
270
+ 'success': True,
271
+ 'message': 'Payment status refreshed successfully',
272
+ 'payment': serializer.data,
273
+ 'provider_response': result.data.get('provider_response') if result.data else None
274
+ })
275
+ else:
276
+ return Response({
277
+ 'success': False,
278
+ 'message': result.message or 'Failed to refresh payment status',
279
+ 'error_details': result.data if result.data else None
280
+ }, status=status.HTTP_400_BAD_REQUEST)
281
+
282
+ except Exception as e:
283
+ logger.error(f"Error refreshing payment {payment.id} status", extra={
284
+ 'payment_id': payment.id,
285
+ 'error': str(e),
286
+ 'admin_user': request.user.id
287
+ })
288
+
289
+ return Response({
290
+ 'success': False,
291
+ 'message': f'Error refreshing payment status: {str(e)}',
292
+ 'error_type': type(e).__name__
293
+ }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)