django-cfg 1.2.29__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 -9
- 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 +600 -108
- django_cfg/apps/payments/admin/filters.py +306 -199
- django_cfg/apps/payments/admin/payments_admin.py +470 -64
- 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 +381 -0
- django_cfg/apps/payments/management/commands/manage_providers.py +408 -0
- 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 +343 -163
- django_cfg/apps/payments/middleware/usage_tracking.py +250 -238
- django_cfg/apps/payments/migrations/0001_initial.py +708 -536
- django_cfg/apps/payments/models/__init__.py +16 -20
- 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 +207 -67
- 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 -284
- 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 +344 -468
- django_cfg/apps/payments/services/core/subscription_service.py +425 -484
- 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 +232 -71
- django_cfg/apps/payments/services/providers/nowpayments.py +404 -219
- django_cfg/apps/payments/services/providers/registry.py +429 -80
- 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 +211 -130
- django_cfg/apps/payments/signals/balance_signals.py +174 -0
- django_cfg/apps/payments/signals/payment_signals.py +129 -98
- django_cfg/apps/payments/signals/subscription_signals.py +195 -143
- 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 +46 -47
- django_cfg/apps/payments/urls_admin.py +49 -0
- 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/apps/tasks/urls.py +0 -2
- django_cfg/apps/tasks/urls_admin.py +14 -0
- django_cfg/apps/urls.py +4 -4
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +75 -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 -498
- 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_logger.py +160 -146
- django_cfg/modules/django_unfold/dashboard.py +65 -12
- django_cfg/registry/core.py +1 -0
- 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/utils/smart_defaults.py +222 -571
- django_cfg/utils/toolkit.py +51 -11
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/METADATA +5 -4
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/RECORD +172 -182
- 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 -178
- django_cfg/apps/payments/management/commands/currency_stats.py +0 -323
- 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/managers/__init__.py +0 -22
- 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 -83
- django_cfg/apps/payments/managers/payment_manager.py +0 -44
- django_cfg/apps/payments/managers/subscription_manager.py +0 -37
- django_cfg/apps/payments/managers/tariff_manager.py +0 -29
- django_cfg/apps/payments/models/events.py +0 -73
- django_cfg/apps/payments/serializers/__init__.py +0 -56
- 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 -55
- 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 -297
- 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 -222
- django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
- 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/security/__init__.py +0 -34
- django_cfg/apps/payments/services/security/error_handler.py +0 -637
- django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
- django_cfg/apps/payments/services/security/webhook_validator.py +0 -475
- django_cfg/apps/payments/services/validators/__init__.py +0 -8
- 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/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 -36
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
- django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -27
- django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -144
- django_cfg/apps/payments/templates/payments/dashboard.html +0 -346
- django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
- django_cfg/apps/payments/urls_templates.py +0 -52
- django_cfg/apps/payments/utils/__init__.py +0 -45
- django_cfg/apps/payments/utils/billing_utils.py +0 -342
- django_cfg/apps/payments/utils/config_utils.py +0 -245
- 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 -62
- 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 -111
- 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 -312
- django_cfg/apps/payments/views/templates/base.py +0 -204
- 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 -164
- django_cfg/apps/payments/views/templates/qr_code.py +0 -174
- django_cfg/apps/payments/views/templates/stats.py +0 -240
- 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 -65
- django_cfg/core/integration.py +0 -160
- django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
- django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
- 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.29.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,164 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Payment management views.
|
3
|
-
|
4
|
-
Provides list, create, and management functionality for payments.
|
5
|
-
"""
|
6
|
-
|
7
|
-
from django.views.generic import TemplateView, ListView
|
8
|
-
from .base import (
|
9
|
-
SuperuserRequiredMixin,
|
10
|
-
PaymentFilterMixin,
|
11
|
-
PaymentContextMixin,
|
12
|
-
log_view_access
|
13
|
-
)
|
14
|
-
from ...models import UniversalPayment
|
15
|
-
|
16
|
-
|
17
|
-
class PaymentCreateView(
|
18
|
-
SuperuserRequiredMixin,
|
19
|
-
PaymentContextMixin,
|
20
|
-
TemplateView
|
21
|
-
):
|
22
|
-
"""Form view for creating a new payment."""
|
23
|
-
|
24
|
-
template_name = 'payments/payment_create.html'
|
25
|
-
page_title = 'Create Payment'
|
26
|
-
|
27
|
-
def get_breadcrumbs(self):
|
28
|
-
return [
|
29
|
-
{'name': 'Dashboard', 'url': '/payments/admin/'},
|
30
|
-
{'name': 'Payments', 'url': '/payments/admin/list/'},
|
31
|
-
{'name': 'Create Payment', 'url': ''},
|
32
|
-
]
|
33
|
-
|
34
|
-
def get_context_data(self, **kwargs):
|
35
|
-
context = super().get_context_data(**kwargs)
|
36
|
-
|
37
|
-
# Log access for audit
|
38
|
-
log_view_access('payment_create', self.request.user)
|
39
|
-
|
40
|
-
# Get available providers
|
41
|
-
providers = self._get_available_providers()
|
42
|
-
|
43
|
-
# Get available currencies
|
44
|
-
currencies = self._get_available_currencies()
|
45
|
-
|
46
|
-
# Get common context
|
47
|
-
common_context = self.get_common_context()
|
48
|
-
|
49
|
-
context.update({
|
50
|
-
'providers': providers,
|
51
|
-
'currencies': currencies,
|
52
|
-
'default_amount': 10.0, # Default test amount
|
53
|
-
**common_context
|
54
|
-
})
|
55
|
-
|
56
|
-
return context
|
57
|
-
|
58
|
-
def _get_available_providers(self):
|
59
|
-
"""Get list of available payment providers."""
|
60
|
-
try:
|
61
|
-
from ...services.providers.registry import ProviderRegistry
|
62
|
-
providers = []
|
63
|
-
for provider_name, provider_class in ProviderRegistry.get_all_providers().items():
|
64
|
-
providers.append({
|
65
|
-
'name': provider_name,
|
66
|
-
'display_name': provider_name.title(),
|
67
|
-
'is_crypto': provider_name in ['nowpayments', 'cryptapi', 'cryptomus'],
|
68
|
-
'description': getattr(provider_class, '__doc__', ''),
|
69
|
-
})
|
70
|
-
return providers
|
71
|
-
except Exception:
|
72
|
-
# Fallback if registry is not available
|
73
|
-
return [
|
74
|
-
{'name': 'nowpayments', 'display_name': 'NowPayments', 'is_crypto': True},
|
75
|
-
{'name': 'cryptapi', 'display_name': 'CryptAPI', 'is_crypto': True},
|
76
|
-
{'name': 'cryptomus', 'display_name': 'Cryptomus', 'is_crypto': True},
|
77
|
-
{'name': 'stripe', 'display_name': 'Stripe', 'is_crypto': False},
|
78
|
-
]
|
79
|
-
|
80
|
-
def _get_available_currencies(self):
|
81
|
-
"""Get list of available currencies."""
|
82
|
-
from ...models import Currency
|
83
|
-
|
84
|
-
try:
|
85
|
-
# Get currencies from database
|
86
|
-
currencies = Currency.objects.filter(is_active=True).order_by('code')
|
87
|
-
return [{'code': c.code, 'name': c.name} for c in currencies]
|
88
|
-
except Exception:
|
89
|
-
# Fallback list
|
90
|
-
return [
|
91
|
-
{'code': 'USD', 'name': 'US Dollar'},
|
92
|
-
{'code': 'EUR', 'name': 'Euro'},
|
93
|
-
{'code': 'BTC', 'name': 'Bitcoin'},
|
94
|
-
{'code': 'ETH', 'name': 'Ethereum'},
|
95
|
-
{'code': 'LTC', 'name': 'Litecoin'},
|
96
|
-
]
|
97
|
-
|
98
|
-
|
99
|
-
class PaymentListView(
|
100
|
-
SuperuserRequiredMixin,
|
101
|
-
PaymentFilterMixin,
|
102
|
-
PaymentContextMixin,
|
103
|
-
ListView
|
104
|
-
):
|
105
|
-
"""Paginated list view for all payments."""
|
106
|
-
|
107
|
-
model = UniversalPayment
|
108
|
-
template_name = 'payments/payment_list.html'
|
109
|
-
context_object_name = 'payments'
|
110
|
-
paginate_by = 20
|
111
|
-
ordering = ['-created_at']
|
112
|
-
page_title = 'All Payments'
|
113
|
-
|
114
|
-
def get_breadcrumbs(self):
|
115
|
-
return [
|
116
|
-
{'name': 'Dashboard', 'url': '/payments/admin/'},
|
117
|
-
{'name': 'All Payments', 'url': ''},
|
118
|
-
]
|
119
|
-
|
120
|
-
def get_queryset(self):
|
121
|
-
# Log access for audit
|
122
|
-
log_view_access('payment_list', self.request.user)
|
123
|
-
|
124
|
-
# Use filter mixin to get filtered queryset
|
125
|
-
return self.get_filtered_payments().order_by(*self.ordering)
|
126
|
-
|
127
|
-
def get_context_data(self, **kwargs):
|
128
|
-
context = super().get_context_data(**kwargs)
|
129
|
-
|
130
|
-
# Get filter context
|
131
|
-
filter_context = self.get_filter_context()
|
132
|
-
|
133
|
-
# Get available filter options
|
134
|
-
filter_options = self._get_filter_options()
|
135
|
-
|
136
|
-
# Get common context
|
137
|
-
common_context = self.get_common_context()
|
138
|
-
|
139
|
-
context.update({
|
140
|
-
'filters': filter_context,
|
141
|
-
'filter_options': filter_options,
|
142
|
-
'total_count': self.get_queryset().count(),
|
143
|
-
**common_context
|
144
|
-
})
|
145
|
-
|
146
|
-
return context
|
147
|
-
|
148
|
-
def _get_filter_options(self):
|
149
|
-
"""Get available options for filter dropdowns."""
|
150
|
-
from django.db.models import Value
|
151
|
-
from django.db.models.functions import Concat
|
152
|
-
|
153
|
-
# Get unique statuses
|
154
|
-
statuses = UniversalPayment.objects.values_list('status', flat=True).distinct()
|
155
|
-
status_choices = [(status, status.title()) for status in statuses if status]
|
156
|
-
|
157
|
-
# Get unique providers
|
158
|
-
providers = UniversalPayment.objects.values_list('provider', flat=True).distinct()
|
159
|
-
provider_choices = [(provider, provider.title()) for provider in providers if provider]
|
160
|
-
|
161
|
-
return {
|
162
|
-
'statuses': status_choices,
|
163
|
-
'providers': provider_choices,
|
164
|
-
}
|
@@ -1,174 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
QR code views for crypto payments.
|
3
|
-
|
4
|
-
Provides QR code generation and display for cryptocurrency payments.
|
5
|
-
"""
|
6
|
-
|
7
|
-
from django.views.generic import DetailView
|
8
|
-
from django.http import JsonResponse
|
9
|
-
from .base import (
|
10
|
-
SuperuserRequiredMixin,
|
11
|
-
PaymentContextMixin,
|
12
|
-
superuser_required,
|
13
|
-
log_view_access
|
14
|
-
)
|
15
|
-
from ...models import UniversalPayment
|
16
|
-
|
17
|
-
|
18
|
-
class PaymentQRCodeView(
|
19
|
-
SuperuserRequiredMixin,
|
20
|
-
PaymentContextMixin,
|
21
|
-
DetailView
|
22
|
-
):
|
23
|
-
"""QR code view for crypto payments."""
|
24
|
-
|
25
|
-
model = UniversalPayment
|
26
|
-
template_name = 'payments/payment_qr.html'
|
27
|
-
context_object_name = 'payment'
|
28
|
-
page_title = 'Payment QR Code'
|
29
|
-
|
30
|
-
def get_breadcrumbs(self):
|
31
|
-
payment = self.get_object()
|
32
|
-
return [
|
33
|
-
{'name': 'Dashboard', 'url': '/payments/admin/'},
|
34
|
-
{'name': 'Payments', 'url': '/payments/admin/list/'},
|
35
|
-
{'name': f'Payment #{payment.internal_payment_id or str(payment.id)[:8]}',
|
36
|
-
'url': f'/payments/admin/payment/{payment.id}/'},
|
37
|
-
{'name': 'QR Code', 'url': ''},
|
38
|
-
]
|
39
|
-
|
40
|
-
def get_context_data(self, **kwargs):
|
41
|
-
context = super().get_context_data(**kwargs)
|
42
|
-
payment = self.get_object()
|
43
|
-
|
44
|
-
# Log access for audit
|
45
|
-
log_view_access('payment_qr', self.request.user, payment_id=payment.id)
|
46
|
-
|
47
|
-
# Check if payment supports QR codes
|
48
|
-
if not self._supports_qr_code(payment):
|
49
|
-
context['error'] = "QR codes are not supported for this payment method"
|
50
|
-
return context
|
51
|
-
|
52
|
-
# Generate QR code data
|
53
|
-
qr_data = self._generate_qr_data(payment)
|
54
|
-
|
55
|
-
# Get payment instructions
|
56
|
-
instructions = self._get_payment_instructions(payment)
|
57
|
-
|
58
|
-
# Get common context
|
59
|
-
common_context = self.get_common_context()
|
60
|
-
|
61
|
-
context.update({
|
62
|
-
'qr_data': qr_data,
|
63
|
-
'qr_size': self.request.GET.get('size', 256),
|
64
|
-
'instructions': instructions,
|
65
|
-
'is_crypto': self._is_crypto_payment(payment),
|
66
|
-
'can_copy': True,
|
67
|
-
**common_context
|
68
|
-
})
|
69
|
-
|
70
|
-
return context
|
71
|
-
|
72
|
-
def _supports_qr_code(self, payment):
|
73
|
-
"""Check if payment method supports QR codes."""
|
74
|
-
crypto_providers = ['nowpayments', 'cryptapi', 'cryptomus']
|
75
|
-
return payment.provider in crypto_providers and payment.pay_address
|
76
|
-
|
77
|
-
def _is_crypto_payment(self, payment):
|
78
|
-
"""Check if payment is cryptocurrency-based."""
|
79
|
-
crypto_providers = ['nowpayments', 'cryptapi', 'cryptomus']
|
80
|
-
return payment.provider in crypto_providers
|
81
|
-
|
82
|
-
def _generate_qr_data(self, payment):
|
83
|
-
"""Generate QR code data for the payment."""
|
84
|
-
if not payment.pay_address:
|
85
|
-
return None
|
86
|
-
|
87
|
-
# For crypto payments, use standard format
|
88
|
-
if self._is_crypto_payment(payment):
|
89
|
-
qr_data = payment.pay_address
|
90
|
-
|
91
|
-
# Add amount if available
|
92
|
-
if payment.pay_amount:
|
93
|
-
# Use appropriate URI scheme based on currency
|
94
|
-
uri_schemes = {
|
95
|
-
'BTC': 'bitcoin',
|
96
|
-
'LTC': 'litecoin',
|
97
|
-
'ETH': 'ethereum',
|
98
|
-
'BCH': 'bitcoincash',
|
99
|
-
}
|
100
|
-
|
101
|
-
scheme = uri_schemes.get(payment.currency_code.upper(), 'crypto')
|
102
|
-
qr_data = f"{scheme}:{payment.pay_address}?amount={payment.pay_amount}"
|
103
|
-
|
104
|
-
# Add label if available
|
105
|
-
if payment.internal_payment_id:
|
106
|
-
qr_data += f"&label=Payment%20{payment.internal_payment_id}"
|
107
|
-
|
108
|
-
return qr_data
|
109
|
-
|
110
|
-
return payment.pay_address
|
111
|
-
|
112
|
-
def _get_payment_instructions(self, payment):
|
113
|
-
"""Get step-by-step payment instructions."""
|
114
|
-
if not self._is_crypto_payment(payment):
|
115
|
-
return []
|
116
|
-
|
117
|
-
instructions = [
|
118
|
-
"Scan the QR code with your crypto wallet app",
|
119
|
-
f"Send exactly {payment.pay_amount} {payment.currency_code} to the address",
|
120
|
-
"Wait for network confirmations",
|
121
|
-
"Payment will be automatically confirmed"
|
122
|
-
]
|
123
|
-
|
124
|
-
# Add provider-specific instructions
|
125
|
-
if payment.provider == 'cryptapi':
|
126
|
-
instructions.append("Minimum 1 confirmation required")
|
127
|
-
elif payment.provider == 'nowpayments':
|
128
|
-
instructions.append("Minimum 2 confirmations required")
|
129
|
-
elif payment.provider == 'cryptomus':
|
130
|
-
instructions.append("Confirmations depend on selected network")
|
131
|
-
|
132
|
-
return instructions
|
133
|
-
|
134
|
-
|
135
|
-
@superuser_required
|
136
|
-
def qr_code_data_ajax(request, payment_id):
|
137
|
-
"""AJAX endpoint to get QR code data."""
|
138
|
-
try:
|
139
|
-
payment = UniversalPayment.objects.get(id=payment_id)
|
140
|
-
|
141
|
-
# Log access for audit
|
142
|
-
log_view_access('qr_ajax', request.user, payment_id=payment_id)
|
143
|
-
|
144
|
-
view = PaymentQRCodeView()
|
145
|
-
|
146
|
-
# Check if payment supports QR codes
|
147
|
-
if not view._supports_qr_code(payment):
|
148
|
-
return JsonResponse({
|
149
|
-
'error': 'QR codes not supported for this payment method'
|
150
|
-
}, status=400)
|
151
|
-
|
152
|
-
# Generate QR data
|
153
|
-
qr_data = view._generate_qr_data(payment)
|
154
|
-
|
155
|
-
if not qr_data:
|
156
|
-
return JsonResponse({
|
157
|
-
'error': 'Unable to generate QR code data'
|
158
|
-
}, status=400)
|
159
|
-
|
160
|
-
response_data = {
|
161
|
-
'qr_data': qr_data,
|
162
|
-
'payment_address': payment.pay_address,
|
163
|
-
'payment_amount': str(payment.pay_amount) if payment.pay_amount else None,
|
164
|
-
'currency': payment.currency_code,
|
165
|
-
'provider': payment.provider,
|
166
|
-
'instructions': view._get_payment_instructions(payment),
|
167
|
-
}
|
168
|
-
|
169
|
-
return JsonResponse(response_data)
|
170
|
-
|
171
|
-
except UniversalPayment.DoesNotExist:
|
172
|
-
return JsonResponse({'error': 'Payment not found'}, status=404)
|
173
|
-
except Exception as e:
|
174
|
-
return JsonResponse({'error': str(e)}, status=500)
|
@@ -1,240 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Payment statistics and analytics views.
|
3
|
-
|
4
|
-
Provides comprehensive analytics for payment performance and trends.
|
5
|
-
"""
|
6
|
-
|
7
|
-
from django.views.generic import TemplateView
|
8
|
-
from django.utils import timezone
|
9
|
-
from django.db.models import Q
|
10
|
-
from datetime import timedelta
|
11
|
-
from .base import (
|
12
|
-
SuperuserRequiredMixin,
|
13
|
-
PaymentStatsMixin,
|
14
|
-
PaymentContextMixin,
|
15
|
-
log_view_access
|
16
|
-
)
|
17
|
-
|
18
|
-
|
19
|
-
class PaymentStatsView(
|
20
|
-
SuperuserRequiredMixin,
|
21
|
-
PaymentStatsMixin,
|
22
|
-
PaymentContextMixin,
|
23
|
-
TemplateView
|
24
|
-
):
|
25
|
-
"""Analytics and statistics view."""
|
26
|
-
|
27
|
-
template_name = 'payments/stats.html'
|
28
|
-
page_title = 'Payment Analytics'
|
29
|
-
|
30
|
-
def get_breadcrumbs(self):
|
31
|
-
return [
|
32
|
-
{'name': 'Dashboard', 'url': '/payments/admin/'},
|
33
|
-
{'name': 'Analytics', 'url': ''},
|
34
|
-
]
|
35
|
-
|
36
|
-
def get_context_data(self, **kwargs):
|
37
|
-
context = super().get_context_data(**kwargs)
|
38
|
-
|
39
|
-
# Log access for audit
|
40
|
-
log_view_access('payment_stats', self.request.user)
|
41
|
-
|
42
|
-
# Get time period from request (default to 30 days)
|
43
|
-
days = int(self.request.GET.get('days', 30))
|
44
|
-
|
45
|
-
# Calculate date ranges
|
46
|
-
now = timezone.now()
|
47
|
-
ranges = self._get_date_ranges(now, days)
|
48
|
-
|
49
|
-
# Get comprehensive statistics
|
50
|
-
stats = {
|
51
|
-
'overview': self._get_overview_stats(),
|
52
|
-
'time_periods': self._get_time_period_stats(ranges),
|
53
|
-
'providers': self._get_detailed_provider_stats(),
|
54
|
-
'status_distribution': self._get_status_distribution(),
|
55
|
-
'trends': self._get_trend_data(days),
|
56
|
-
'performance': self._get_performance_metrics(),
|
57
|
-
}
|
58
|
-
|
59
|
-
# Get common context
|
60
|
-
common_context = self.get_common_context()
|
61
|
-
|
62
|
-
context.update({
|
63
|
-
'stats': stats,
|
64
|
-
'selected_days': days,
|
65
|
-
'available_periods': [7, 30, 90, 365],
|
66
|
-
'date_ranges': ranges,
|
67
|
-
**common_context
|
68
|
-
})
|
69
|
-
|
70
|
-
return context
|
71
|
-
|
72
|
-
def _get_date_ranges(self, now, days):
|
73
|
-
"""Calculate various date ranges for statistics."""
|
74
|
-
return {
|
75
|
-
'current_period_start': now - timedelta(days=days),
|
76
|
-
'current_period_end': now,
|
77
|
-
'previous_period_start': now - timedelta(days=days * 2),
|
78
|
-
'previous_period_end': now - timedelta(days=days),
|
79
|
-
'last_7_days': now - timedelta(days=7),
|
80
|
-
'last_30_days': now - timedelta(days=30),
|
81
|
-
'last_year': now - timedelta(days=365),
|
82
|
-
}
|
83
|
-
|
84
|
-
def _get_overview_stats(self):
|
85
|
-
"""Get overall payment statistics."""
|
86
|
-
return self.get_payment_stats()
|
87
|
-
|
88
|
-
def _get_time_period_stats(self, ranges):
|
89
|
-
"""Get statistics for different time periods."""
|
90
|
-
from ...models import UniversalPayment
|
91
|
-
|
92
|
-
periods = {}
|
93
|
-
|
94
|
-
# Current period
|
95
|
-
current_qs = UniversalPayment.objects.filter(
|
96
|
-
created_at__gte=ranges['current_period_start'],
|
97
|
-
created_at__lte=ranges['current_period_end']
|
98
|
-
)
|
99
|
-
periods['current'] = self.get_payment_stats(current_qs)
|
100
|
-
|
101
|
-
# Previous period for comparison
|
102
|
-
previous_qs = UniversalPayment.objects.filter(
|
103
|
-
created_at__gte=ranges['previous_period_start'],
|
104
|
-
created_at__lte=ranges['previous_period_end']
|
105
|
-
)
|
106
|
-
periods['previous'] = self.get_payment_stats(previous_qs)
|
107
|
-
|
108
|
-
# Calculate growth rates
|
109
|
-
periods['growth'] = self._calculate_growth_rates(
|
110
|
-
periods['current'],
|
111
|
-
periods['previous']
|
112
|
-
)
|
113
|
-
|
114
|
-
# Last 7 days
|
115
|
-
last_7_qs = UniversalPayment.objects.filter(created_at__gte=ranges['last_7_days'])
|
116
|
-
periods['last_7_days'] = self.get_payment_stats(last_7_qs)
|
117
|
-
|
118
|
-
# Last 30 days
|
119
|
-
last_30_qs = UniversalPayment.objects.filter(created_at__gte=ranges['last_30_days'])
|
120
|
-
periods['last_30_days'] = self.get_payment_stats(last_30_qs)
|
121
|
-
|
122
|
-
return periods
|
123
|
-
|
124
|
-
def _get_detailed_provider_stats(self):
|
125
|
-
"""Get detailed provider statistics."""
|
126
|
-
provider_stats = self.get_provider_stats()
|
127
|
-
|
128
|
-
# Add additional metrics for each provider
|
129
|
-
for stat in provider_stats:
|
130
|
-
stat['avg_amount'] = stat['volume'] / stat['count'] if stat['count'] > 0 else 0
|
131
|
-
stat['failure_rate'] = 100 - stat['success_rate']
|
132
|
-
|
133
|
-
return provider_stats
|
134
|
-
|
135
|
-
def _get_status_distribution(self):
|
136
|
-
"""Get payment status distribution."""
|
137
|
-
from ...models import UniversalPayment
|
138
|
-
from django.db.models import Count
|
139
|
-
|
140
|
-
distribution = UniversalPayment.objects.values('status').annotate(
|
141
|
-
count=Count('id')
|
142
|
-
).order_by('-count')
|
143
|
-
|
144
|
-
total = sum(item['count'] for item in distribution)
|
145
|
-
|
146
|
-
# Add percentage
|
147
|
-
for item in distribution:
|
148
|
-
item['percentage'] = (item['count'] / total * 100) if total > 0 else 0
|
149
|
-
|
150
|
-
return distribution
|
151
|
-
|
152
|
-
def _get_trend_data(self, days):
|
153
|
-
"""Get trend data for charts."""
|
154
|
-
from ...models import UniversalPayment
|
155
|
-
from django.db.models import Count, Sum
|
156
|
-
from django.db.models.functions import TruncDate
|
157
|
-
|
158
|
-
end_date = timezone.now()
|
159
|
-
start_date = end_date - timedelta(days=days)
|
160
|
-
|
161
|
-
# Daily trends
|
162
|
-
daily_trends = UniversalPayment.objects.filter(
|
163
|
-
created_at__gte=start_date,
|
164
|
-
created_at__lte=end_date
|
165
|
-
).annotate(
|
166
|
-
date=TruncDate('created_at')
|
167
|
-
).values('date').annotate(
|
168
|
-
count=Count('id'),
|
169
|
-
volume=Sum('amount_usd'),
|
170
|
-
completed=Count('id', filter=Q(status='completed'))
|
171
|
-
).order_by('date')
|
172
|
-
|
173
|
-
# Convert to list and add calculated fields
|
174
|
-
trends = []
|
175
|
-
for item in daily_trends:
|
176
|
-
trends.append({
|
177
|
-
'date': item['date'].isoformat(),
|
178
|
-
'count': item['count'],
|
179
|
-
'volume': float(item['volume'] or 0),
|
180
|
-
'completed': item['completed'],
|
181
|
-
'success_rate': (item['completed'] / item['count'] * 100) if item['count'] > 0 else 0
|
182
|
-
})
|
183
|
-
|
184
|
-
return trends
|
185
|
-
|
186
|
-
def _get_performance_metrics(self):
|
187
|
-
"""Get performance metrics."""
|
188
|
-
from ...models import UniversalPayment, PaymentEvent
|
189
|
-
from django.db.models import Avg, Min, Max
|
190
|
-
|
191
|
-
# Average processing time (from created to completed)
|
192
|
-
completed_payments = UniversalPayment.objects.filter(
|
193
|
-
status='completed',
|
194
|
-
completed_at__isnull=False
|
195
|
-
)
|
196
|
-
|
197
|
-
processing_times = []
|
198
|
-
for payment in completed_payments[:100]: # Sample for performance
|
199
|
-
if payment.completed_at and payment.created_at:
|
200
|
-
duration = payment.completed_at - payment.created_at
|
201
|
-
processing_times.append(duration.total_seconds())
|
202
|
-
|
203
|
-
metrics = {
|
204
|
-
'avg_processing_time': sum(processing_times) / len(processing_times) if processing_times else 0,
|
205
|
-
'min_processing_time': min(processing_times) if processing_times else 0,
|
206
|
-
'max_processing_time': max(processing_times) if processing_times else 0,
|
207
|
-
}
|
208
|
-
|
209
|
-
# Convert seconds to human readable format
|
210
|
-
for key in metrics:
|
211
|
-
if metrics[key] > 0:
|
212
|
-
metrics[f"{key}_formatted"] = self._format_duration(metrics[key])
|
213
|
-
else:
|
214
|
-
metrics[f"{key}_formatted"] = "N/A"
|
215
|
-
|
216
|
-
return metrics
|
217
|
-
|
218
|
-
def _calculate_growth_rates(self, current, previous):
|
219
|
-
"""Calculate growth rates between two periods."""
|
220
|
-
growth = {}
|
221
|
-
|
222
|
-
for key in ['total_count', 'total_volume', 'completed_count']:
|
223
|
-
current_val = current.get(key, 0)
|
224
|
-
previous_val = previous.get(key, 0)
|
225
|
-
|
226
|
-
if previous_val > 0:
|
227
|
-
growth[f"{key}_rate"] = ((current_val - previous_val) / previous_val) * 100
|
228
|
-
else:
|
229
|
-
growth[f"{key}_rate"] = 100 if current_val > 0 else 0
|
230
|
-
|
231
|
-
return growth
|
232
|
-
|
233
|
-
def _format_duration(self, seconds):
|
234
|
-
"""Format duration in seconds to human readable format."""
|
235
|
-
if seconds < 60:
|
236
|
-
return f"{seconds:.1f}s"
|
237
|
-
elif seconds < 3600:
|
238
|
-
return f"{seconds/60:.1f}m"
|
239
|
-
else:
|
240
|
-
return f"{seconds/3600:.1f}h"
|