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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/admin/__init__.py +24 -8
- django_cfg/apps/accounts/admin/activity_admin.py +146 -0
- django_cfg/apps/accounts/admin/filters.py +98 -22
- django_cfg/apps/accounts/admin/group_admin.py +86 -0
- django_cfg/apps/accounts/admin/inlines.py +42 -13
- django_cfg/apps/accounts/admin/otp_admin.py +115 -0
- django_cfg/apps/accounts/admin/registration_admin.py +173 -0
- django_cfg/apps/accounts/admin/resources.py +123 -19
- django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
- django_cfg/apps/accounts/admin/user_admin.py +362 -0
- django_cfg/apps/agents/admin/__init__.py +17 -4
- django_cfg/apps/agents/admin/execution_admin.py +204 -183
- django_cfg/apps/agents/admin/registry_admin.py +230 -255
- django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
- django_cfg/apps/agents/core/__init__.py +1 -1
- django_cfg/apps/agents/core/django_agent.py +221 -0
- django_cfg/apps/agents/core/exceptions.py +14 -0
- django_cfg/apps/agents/core/orchestrator.py +18 -3
- django_cfg/apps/knowbase/admin/__init__.py +1 -1
- django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
- django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
- django_cfg/apps/knowbase/admin/document_admin.py +269 -262
- django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
- django_cfg/apps/knowbase/config/settings.py +21 -4
- django_cfg/apps/knowbase/views/chat_views.py +3 -0
- django_cfg/apps/leads/admin/__init__.py +3 -1
- django_cfg/apps/leads/admin/leads_admin.py +235 -35
- django_cfg/apps/maintenance/admin/__init__.py +2 -2
- django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
- django_cfg/apps/maintenance/admin/log_admin.py +143 -61
- django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
- django_cfg/apps/maintenance/admin/site_admin.py +213 -352
- django_cfg/apps/newsletter/admin/__init__.py +29 -2
- django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
- django_cfg/apps/payments/admin/__init__.py +18 -27
- django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
- django_cfg/apps/payments/admin/balance_admin.py +166 -632
- django_cfg/apps/payments/admin/currencies_admin.py +235 -607
- django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
- django_cfg/apps/payments/admin/filters.py +83 -3
- django_cfg/apps/payments/admin/networks_admin.py +269 -0
- django_cfg/apps/payments/admin/payments_admin.py +183 -460
- django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
- django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
- django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +153 -34
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +43 -17
- django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
- django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +109 -63
- django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
- django_cfg/apps/payments/config/__init__.py +14 -15
- django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
- django_cfg/apps/payments/config/helpers.py +8 -13
- django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
- django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
- django_cfg/apps/payments/middleware/api_access.py +32 -6
- django_cfg/apps/payments/migrations/0001_initial.py +33 -46
- django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
- django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
- django_cfg/apps/payments/models/balance.py +12 -0
- django_cfg/apps/payments/models/currencies.py +106 -32
- django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
- django_cfg/apps/payments/models/payments.py +94 -0
- django_cfg/apps/payments/services/core/base.py +4 -4
- django_cfg/apps/payments/services/core/currency_service.py +35 -28
- django_cfg/apps/payments/services/core/payment_service.py +266 -39
- django_cfg/apps/payments/services/providers/__init__.py +3 -0
- django_cfg/apps/payments/services/providers/base.py +303 -41
- django_cfg/apps/payments/services/providers/models/__init__.py +42 -0
- django_cfg/apps/payments/services/providers/models/base.py +145 -0
- django_cfg/apps/payments/services/providers/models/providers.py +87 -0
- django_cfg/apps/payments/services/providers/models/universal.py +48 -0
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
- django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
- django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +557 -0
- django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
- django_cfg/apps/payments/services/providers/registry.py +9 -37
- django_cfg/apps/payments/services/providers/sync_service.py +277 -0
- django_cfg/apps/payments/services/types/requests.py +19 -7
- django_cfg/apps/payments/signals/payment_signals.py +31 -2
- django_cfg/apps/payments/static/payments/js/api-client.js +29 -6
- django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
- django_cfg/apps/payments/static/payments/js/payment-form.js +98 -32
- django_cfg/apps/payments/tasks/__init__.py +39 -0
- django_cfg/apps/payments/tasks/types.py +73 -0
- django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
- django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
- django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
- django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
- django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
- django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
- django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
- django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
- django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
- django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
- django_cfg/apps/payments/urls.py +3 -2
- django_cfg/apps/payments/urls_admin.py +1 -1
- django_cfg/apps/payments/views/api/currencies.py +8 -5
- django_cfg/apps/payments/views/overview/services.py +2 -2
- django_cfg/apps/payments/views/serializers/currencies.py +22 -8
- django_cfg/apps/support/admin/__init__.py +10 -1
- django_cfg/apps/support/admin/support_admin.py +338 -141
- django_cfg/apps/tasks/admin/__init__.py +11 -0
- django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
- django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
- django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
- django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
- django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
- django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
- django_cfg/apps/tasks/tasks/__init__.py +10 -0
- django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
- django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
- django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
- django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
- django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
- django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
- django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
- django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
- django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
- django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
- django_cfg/apps/tasks/urls.py +2 -2
- django_cfg/apps/tasks/urls_admin.py +2 -2
- django_cfg/apps/tasks/utils/__init__.py +1 -0
- django_cfg/apps/tasks/utils/simulator.py +356 -0
- django_cfg/apps/tasks/views/__init__.py +16 -0
- django_cfg/apps/tasks/views/api.py +569 -0
- django_cfg/apps/tasks/views/dashboard.py +58 -0
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +10 -5
- django_cfg/core/generation.py +1 -1
- django_cfg/core/integration/__init__.py +21 -0
- django_cfg/management/commands/__init__.py +13 -1
- django_cfg/management/commands/migrate_all.py +9 -3
- django_cfg/management/commands/migrator.py +11 -6
- django_cfg/management/commands/rundramatiq.py +3 -2
- django_cfg/management/commands/rundramatiq_simulator.py +430 -0
- django_cfg/middleware/__init__.py +0 -2
- django_cfg/models/api_keys.py +115 -0
- django_cfg/models/constance.py +0 -11
- django_cfg/models/payments.py +137 -3
- django_cfg/modules/django_admin/__init__.py +64 -0
- django_cfg/modules/django_admin/decorators/__init__.py +13 -0
- django_cfg/modules/django_admin/decorators/actions.py +106 -0
- django_cfg/modules/django_admin/decorators/display.py +106 -0
- django_cfg/modules/django_admin/mixins/__init__.py +14 -0
- django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
- django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
- django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
- django_cfg/modules/django_admin/models/__init__.py +20 -0
- django_cfg/modules/django_admin/models/action_models.py +33 -0
- django_cfg/modules/django_admin/models/badge_models.py +20 -0
- django_cfg/modules/django_admin/models/base.py +26 -0
- django_cfg/modules/django_admin/models/display_models.py +31 -0
- django_cfg/modules/django_admin/utils/badges.py +159 -0
- django_cfg/modules/django_admin/utils/displays.py +247 -0
- django_cfg/modules/django_currency/__init__.py +2 -2
- django_cfg/modules/django_currency/clients/__init__.py +2 -2
- django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
- django_cfg/modules/django_currency/core/converter.py +12 -12
- django_cfg/modules/django_currency/database/__init__.py +2 -2
- django_cfg/modules/django_currency/database/database_loader.py +93 -42
- django_cfg/modules/django_llm/llm/client.py +10 -2
- django_cfg/modules/django_tasks.py +54 -21
- django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
- django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
- django_cfg/modules/django_unfold/dashboard.py +14 -13
- django_cfg/modules/django_unfold/models/config.py +1 -1
- django_cfg/registry/core.py +7 -9
- django_cfg/registry/third_party.py +2 -2
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -1
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/RECORD +198 -160
- django_cfg/apps/accounts/admin/activity.py +0 -96
- django_cfg/apps/accounts/admin/group.py +0 -17
- django_cfg/apps/accounts/admin/otp.py +0 -59
- django_cfg/apps/accounts/admin/registration_source.py +0 -97
- django_cfg/apps/accounts/admin/twilio_response.py +0 -227
- django_cfg/apps/accounts/admin/user.py +0 -300
- django_cfg/apps/agents/core/agent.py +0 -281
- django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
- django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
- django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
- django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
- django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
- django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
- django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
- django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
- django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
- django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
- django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
- django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
- django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
- django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
- django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
- django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
- django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
- django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
- django_cfg/apps/payments/config/constance/__init__.py +0 -22
- django_cfg/apps/payments/config/constance/config_service.py +0 -123
- django_cfg/apps/payments/config/constance/fields.py +0 -69
- django_cfg/apps/payments/config/constance/settings.py +0 -160
- django_cfg/apps/payments/services/providers/nowpayments.py +0 -478
- django_cfg/apps/tasks/admin.py +0 -320
- django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
- django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
- django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
- django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
- django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
- django_cfg/apps/tasks/templates/tasks/base.html +0 -96
- django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
- django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
- django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
- django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
- django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
- django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
- django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
- django_cfg/apps/tasks/views.py +0 -461
- django_cfg/management/commands/auto_generate.py +0 -486
- django_cfg/middleware/static_nocache.py +0 -55
- django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
- /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.7.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,12 +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">
|
100
|
-
<option :value="currency.code">
|
101
|
-
<span x-text="currency.code"></span> -
|
102
|
-
<span x-text="currency.name"></span>
|
103
|
-
<span x-show="currency.type === 'crypto'" class="text-blue-600"> (Crypto)</span>
|
104
|
-
<span x-show="currency.network_name" class="text-gray-500" x-text="`[${currency.network_name}]`"></span>
|
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 + ']' : ''}`">
|
105
131
|
</option>
|
106
132
|
</template>
|
107
133
|
</select>
|
@@ -122,22 +148,22 @@
|
|
122
148
|
<span class="text-gray-500">Network:</span>
|
123
149
|
<span x-text="getCurrencyInfo(form.currency_code)?.network_name"></span>
|
124
150
|
</div>
|
125
|
-
<div x-show="getCurrencyInfo(form.currency_code)?.
|
151
|
+
<div x-show="getCurrencyInfo(form.currency_code)?.provider_min_amount_usd || getCurrencyInfo(form.currency_code)?.provider_max_amount_usd" class="mt-1">
|
126
152
|
<span class="text-gray-500">Limits:</span>
|
127
|
-
<span x-show="getCurrencyInfo(form.currency_code)?.
|
128
|
-
Min: $<span x-text="getCurrencyInfo(form.currency_code)?.
|
153
|
+
<span x-show="getCurrencyInfo(form.currency_code)?.provider_min_amount_usd">
|
154
|
+
Min: $<span x-text="getCurrencyInfo(form.currency_code)?.provider_min_amount_usd"></span>
|
129
155
|
</span>
|
130
|
-
<span x-show="getCurrencyInfo(form.currency_code)?.
|
131
|
-
Max: $<span x-text="getCurrencyInfo(form.currency_code)?.
|
156
|
+
<span x-show="getCurrencyInfo(form.currency_code)?.provider_max_amount_usd">
|
157
|
+
Max: $<span x-text="getCurrencyInfo(form.currency_code)?.provider_max_amount_usd"></span>
|
132
158
|
</span>
|
133
159
|
</div>
|
134
|
-
<div x-show="getCurrencyInfo(form.currency_code)?.
|
160
|
+
<div x-show="getCurrencyInfo(form.currency_code)?.provider_fee_percentage > 0 || getCurrencyInfo(form.currency_code)?.provider_fixed_fee_usd > 0" class="mt-1">
|
135
161
|
<span class="text-gray-500">Fees:</span>
|
136
|
-
<span x-show="getCurrencyInfo(form.currency_code)?.
|
137
|
-
<span x-text="(getCurrencyInfo(form.currency_code)?.
|
162
|
+
<span x-show="getCurrencyInfo(form.currency_code)?.provider_fee_percentage > 0">
|
163
|
+
<span x-text="(getCurrencyInfo(form.currency_code)?.provider_fee_percentage * 100).toFixed(2)"></span>%
|
138
164
|
</span>
|
139
|
-
<span x-show="getCurrencyInfo(form.currency_code)?.
|
140
|
-
+ $<span x-text="getCurrencyInfo(form.currency_code)?.
|
165
|
+
<span x-show="getCurrencyInfo(form.currency_code)?.provider_fixed_fee_usd > 0">
|
166
|
+
+ $<span x-text="getCurrencyInfo(form.currency_code)?.provider_fixed_fee_usd"></span>
|
141
167
|
</span>
|
142
168
|
</div>
|
143
169
|
</div>
|
@@ -187,7 +213,7 @@
|
|
187
213
|
Cancel
|
188
214
|
</a>
|
189
215
|
<button type="submit"
|
190
|
-
:disabled="loading || !form.
|
216
|
+
:disabled="loading || !form.amount_usd || !form.currency_code || !form.provider || !form.user"
|
191
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">
|
192
218
|
<span x-show="!loading">Create Payment</span>
|
193
219
|
<span x-show="loading">Creating...</span>
|
@@ -13,6 +13,7 @@ from .api import (
|
|
13
13
|
AdminPaymentViewSet,
|
14
14
|
AdminWebhookViewSet,
|
15
15
|
AdminWebhookEventViewSet,
|
16
|
+
WebhookTestViewSet,
|
16
17
|
AdminStatsViewSet,
|
17
18
|
AdminUserViewSet,
|
18
19
|
)
|
@@ -29,6 +30,7 @@ __all__ = [
|
|
29
30
|
'AdminPaymentViewSet',
|
30
31
|
'AdminWebhookViewSet',
|
31
32
|
'AdminWebhookEventViewSet',
|
33
|
+
'WebhookTestViewSet',
|
32
34
|
'AdminStatsViewSet',
|
33
35
|
'AdminUserViewSet',
|
34
36
|
]
|
@@ -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)
|