django-cfg 1.2.31__py3-none-any.whl → 1.3.1__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/api/health/views.py +4 -2
- django_cfg/apps/knowbase/config/settings.py +16 -15
- django_cfg/apps/payments/README.md +326 -0
- django_cfg/apps/payments/admin/__init__.py +20 -10
- django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
- django_cfg/apps/payments/admin/balance_admin.py +592 -297
- django_cfg/apps/payments/admin/currencies_admin.py +526 -222
- django_cfg/apps/payments/admin/filters.py +306 -199
- django_cfg/apps/payments/admin/payments_admin.py +465 -70
- django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
- django_cfg/apps/payments/admin_interface/__init__.py +18 -0
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
- django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
- django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
- django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
- django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
- django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
- django_cfg/apps/payments/apps.py +34 -9
- django_cfg/apps/payments/config/__init__.py +28 -51
- django_cfg/apps/payments/config/constance/__init__.py +22 -0
- django_cfg/apps/payments/config/constance/config_service.py +123 -0
- django_cfg/apps/payments/config/constance/fields.py +69 -0
- django_cfg/apps/payments/config/constance/settings.py +160 -0
- django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
- django_cfg/apps/payments/config/helpers.py +130 -0
- django_cfg/apps/payments/management/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/manage_currencies.py +303 -151
- django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
- django_cfg/apps/payments/middleware/__init__.py +3 -1
- django_cfg/apps/payments/middleware/api_access.py +329 -222
- django_cfg/apps/payments/middleware/rate_limiting.py +342 -152
- django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
- django_cfg/apps/payments/migrations/0001_initial.py +708 -536
- django_cfg/apps/payments/models/__init__.py +13 -18
- django_cfg/apps/payments/models/api_keys.py +121 -43
- django_cfg/apps/payments/models/balance.py +150 -115
- django_cfg/apps/payments/models/base.py +68 -15
- django_cfg/apps/payments/models/currencies.py +172 -148
- django_cfg/apps/payments/models/managers/__init__.py +44 -0
- django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
- django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
- django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
- django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
- django_cfg/apps/payments/models/payments.py +235 -285
- django_cfg/apps/payments/models/subscriptions.py +257 -177
- django_cfg/apps/payments/models/tariffs.py +147 -40
- django_cfg/apps/payments/services/__init__.py +209 -56
- django_cfg/apps/payments/services/cache/__init__.py +6 -6
- django_cfg/apps/payments/services/cache/{simple_cache.py → cache_service.py} +112 -12
- django_cfg/apps/payments/services/core/__init__.py +10 -6
- django_cfg/apps/payments/services/core/balance_service.py +435 -360
- django_cfg/apps/payments/services/core/base.py +166 -0
- django_cfg/apps/payments/services/core/currency_service.py +478 -0
- django_cfg/apps/payments/services/core/payment_service.py +346 -467
- django_cfg/apps/payments/services/core/subscription_service.py +425 -481
- django_cfg/apps/payments/services/core/webhook_service.py +410 -0
- django_cfg/apps/payments/services/integrations/__init__.py +29 -0
- django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
- django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
- django_cfg/apps/payments/services/providers/__init__.py +9 -14
- django_cfg/apps/payments/services/providers/base.py +234 -174
- django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
- django_cfg/apps/payments/services/providers/registry.py +367 -301
- django_cfg/apps/payments/services/types/__init__.py +78 -0
- django_cfg/apps/payments/services/types/data.py +177 -0
- django_cfg/apps/payments/services/types/requests.py +150 -0
- django_cfg/apps/payments/services/types/responses.py +156 -0
- django_cfg/apps/payments/services/types/webhooks.py +232 -0
- django_cfg/apps/payments/signals/__init__.py +33 -8
- django_cfg/apps/payments/signals/api_key_signals.py +210 -129
- django_cfg/apps/payments/signals/balance_signals.py +174 -0
- django_cfg/apps/payments/signals/payment_signals.py +128 -103
- django_cfg/apps/payments/signals/subscription_signals.py +194 -142
- django_cfg/apps/payments/static/payments/css/components.css +380 -0
- django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
- django_cfg/apps/payments/static/payments/js/components.js +545 -0
- django_cfg/apps/payments/static/payments/js/utils.js +412 -0
- django_cfg/apps/payments/templatetags/__init__.py +1 -1
- django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
- django_cfg/apps/payments/urls.py +45 -48
- django_cfg/apps/payments/urls_admin.py +33 -42
- django_cfg/apps/payments/views/api/__init__.py +101 -0
- django_cfg/apps/payments/views/api/api_keys.py +387 -0
- django_cfg/apps/payments/views/api/balances.py +381 -0
- django_cfg/apps/payments/views/api/base.py +298 -0
- django_cfg/apps/payments/views/api/currencies.py +402 -0
- django_cfg/apps/payments/views/api/payments.py +415 -0
- django_cfg/apps/payments/views/api/subscriptions.py +475 -0
- django_cfg/apps/payments/views/api/webhooks.py +476 -0
- django_cfg/apps/payments/views/serializers/__init__.py +99 -0
- django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
- django_cfg/apps/payments/views/serializers/balances.py +300 -0
- django_cfg/apps/payments/views/serializers/currencies.py +335 -0
- django_cfg/apps/payments/views/serializers/payments.py +387 -0
- django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
- django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +40 -4
- django_cfg/core/generation.py +25 -4
- django_cfg/core/integration/README.md +363 -0
- django_cfg/core/integration/__init__.py +47 -0
- django_cfg/core/integration/commands_collector.py +239 -0
- django_cfg/core/integration/display/__init__.py +15 -0
- django_cfg/core/integration/display/base.py +157 -0
- django_cfg/core/integration/display/ngrok.py +164 -0
- django_cfg/core/integration/display/startup.py +815 -0
- django_cfg/core/integration/url_integration.py +123 -0
- django_cfg/core/integration/version_checker.py +160 -0
- django_cfg/management/commands/auto_generate.py +4 -0
- django_cfg/management/commands/check_settings.py +6 -0
- django_cfg/management/commands/clear_constance.py +5 -2
- django_cfg/management/commands/create_token.py +6 -0
- django_cfg/management/commands/list_urls.py +6 -0
- django_cfg/management/commands/migrate_all.py +6 -0
- django_cfg/management/commands/migrator.py +3 -0
- django_cfg/management/commands/rundramatiq.py +6 -0
- django_cfg/management/commands/runserver_ngrok.py +51 -29
- django_cfg/management/commands/script.py +6 -0
- django_cfg/management/commands/show_config.py +12 -2
- django_cfg/management/commands/show_urls.py +4 -0
- django_cfg/management/commands/superuser.py +6 -0
- django_cfg/management/commands/task_clear.py +4 -1
- django_cfg/management/commands/task_status.py +3 -1
- django_cfg/management/commands/test_email.py +3 -0
- django_cfg/management/commands/test_telegram.py +6 -0
- django_cfg/management/commands/test_twilio.py +6 -0
- django_cfg/management/commands/tree.py +6 -0
- django_cfg/management/commands/validate_config.py +155 -149
- django_cfg/models/constance.py +31 -11
- django_cfg/models/payments.py +175 -492
- django_cfg/modules/django_logger.py +160 -146
- django_cfg/modules/django_unfold/dashboard.py +64 -16
- django_cfg/registry/core.py +1 -0
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/utils/smart_defaults.py +222 -571
- django_cfg/utils/toolkit.py +51 -11
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/METADATA +4 -1
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/RECORD +153 -185
- django_cfg/apps/payments/__init__.py +0 -8
- django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
- django_cfg/apps/payments/config/module.py +0 -70
- django_cfg/apps/payments/config/providers.py +0 -105
- django_cfg/apps/payments/config/settings.py +0 -96
- django_cfg/apps/payments/config/utils.py +0 -52
- django_cfg/apps/payments/decorators.py +0 -291
- django_cfg/apps/payments/management/commands/README.md +0 -146
- django_cfg/apps/payments/management/commands/currency_stats.py +0 -304
- django_cfg/apps/payments/managers/__init__.py +0 -23
- django_cfg/apps/payments/managers/api_key_manager.py +0 -35
- django_cfg/apps/payments/managers/balance_manager.py +0 -361
- django_cfg/apps/payments/managers/currency_manager.py +0 -306
- django_cfg/apps/payments/managers/payment_manager.py +0 -192
- django_cfg/apps/payments/managers/subscription_manager.py +0 -37
- django_cfg/apps/payments/managers/tariff_manager.py +0 -29
- django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
- django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
- django_cfg/apps/payments/models/events.py +0 -73
- django_cfg/apps/payments/serializers/__init__.py +0 -57
- django_cfg/apps/payments/serializers/api_keys.py +0 -51
- django_cfg/apps/payments/serializers/balance.py +0 -59
- django_cfg/apps/payments/serializers/currencies.py +0 -63
- django_cfg/apps/payments/serializers/payments.py +0 -62
- django_cfg/apps/payments/serializers/subscriptions.py +0 -71
- django_cfg/apps/payments/serializers/tariffs.py +0 -56
- django_cfg/apps/payments/services/billing/__init__.py +0 -8
- django_cfg/apps/payments/services/cache/base.py +0 -30
- django_cfg/apps/payments/services/core/fallback_service.py +0 -432
- django_cfg/apps/payments/services/internal_types.py +0 -461
- django_cfg/apps/payments/services/middleware/__init__.py +0 -8
- django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
- django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
- django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
- django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
- django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
- django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
- django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
- django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
- django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
- django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
- django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
- django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
- django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
- django_cfg/apps/payments/services/security/__init__.py +0 -34
- django_cfg/apps/payments/services/security/error_handler.py +0 -635
- django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
- django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
- django_cfg/apps/payments/static/payments/css/payments.css +0 -340
- django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
- django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
- django_cfg/apps/payments/static/payments/js/theme.js +0 -86
- django_cfg/apps/payments/tasks/__init__.py +0 -12
- django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
- django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
- django_cfg/apps/payments/templates/payments/base.html +0 -182
- django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
- django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
- django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
- django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
- django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
- django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
- django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
- django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
- django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
- django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
- django_cfg/apps/payments/templates/payments/stats.html +0 -261
- django_cfg/apps/payments/templates/payments/test.html +0 -213
- django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
- django_cfg/apps/payments/utils/__init__.py +0 -43
- django_cfg/apps/payments/utils/billing_utils.py +0 -342
- django_cfg/apps/payments/utils/config_utils.py +0 -239
- django_cfg/apps/payments/utils/middleware_utils.py +0 -228
- django_cfg/apps/payments/utils/validation_utils.py +0 -94
- django_cfg/apps/payments/views/__init__.py +0 -63
- django_cfg/apps/payments/views/api_key_views.py +0 -164
- django_cfg/apps/payments/views/balance_views.py +0 -75
- django_cfg/apps/payments/views/currency_views.py +0 -122
- django_cfg/apps/payments/views/payment_views.py +0 -149
- django_cfg/apps/payments/views/subscription_views.py +0 -135
- django_cfg/apps/payments/views/tariff_views.py +0 -131
- django_cfg/apps/payments/views/templates/__init__.py +0 -25
- django_cfg/apps/payments/views/templates/ajax.py +0 -451
- django_cfg/apps/payments/views/templates/base.py +0 -212
- django_cfg/apps/payments/views/templates/dashboard.py +0 -60
- django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
- django_cfg/apps/payments/views/templates/payment_management.py +0 -158
- django_cfg/apps/payments/views/templates/qr_code.py +0 -174
- django_cfg/apps/payments/views/templates/stats.py +0 -244
- django_cfg/apps/payments/views/templates/utils.py +0 -181
- django_cfg/apps/payments/views/webhook_views.py +0 -266
- django_cfg/apps/payments/viewsets.py +0 -66
- django_cfg/core/integration.py +0 -160
- django_cfg/template_archive/.gitignore +0 -1
- django_cfg/template_archive/__init__.py +0 -0
- django_cfg/urls.py +0 -33
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,451 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
AJAX endpoints for payment dashboard.
|
3
|
-
|
4
|
-
Provides real-time data updates and interactive functionality.
|
5
|
-
"""
|
6
|
-
|
7
|
-
from django.http import JsonResponse
|
8
|
-
from django.shortcuts import get_object_or_404
|
9
|
-
from django.utils import timezone
|
10
|
-
from .base import superuser_required, get_progress_percentage, log_view_access
|
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")
|
17
|
-
|
18
|
-
|
19
|
-
@superuser_required
|
20
|
-
def payment_status_ajax(request, payment_id):
|
21
|
-
"""AJAX endpoint for real-time payment status."""
|
22
|
-
try:
|
23
|
-
payment = get_object_or_404(UniversalPayment, id=payment_id)
|
24
|
-
|
25
|
-
# Log access for audit
|
26
|
-
log_view_access('payment_status_ajax', request.user, payment_id=payment_id)
|
27
|
-
|
28
|
-
data = {
|
29
|
-
'id': str(payment.id),
|
30
|
-
'status': payment.status,
|
31
|
-
'status_display': payment.get_status_display(),
|
32
|
-
'progress_percentage': get_progress_percentage(payment.status),
|
33
|
-
'amount_usd': float(payment.amount_usd),
|
34
|
-
'currency_code': payment.currency_code,
|
35
|
-
'provider': payment.provider,
|
36
|
-
'provider_display': payment.provider.title(),
|
37
|
-
'created_at': payment.created_at.isoformat(),
|
38
|
-
'updated_at': payment.updated_at.isoformat(),
|
39
|
-
}
|
40
|
-
|
41
|
-
# Add completion time if available
|
42
|
-
if payment.completed_at:
|
43
|
-
data['completed_at'] = payment.completed_at.isoformat()
|
44
|
-
|
45
|
-
# Add crypto-specific data
|
46
|
-
if payment.provider in ['nowpayments', 'cryptapi', 'cryptomus']:
|
47
|
-
data.update({
|
48
|
-
'is_crypto': True,
|
49
|
-
'pay_address': payment.pay_address,
|
50
|
-
'pay_amount': str(payment.pay_amount) if payment.pay_amount else None,
|
51
|
-
'network': getattr(payment, 'network', None),
|
52
|
-
'confirmations': getattr(payment, 'confirmations_count', 0),
|
53
|
-
})
|
54
|
-
else:
|
55
|
-
data['is_crypto'] = False
|
56
|
-
|
57
|
-
return JsonResponse(data)
|
58
|
-
|
59
|
-
except Exception as e:
|
60
|
-
return JsonResponse({'error': str(e)}, status=400)
|
61
|
-
|
62
|
-
|
63
|
-
@superuser_required
|
64
|
-
def payment_events_ajax(request, payment_id):
|
65
|
-
"""AJAX endpoint for payment events."""
|
66
|
-
try:
|
67
|
-
payment = get_object_or_404(UniversalPayment, id=payment_id)
|
68
|
-
|
69
|
-
# Log access for audit
|
70
|
-
log_view_access('payment_events_ajax', request.user, payment_id=payment_id)
|
71
|
-
|
72
|
-
events = PaymentEvent.objects.filter(payment_id=payment.id).order_by('-created_at')
|
73
|
-
|
74
|
-
events_data = []
|
75
|
-
for event in events:
|
76
|
-
event_data = {
|
77
|
-
'id': event.id,
|
78
|
-
'event_type': event.event_type,
|
79
|
-
'created_at': event.created_at.isoformat(),
|
80
|
-
'metadata': event.metadata or {},
|
81
|
-
}
|
82
|
-
|
83
|
-
# Add human-readable description
|
84
|
-
event_data['description'] = _get_event_description(event)
|
85
|
-
|
86
|
-
events_data.append(event_data)
|
87
|
-
|
88
|
-
return JsonResponse({
|
89
|
-
'events': events_data,
|
90
|
-
'count': len(events_data)
|
91
|
-
})
|
92
|
-
|
93
|
-
except Exception as e:
|
94
|
-
return JsonResponse({'error': str(e)}, status=400)
|
95
|
-
|
96
|
-
|
97
|
-
@superuser_required
|
98
|
-
def payment_stats_ajax(request):
|
99
|
-
"""AJAX endpoint for dashboard statistics."""
|
100
|
-
try:
|
101
|
-
# Log access for audit
|
102
|
-
log_view_access('payment_stats_ajax', request.user)
|
103
|
-
|
104
|
-
from .base import PaymentStatsMixin
|
105
|
-
|
106
|
-
# Create instance to use mixin methods
|
107
|
-
stats_mixin = PaymentStatsMixin()
|
108
|
-
|
109
|
-
# Get basic stats
|
110
|
-
payment_stats = stats_mixin.get_payment_stats()
|
111
|
-
provider_stats = stats_mixin.get_provider_stats()
|
112
|
-
|
113
|
-
# Get time range stats if requested
|
114
|
-
days = int(request.GET.get('days', 30))
|
115
|
-
time_range_stats = stats_mixin.get_time_range_stats(days)
|
116
|
-
|
117
|
-
data = {
|
118
|
-
'payment_stats': payment_stats,
|
119
|
-
'provider_stats': list(provider_stats),
|
120
|
-
'time_range_stats': time_range_stats,
|
121
|
-
'last_updated': timezone.now().isoformat(),
|
122
|
-
}
|
123
|
-
|
124
|
-
return JsonResponse(data)
|
125
|
-
|
126
|
-
except Exception as e:
|
127
|
-
return JsonResponse({'error': str(e)}, status=500)
|
128
|
-
|
129
|
-
|
130
|
-
@superuser_required
|
131
|
-
def payment_search_ajax(request):
|
132
|
-
"""AJAX endpoint for payment search."""
|
133
|
-
try:
|
134
|
-
query = request.GET.get('q', '').strip()
|
135
|
-
if not query:
|
136
|
-
return JsonResponse({'results': []})
|
137
|
-
|
138
|
-
# Log access for audit
|
139
|
-
log_view_access('payment_search_ajax', request.user, query=query)
|
140
|
-
|
141
|
-
from django.db.models import Q
|
142
|
-
|
143
|
-
# Search payments
|
144
|
-
payments = UniversalPayment.objects.filter(
|
145
|
-
Q(internal_payment_id__icontains=query) |
|
146
|
-
Q(provider_payment_id__icontains=query) |
|
147
|
-
Q(user__email__icontains=query) |
|
148
|
-
Q(pay_address__icontains=query)
|
149
|
-
).order_by('-created_at')[:20]
|
150
|
-
|
151
|
-
results = []
|
152
|
-
for payment in payments:
|
153
|
-
results.append({
|
154
|
-
'id': str(payment.id),
|
155
|
-
'internal_id': payment.internal_payment_id,
|
156
|
-
'provider_id': payment.provider_payment_id,
|
157
|
-
'amount_usd': float(payment.amount_usd),
|
158
|
-
'currency_code': payment.currency_code,
|
159
|
-
'status': payment.status,
|
160
|
-
'status_display': payment.get_status_display(),
|
161
|
-
'provider': payment.provider,
|
162
|
-
'provider_display': payment.provider.title(),
|
163
|
-
'user_email': payment.user.email,
|
164
|
-
'created_at': payment.created_at.isoformat(),
|
165
|
-
'url': f'/payments/payment/{payment.id}/',
|
166
|
-
})
|
167
|
-
|
168
|
-
return JsonResponse({
|
169
|
-
'results': results,
|
170
|
-
'count': len(results),
|
171
|
-
'query': query
|
172
|
-
})
|
173
|
-
|
174
|
-
except Exception as e:
|
175
|
-
return JsonResponse({'error': str(e)}, status=500)
|
176
|
-
|
177
|
-
|
178
|
-
@superuser_required
|
179
|
-
def payment_action_ajax(request, payment_id):
|
180
|
-
"""AJAX endpoint for payment actions (cancel, retry, etc.)."""
|
181
|
-
try:
|
182
|
-
if request.method != 'POST':
|
183
|
-
return JsonResponse({'error': 'POST method required'}, status=405)
|
184
|
-
|
185
|
-
payment = get_object_or_404(UniversalPayment, id=payment_id)
|
186
|
-
action = request.POST.get('action')
|
187
|
-
|
188
|
-
# Log access for audit
|
189
|
-
log_view_access('payment_action_ajax', request.user,
|
190
|
-
payment_id=payment_id, action=action)
|
191
|
-
|
192
|
-
if action == 'cancel':
|
193
|
-
return _handle_cancel_payment(payment, request.user)
|
194
|
-
elif action == 'retry':
|
195
|
-
return _handle_retry_payment(payment, request.user)
|
196
|
-
elif action == 'refresh':
|
197
|
-
return _handle_refresh_payment(payment, request.user)
|
198
|
-
else:
|
199
|
-
return JsonResponse({'error': 'Invalid action'}, status=400)
|
200
|
-
|
201
|
-
except Exception as e:
|
202
|
-
return JsonResponse({'error': str(e)}, status=500)
|
203
|
-
|
204
|
-
|
205
|
-
def _get_event_description(event):
|
206
|
-
"""Get human-readable description for payment event."""
|
207
|
-
descriptions = {
|
208
|
-
'created': 'Payment was created',
|
209
|
-
'pending': 'Payment is pending confirmation',
|
210
|
-
'confirming': 'Payment is being confirmed',
|
211
|
-
'confirmed': 'Payment has been confirmed',
|
212
|
-
'completed': 'Payment was completed successfully',
|
213
|
-
'failed': 'Payment failed',
|
214
|
-
'cancelled': 'Payment was cancelled',
|
215
|
-
'expired': 'Payment expired',
|
216
|
-
'refunded': 'Payment was refunded',
|
217
|
-
'webhook_received': 'Webhook notification received',
|
218
|
-
'status_updated': 'Payment status was updated',
|
219
|
-
}
|
220
|
-
|
221
|
-
description = descriptions.get(event.event_type, f'Event: {event.event_type}')
|
222
|
-
|
223
|
-
# Add metadata details if available
|
224
|
-
if event.metadata:
|
225
|
-
if 'reason' in event.metadata:
|
226
|
-
description += f" - {event.metadata['reason']}"
|
227
|
-
if 'amount' in event.metadata:
|
228
|
-
description += f" (Amount: ${event.metadata['amount']})"
|
229
|
-
|
230
|
-
return description
|
231
|
-
|
232
|
-
|
233
|
-
def _handle_cancel_payment(payment, user):
|
234
|
-
"""Handle payment cancellation."""
|
235
|
-
if payment.status not in ['pending', 'confirming']:
|
236
|
-
return JsonResponse({
|
237
|
-
'error': 'Payment cannot be cancelled in current status'
|
238
|
-
}, status=400)
|
239
|
-
|
240
|
-
try:
|
241
|
-
# Update payment status
|
242
|
-
payment.status = 'cancelled'
|
243
|
-
payment.save()
|
244
|
-
|
245
|
-
# Create event
|
246
|
-
PaymentEvent.objects.create(
|
247
|
-
payment=payment,
|
248
|
-
event_type='cancelled',
|
249
|
-
metadata={'cancelled_by': user.email}
|
250
|
-
)
|
251
|
-
|
252
|
-
return JsonResponse({
|
253
|
-
'success': True,
|
254
|
-
'message': 'Payment cancelled successfully',
|
255
|
-
'new_status': payment.status
|
256
|
-
})
|
257
|
-
|
258
|
-
except Exception as e:
|
259
|
-
return JsonResponse({'error': f'Failed to cancel payment: {str(e)}'}, status=500)
|
260
|
-
|
261
|
-
|
262
|
-
def _handle_retry_payment(payment, user):
|
263
|
-
"""Handle payment retry."""
|
264
|
-
if payment.status not in ['failed', 'expired']:
|
265
|
-
return JsonResponse({
|
266
|
-
'error': 'Payment cannot be retried in current status'
|
267
|
-
}, status=400)
|
268
|
-
|
269
|
-
try:
|
270
|
-
# Reset payment status
|
271
|
-
payment.status = 'pending'
|
272
|
-
payment.save()
|
273
|
-
|
274
|
-
# Create event
|
275
|
-
PaymentEvent.objects.create(
|
276
|
-
payment=payment,
|
277
|
-
event_type='retried',
|
278
|
-
metadata={'retried_by': user.email}
|
279
|
-
)
|
280
|
-
|
281
|
-
return JsonResponse({
|
282
|
-
'success': True,
|
283
|
-
'message': 'Payment retry initiated',
|
284
|
-
'new_status': payment.status
|
285
|
-
})
|
286
|
-
|
287
|
-
except Exception as e:
|
288
|
-
return JsonResponse({'error': f'Failed to retry payment: {str(e)}'}, status=500)
|
289
|
-
|
290
|
-
|
291
|
-
def _handle_refresh_payment(payment, user):
|
292
|
-
"""Handle payment status refresh."""
|
293
|
-
try:
|
294
|
-
# Try to refresh payment status from provider
|
295
|
-
from ...services.core.payment_service import PaymentService
|
296
|
-
|
297
|
-
payment_service = PaymentService()
|
298
|
-
updated_payment = payment_service.refresh_payment_status(payment.id)
|
299
|
-
|
300
|
-
# Create event
|
301
|
-
PaymentEvent.objects.create(
|
302
|
-
payment=payment,
|
303
|
-
event_type='refreshed',
|
304
|
-
metadata={'refreshed_by': user.email}
|
305
|
-
)
|
306
|
-
|
307
|
-
return JsonResponse({
|
308
|
-
'success': True,
|
309
|
-
'message': 'Payment status refreshed',
|
310
|
-
'new_status': updated_payment.status
|
311
|
-
})
|
312
|
-
|
313
|
-
except Exception as e:
|
314
|
-
return JsonResponse({
|
315
|
-
'success': False,
|
316
|
-
'message': f'Failed to refresh payment: {str(e)}'
|
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)
|
@@ -1,212 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Base mixins and decorators for payment template views.
|
3
|
-
|
4
|
-
Provides security and common functionality for all payment dashboard views.
|
5
|
-
"""
|
6
|
-
|
7
|
-
from django.contrib.auth.decorators import user_passes_test
|
8
|
-
from django.utils.decorators import method_decorator
|
9
|
-
from django.db.models import Q, Count, Sum
|
10
|
-
from django.utils import timezone
|
11
|
-
from datetime import timedelta
|
12
|
-
from django_cfg.modules.django_logger import get_logger
|
13
|
-
|
14
|
-
logger = get_logger("view_base")
|
15
|
-
|
16
|
-
|
17
|
-
def superuser_required(function=None):
|
18
|
-
"""Decorator that checks if user is superuser."""
|
19
|
-
actual_decorator = user_passes_test(
|
20
|
-
lambda u: u.is_authenticated and u.is_superuser,
|
21
|
-
login_url='/admin/login/'
|
22
|
-
)
|
23
|
-
if function:
|
24
|
-
return actual_decorator(function)
|
25
|
-
return actual_decorator
|
26
|
-
|
27
|
-
|
28
|
-
class SuperuserRequiredMixin:
|
29
|
-
"""Mixin that requires superuser access for all views."""
|
30
|
-
|
31
|
-
@method_decorator(superuser_required)
|
32
|
-
def dispatch(self, request, *args, **kwargs):
|
33
|
-
return super().dispatch(request, *args, **kwargs)
|
34
|
-
|
35
|
-
|
36
|
-
class PaymentFilterMixin:
|
37
|
-
"""Mixin that provides common payment filtering functionality."""
|
38
|
-
|
39
|
-
def get_filtered_payments(self, request=None):
|
40
|
-
"""Get payments with common filters applied."""
|
41
|
-
if request is None:
|
42
|
-
request = self.request
|
43
|
-
|
44
|
-
from ...models import UniversalPayment
|
45
|
-
|
46
|
-
# Base queryset
|
47
|
-
payments_qs = UniversalPayment.objects.select_related('user')
|
48
|
-
|
49
|
-
# Apply filters from GET parameters
|
50
|
-
status_filter = request.GET.get('status', '')
|
51
|
-
provider_filter = request.GET.get('provider', '')
|
52
|
-
search_query = request.GET.get('search', '')
|
53
|
-
date_filter = request.GET.get('date', '')
|
54
|
-
|
55
|
-
if status_filter:
|
56
|
-
payments_qs = payments_qs.filter(status=status_filter)
|
57
|
-
if provider_filter:
|
58
|
-
payments_qs = payments_qs.filter(provider=provider_filter)
|
59
|
-
if search_query:
|
60
|
-
payments_qs = payments_qs.filter(
|
61
|
-
Q(internal_payment_id__icontains=search_query) |
|
62
|
-
Q(provider_payment_id__icontains=search_query) |
|
63
|
-
Q(amount_usd__icontains=search_query) |
|
64
|
-
Q(user__email__icontains=search_query)
|
65
|
-
)
|
66
|
-
if date_filter:
|
67
|
-
try:
|
68
|
-
filter_date = timezone.datetime.strptime(date_filter, '%Y-%m-%d').date()
|
69
|
-
payments_qs = payments_qs.filter(created_at__date=filter_date)
|
70
|
-
except ValueError:
|
71
|
-
pass # Invalid date format, ignore filter
|
72
|
-
|
73
|
-
return payments_qs
|
74
|
-
|
75
|
-
def get_filter_context(self, request=None):
|
76
|
-
"""Get filter values for template context."""
|
77
|
-
if request is None:
|
78
|
-
request = self.request
|
79
|
-
|
80
|
-
return {
|
81
|
-
'status': request.GET.get('status', ''),
|
82
|
-
'provider': request.GET.get('provider', ''),
|
83
|
-
'search': request.GET.get('search', ''),
|
84
|
-
'date': request.GET.get('date', ''),
|
85
|
-
}
|
86
|
-
|
87
|
-
|
88
|
-
class PaymentStatsMixin:
|
89
|
-
"""Mixin that provides payment statistics functionality."""
|
90
|
-
|
91
|
-
def get_payment_stats(self, queryset=None):
|
92
|
-
"""Get payment statistics from queryset or all payments."""
|
93
|
-
if queryset is None:
|
94
|
-
from ...models import UniversalPayment
|
95
|
-
queryset = UniversalPayment.objects.all()
|
96
|
-
|
97
|
-
stats = queryset.aggregate(
|
98
|
-
total_count=Count('id'),
|
99
|
-
pending_count=Count('id', filter=Q(status='pending')),
|
100
|
-
confirming_count=Count('id', filter=Q(status='confirming')),
|
101
|
-
completed_count=Count('id', filter=Q(status='completed')),
|
102
|
-
failed_count=Count('id', filter=Q(status='failed')),
|
103
|
-
total_volume=Sum('amount_usd')
|
104
|
-
)
|
105
|
-
|
106
|
-
# Convert to template format
|
107
|
-
return {
|
108
|
-
'total_payments_count': stats['total_count'] or 0,
|
109
|
-
'pending_payments_count': stats['pending_count'] or 0,
|
110
|
-
'confirming_payments_count': stats['confirming_count'] or 0,
|
111
|
-
'completed_payments_count': stats['completed_count'] or 0,
|
112
|
-
'failed_payments_count': stats['failed_count'] or 0,
|
113
|
-
'total_volume': float(stats['total_volume'] or 0),
|
114
|
-
}
|
115
|
-
|
116
|
-
def get_provider_stats(self, queryset=None):
|
117
|
-
"""Get provider-specific statistics."""
|
118
|
-
if queryset is None:
|
119
|
-
from ...models import UniversalPayment
|
120
|
-
queryset = UniversalPayment.objects.all()
|
121
|
-
|
122
|
-
provider_stats = queryset.values('provider').annotate(
|
123
|
-
count=Count('id'),
|
124
|
-
volume=Sum('amount_usd'),
|
125
|
-
completed_count=Count('id', filter=Q(status='completed')),
|
126
|
-
).order_by('-volume')
|
127
|
-
|
128
|
-
# Calculate success rate and convert to list of dicts
|
129
|
-
stats_list = []
|
130
|
-
for stat in provider_stats:
|
131
|
-
if stat['count'] > 0:
|
132
|
-
stat['success_rate'] = (stat['completed_count'] / stat['count']) * 100
|
133
|
-
else:
|
134
|
-
stat['success_rate'] = 0
|
135
|
-
|
136
|
-
# Convert Decimal to float
|
137
|
-
if stat['volume']:
|
138
|
-
stat['volume'] = float(stat['volume'])
|
139
|
-
else:
|
140
|
-
stat['volume'] = 0.0
|
141
|
-
|
142
|
-
stats_list.append(stat)
|
143
|
-
|
144
|
-
return stats_list
|
145
|
-
|
146
|
-
def get_time_range_stats(self, days=30):
|
147
|
-
"""Get statistics for a specific time range."""
|
148
|
-
from ...models import UniversalPayment
|
149
|
-
|
150
|
-
end_date = timezone.now()
|
151
|
-
start_date = end_date - timedelta(days=days)
|
152
|
-
|
153
|
-
queryset = UniversalPayment.objects.filter(
|
154
|
-
created_at__gte=start_date,
|
155
|
-
created_at__lte=end_date
|
156
|
-
)
|
157
|
-
|
158
|
-
return self.get_payment_stats(queryset)
|
159
|
-
|
160
|
-
|
161
|
-
class PaymentContextMixin:
|
162
|
-
"""Mixin that provides common context data for payment views."""
|
163
|
-
|
164
|
-
def get_common_context(self):
|
165
|
-
"""Get common context data used across multiple views."""
|
166
|
-
from ...models import PaymentEvent
|
167
|
-
|
168
|
-
# Get recent events for activity feed (if any exist)
|
169
|
-
try:
|
170
|
-
recent_events = PaymentEvent.objects.order_by('-created_at')[:10]
|
171
|
-
except Exception:
|
172
|
-
recent_events = []
|
173
|
-
|
174
|
-
return {
|
175
|
-
'recent_events': recent_events,
|
176
|
-
'page_title': self.get_page_title(),
|
177
|
-
'breadcrumbs': self.get_breadcrumbs(),
|
178
|
-
}
|
179
|
-
|
180
|
-
def get_page_title(self):
|
181
|
-
"""Get page title for the view."""
|
182
|
-
return getattr(self, 'page_title', 'Payment Dashboard')
|
183
|
-
|
184
|
-
def get_breadcrumbs(self):
|
185
|
-
"""Get breadcrumb navigation for the view."""
|
186
|
-
return getattr(self, 'breadcrumbs', [
|
187
|
-
{'name': 'Payment Dashboard', 'url': '/payments/admin/'},
|
188
|
-
])
|
189
|
-
|
190
|
-
|
191
|
-
def get_progress_percentage(status):
|
192
|
-
"""Helper function to calculate progress percentage."""
|
193
|
-
progress_map = {
|
194
|
-
'pending': 10,
|
195
|
-
'confirming': 40,
|
196
|
-
'confirmed': 70,
|
197
|
-
'completed': 100,
|
198
|
-
'failed': 0,
|
199
|
-
'expired': 0,
|
200
|
-
'cancelled': 0,
|
201
|
-
'refunded': 50,
|
202
|
-
}
|
203
|
-
return progress_map.get(status, 0)
|
204
|
-
|
205
|
-
|
206
|
-
def log_view_access(view_name, user, **kwargs):
|
207
|
-
"""Log access to payment views for audit purposes."""
|
208
|
-
extra_info = ', '.join([f"{k}={v}" for k, v in kwargs.items()])
|
209
|
-
logger.info(
|
210
|
-
f"Payment dashboard access: {view_name} by {user.email} "
|
211
|
-
f"(superuser={user.is_superuser}) {extra_info}"
|
212
|
-
)
|