django-cfg 1.2.29__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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/payments/admin/__init__.py +3 -2
- django_cfg/apps/payments/admin/balance_admin.py +18 -18
- django_cfg/apps/payments/admin/currencies_admin.py +319 -131
- django_cfg/apps/payments/admin/payments_admin.py +15 -4
- django_cfg/apps/payments/config/module.py +2 -2
- django_cfg/apps/payments/config/utils.py +2 -2
- django_cfg/apps/payments/decorators.py +2 -2
- django_cfg/apps/payments/management/commands/README.md +95 -127
- django_cfg/apps/payments/management/commands/currency_stats.py +5 -24
- django_cfg/apps/payments/management/commands/manage_currencies.py +229 -0
- django_cfg/apps/payments/management/commands/manage_providers.py +235 -0
- django_cfg/apps/payments/managers/__init__.py +3 -2
- django_cfg/apps/payments/managers/balance_manager.py +2 -2
- django_cfg/apps/payments/managers/currency_manager.py +272 -49
- django_cfg/apps/payments/managers/payment_manager.py +161 -13
- django_cfg/apps/payments/middleware/api_access.py +2 -2
- django_cfg/apps/payments/middleware/rate_limiting.py +8 -18
- django_cfg/apps/payments/middleware/usage_tracking.py +20 -17
- django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +241 -0
- django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +30 -0
- django_cfg/apps/payments/models/__init__.py +3 -2
- django_cfg/apps/payments/models/currencies.py +187 -71
- django_cfg/apps/payments/models/payments.py +3 -2
- django_cfg/apps/payments/serializers/__init__.py +3 -2
- django_cfg/apps/payments/serializers/currencies.py +20 -12
- django_cfg/apps/payments/services/cache/simple_cache.py +2 -2
- django_cfg/apps/payments/services/core/balance_service.py +2 -2
- django_cfg/apps/payments/services/core/fallback_service.py +2 -2
- django_cfg/apps/payments/services/core/payment_service.py +3 -6
- django_cfg/apps/payments/services/core/subscription_service.py +4 -7
- django_cfg/apps/payments/services/internal_types.py +171 -7
- django_cfg/apps/payments/services/monitoring/api_schemas.py +58 -204
- django_cfg/apps/payments/services/monitoring/provider_health.py +2 -2
- django_cfg/apps/payments/services/providers/base.py +144 -43
- django_cfg/apps/payments/services/providers/cryptapi/__init__.py +4 -0
- django_cfg/apps/payments/services/providers/cryptapi/config.py +8 -0
- django_cfg/apps/payments/services/providers/cryptapi/models.py +192 -0
- django_cfg/apps/payments/services/providers/cryptapi/provider.py +439 -0
- django_cfg/apps/payments/services/providers/cryptomus/__init__.py +4 -0
- django_cfg/apps/payments/services/providers/cryptomus/models.py +176 -0
- django_cfg/apps/payments/services/providers/cryptomus/provider.py +429 -0
- django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +564 -0
- django_cfg/apps/payments/services/providers/models/__init__.py +34 -0
- django_cfg/apps/payments/services/providers/models/currencies.py +190 -0
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +4 -0
- django_cfg/apps/payments/services/providers/nowpayments/models.py +196 -0
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +380 -0
- django_cfg/apps/payments/services/providers/registry.py +294 -11
- django_cfg/apps/payments/services/providers/stripe/__init__.py +4 -0
- django_cfg/apps/payments/services/providers/stripe/models.py +184 -0
- django_cfg/apps/payments/services/providers/stripe/provider.py +109 -0
- django_cfg/apps/payments/services/security/error_handler.py +6 -8
- django_cfg/apps/payments/services/security/payment_notifications.py +2 -2
- django_cfg/apps/payments/services/security/webhook_validator.py +3 -4
- django_cfg/apps/payments/signals/api_key_signals.py +2 -2
- django_cfg/apps/payments/signals/payment_signals.py +11 -5
- django_cfg/apps/payments/signals/subscription_signals.py +2 -2
- django_cfg/apps/payments/tasks/webhook_processing.py +2 -2
- django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +50 -0
- django_cfg/apps/payments/templates/payments/base.html +4 -4
- django_cfg/apps/payments/templates/payments/components/payment_card.html +6 -6
- django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +4 -4
- django_cfg/apps/payments/templates/payments/components/progress_bar.html +14 -7
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +2 -2
- django_cfg/apps/payments/templates/payments/components/status_badge.html +8 -1
- django_cfg/apps/payments/templates/payments/components/status_overview.html +34 -30
- django_cfg/apps/payments/templates/payments/dashboard.html +202 -290
- django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +35 -0
- django_cfg/apps/payments/templates/payments/payment_create.html +579 -0
- django_cfg/apps/payments/templates/payments/payment_detail.html +373 -0
- django_cfg/apps/payments/templates/payments/payment_list.html +354 -0
- django_cfg/apps/payments/templates/payments/stats.html +261 -0
- django_cfg/apps/payments/templates/payments/test.html +213 -0
- django_cfg/apps/payments/urls.py +3 -1
- django_cfg/apps/payments/{urls_templates.py → urls_admin.py} +6 -0
- django_cfg/apps/payments/utils/__init__.py +1 -3
- django_cfg/apps/payments/utils/billing_utils.py +2 -2
- django_cfg/apps/payments/utils/config_utils.py +2 -8
- django_cfg/apps/payments/utils/validation_utils.py +2 -2
- django_cfg/apps/payments/views/__init__.py +3 -2
- django_cfg/apps/payments/views/currency_views.py +31 -20
- django_cfg/apps/payments/views/payment_views.py +2 -2
- django_cfg/apps/payments/views/templates/ajax.py +141 -2
- django_cfg/apps/payments/views/templates/base.py +21 -13
- django_cfg/apps/payments/views/templates/payment_detail.py +1 -1
- django_cfg/apps/payments/views/templates/payment_management.py +34 -40
- django_cfg/apps/payments/views/templates/stats.py +8 -4
- django_cfg/apps/payments/views/webhook_views.py +2 -2
- django_cfg/apps/payments/viewsets.py +3 -2
- django_cfg/apps/tasks/urls.py +0 -2
- django_cfg/apps/tasks/urls_admin.py +14 -0
- django_cfg/apps/urls.py +4 -4
- django_cfg/core/config.py +35 -0
- django_cfg/models/payments.py +2 -8
- django_cfg/modules/django_currency/__init__.py +16 -11
- django_cfg/modules/django_currency/clients/__init__.py +4 -4
- django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
- django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
- django_cfg/modules/django_currency/core/__init__.py +1 -7
- django_cfg/modules/django_currency/core/converter.py +18 -23
- django_cfg/modules/django_currency/core/models.py +122 -11
- django_cfg/modules/django_currency/database/__init__.py +4 -4
- django_cfg/modules/django_currency/database/database_loader.py +190 -309
- django_cfg/modules/django_unfold/dashboard.py +7 -2
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/templates/admin/components/action_grid.html +9 -9
- django_cfg/templates/admin/components/metric_card.html +5 -5
- django_cfg/templates/admin/components/status_badge.html +2 -2
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
- django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
- django_cfg/templates/admin/snippets/components/system_health.html +1 -1
- django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
- {django_cfg-1.2.29.dist-info → django_cfg-1.2.31.dist-info}/METADATA +2 -4
- {django_cfg-1.2.29.dist-info → django_cfg-1.2.31.dist-info}/RECORD +118 -96
- django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
- django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
- django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
- django_cfg/apps/payments/services/providers/cryptomus.py +0 -311
- django_cfg/apps/payments/services/providers/nowpayments.py +0 -293
- django_cfg/apps/payments/services/validators/__init__.py +0 -8
- django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
- django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
- {django_cfg-1.2.29.dist-info → django_cfg-1.2.31.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.2.31.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.2.31.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,213 @@
|
|
1
|
+
{% extends 'payments/base.html' %}
|
2
|
+
{% load static %}
|
3
|
+
{% load payments_tags %}
|
4
|
+
|
5
|
+
{% block header_title %}Payment Testing{% endblock %}
|
6
|
+
{% block header_subtitle %}Test payment integrations and webhooks{% endblock %}
|
7
|
+
|
8
|
+
{% block content %}
|
9
|
+
<div class="space-y-6">
|
10
|
+
<!-- Test Environment Status -->
|
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 border-b border-base-200 dark:border-base-700">
|
13
|
+
<h2 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">Test Environment Status</h2>
|
14
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">Current configuration and provider status</p>
|
15
|
+
</div>
|
16
|
+
<div class="p-6">
|
17
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
18
|
+
<div class="bg-base-50 dark:bg-base-800 p-4 rounded-default border border-base-200 dark:border-base-700">
|
19
|
+
<div class="flex items-center">
|
20
|
+
<span class="material-icons text-green-600 dark:text-green-400 text-xl mr-3">check_circle</span>
|
21
|
+
<div>
|
22
|
+
<h3 class="font-semibold text-font-important-light dark:text-font-important-dark">Test Mode</h3>
|
23
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark">Enabled</p>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
<div class="bg-base-50 dark:bg-base-800 p-4 rounded-default border border-base-200 dark:border-base-700">
|
28
|
+
<div class="flex items-center">
|
29
|
+
<span class="material-icons text-blue-600 dark:text-blue-400 text-xl mr-3">webhook</span>
|
30
|
+
<div>
|
31
|
+
<h3 class="font-semibold text-font-important-light dark:text-font-important-dark">Webhooks</h3>
|
32
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark">Active</p>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
<div class="bg-base-50 dark:bg-base-800 p-4 rounded-default border border-base-200 dark:border-base-700">
|
37
|
+
<div class="flex items-center">
|
38
|
+
<span class="material-icons text-orange-600 dark:text-orange-400 text-xl mr-3">security</span>
|
39
|
+
<div>
|
40
|
+
<h3 class="font-semibold text-font-important-light dark:text-font-important-dark">Security</h3>
|
41
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark">Validated</p>
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
|
49
|
+
<!-- Provider Testing -->
|
50
|
+
<div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs">
|
51
|
+
<div class="p-6 border-b border-base-200 dark:border-base-700">
|
52
|
+
<h2 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">Provider Testing</h2>
|
53
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">Test individual payment providers</p>
|
54
|
+
</div>
|
55
|
+
<div class="p-6">
|
56
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
57
|
+
<!-- CryptAPI Test -->
|
58
|
+
<div class="bg-base-50 dark:bg-base-800 p-4 rounded-default border border-base-200 dark:border-base-700">
|
59
|
+
<div class="flex items-center justify-between mb-3">
|
60
|
+
<h3 class="font-semibold text-font-important-light dark:text-font-important-dark">CryptAPI</h3>
|
61
|
+
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
|
62
|
+
Active
|
63
|
+
</span>
|
64
|
+
</div>
|
65
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mb-4">Test Bitcoin payments</p>
|
66
|
+
<button class="w-full bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-default text-sm font-medium transition-colors duration-200">
|
67
|
+
Test CryptAPI
|
68
|
+
</button>
|
69
|
+
</div>
|
70
|
+
|
71
|
+
<!-- Cryptomus Test -->
|
72
|
+
<div class="bg-base-50 dark:bg-base-800 p-4 rounded-default border border-base-200 dark:border-base-700">
|
73
|
+
<div class="flex items-center justify-between mb-3">
|
74
|
+
<h3 class="font-semibold text-font-important-light dark:text-font-important-dark">Cryptomus</h3>
|
75
|
+
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
|
76
|
+
Active
|
77
|
+
</span>
|
78
|
+
</div>
|
79
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mb-4">Test multi-crypto payments</p>
|
80
|
+
<button class="w-full bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-default text-sm font-medium transition-colors duration-200">
|
81
|
+
Test Cryptomus
|
82
|
+
</button>
|
83
|
+
</div>
|
84
|
+
|
85
|
+
<!-- Stripe Test -->
|
86
|
+
<div class="bg-base-50 dark:bg-base-800 p-4 rounded-default border border-base-200 dark:border-base-700">
|
87
|
+
<div class="flex items-center justify-between mb-3">
|
88
|
+
<h3 class="font-semibold text-font-important-light dark:text-font-important-dark">Stripe</h3>
|
89
|
+
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200">
|
90
|
+
Configured
|
91
|
+
</span>
|
92
|
+
</div>
|
93
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mb-4">Test card payments</p>
|
94
|
+
<button class="w-full bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-default text-sm font-medium transition-colors duration-200">
|
95
|
+
Test Stripe
|
96
|
+
</button>
|
97
|
+
</div>
|
98
|
+
</div>
|
99
|
+
</div>
|
100
|
+
</div>
|
101
|
+
|
102
|
+
<!-- Webhook Testing -->
|
103
|
+
<div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs">
|
104
|
+
<div class="p-6 border-b border-base-200 dark:border-base-700">
|
105
|
+
<h2 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">Webhook Testing</h2>
|
106
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">Test webhook endpoints and processing</p>
|
107
|
+
</div>
|
108
|
+
<div class="p-6">
|
109
|
+
<div class="space-y-4">
|
110
|
+
<div class="bg-base-50 dark:bg-base-800 p-4 rounded-default border border-base-200 dark:border-base-700">
|
111
|
+
<h3 class="font-semibold text-font-important-light dark:text-font-important-dark mb-2">Ngrok Integration</h3>
|
112
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mb-3">
|
113
|
+
Automatic tunnel creation for webhook testing
|
114
|
+
</p>
|
115
|
+
<div class="flex items-center space-x-3">
|
116
|
+
<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">
|
117
|
+
Start Ngrok
|
118
|
+
</button>
|
119
|
+
<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">
|
120
|
+
View Logs
|
121
|
+
</button>
|
122
|
+
<span class="text-sm text-font-subtle-light dark:text-font-subtle-dark">
|
123
|
+
Status: <span class="text-green-600 dark:text-green-400 font-medium">Ready</span>
|
124
|
+
</span>
|
125
|
+
</div>
|
126
|
+
</div>
|
127
|
+
|
128
|
+
<div class="bg-base-50 dark:bg-base-800 p-4 rounded-default border border-base-200 dark:border-base-700">
|
129
|
+
<h3 class="font-semibold text-font-important-light dark:text-font-important-dark mb-2">Webhook Simulation</h3>
|
130
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mb-3">
|
131
|
+
Send test webhooks to validate processing
|
132
|
+
</p>
|
133
|
+
<div class="flex items-center space-x-3">
|
134
|
+
<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">
|
135
|
+
Send Test Payment
|
136
|
+
</button>
|
137
|
+
<button class="bg-orange-600 hover:bg-orange-700 text-white px-4 py-2 rounded-default text-sm font-medium transition-colors duration-200">
|
138
|
+
Send Test Failure
|
139
|
+
</button>
|
140
|
+
<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">
|
141
|
+
Send Test Confirmation
|
142
|
+
</button>
|
143
|
+
</div>
|
144
|
+
</div>
|
145
|
+
</div>
|
146
|
+
</div>
|
147
|
+
</div>
|
148
|
+
|
149
|
+
<!-- Test Results -->
|
150
|
+
<div class="bg-white dark:bg-base-900 border border-base-200 dark:border-base-700 rounded-default shadow-xs">
|
151
|
+
<div class="p-6 border-b border-base-200 dark:border-base-700">
|
152
|
+
<h2 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">Recent Test Results</h2>
|
153
|
+
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">Latest testing activity</p>
|
154
|
+
</div>
|
155
|
+
<div class="p-6">
|
156
|
+
<div class="space-y-3">
|
157
|
+
<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">
|
158
|
+
<div class="flex items-center">
|
159
|
+
<span class="material-icons text-green-600 dark:text-green-400 text-sm mr-3">check_circle</span>
|
160
|
+
<div>
|
161
|
+
<p class="text-sm font-medium text-font-important-light dark:text-font-important-dark">CryptAPI Payment Test</p>
|
162
|
+
<p class="text-xs text-font-subtle-light dark:text-font-subtle-dark">Webhook received successfully</p>
|
163
|
+
</div>
|
164
|
+
</div>
|
165
|
+
<span class="text-xs text-font-subtle-light dark:text-font-subtle-dark">2 minutes ago</span>
|
166
|
+
</div>
|
167
|
+
|
168
|
+
<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">
|
169
|
+
<div class="flex items-center">
|
170
|
+
<span class="material-icons text-blue-600 dark:text-blue-400 text-sm mr-3">info</span>
|
171
|
+
<div>
|
172
|
+
<p class="text-sm font-medium text-font-important-light dark:text-font-important-dark">Ngrok Tunnel</p>
|
173
|
+
<p class="text-xs text-font-subtle-light dark:text-font-subtle-dark">Started on https://abc123.ngrok.io</p>
|
174
|
+
</div>
|
175
|
+
</div>
|
176
|
+
<span class="text-xs text-font-subtle-light dark:text-font-subtle-dark">5 minutes ago</span>
|
177
|
+
</div>
|
178
|
+
|
179
|
+
<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">
|
180
|
+
<div class="flex items-center">
|
181
|
+
<span class="material-icons text-orange-600 dark:text-orange-400 text-sm mr-3">warning</span>
|
182
|
+
<div>
|
183
|
+
<p class="text-sm font-medium text-font-important-light dark:text-font-important-dark">Cryptomus Configuration</p>
|
184
|
+
<p class="text-xs text-font-subtle-light dark:text-font-subtle-dark">API key validation required</p>
|
185
|
+
</div>
|
186
|
+
</div>
|
187
|
+
<span class="text-xs text-font-subtle-light dark:text-font-subtle-dark">10 minutes ago</span>
|
188
|
+
</div>
|
189
|
+
</div>
|
190
|
+
</div>
|
191
|
+
</div>
|
192
|
+
</div>
|
193
|
+
{% endblock %}
|
194
|
+
|
195
|
+
{% block extra_js %}
|
196
|
+
{{ block.super }}
|
197
|
+
<script>
|
198
|
+
// Testing functionality would be implemented here
|
199
|
+
document.addEventListener('DOMContentLoaded', function() {
|
200
|
+
console.log('Payment testing interface loaded');
|
201
|
+
|
202
|
+
// Add click handlers for test buttons
|
203
|
+
document.querySelectorAll('button').forEach(button => {
|
204
|
+
button.addEventListener('click', function() {
|
205
|
+
if (this.textContent.includes('Test')) {
|
206
|
+
console.log('Test initiated:', this.textContent);
|
207
|
+
// Actual testing logic would go here
|
208
|
+
}
|
209
|
+
});
|
210
|
+
});
|
211
|
+
});
|
212
|
+
</script>
|
213
|
+
{% endblock %}
|
django_cfg/apps/payments/urls.py
CHANGED
@@ -20,7 +20,8 @@ router.register(r'api-keys', views.APIKeyViewSet, basename='apikey')
|
|
20
20
|
router.register(r'balances', views.UserBalanceViewSet, basename='balance')
|
21
21
|
router.register(r'transactions', views.TransactionViewSet, basename='transaction')
|
22
22
|
router.register(r'currencies', views.CurrencyViewSet, basename='currency')
|
23
|
-
router.register(r'
|
23
|
+
router.register(r'networks', views.NetworkViewSet, basename='network')
|
24
|
+
router.register(r'provider-currencies', views.ProviderCurrencyViewSet, basename='providercurrency')
|
24
25
|
router.register(r'endpoint-groups', views.EndpointGroupViewSet, basename='endpointgroup')
|
25
26
|
router.register(r'tariffs', views.TariffViewSet, basename='tariff')
|
26
27
|
router.register(r'tariff-endpoint-groups', views.TariffEndpointGroupViewSet, basename='tariffendpointgroup')
|
@@ -75,4 +76,5 @@ urlpatterns = [
|
|
75
76
|
|
76
77
|
# Include generic API endpoints
|
77
78
|
path('', include(generic_patterns)),
|
79
|
+
|
78
80
|
]
|
@@ -20,6 +20,8 @@ from .views.templates.ajax import (
|
|
20
20
|
payment_stats_ajax,
|
21
21
|
payment_search_ajax,
|
22
22
|
payment_action_ajax,
|
23
|
+
provider_currencies_ajax,
|
24
|
+
all_providers_data_ajax,
|
23
25
|
)
|
24
26
|
from .views.templates.qr_code import qr_code_data_ajax
|
25
27
|
|
@@ -47,6 +49,10 @@ urlpatterns = [
|
|
47
49
|
path('ajax/stats/', payment_stats_ajax, name='payment_stats_ajax'),
|
48
50
|
path('ajax/search/', payment_search_ajax, name='payment_search_ajax'),
|
49
51
|
|
52
|
+
# Provider and Currency AJAX endpoints
|
53
|
+
path('ajax/provider/currencies/', provider_currencies_ajax, name='provider_currencies_ajax'),
|
54
|
+
path('ajax/providers/all/', all_providers_data_ajax, name='all_providers_data_ajax'),
|
55
|
+
|
50
56
|
# Development/testing
|
51
57
|
path('test/', PaymentTestView.as_view(), name='test'),
|
52
58
|
]
|
@@ -13,8 +13,7 @@ from .config_utils import (
|
|
13
13
|
CacheConfigHelper,
|
14
14
|
ProviderConfigHelper,
|
15
15
|
get_payments_config,
|
16
|
-
is_payments_enabled
|
17
|
-
is_debug_mode
|
16
|
+
is_payments_enabled
|
18
17
|
)
|
19
18
|
|
20
19
|
__all__ = [
|
@@ -41,5 +40,4 @@ __all__ = [
|
|
41
40
|
'ProviderConfigHelper',
|
42
41
|
'get_payments_config',
|
43
42
|
'is_payments_enabled',
|
44
|
-
'is_debug_mode',
|
45
43
|
]
|
@@ -5,7 +5,7 @@ Provides essential billing calculations and transaction management
|
|
5
5
|
without over-engineering.
|
6
6
|
"""
|
7
7
|
|
8
|
-
import
|
8
|
+
from django_cfg.modules.django_logger import get_logger
|
9
9
|
from typing import Dict, Any, Optional, Tuple
|
10
10
|
from decimal import Decimal, ROUND_HALF_UP
|
11
11
|
from datetime import datetime, timedelta
|
@@ -16,7 +16,7 @@ from django.contrib.auth import get_user_model
|
|
16
16
|
from ..models import UserBalance, Transaction, Subscription
|
17
17
|
|
18
18
|
User = get_user_model()
|
19
|
-
logger =
|
19
|
+
logger = get_logger("billing_utils")
|
20
20
|
|
21
21
|
|
22
22
|
def calculate_usage_cost(
|
@@ -4,14 +4,14 @@ Configuration utilities for payments module.
|
|
4
4
|
Universal utilities for working with django-cfg settings and configuration.
|
5
5
|
"""
|
6
6
|
|
7
|
-
import logging
|
8
7
|
from typing import Optional, Dict, Any, Type
|
9
8
|
from django.conf import settings
|
10
9
|
|
11
10
|
from django_cfg.modules.base import BaseCfgModule
|
12
11
|
from ..config.settings import PaymentsSettings
|
12
|
+
from django_cfg.modules.django_logger import get_logger
|
13
13
|
|
14
|
-
logger =
|
14
|
+
logger = get_logger("config_utils")
|
15
15
|
|
16
16
|
|
17
17
|
class PaymentsConfigMixin:
|
@@ -227,11 +227,6 @@ class PaymentsConfigUtil:
|
|
227
227
|
config = PaymentsConfigMixin.get_payments_config()
|
228
228
|
return config.enabled
|
229
229
|
|
230
|
-
@staticmethod
|
231
|
-
def is_debug_mode() -> bool:
|
232
|
-
"""Check if payments module is in debug mode."""
|
233
|
-
config = PaymentsConfigMixin.get_payments_config()
|
234
|
-
return getattr(config, 'debug_mode', False)
|
235
230
|
|
236
231
|
@staticmethod
|
237
232
|
def reset_all_caches():
|
@@ -242,4 +237,3 @@ class PaymentsConfigUtil:
|
|
242
237
|
# Convenience exports
|
243
238
|
get_payments_config = PaymentsConfigUtil.get_config
|
244
239
|
is_payments_enabled = PaymentsConfigUtil.is_payments_enabled
|
245
|
-
is_debug_mode = PaymentsConfigUtil.is_debug_mode
|
@@ -4,7 +4,7 @@ Validation utilities for payments module.
|
|
4
4
|
Basic validation functions for API keys and subscription access.
|
5
5
|
"""
|
6
6
|
|
7
|
-
import
|
7
|
+
from django_cfg.modules.django_logger import get_logger
|
8
8
|
from typing import Optional, Dict, Any
|
9
9
|
from django.contrib.auth import get_user_model
|
10
10
|
from django.utils import timezone
|
@@ -12,7 +12,7 @@ from django.utils import timezone
|
|
12
12
|
from ..models import APIKey, Subscription
|
13
13
|
|
14
14
|
User = get_user_model()
|
15
|
-
logger =
|
15
|
+
logger = get_logger("validation_utils")
|
16
16
|
|
17
17
|
|
18
18
|
def validate_api_key(api_key: str) -> bool:
|
@@ -16,7 +16,7 @@ from .api_key_views import (
|
|
16
16
|
APIKeyCreateView, APIKeyValidateView
|
17
17
|
)
|
18
18
|
from .currency_views import (
|
19
|
-
CurrencyViewSet,
|
19
|
+
CurrencyViewSet, NetworkViewSet, ProviderCurrencyViewSet,
|
20
20
|
SupportedCurrenciesView, CurrencyRatesView
|
21
21
|
)
|
22
22
|
from .tariff_views import (
|
@@ -50,7 +50,8 @@ __all__ = [
|
|
50
50
|
|
51
51
|
# Currency ViewSets & Generics
|
52
52
|
'CurrencyViewSet',
|
53
|
-
'
|
53
|
+
'NetworkViewSet',
|
54
|
+
'ProviderCurrencyViewSet',
|
54
55
|
'SupportedCurrenciesView',
|
55
56
|
'CurrencyRatesView',
|
56
57
|
|
@@ -6,20 +6,20 @@ from rest_framework import viewsets, permissions, generics
|
|
6
6
|
from rest_framework.decorators import action
|
7
7
|
from rest_framework.response import Response
|
8
8
|
from django_filters.rest_framework import DjangoFilterBackend
|
9
|
-
from ..models import Currency,
|
9
|
+
from ..models import Currency, Network, ProviderCurrency
|
10
10
|
from ..serializers import (
|
11
|
-
CurrencySerializer,
|
11
|
+
CurrencySerializer, NetworkSerializer, ProviderCurrencySerializer, CurrencyListSerializer
|
12
12
|
)
|
13
13
|
|
14
14
|
|
15
15
|
class CurrencyViewSet(viewsets.ReadOnlyModelViewSet):
|
16
16
|
"""Currency ViewSet: /currencies/"""
|
17
17
|
|
18
|
-
queryset = Currency.objects.
|
18
|
+
queryset = Currency.objects.all()
|
19
19
|
serializer_class = CurrencySerializer
|
20
20
|
permission_classes = [permissions.IsAuthenticated]
|
21
21
|
filter_backends = [DjangoFilterBackend]
|
22
|
-
filterset_fields = ['currency_type'
|
22
|
+
filterset_fields = ['currency_type']
|
23
23
|
|
24
24
|
def get_serializer_class(self):
|
25
25
|
"""Use list serializer for list action."""
|
@@ -45,22 +45,34 @@ class CurrencyViewSet(viewsets.ReadOnlyModelViewSet):
|
|
45
45
|
def networks(self, request, pk=None):
|
46
46
|
"""Get networks for specific currency."""
|
47
47
|
currency = self.get_object()
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
)
|
52
|
-
|
48
|
+
provider_currencies = ProviderCurrency.objects.filter(
|
49
|
+
base_currency=currency,
|
50
|
+
is_enabled=True
|
51
|
+
).select_related('network').distinct('network')
|
52
|
+
|
53
|
+
networks = [pc.network for pc in provider_currencies if pc.network]
|
54
|
+
serializer = NetworkSerializer(networks, many=True)
|
53
55
|
return Response(serializer.data)
|
54
56
|
|
55
57
|
|
56
|
-
class
|
57
|
-
"""
|
58
|
+
class NetworkViewSet(viewsets.ReadOnlyModelViewSet):
|
59
|
+
"""Network ViewSet: /networks/"""
|
60
|
+
|
61
|
+
queryset = Network.objects.all()
|
62
|
+
serializer_class = NetworkSerializer
|
63
|
+
permission_classes = [permissions.IsAuthenticated]
|
64
|
+
filter_backends = [DjangoFilterBackend]
|
65
|
+
filterset_fields = ['code', 'name']
|
66
|
+
|
67
|
+
|
68
|
+
class ProviderCurrencyViewSet(viewsets.ReadOnlyModelViewSet):
|
69
|
+
"""Provider Currency ViewSet: /provider-currencies/"""
|
58
70
|
|
59
|
-
queryset =
|
60
|
-
serializer_class =
|
71
|
+
queryset = ProviderCurrency.objects.select_related('base_currency', 'network')
|
72
|
+
serializer_class = ProviderCurrencySerializer
|
61
73
|
permission_classes = [permissions.IsAuthenticated]
|
62
74
|
filter_backends = [DjangoFilterBackend]
|
63
|
-
filterset_fields = ['
|
75
|
+
filterset_fields = ['provider_name', 'base_currency', 'network', 'is_enabled']
|
64
76
|
|
65
77
|
@action(detail=False, methods=['get'])
|
66
78
|
def by_currency(self, request):
|
@@ -70,7 +82,7 @@ class CurrencyNetworkViewSet(viewsets.ReadOnlyModelViewSet):
|
|
70
82
|
return Response({'error': 'currency parameter required'}, status=400)
|
71
83
|
|
72
84
|
try:
|
73
|
-
currency = Currency.objects.get(code=currency_code
|
85
|
+
currency = Currency.objects.get(code=currency_code)
|
74
86
|
networks = self.get_queryset().filter(currency=currency)
|
75
87
|
serializer = self.get_serializer(networks, many=True)
|
76
88
|
return Response(serializer.data)
|
@@ -87,7 +99,7 @@ class SupportedCurrenciesView(generics.ListAPIView):
|
|
87
99
|
|
88
100
|
def get_queryset(self):
|
89
101
|
"""Get active currencies."""
|
90
|
-
return Currency.objects.
|
102
|
+
return Currency.objects.all().order_by('code')
|
91
103
|
|
92
104
|
|
93
105
|
class CurrencyRatesView(generics.GenericAPIView):
|
@@ -98,14 +110,13 @@ class CurrencyRatesView(generics.GenericAPIView):
|
|
98
110
|
|
99
111
|
def get(self, request):
|
100
112
|
"""Get current exchange rates."""
|
101
|
-
currencies = Currency.objects.
|
113
|
+
currencies = Currency.objects.all()
|
102
114
|
|
103
115
|
rates = {}
|
104
116
|
for currency in currencies:
|
105
117
|
rates[currency.code] = {
|
106
|
-
'
|
107
|
-
'
|
108
|
-
'type': currency.currency_type,
|
118
|
+
'name': currency.name,
|
119
|
+
'currency_type': currency.currency_type,
|
109
120
|
}
|
110
121
|
|
111
122
|
return Response(rates)
|
@@ -76,8 +76,8 @@ class UserPaymentViewSet(viewsets.ModelViewSet):
|
|
76
76
|
|
77
77
|
except Exception as e:
|
78
78
|
# Log error but don't fail completely
|
79
|
-
import
|
80
|
-
logger =
|
79
|
+
from django_cfg.modules.django_logger import get_logger
|
80
|
+
logger = get_logger("payment_views")
|
81
81
|
logger.error(f"Payment status check failed for {payment.id}: {e}")
|
82
82
|
|
83
83
|
return Response({
|
@@ -8,7 +8,12 @@ from django.http import JsonResponse
|
|
8
8
|
from django.shortcuts import get_object_or_404
|
9
9
|
from django.utils import timezone
|
10
10
|
from .base import superuser_required, get_progress_percentage, log_view_access
|
11
|
-
from ...models import UniversalPayment, PaymentEvent
|
11
|
+
from ...models import UniversalPayment, PaymentEvent, Currency, Network, ProviderCurrency
|
12
|
+
from ...services.providers.registry import get_provider_registry
|
13
|
+
from ...services.internal_types import ProviderCurrencyOptionsResponse, CurrencyOptionModel
|
14
|
+
from django_cfg.modules.django_logger import get_logger
|
15
|
+
|
16
|
+
logger = get_logger("ajax_views")
|
12
17
|
|
13
18
|
|
14
19
|
@superuser_required
|
@@ -64,7 +69,7 @@ def payment_events_ajax(request, payment_id):
|
|
64
69
|
# Log access for audit
|
65
70
|
log_view_access('payment_events_ajax', request.user, payment_id=payment_id)
|
66
71
|
|
67
|
-
events = PaymentEvent.objects.filter(
|
72
|
+
events = PaymentEvent.objects.filter(payment_id=payment.id).order_by('-created_at')
|
68
73
|
|
69
74
|
events_data = []
|
70
75
|
for event in events:
|
@@ -310,3 +315,137 @@ def _handle_refresh_payment(payment, user):
|
|
310
315
|
'success': False,
|
311
316
|
'message': f'Failed to refresh payment: {str(e)}'
|
312
317
|
})
|
318
|
+
|
319
|
+
|
320
|
+
@superuser_required
|
321
|
+
def provider_currencies_ajax(request):
|
322
|
+
"""AJAX endpoint for getting supported currencies by provider using new ProviderCurrency model."""
|
323
|
+
try:
|
324
|
+
provider_name = request.GET.get('provider')
|
325
|
+
if not provider_name:
|
326
|
+
return JsonResponse({'error': 'Provider parameter required'}, status=400)
|
327
|
+
|
328
|
+
# Log access for audit
|
329
|
+
log_view_access('provider_currencies_ajax', request.user, provider=provider_name)
|
330
|
+
|
331
|
+
# Get flat currency options using manager method
|
332
|
+
try:
|
333
|
+
currency_options_dicts = ProviderCurrency.objects.get_currency_options_for_provider(provider_name)
|
334
|
+
|
335
|
+
# Convert dicts to Pydantic models for validation
|
336
|
+
currency_options = [CurrencyOptionModel(**option) for option in currency_options_dicts]
|
337
|
+
|
338
|
+
# Create typed response
|
339
|
+
response_data = ProviderCurrencyOptionsResponse(
|
340
|
+
success=True,
|
341
|
+
provider=provider_name,
|
342
|
+
currency_options=currency_options,
|
343
|
+
count=len(currency_options)
|
344
|
+
)
|
345
|
+
|
346
|
+
return JsonResponse(response_data.model_dump())
|
347
|
+
|
348
|
+
except Exception as e:
|
349
|
+
logger.error(f"Error getting currencies for {provider_name}: {e}")
|
350
|
+
|
351
|
+
# Create typed error response
|
352
|
+
error_response = ProviderCurrencyOptionsResponse(
|
353
|
+
success=False,
|
354
|
+
provider=provider_name,
|
355
|
+
currency_options=[],
|
356
|
+
count=0,
|
357
|
+
error=f'Failed to get currencies for {provider_name}: {str(e)}'
|
358
|
+
)
|
359
|
+
return JsonResponse(error_response.model_dump())
|
360
|
+
|
361
|
+
except Exception as e:
|
362
|
+
logger.error(f"Provider currencies AJAX error: {e}")
|
363
|
+
return JsonResponse({'error': str(e)}, status=500)
|
364
|
+
|
365
|
+
|
366
|
+
# currency_networks_ajax removed - networks now included in provider_currencies_ajax
|
367
|
+
|
368
|
+
|
369
|
+
@superuser_required
|
370
|
+
def all_providers_data_ajax(request):
|
371
|
+
"""AJAX endpoint for getting all providers with their currencies and capabilities."""
|
372
|
+
try:
|
373
|
+
# Log access for audit
|
374
|
+
log_view_access('all_providers_data_ajax', request.user)
|
375
|
+
|
376
|
+
# Get provider registry
|
377
|
+
registry = get_provider_registry()
|
378
|
+
all_providers = registry.list_providers()
|
379
|
+
|
380
|
+
providers_data = {}
|
381
|
+
|
382
|
+
for provider_name in all_providers:
|
383
|
+
try:
|
384
|
+
provider_instance = registry.get_provider(provider_name)
|
385
|
+
|
386
|
+
if provider_instance:
|
387
|
+
# Get provider info
|
388
|
+
provider_info = {
|
389
|
+
'name': provider_name,
|
390
|
+
'display_name': provider_name.title(),
|
391
|
+
'enabled': provider_instance.enabled,
|
392
|
+
'is_crypto': provider_name in ['nowpayments', 'cryptapi', 'cryptomus'],
|
393
|
+
'supports_webhooks': hasattr(provider_instance, 'validate_webhook'),
|
394
|
+
'currencies': [],
|
395
|
+
'default_currency': None
|
396
|
+
}
|
397
|
+
|
398
|
+
# Get supported currencies from ProviderCurrency model
|
399
|
+
try:
|
400
|
+
provider_currencies = ProviderCurrency.objects.enabled_for_provider(provider_name).select_related(
|
401
|
+
'base_currency'
|
402
|
+
).distinct('base_currency')[:10] # Limit to first 10 for performance
|
403
|
+
|
404
|
+
for pc in provider_currencies:
|
405
|
+
provider_info['currencies'].append({
|
406
|
+
'code': pc.base_currency.code,
|
407
|
+
'name': pc.base_currency.name,
|
408
|
+
'type': pc.base_currency.currency_type
|
409
|
+
})
|
410
|
+
|
411
|
+
# Set default currency
|
412
|
+
if provider_info['currencies']:
|
413
|
+
defaults = {
|
414
|
+
'cryptapi': 'BTC',
|
415
|
+
'cryptomus': 'USDT',
|
416
|
+
'nowpayments': 'BTC',
|
417
|
+
'stripe': 'USD'
|
418
|
+
}
|
419
|
+
default_code = defaults.get(provider_name)
|
420
|
+
if default_code and any(c['code'] == default_code for c in provider_info['currencies']):
|
421
|
+
provider_info['default_currency'] = default_code
|
422
|
+
else:
|
423
|
+
provider_info['default_currency'] = provider_info['currencies'][0]['code']
|
424
|
+
|
425
|
+
except Exception as e:
|
426
|
+
provider_info['error'] = f'Failed to load currencies: {str(e)}'
|
427
|
+
|
428
|
+
providers_data[provider_name] = provider_info
|
429
|
+
|
430
|
+
else:
|
431
|
+
providers_data[provider_name] = {
|
432
|
+
'name': provider_name,
|
433
|
+
'enabled': False,
|
434
|
+
'error': 'Provider instance not available'
|
435
|
+
}
|
436
|
+
|
437
|
+
except Exception as e:
|
438
|
+
providers_data[provider_name] = {
|
439
|
+
'name': provider_name,
|
440
|
+
'enabled': False,
|
441
|
+
'error': str(e)
|
442
|
+
}
|
443
|
+
|
444
|
+
return JsonResponse({
|
445
|
+
'success': True,
|
446
|
+
'providers': providers_data,
|
447
|
+
'count': len(providers_data)
|
448
|
+
})
|
449
|
+
|
450
|
+
except Exception as e:
|
451
|
+
return JsonResponse({'error': str(e)}, status=500)
|