django-cfg 1.2.27__py3-none-any.whl → 1.2.31__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 (138) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin/__init__.py +3 -2
  3. django_cfg/apps/payments/admin/balance_admin.py +18 -18
  4. django_cfg/apps/payments/admin/currencies_admin.py +319 -131
  5. django_cfg/apps/payments/admin/payments_admin.py +15 -4
  6. django_cfg/apps/payments/config/module.py +2 -2
  7. django_cfg/apps/payments/config/utils.py +2 -2
  8. django_cfg/apps/payments/decorators.py +2 -2
  9. django_cfg/apps/payments/management/commands/README.md +95 -127
  10. django_cfg/apps/payments/management/commands/currency_stats.py +5 -24
  11. django_cfg/apps/payments/management/commands/manage_currencies.py +229 -0
  12. django_cfg/apps/payments/management/commands/manage_providers.py +235 -0
  13. django_cfg/apps/payments/managers/__init__.py +3 -2
  14. django_cfg/apps/payments/managers/balance_manager.py +2 -2
  15. django_cfg/apps/payments/managers/currency_manager.py +272 -49
  16. django_cfg/apps/payments/managers/payment_manager.py +161 -13
  17. django_cfg/apps/payments/middleware/api_access.py +2 -2
  18. django_cfg/apps/payments/middleware/rate_limiting.py +8 -18
  19. django_cfg/apps/payments/middleware/usage_tracking.py +20 -17
  20. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +241 -0
  21. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +30 -0
  22. django_cfg/apps/payments/models/__init__.py +3 -2
  23. django_cfg/apps/payments/models/currencies.py +187 -71
  24. django_cfg/apps/payments/models/payments.py +3 -2
  25. django_cfg/apps/payments/serializers/__init__.py +3 -2
  26. django_cfg/apps/payments/serializers/currencies.py +20 -12
  27. django_cfg/apps/payments/services/cache/simple_cache.py +2 -2
  28. django_cfg/apps/payments/services/core/balance_service.py +2 -2
  29. django_cfg/apps/payments/services/core/fallback_service.py +2 -2
  30. django_cfg/apps/payments/services/core/payment_service.py +3 -6
  31. django_cfg/apps/payments/services/core/subscription_service.py +4 -7
  32. django_cfg/apps/payments/services/internal_types.py +171 -7
  33. django_cfg/apps/payments/services/monitoring/api_schemas.py +58 -204
  34. django_cfg/apps/payments/services/monitoring/provider_health.py +2 -2
  35. django_cfg/apps/payments/services/providers/base.py +144 -43
  36. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +4 -0
  37. django_cfg/apps/payments/services/providers/cryptapi/config.py +8 -0
  38. django_cfg/apps/payments/services/providers/cryptapi/models.py +192 -0
  39. django_cfg/apps/payments/services/providers/cryptapi/provider.py +439 -0
  40. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +4 -0
  41. django_cfg/apps/payments/services/providers/cryptomus/models.py +176 -0
  42. django_cfg/apps/payments/services/providers/cryptomus/provider.py +429 -0
  43. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +564 -0
  44. django_cfg/apps/payments/services/providers/models/__init__.py +34 -0
  45. django_cfg/apps/payments/services/providers/models/currencies.py +190 -0
  46. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +4 -0
  47. django_cfg/apps/payments/services/providers/nowpayments/models.py +196 -0
  48. django_cfg/apps/payments/services/providers/nowpayments/provider.py +380 -0
  49. django_cfg/apps/payments/services/providers/registry.py +294 -11
  50. django_cfg/apps/payments/services/providers/stripe/__init__.py +4 -0
  51. django_cfg/apps/payments/services/providers/stripe/models.py +184 -0
  52. django_cfg/apps/payments/services/providers/stripe/provider.py +109 -0
  53. django_cfg/apps/payments/services/security/error_handler.py +6 -8
  54. django_cfg/apps/payments/services/security/payment_notifications.py +2 -2
  55. django_cfg/apps/payments/services/security/webhook_validator.py +3 -4
  56. django_cfg/apps/payments/signals/api_key_signals.py +2 -2
  57. django_cfg/apps/payments/signals/payment_signals.py +11 -5
  58. django_cfg/apps/payments/signals/subscription_signals.py +2 -2
  59. django_cfg/apps/payments/static/payments/css/payments.css +340 -0
  60. django_cfg/apps/payments/static/payments/js/notifications.js +202 -0
  61. django_cfg/apps/payments/static/payments/js/payment-utils.js +318 -0
  62. django_cfg/apps/payments/static/payments/js/theme.js +86 -0
  63. django_cfg/apps/payments/tasks/webhook_processing.py +2 -2
  64. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +50 -0
  65. django_cfg/apps/payments/templates/payments/base.html +182 -0
  66. django_cfg/apps/payments/templates/payments/components/payment_card.html +201 -0
  67. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +109 -0
  68. django_cfg/apps/payments/templates/payments/components/progress_bar.html +43 -0
  69. django_cfg/apps/payments/templates/payments/components/provider_stats.html +40 -0
  70. django_cfg/apps/payments/templates/payments/components/status_badge.html +34 -0
  71. django_cfg/apps/payments/templates/payments/components/status_overview.html +148 -0
  72. django_cfg/apps/payments/templates/payments/dashboard.html +258 -0
  73. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +35 -0
  74. django_cfg/apps/payments/templates/payments/payment_create.html +579 -0
  75. django_cfg/apps/payments/templates/payments/payment_detail.html +373 -0
  76. django_cfg/apps/payments/templates/payments/payment_list.html +354 -0
  77. django_cfg/apps/payments/templates/payments/stats.html +261 -0
  78. django_cfg/apps/payments/templates/payments/test.html +213 -0
  79. django_cfg/apps/payments/templatetags/__init__.py +1 -0
  80. django_cfg/apps/payments/templatetags/payments_tags.py +315 -0
  81. django_cfg/apps/payments/urls.py +3 -1
  82. django_cfg/apps/payments/urls_admin.py +58 -0
  83. django_cfg/apps/payments/utils/__init__.py +1 -3
  84. django_cfg/apps/payments/utils/billing_utils.py +2 -2
  85. django_cfg/apps/payments/utils/config_utils.py +2 -8
  86. django_cfg/apps/payments/utils/validation_utils.py +2 -2
  87. django_cfg/apps/payments/views/__init__.py +3 -2
  88. django_cfg/apps/payments/views/currency_views.py +31 -20
  89. django_cfg/apps/payments/views/payment_views.py +2 -2
  90. django_cfg/apps/payments/views/templates/__init__.py +25 -0
  91. django_cfg/apps/payments/views/templates/ajax.py +451 -0
  92. django_cfg/apps/payments/views/templates/base.py +212 -0
  93. django_cfg/apps/payments/views/templates/dashboard.py +60 -0
  94. django_cfg/apps/payments/views/templates/payment_detail.py +102 -0
  95. django_cfg/apps/payments/views/templates/payment_management.py +158 -0
  96. django_cfg/apps/payments/views/templates/qr_code.py +174 -0
  97. django_cfg/apps/payments/views/templates/stats.py +244 -0
  98. django_cfg/apps/payments/views/templates/utils.py +181 -0
  99. django_cfg/apps/payments/views/webhook_views.py +2 -2
  100. django_cfg/apps/payments/viewsets.py +3 -2
  101. django_cfg/apps/tasks/urls.py +0 -2
  102. django_cfg/apps/tasks/urls_admin.py +14 -0
  103. django_cfg/apps/urls.py +6 -3
  104. django_cfg/core/config.py +35 -0
  105. django_cfg/models/payments.py +2 -8
  106. django_cfg/modules/django_currency/__init__.py +16 -11
  107. django_cfg/modules/django_currency/clients/__init__.py +4 -4
  108. django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
  109. django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
  110. django_cfg/modules/django_currency/core/__init__.py +1 -7
  111. django_cfg/modules/django_currency/core/converter.py +18 -23
  112. django_cfg/modules/django_currency/core/models.py +122 -11
  113. django_cfg/modules/django_currency/database/__init__.py +4 -4
  114. django_cfg/modules/django_currency/database/database_loader.py +190 -309
  115. django_cfg/modules/django_unfold/dashboard.py +7 -2
  116. django_cfg/registry/core.py +1 -0
  117. django_cfg/template_archive/.gitignore +1 -0
  118. django_cfg/template_archive/django_sample.zip +0 -0
  119. django_cfg/templates/admin/components/action_grid.html +9 -9
  120. django_cfg/templates/admin/components/metric_card.html +5 -5
  121. django_cfg/templates/admin/components/status_badge.html +2 -2
  122. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
  123. django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
  124. django_cfg/templates/admin/snippets/components/system_health.html +1 -1
  125. django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
  126. {django_cfg-1.2.27.dist-info → django_cfg-1.2.31.dist-info}/METADATA +13 -18
  127. {django_cfg-1.2.27.dist-info → django_cfg-1.2.31.dist-info}/RECORD +130 -83
  128. django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
  129. django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
  130. django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
  131. django_cfg/apps/payments/services/providers/cryptomus.py +0 -310
  132. django_cfg/apps/payments/services/providers/nowpayments.py +0 -293
  133. django_cfg/apps/payments/services/validators/__init__.py +0 -8
  134. django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
  135. django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
  136. {django_cfg-1.2.27.dist-info → django_cfg-1.2.31.dist-info}/WHEEL +0 -0
  137. {django_cfg-1.2.27.dist-info → django_cfg-1.2.31.dist-info}/entry_points.txt +0 -0
  138. {django_cfg-1.2.27.dist-info → django_cfg-1.2.31.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,354 @@
1
+ {% extends 'payments/base.html' %}
2
+ {% load static %}
3
+ {% load payments_tags %}
4
+
5
+ {% block header_title %}Payment History{% endblock %}
6
+ {% block header_subtitle %}View and manage all payment transactions{% endblock %}
7
+
8
+ {% block content %}
9
+ <div class="space-y-6">
10
+ <!-- Filters and Search -->
11
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs">
12
+ <div class="p-6">
13
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
14
+ <!-- Search -->
15
+ <div class="md:col-span-2">
16
+ <label for="search" class="block text-sm font-medium text-font-default-light dark:text-font-default-dark mb-2">Search Payments</label>
17
+ <input type="text" id="search" placeholder="Search by ID, email, or provider..."
18
+ class="w-full px-3 py-2 border border-base-200 dark:border-base-700 rounded-default bg-white dark:bg-base-900 text-font-default-light dark:text-font-default-dark placeholder-font-subtle-light dark:placeholder-font-subtle-dark focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
19
+ </div>
20
+
21
+ <!-- Status Filter -->
22
+ <div>
23
+ <label for="status" class="block text-sm font-medium text-font-default-light dark:text-font-default-dark mb-2">Status</label>
24
+ <select id="status" class="w-full px-3 py-2 border border-base-200 dark:border-base-700 rounded-default bg-white dark:bg-base-900 text-font-default-light dark:text-font-default-dark focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
25
+ <option value="">All Statuses</option>
26
+ <option value="pending">Pending</option>
27
+ <option value="confirming">Confirming</option>
28
+ <option value="completed">Completed</option>
29
+ <option value="failed">Failed</option>
30
+ <option value="expired">Expired</option>
31
+ <option value="cancelled">Cancelled</option>
32
+ </select>
33
+ </div>
34
+
35
+ <!-- Provider Filter -->
36
+ <div>
37
+ <label for="provider" class="block text-sm font-medium text-font-default-light dark:text-font-default-dark mb-2">Provider</label>
38
+ <select id="provider" class="w-full px-3 py-2 border border-base-200 dark:border-base-700 rounded-default bg-white dark:bg-base-900 text-font-default-light dark:text-font-default-dark focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
39
+ <option value="">All Providers</option>
40
+ <option value="cryptapi">CryptAPI</option>
41
+ <option value="cryptomus">Cryptomus</option>
42
+ <option value="stripe">Stripe</option>
43
+ <option value="nowpayments">NowPayments</option>
44
+ </select>
45
+ </div>
46
+ </div>
47
+
48
+ <!-- Date Range -->
49
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
50
+ <div>
51
+ <label for="date_from" class="block text-sm font-medium text-font-default-light dark:text-font-default-dark mb-2">Date From</label>
52
+ <input type="date" id="date_from"
53
+ class="w-full px-3 py-2 border border-base-200 dark:border-base-700 rounded-default bg-white dark:bg-base-900 text-font-default-light dark:text-font-default-dark focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
54
+ </div>
55
+ <div>
56
+ <label for="date_to" class="block text-sm font-medium text-font-default-light dark:text-font-default-dark mb-2">Date To</label>
57
+ <input type="date" id="date_to"
58
+ class="w-full px-3 py-2 border border-base-200 dark:border-base-700 rounded-default bg-white dark:bg-base-900 text-font-default-light dark:text-font-default-dark focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
59
+ </div>
60
+ </div>
61
+
62
+ <!-- Action Buttons -->
63
+ <div class="flex flex-wrap gap-3 mt-4">
64
+ <button id="apply-filters" class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-default text-sm font-medium transition-colors duration-200">
65
+ <span class="material-icons text-sm mr-2">search</span>
66
+ Apply Filters
67
+ </button>
68
+ <button id="clear-filters" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-default text-sm font-medium transition-colors duration-200">
69
+ <span class="material-icons text-sm mr-2">clear</span>
70
+ Clear
71
+ </button>
72
+ <button id="export-csv" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-default text-sm font-medium transition-colors duration-200">
73
+ <span class="material-icons text-sm mr-2">file_download</span>
74
+ Export CSV
75
+ </button>
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ <!-- Quick Stats -->
81
+ {% include 'payments/components/status_overview.html' %}
82
+
83
+ <!-- Payments Table -->
84
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs">
85
+ <div class="p-6 border-b border-base-200 dark:border-base-700">
86
+ <div class="flex items-center justify-between">
87
+ <div>
88
+ <h3 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">Payment Transactions</h3>
89
+ <p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">
90
+ Showing {{ payments.count|default:"0" }} of {{ total_payments|default:"0" }} payments
91
+ </p>
92
+ </div>
93
+ <div class="flex items-center space-x-2">
94
+ <label for="per_page" class="text-sm text-font-default-light dark:text-font-default-dark">Show:</label>
95
+ <select id="per_page" class="px-2 py-1 border border-base-200 dark:border-base-700 rounded-default bg-white dark:bg-base-900 text-font-default-light dark:text-font-default-dark text-sm">
96
+ <option value="25">25</option>
97
+ <option value="50" selected>50</option>
98
+ <option value="100">100</option>
99
+ </select>
100
+ </div>
101
+ </div>
102
+ </div>
103
+
104
+ <div class="overflow-x-auto">
105
+ <table class="min-w-full">
106
+ <thead class="bg-base-50 dark:bg-base-800">
107
+ <tr>
108
+ <th class="px-6 py-3 text-left text-xs font-medium text-font-subtle-light dark:text-font-subtle-dark uppercase tracking-wider">
109
+ Payment ID
110
+ </th>
111
+ <th class="px-6 py-3 text-left text-xs font-medium text-font-subtle-light dark:text-font-subtle-dark uppercase tracking-wider">
112
+ User
113
+ </th>
114
+ <th class="px-6 py-3 text-left text-xs font-medium text-font-subtle-light dark:text-font-subtle-dark uppercase tracking-wider">
115
+ Amount
116
+ </th>
117
+ <th class="px-6 py-3 text-left text-xs font-medium text-font-subtle-light dark:text-font-subtle-dark uppercase tracking-wider">
118
+ Provider
119
+ </th>
120
+ <th class="px-6 py-3 text-left text-xs font-medium text-font-subtle-light dark:text-font-subtle-dark uppercase tracking-wider">
121
+ Status
122
+ </th>
123
+ <th class="px-6 py-3 text-left text-xs font-medium text-font-subtle-light dark:text-font-subtle-dark uppercase tracking-wider">
124
+ Created
125
+ </th>
126
+ <th class="px-6 py-3 text-left text-xs font-medium text-font-subtle-light dark:text-font-subtle-dark uppercase tracking-wider">
127
+ Actions
128
+ </th>
129
+ </tr>
130
+ </thead>
131
+ <tbody class="bg-white dark:bg-base-900 divide-y divide-base-200 dark:divide-base-700">
132
+ {% for payment in payments %}
133
+ <tr class="hover:bg-base-50 dark:hover:bg-base-800 transition-colors duration-200">
134
+ <td class="px-6 py-4 whitespace-nowrap">
135
+ <div class="text-sm">
136
+ <div class="font-medium text-font-important-light dark:text-font-important-dark">
137
+ {{ payment.internal_payment_id|truncatechars:12 }}
138
+ </div>
139
+ {% if payment.provider_payment_id %}
140
+ <div class="text-font-subtle-light dark:text-font-subtle-dark text-xs">
141
+ {{ payment.provider_payment_id|truncatechars:12 }}
142
+ </div>
143
+ {% endif %}
144
+ </div>
145
+ </td>
146
+ <td class="px-6 py-4 whitespace-nowrap">
147
+ <div class="text-sm">
148
+ <div class="font-medium text-font-important-light dark:text-font-important-dark">
149
+ {{ payment.user.get_full_name|default:payment.user.email }}
150
+ </div>
151
+ <div class="text-font-subtle-light dark:text-font-subtle-dark text-xs">
152
+ {{ payment.user.email }}
153
+ </div>
154
+ </div>
155
+ </td>
156
+ <td class="px-6 py-4 whitespace-nowrap">
157
+ <div class="text-sm">
158
+ <div class="font-semibold text-font-important-light dark:text-font-important-dark">
159
+ ${{ payment.amount_usd|floatformat:2 }}
160
+ </div>
161
+ {% if payment.crypto_amount %}
162
+ <div class="text-font-subtle-light dark:text-font-subtle-dark text-xs">
163
+ {{ payment.crypto_amount|floatformat:8 }} {{ payment.currency_code }}
164
+ </div>
165
+ {% endif %}
166
+ </div>
167
+ </td>
168
+ <td class="px-6 py-4 whitespace-nowrap">
169
+ <div class="flex items-center">
170
+ <div class="w-2 h-2 rounded-full mr-3
171
+ {% if payment.provider == 'cryptapi' %}bg-orange-500
172
+ {% elif payment.provider == 'cryptomus' %}bg-blue-500
173
+ {% elif payment.provider == 'stripe' %}bg-purple-500
174
+ {% elif payment.provider == 'nowpayments' %}bg-green-500
175
+ {% else %}bg-gray-500{% endif %}">
176
+ </div>
177
+ <span class="text-sm text-font-default-light dark:text-font-default-dark capitalize">
178
+ {{ payment.provider }}
179
+ </span>
180
+ </div>
181
+ </td>
182
+ <td class="px-6 py-4 whitespace-nowrap">
183
+ {% payment_status_badge payment %}
184
+ </td>
185
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-font-default-light dark:text-font-default-dark">
186
+ <div>{{ payment.created_at|date:"M d, Y" }}</div>
187
+ <div class="text-xs text-font-subtle-light dark:text-font-subtle-dark">
188
+ {{ payment.created_at|time:"H:i" }}
189
+ </div>
190
+ </td>
191
+ <td class="px-6 py-4 whitespace-nowrap text-sm space-x-2">
192
+ <a href="{% url 'payments_dashboard:detail' payment.pk %}"
193
+ class="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 font-medium">
194
+ View
195
+ </a>
196
+ {% if payment.pay_address and payment.pay_amount %}
197
+ <a href="{% url 'payments_dashboard:qr_code' payment.pk %}"
198
+ class="text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 font-medium">
199
+ QR
200
+ </a>
201
+ {% endif %}
202
+ {% if payment.status == 'pending' %}
203
+ <button class="text-orange-600 dark:text-orange-400 hover:text-orange-700 dark:hover:text-orange-300 font-medium check-status"
204
+ data-payment-id="{{ payment.pk }}">
205
+ Check
206
+ </button>
207
+ {% endif %}
208
+ </td>
209
+ </tr>
210
+ {% empty %}
211
+ <tr>
212
+ <td colspan="7" class="px-6 py-12 text-center">
213
+ <div class="text-font-subtle-light dark:text-font-subtle-dark">
214
+ <span class="material-icons text-4xl mb-4 block">payment</span>
215
+ <h3 class="text-lg font-medium mb-2">No payments found</h3>
216
+ <p class="text-sm">Try adjusting your filters or create a new payment.</p>
217
+ </div>
218
+ </td>
219
+ </tr>
220
+ {% endfor %}
221
+ </tbody>
222
+ </table>
223
+ </div>
224
+
225
+ <!-- Pagination -->
226
+ {% if payments.has_other_pages %}
227
+ <div class="px-6 py-3 border-t border-base-200 dark:border-base-700 bg-base-50 dark:bg-base-800">
228
+ <div class="flex items-center justify-between">
229
+ <div class="text-sm text-font-subtle-light dark:text-font-subtle-dark">
230
+ Showing {{ payments.start_index }} to {{ payments.end_index }} of {{ payments.paginator.count }} results
231
+ </div>
232
+ <div class="flex items-center space-x-2">
233
+ {% if payments.has_previous %}
234
+ <a href="?page={{ payments.previous_page_number }}"
235
+ class="px-3 py-1 border border-base-200 dark:border-base-700 rounded-default text-font-default-light dark:text-font-default-dark hover:bg-base-100 dark:hover:bg-base-700 transition-colors duration-200">
236
+ Previous
237
+ </a>
238
+ {% endif %}
239
+
240
+ <span class="px-3 py-1 bg-primary-600 text-white rounded-default">
241
+ {{ payments.number }}
242
+ </span>
243
+
244
+ {% if payments.has_next %}
245
+ <a href="?page={{ payments.next_page_number }}"
246
+ class="px-3 py-1 border border-base-200 dark:border-base-700 rounded-default text-font-default-light dark:text-font-default-dark hover:bg-base-100 dark:hover:bg-base-700 transition-colors duration-200">
247
+ Next
248
+ </a>
249
+ {% endif %}
250
+ </div>
251
+ </div>
252
+ </div>
253
+ {% endif %}
254
+ </div>
255
+ </div>
256
+ {% endblock %}
257
+
258
+ {% block extra_js %}
259
+ {{ block.super }}
260
+ <script>
261
+ document.addEventListener('DOMContentLoaded', function() {
262
+ console.log('Payment list loaded');
263
+
264
+ // Filter functionality
265
+ const applyFiltersBtn = document.getElementById('apply-filters');
266
+ const clearFiltersBtn = document.getElementById('clear-filters');
267
+ const exportCsvBtn = document.getElementById('export-csv');
268
+ const checkStatusBtns = document.querySelectorAll('.check-status');
269
+
270
+ // Apply filters
271
+ applyFiltersBtn?.addEventListener('click', function() {
272
+ const filters = {
273
+ search: document.getElementById('search').value,
274
+ status: document.getElementById('status').value,
275
+ provider: document.getElementById('provider').value,
276
+ date_from: document.getElementById('date_from').value,
277
+ date_to: document.getElementById('date_to').value
278
+ };
279
+
280
+ console.log('Applying filters:', filters);
281
+ // Implement filter logic here
282
+ applyFilters(filters);
283
+ });
284
+
285
+ // Clear filters
286
+ clearFiltersBtn?.addEventListener('click', function() {
287
+ document.getElementById('search').value = '';
288
+ document.getElementById('status').value = '';
289
+ document.getElementById('provider').value = '';
290
+ document.getElementById('date_from').value = '';
291
+ document.getElementById('date_to').value = '';
292
+
293
+ // Reload page without filters
294
+ window.location.href = window.location.pathname;
295
+ });
296
+
297
+ // Export CSV
298
+ exportCsvBtn?.addEventListener('click', function() {
299
+ console.log('Exporting CSV...');
300
+ // Implement CSV export logic here
301
+ });
302
+
303
+ // Check payment status
304
+ checkStatusBtns.forEach(btn => {
305
+ btn.addEventListener('click', function() {
306
+ const paymentId = this.dataset.paymentId;
307
+ console.log('Checking payment status:', paymentId);
308
+ checkPaymentStatus(paymentId);
309
+ });
310
+ });
311
+
312
+ // Real-time search
313
+ const searchInput = document.getElementById('search');
314
+ let searchTimeout;
315
+ searchInput?.addEventListener('input', function() {
316
+ clearTimeout(searchTimeout);
317
+ searchTimeout = setTimeout(() => {
318
+ if (this.value.length >= 3) {
319
+ performSearch(this.value);
320
+ }
321
+ }, 500);
322
+ });
323
+
324
+ function applyFilters(filters) {
325
+ const params = new URLSearchParams();
326
+ for (const [key, value] of Object.entries(filters)) {
327
+ if (value) params.append(key, value);
328
+ }
329
+
330
+ window.location.search = params.toString();
331
+ }
332
+
333
+ function performSearch(query) {
334
+ console.log('Searching for:', query);
335
+ // Implement AJAX search logic here
336
+ }
337
+
338
+ function checkPaymentStatus(paymentId) {
339
+ // Show loading state
340
+ const btn = document.querySelector(`[data-payment-id="${paymentId}"]`);
341
+ const originalText = btn.textContent;
342
+ btn.textContent = 'Checking...';
343
+ btn.disabled = true;
344
+
345
+ // Simulate API call
346
+ setTimeout(() => {
347
+ btn.textContent = originalText;
348
+ btn.disabled = false;
349
+ console.log('Status check completed for payment:', paymentId);
350
+ }, 2000);
351
+ }
352
+ });
353
+ </script>
354
+ {% endblock %}
@@ -0,0 +1,261 @@
1
+ {% extends 'payments/base.html' %}
2
+ {% load static %}
3
+ {% load payments_tags %}
4
+
5
+ {% block header_title %}Payment Analytics{% endblock %}
6
+ {% block header_subtitle %}Comprehensive payment system statistics and insights{% endblock %}
7
+
8
+ {% block content %}
9
+ <div class="space-y-6">
10
+ <!-- Stats Overview -->
11
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
12
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs p-6">
13
+ <div class="flex items-center">
14
+ <div class="flex-shrink-0">
15
+ <span class="material-icons text-blue-600 dark:text-blue-400 text-3xl">payments</span>
16
+ </div>
17
+ <div class="ml-4">
18
+ <p class="text-sm font-medium text-font-subtle-light dark:text-font-subtle-dark">Total Payments</p>
19
+ <p class="text-2xl font-bold text-font-important-light dark:text-font-important-dark">{{ stats.total_payments|default:"0" }}</p>
20
+ </div>
21
+ </div>
22
+ </div>
23
+
24
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs p-6">
25
+ <div class="flex items-center">
26
+ <div class="flex-shrink-0">
27
+ <span class="material-icons text-green-600 dark:text-green-400 text-3xl">check_circle</span>
28
+ </div>
29
+ <div class="ml-4">
30
+ <p class="text-sm font-medium text-font-subtle-light dark:text-font-subtle-dark">Completed</p>
31
+ <p class="text-2xl font-bold text-font-important-light dark:text-font-important-dark">{{ stats.completed_payments|default:"0" }}</p>
32
+ </div>
33
+ </div>
34
+ </div>
35
+
36
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs p-6">
37
+ <div class="flex items-center">
38
+ <div class="flex-shrink-0">
39
+ <span class="material-icons text-yellow-600 dark:text-yellow-400 text-3xl">pending</span>
40
+ </div>
41
+ <div class="ml-4">
42
+ <p class="text-sm font-medium text-font-subtle-light dark:text-font-subtle-dark">Pending</p>
43
+ <p class="text-2xl font-bold text-font-important-light dark:text-font-important-dark">{{ stats.pending_payments|default:"0" }}</p>
44
+ </div>
45
+ </div>
46
+ </div>
47
+
48
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs p-6">
49
+ <div class="flex items-center">
50
+ <div class="flex-shrink-0">
51
+ <span class="material-icons text-purple-600 dark:text-purple-400 text-3xl">attach_money</span>
52
+ </div>
53
+ <div class="ml-4">
54
+ <p class="text-sm font-medium text-font-subtle-light dark:text-font-subtle-dark">Total Volume</p>
55
+ <p class="text-2xl font-bold text-font-important-light dark:text-font-important-dark">${{ stats.total_volume|default:"0.00"|floatformat:2 }}</p>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </div>
60
+
61
+ <!-- Charts Section -->
62
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
63
+ <!-- Payment Volume Chart -->
64
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs">
65
+ <div class="p-6 border-b border-base-200 dark:border-base-700">
66
+ <h3 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">Payment Volume Trend</h3>
67
+ <p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">Last 30 days</p>
68
+ </div>
69
+ <div class="p-6">
70
+ <div class="h-64 bg-base-50 dark:bg-base-800 rounded-default border border-base-200 dark:border-base-700 flex items-center justify-center">
71
+ <div class="text-center">
72
+ <span class="material-icons text-font-subtle-light dark:text-font-subtle-dark text-4xl mb-2">bar_chart</span>
73
+ <p class="text-font-subtle-light dark:text-font-subtle-dark">Chart will be rendered here</p>
74
+ <p class="text-xs text-font-subtle-light dark:text-font-subtle-dark mt-1">JavaScript charting library integration</p>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ <!-- Status Distribution -->
81
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs">
82
+ <div class="p-6 border-b border-base-200 dark:border-base-700">
83
+ <h3 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">Payment Status Distribution</h3>
84
+ <p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">Current status breakdown</p>
85
+ </div>
86
+ <div class="p-6">
87
+ <div class="space-y-4">
88
+ {% for status, count in status_distribution.items %}
89
+ <div class="flex items-center justify-between">
90
+ <div class="flex items-center">
91
+ <div class="w-3 h-3 rounded-full mr-3 {% if status == 'completed' %}bg-green-500{% elif status == 'pending' %}bg-yellow-500{% elif status == 'failed' %}bg-red-500{% else %}bg-gray-500{% endif %}"></div>
92
+ <span class="text-font-default-light dark:text-font-default-dark capitalize">{{ status }}</span>
93
+ </div>
94
+ <span class="text-font-important-light dark:text-font-important-dark font-semibold">{{ count|default:"0" }}</span>
95
+ </div>
96
+ {% empty %}
97
+ <p class="text-font-subtle-light dark:text-font-subtle-dark text-center py-4">No payment data available</p>
98
+ {% endfor %}
99
+ </div>
100
+ </div>
101
+ </div>
102
+ </div>
103
+
104
+ <!-- Provider Statistics -->
105
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs">
106
+ <div class="p-6 border-b border-base-200 dark:border-base-700">
107
+ <h3 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">Provider Performance</h3>
108
+ <p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">Payment provider comparison</p>
109
+ </div>
110
+ <div class="p-6">
111
+ <div class="overflow-x-auto">
112
+ <table class="min-w-full">
113
+ <thead>
114
+ <tr class="border-b border-base-200 dark:border-base-700">
115
+ <th class="text-left py-3 text-font-important-light dark:text-font-important-dark font-semibold">Provider</th>
116
+ <th class="text-left py-3 text-font-important-light dark:text-font-important-dark font-semibold">Total Payments</th>
117
+ <th class="text-left py-3 text-font-important-light dark:text-font-important-dark font-semibold">Success Rate</th>
118
+ <th class="text-left py-3 text-font-important-light dark:text-font-important-dark font-semibold">Volume</th>
119
+ </tr>
120
+ </thead>
121
+ <tbody>
122
+ {% for provider in provider_stats %}
123
+ <tr class="border-b border-base-200 dark:border-base-700">
124
+ <td class="py-3">
125
+ <div class="flex items-center">
126
+ <span class="w-2 h-2 rounded-full mr-3 {% if provider.name == 'cryptapi' %}bg-orange-500{% elif provider.name == 'cryptomus' %}bg-blue-500{% elif provider.name == 'stripe' %}bg-purple-500{% else %}bg-gray-500{% endif %}"></span>
127
+ <span class="text-font-default-light dark:text-font-default-dark font-medium capitalize">{{ provider.name|default:"Unknown" }}</span>
128
+ </div>
129
+ </td>
130
+ <td class="py-3 text-font-default-light dark:text-font-default-dark">{{ provider.total_payments|default:"0" }}</td>
131
+ <td class="py-3">
132
+ <span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium
133
+ {% if provider.success_rate >= 95 %}bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200{% elif provider.success_rate >= 85 %}bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200{% else %}bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200{% endif %}">
134
+ {{ provider.success_rate|default:"0"|floatformat:1 }}%
135
+ </span>
136
+ </td>
137
+ <td class="py-3 text-font-default-light dark:text-font-default-dark">${{ provider.volume|default:"0.00"|floatformat:2 }}</td>
138
+ </tr>
139
+ {% empty %}
140
+ <tr>
141
+ <td colspan="4" class="py-8 text-center text-font-subtle-light dark:text-font-subtle-dark">
142
+ No provider data available
143
+ </td>
144
+ </tr>
145
+ {% endfor %}
146
+ </tbody>
147
+ </table>
148
+ </div>
149
+ </div>
150
+ </div>
151
+
152
+ <!-- Performance Metrics -->
153
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
154
+ <!-- Processing Times -->
155
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs">
156
+ <div class="p-6 border-b border-base-200 dark:border-base-700">
157
+ <h3 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">Processing Performance</h3>
158
+ <p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">Payment processing times</p>
159
+ </div>
160
+ <div class="p-6">
161
+ <div class="space-y-4">
162
+ <div class="flex justify-between items-center">
163
+ <span class="text-font-default-light dark:text-font-default-dark">Average Processing Time</span>
164
+ <span class="text-font-important-light dark:text-font-important-dark font-semibold">{{ performance_metrics.avg_processing_time_formatted|default:"N/A" }}</span>
165
+ </div>
166
+ <div class="flex justify-between items-center">
167
+ <span class="text-font-default-light dark:text-font-default-dark">Fastest Processing</span>
168
+ <span class="text-font-important-light dark:text-font-important-dark font-semibold">{{ performance_metrics.min_processing_time_formatted|default:"N/A" }}</span>
169
+ </div>
170
+ <div class="flex justify-between items-center">
171
+ <span class="text-font-default-light dark:text-font-default-dark">Slowest Processing</span>
172
+ <span class="text-font-important-light dark:text-font-important-dark font-semibold">{{ performance_metrics.max_processing_time_formatted|default:"N/A" }}</span>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ </div>
177
+
178
+ <!-- Recent Activity -->
179
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs">
180
+ <div class="p-6 border-b border-base-200 dark:border-base-700">
181
+ <h3 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">Recent Activity</h3>
182
+ <p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">Latest payment events</p>
183
+ </div>
184
+ <div class="p-6">
185
+ <div class="space-y-3">
186
+ {% for payment in recent_payments %}
187
+ <div class="flex items-center justify-between p-3 bg-base-50 dark:bg-base-800 rounded-default border border-base-200 dark:border-base-700">
188
+ <div class="flex items-center">
189
+ <span class="material-icons text-sm mr-3
190
+ {% if payment.status == 'completed' %}text-green-600 dark:text-green-400{% elif payment.status == 'pending' %}text-yellow-600 dark:text-yellow-400{% elif payment.status == 'failed' %}text-red-600 dark:text-red-400{% else %}text-gray-600 dark:text-gray-400{% endif %}">
191
+ {% if payment.status == 'completed' %}check_circle{% elif payment.status == 'pending' %}pending{% elif payment.status == 'failed' %}error{% else %}help{% endif %}
192
+ </span>
193
+ <div>
194
+ <p class="text-sm font-medium text-font-important-light dark:text-font-important-dark">${{ payment.amount_usd|floatformat:2 }}</p>
195
+ <p class="text-xs text-font-subtle-light dark:text-font-subtle-dark">{{ payment.provider|capfirst }} • {{ payment.currency_code }}</p>
196
+ </div>
197
+ </div>
198
+ <span class="text-xs text-font-subtle-light dark:text-font-subtle-dark">{{ payment.created_at|timesince }} ago</span>
199
+ </div>
200
+ {% empty %}
201
+ <p class="text-font-subtle-light dark:text-font-subtle-dark text-center py-4">No recent payments</p>
202
+ {% endfor %}
203
+ </div>
204
+ </div>
205
+ </div>
206
+ </div>
207
+
208
+ <!-- Export and Actions -->
209
+ <div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs">
210
+ <div class="p-6 border-b border-base-200 dark:border-base-700">
211
+ <h3 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">Analytics Actions</h3>
212
+ <p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">Export data and manage analytics</p>
213
+ </div>
214
+ <div class="p-6">
215
+ <div class="flex flex-wrap gap-3">
216
+ <button class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-default text-sm font-medium transition-colors duration-200">
217
+ <span class="material-icons text-sm mr-2">file_download</span>
218
+ Export CSV
219
+ </button>
220
+ <button class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-default text-sm font-medium transition-colors duration-200">
221
+ <span class="material-icons text-sm mr-2">table_chart</span>
222
+ Export Excel
223
+ </button>
224
+ <button class="bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded-default text-sm font-medium transition-colors duration-200">
225
+ <span class="material-icons text-sm mr-2">picture_as_pdf</span>
226
+ Generate Report
227
+ </button>
228
+ <button class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-default text-sm font-medium transition-colors duration-200">
229
+ <span class="material-icons text-sm mr-2">refresh</span>
230
+ Refresh Data
231
+ </button>
232
+ </div>
233
+ </div>
234
+ </div>
235
+ </div>
236
+ {% endblock %}
237
+
238
+ {% block extra_js %}
239
+ {{ block.super }}
240
+ <script>
241
+ document.addEventListener('DOMContentLoaded', function() {
242
+ console.log('Payment analytics loaded');
243
+
244
+ // Real-time updates would be implemented here
245
+ // Chart.js or similar charting library integration
246
+
247
+ // Add click handlers for export buttons
248
+ document.querySelectorAll('button').forEach(button => {
249
+ button.addEventListener('click', function() {
250
+ if (this.textContent.includes('Export') || this.textContent.includes('Generate')) {
251
+ console.log('Export action:', this.textContent);
252
+ // Export functionality would be implemented here
253
+ } else if (this.textContent.includes('Refresh')) {
254
+ console.log('Refreshing analytics data...');
255
+ // Refresh functionality would be implemented here
256
+ }
257
+ });
258
+ });
259
+ });
260
+ </script>
261
+ {% endblock %}