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
@@ -9,9 +9,9 @@ from django.utils.decorators import method_decorator
|
|
9
9
|
from django.db.models import Q, Count, Sum
|
10
10
|
from django.utils import timezone
|
11
11
|
from datetime import timedelta
|
12
|
-
import
|
12
|
+
from django_cfg.modules.django_logger import get_logger
|
13
13
|
|
14
|
-
logger =
|
14
|
+
logger = get_logger("view_base")
|
15
15
|
|
16
16
|
|
17
17
|
def superuser_required(function=None):
|
@@ -103,13 +103,15 @@ class PaymentStatsMixin:
|
|
103
103
|
total_volume=Sum('amount_usd')
|
104
104
|
)
|
105
105
|
|
106
|
-
# Convert
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
stats['
|
111
|
-
|
112
|
-
|
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
|
+
}
|
113
115
|
|
114
116
|
def get_provider_stats(self, queryset=None):
|
115
117
|
"""Get provider-specific statistics."""
|
@@ -123,7 +125,8 @@ class PaymentStatsMixin:
|
|
123
125
|
completed_count=Count('id', filter=Q(status='completed')),
|
124
126
|
).order_by('-volume')
|
125
127
|
|
126
|
-
# Calculate success rate
|
128
|
+
# Calculate success rate and convert to list of dicts
|
129
|
+
stats_list = []
|
127
130
|
for stat in provider_stats:
|
128
131
|
if stat['count'] > 0:
|
129
132
|
stat['success_rate'] = (stat['completed_count'] / stat['count']) * 100
|
@@ -135,8 +138,10 @@ class PaymentStatsMixin:
|
|
135
138
|
stat['volume'] = float(stat['volume'])
|
136
139
|
else:
|
137
140
|
stat['volume'] = 0.0
|
141
|
+
|
142
|
+
stats_list.append(stat)
|
138
143
|
|
139
|
-
return
|
144
|
+
return stats_list
|
140
145
|
|
141
146
|
def get_time_range_stats(self, days=30):
|
142
147
|
"""Get statistics for a specific time range."""
|
@@ -160,8 +165,11 @@ class PaymentContextMixin:
|
|
160
165
|
"""Get common context data used across multiple views."""
|
161
166
|
from ...models import PaymentEvent
|
162
167
|
|
163
|
-
# Get recent events for activity feed
|
164
|
-
|
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 = []
|
165
173
|
|
166
174
|
return {
|
167
175
|
'recent_events': recent_events,
|
@@ -41,7 +41,7 @@ class PaymentDetailView(
|
|
41
41
|
log_view_access('payment_detail', self.request.user, payment_id=payment.id)
|
42
42
|
|
43
43
|
# Get payment events for this payment
|
44
|
-
events = PaymentEvent.objects.filter(
|
44
|
+
events = PaymentEvent.objects.filter(payment_id=payment.id).order_by('-created_at')
|
45
45
|
|
46
46
|
# Get related payments (same user, similar amount range)
|
47
47
|
related_payments = UniversalPayment.objects.filter(
|
@@ -4,6 +4,7 @@ Payment management views.
|
|
4
4
|
Provides list, create, and management functionality for payments.
|
5
5
|
"""
|
6
6
|
|
7
|
+
import json
|
7
8
|
from django.views.generic import TemplateView, ListView
|
8
9
|
from .base import (
|
9
10
|
SuperuserRequiredMixin,
|
@@ -11,7 +12,8 @@ from .base import (
|
|
11
12
|
PaymentContextMixin,
|
12
13
|
log_view_access
|
13
14
|
)
|
14
|
-
from ...models import UniversalPayment
|
15
|
+
from ...models import UniversalPayment, Currency, PaymentProvider
|
16
|
+
from ...services.providers.registry import ProviderRegistry
|
15
17
|
|
16
18
|
|
17
19
|
class PaymentCreateView(
|
@@ -46,9 +48,20 @@ class PaymentCreateView(
|
|
46
48
|
# Get common context
|
47
49
|
common_context = self.get_common_context()
|
48
50
|
|
51
|
+
# Provider enum with defaults (serialize to JSON for JavaScript)
|
52
|
+
provider_defaults = {
|
53
|
+
PaymentProvider.NOWPAYMENTS.value: 'USDTTRC20',
|
54
|
+
PaymentProvider.CRYPTAPI.value: 'BTC',
|
55
|
+
PaymentProvider.CRYPTOMUS.value: 'USDTTRC20',
|
56
|
+
PaymentProvider.STRIPE.value: 'USD'
|
57
|
+
}
|
58
|
+
provider_defaults_json = json.dumps(provider_defaults)
|
59
|
+
|
49
60
|
context.update({
|
50
61
|
'providers': providers,
|
51
62
|
'currencies': currencies,
|
63
|
+
'provider_enum': PaymentProvider,
|
64
|
+
'provider_defaults_json': provider_defaults_json,
|
52
65
|
'default_amount': 10.0, # Default test amount
|
53
66
|
**common_context
|
54
67
|
})
|
@@ -56,44 +69,27 @@ class PaymentCreateView(
|
|
56
69
|
return context
|
57
70
|
|
58
71
|
def _get_available_providers(self):
|
59
|
-
"""Get list of available payment providers."""
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
]
|
72
|
+
"""Get list of available payment providers from registry."""
|
73
|
+
registry = ProviderRegistry()
|
74
|
+
provider_names = registry.list_providers()
|
75
|
+
|
76
|
+
providers = []
|
77
|
+
for provider_name in provider_names:
|
78
|
+
provider_instance = registry.get_provider(provider_name)
|
79
|
+
providers.append({
|
80
|
+
'name': provider_name,
|
81
|
+
'display_name': provider_name.title(),
|
82
|
+
'is_crypto': provider_name in ['nowpayments', 'cryptapi', 'cryptomus'],
|
83
|
+
'description': getattr(provider_instance.__class__, '__doc__', '') if provider_instance else '',
|
84
|
+
})
|
85
|
+
return providers
|
79
86
|
|
80
87
|
def _get_available_currencies(self):
|
81
|
-
"""Get list of available currencies."""
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
]
|
88
|
+
"""Get list of available currencies from database."""
|
89
|
+
# Get all active currencies - both fiat and crypto
|
90
|
+
return Currency.objects.all().order_by('currency_type', 'code')
|
91
|
+
|
92
|
+
|
97
93
|
|
98
94
|
|
99
95
|
class PaymentListView(
|
@@ -147,9 +143,7 @@ class PaymentListView(
|
|
147
143
|
|
148
144
|
def _get_filter_options(self):
|
149
145
|
"""Get available options for filter dropdowns."""
|
150
|
-
|
151
|
-
from django.db.models.functions import Concat
|
152
|
-
|
146
|
+
|
153
147
|
# Get unique statuses
|
154
148
|
statuses = UniversalPayment.objects.values_list('status', flat=True).distinct()
|
155
149
|
status_choices = [(status, status.title()) for status in statuses if status]
|
@@ -207,11 +207,15 @@ class PaymentStatsView(
|
|
207
207
|
}
|
208
208
|
|
209
209
|
# Convert seconds to human readable format
|
210
|
-
|
211
|
-
|
212
|
-
|
210
|
+
formatted_metrics = {}
|
211
|
+
for key, value in metrics.items():
|
212
|
+
if value > 0:
|
213
|
+
formatted_metrics[f"{key}_formatted"] = self._format_duration(value)
|
213
214
|
else:
|
214
|
-
|
215
|
+
formatted_metrics[f"{key}_formatted"] = "N/A"
|
216
|
+
|
217
|
+
# Add formatted metrics to the original metrics
|
218
|
+
metrics.update(formatted_metrics)
|
215
219
|
|
216
220
|
return metrics
|
217
221
|
|
@@ -3,7 +3,7 @@ Webhook processing views with signature validation.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import json
|
6
|
-
import
|
6
|
+
from django_cfg.modules.django_logger import get_logger
|
7
7
|
from typing import Dict, Any
|
8
8
|
|
9
9
|
from django.http import JsonResponse, HttpResponse
|
@@ -20,7 +20,7 @@ from ..tasks.webhook_processing import process_webhook_with_fallback
|
|
20
20
|
from ..services.security.webhook_validator import webhook_validator
|
21
21
|
from ..services.security.error_handler import error_handler, SecurityError, ValidationError
|
22
22
|
|
23
|
-
logger =
|
23
|
+
logger = get_logger("webhook_views")
|
24
24
|
|
25
25
|
|
26
26
|
@csrf_exempt
|
@@ -19,7 +19,7 @@ from .views import (
|
|
19
19
|
UserAPIKeyViewSet, APIKeyViewSet,
|
20
20
|
|
21
21
|
# Currency ViewSets
|
22
|
-
CurrencyViewSet,
|
22
|
+
CurrencyViewSet, NetworkViewSet, ProviderCurrencyViewSet,
|
23
23
|
|
24
24
|
# Tariff ViewSets
|
25
25
|
TariffViewSet, TariffEndpointGroupViewSet,
|
@@ -49,7 +49,8 @@ class PaymentSystemRouter:
|
|
49
49
|
|
50
50
|
# Currency and pricing
|
51
51
|
self.router.register(r'currencies', CurrencyViewSet, basename='currency')
|
52
|
-
self.router.register(r'
|
52
|
+
self.router.register(r'networks', NetworkViewSet, basename='network')
|
53
|
+
self.router.register(r'provider-currencies', ProviderCurrencyViewSet, basename='provider-currency')
|
53
54
|
self.router.register(r'tariffs', TariffViewSet, basename='tariff')
|
54
55
|
self.router.register(r'tariff-groups', TariffEndpointGroupViewSet, basename='tariff-group')
|
55
56
|
|
django_cfg/apps/tasks/urls.py
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
"""
|
2
|
+
URLs for Django CFG Tasks app.
|
3
|
+
|
4
|
+
Provides RESTful endpoints for task queue management and monitoring using ViewSets and routers.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from django.urls import path
|
8
|
+
from . import views
|
9
|
+
|
10
|
+
urlpatterns = [
|
11
|
+
|
12
|
+
# Dashboard view
|
13
|
+
path('dashboard/', views.dashboard_view, name='dashboard'),
|
14
|
+
]
|
django_cfg/apps/urls.py
CHANGED
@@ -46,14 +46,14 @@ def get_django_cfg_urlpatterns() -> List[URLPattern]:
|
|
46
46
|
|
47
47
|
# Tasks app - enabled when knowbase or agents are enabled
|
48
48
|
if base_module.should_enable_tasks():
|
49
|
-
patterns.append(path('
|
49
|
+
patterns.append(path('admin/django_cfg_tasks/admin/', include('django_cfg.apps.tasks.urls_admin')))
|
50
50
|
|
51
51
|
# Maintenance app - multi-site maintenance mode with Cloudflare
|
52
|
-
if base_module.is_maintenance_enabled():
|
53
|
-
|
52
|
+
# if base_module.is_maintenance_enabled():
|
53
|
+
# patterns.append(path('admin/django_cfg_maintenance/', include('django_cfg.apps.maintenance.urls_admin')))
|
54
54
|
|
55
55
|
if base_module.is_payments_enabled():
|
56
|
-
patterns.append(path('
|
56
|
+
patterns.append(path('admin/django_cfg_payments/admin/', include('django_cfg.apps.payments.urls_admin')))
|
57
57
|
|
58
58
|
except Exception:
|
59
59
|
# Fallback: include all URLs if config is not available
|
django_cfg/core/config.py
CHANGED
@@ -11,6 +11,7 @@ Following CRITICAL_REQUIREMENTS.md:
|
|
11
11
|
from typing import Dict, List, Optional, Any, Union
|
12
12
|
from pathlib import Path
|
13
13
|
from pydantic import BaseModel, Field, field_validator, model_validator, PrivateAttr
|
14
|
+
from enum import Enum
|
14
15
|
import os
|
15
16
|
from pathlib import Path
|
16
17
|
from urllib.parse import urlparse
|
@@ -66,6 +67,18 @@ DEFAULT_APPS = [
|
|
66
67
|
]
|
67
68
|
|
68
69
|
|
70
|
+
class EnvironmentMode(str, Enum):
|
71
|
+
"""Environment mode enumeration."""
|
72
|
+
DEVELOPMENT = "development"
|
73
|
+
PRODUCTION = "production"
|
74
|
+
TEST = "test"
|
75
|
+
|
76
|
+
@classmethod
|
77
|
+
def from_debug(cls, debug: bool) -> "EnvironmentMode":
|
78
|
+
"""Get environment mode from debug flag."""
|
79
|
+
return cls.DEVELOPMENT if debug else cls.PRODUCTION
|
80
|
+
|
81
|
+
|
69
82
|
class DjangoConfig(BaseModel):
|
70
83
|
"""
|
71
84
|
Base configuration class for Django projects.
|
@@ -105,6 +118,12 @@ class DjangoConfig(BaseModel):
|
|
105
118
|
"str_strip_whitespace": True,
|
106
119
|
}
|
107
120
|
|
121
|
+
# === Environment Configuration ===
|
122
|
+
env_mode: EnvironmentMode = Field(
|
123
|
+
default=EnvironmentMode.PRODUCTION,
|
124
|
+
description="Environment mode: development, production, or test",
|
125
|
+
)
|
126
|
+
|
108
127
|
# === Project Information ===
|
109
128
|
project_name: str = Field(
|
110
129
|
...,
|
@@ -401,6 +420,22 @@ class DjangoConfig(BaseModel):
|
|
401
420
|
|
402
421
|
return self
|
403
422
|
|
423
|
+
# === Environment Mode Properties ===
|
424
|
+
@property
|
425
|
+
def is_development(self) -> bool:
|
426
|
+
"""Check if running in development mode."""
|
427
|
+
return self.env_mode == EnvironmentMode.DEVELOPMENT
|
428
|
+
|
429
|
+
@property
|
430
|
+
def is_production(self) -> bool:
|
431
|
+
"""Check if running in production mode."""
|
432
|
+
return self.env_mode == EnvironmentMode.PRODUCTION
|
433
|
+
|
434
|
+
@property
|
435
|
+
def is_test(self) -> bool:
|
436
|
+
"""Check if running in test mode."""
|
437
|
+
return self.env_mode == EnvironmentMode.TEST
|
438
|
+
|
404
439
|
def _detect_environment(self) -> None:
|
405
440
|
"""Detect current environment from various sources."""
|
406
441
|
from django_cfg.core.environment import EnvironmentDetector
|
django_cfg/models/payments.py
CHANGED
@@ -457,7 +457,6 @@ class PaymentsConfig(BaseModel):
|
|
457
457
|
# Helper function for easy provider configuration
|
458
458
|
def create_nowpayments_config(
|
459
459
|
api_key: str,
|
460
|
-
sandbox: bool = True,
|
461
460
|
ipn_secret: Optional[str] = None,
|
462
461
|
**kwargs
|
463
462
|
) -> NowPaymentsConfig:
|
@@ -465,7 +464,6 @@ def create_nowpayments_config(
|
|
465
464
|
return NowPaymentsConfig(
|
466
465
|
name="nowpayments",
|
467
466
|
api_key=SecretStr(api_key),
|
468
|
-
sandbox=sandbox,
|
469
467
|
ipn_secret=SecretStr(ipn_secret) if ipn_secret else None,
|
470
468
|
**kwargs
|
471
469
|
)
|
@@ -489,16 +487,14 @@ def create_stripe_config(
|
|
489
487
|
api_key: str,
|
490
488
|
publishable_key: Optional[str] = None,
|
491
489
|
webhook_endpoint_secret: Optional[str] = None,
|
492
|
-
sandbox: bool = True,
|
493
490
|
**kwargs
|
494
491
|
) -> StripeConfig:
|
495
|
-
"""Helper to create Stripe configuration."""
|
492
|
+
"""Helper to create Stripe configuration with automatic sandbox detection."""
|
496
493
|
return StripeConfig(
|
497
494
|
name="stripe",
|
498
495
|
api_key=SecretStr(api_key),
|
499
496
|
publishable_key=publishable_key,
|
500
497
|
webhook_endpoint_secret=SecretStr(webhook_endpoint_secret) if webhook_endpoint_secret else None,
|
501
|
-
sandbox=sandbox,
|
502
498
|
**kwargs
|
503
499
|
)
|
504
500
|
|
@@ -506,13 +502,12 @@ def create_stripe_config(
|
|
506
502
|
def create_cryptomus_config(
|
507
503
|
api_key: str,
|
508
504
|
merchant_uuid: str,
|
509
|
-
sandbox: bool = True,
|
510
505
|
callback_url: Optional[str] = None,
|
511
506
|
success_url: Optional[str] = None,
|
512
507
|
fail_url: Optional[str] = None,
|
513
508
|
**kwargs
|
514
509
|
):
|
515
|
-
"""Helper to create Cryptomus configuration."""
|
510
|
+
"""Helper to create Cryptomus configuration with automatic sandbox detection."""
|
516
511
|
# Import here to avoid circular imports
|
517
512
|
from django_cfg.apps.payments.config.providers import CryptomusConfig
|
518
513
|
|
@@ -520,7 +515,6 @@ def create_cryptomus_config(
|
|
520
515
|
name="cryptomus",
|
521
516
|
api_key=SecretStr(api_key),
|
522
517
|
merchant_uuid=merchant_uuid,
|
523
|
-
sandbox=sandbox,
|
524
518
|
callback_url=callback_url,
|
525
519
|
success_url=success_url,
|
526
520
|
fail_url=fail_url,
|
@@ -11,9 +11,6 @@ from .core import (
|
|
11
11
|
Rate,
|
12
12
|
ConversionRequest,
|
13
13
|
ConversionResult,
|
14
|
-
SupportedCurrencies,
|
15
|
-
YFinanceCurrencies,
|
16
|
-
CoinGeckoCurrencies,
|
17
14
|
CurrencyError,
|
18
15
|
CurrencyNotFoundError,
|
19
16
|
RateFetchError,
|
@@ -25,7 +22,7 @@ from .core import (
|
|
25
22
|
from .utils import CacheManager
|
26
23
|
|
27
24
|
# Clients
|
28
|
-
from .clients import
|
25
|
+
from .clients import YahooFinanceClient, CoinPaprikaClient
|
29
26
|
|
30
27
|
# Database tools
|
31
28
|
from .database import (
|
@@ -35,6 +32,17 @@ from .database import (
|
|
35
32
|
load_currencies_to_database_format
|
36
33
|
)
|
37
34
|
|
35
|
+
# Shared global converter instance for caching efficiency
|
36
|
+
_global_converter = None
|
37
|
+
|
38
|
+
def _get_converter() -> CurrencyConverter:
|
39
|
+
"""Get or create shared converter instance."""
|
40
|
+
global _global_converter
|
41
|
+
if _global_converter is None:
|
42
|
+
_global_converter = CurrencyConverter(cache_ttl=3600) # 1 hour cache
|
43
|
+
return _global_converter
|
44
|
+
|
45
|
+
|
38
46
|
# Simple public API
|
39
47
|
def convert_currency(amount: float, from_currency: str, to_currency: str) -> float:
|
40
48
|
"""
|
@@ -48,7 +56,7 @@ def convert_currency(amount: float, from_currency: str, to_currency: str) -> flo
|
|
48
56
|
Returns:
|
49
57
|
Converted amount
|
50
58
|
"""
|
51
|
-
converter =
|
59
|
+
converter = _get_converter()
|
52
60
|
result = converter.convert(amount, from_currency, to_currency)
|
53
61
|
return result.result
|
54
62
|
|
@@ -64,7 +72,7 @@ def get_exchange_rate(base: str, quote: str) -> float:
|
|
64
72
|
Returns:
|
65
73
|
Exchange rate
|
66
74
|
"""
|
67
|
-
converter =
|
75
|
+
converter = _get_converter()
|
68
76
|
result = converter.convert(1.0, base, quote)
|
69
77
|
return result.rate.rate
|
70
78
|
|
@@ -75,9 +83,6 @@ __all__ = [
|
|
75
83
|
"Rate",
|
76
84
|
"ConversionRequest",
|
77
85
|
"ConversionResult",
|
78
|
-
"SupportedCurrencies",
|
79
|
-
"YFinanceCurrencies",
|
80
|
-
"CoinGeckoCurrencies",
|
81
86
|
|
82
87
|
# Exceptions
|
83
88
|
"CurrencyError",
|
@@ -90,8 +95,8 @@ __all__ = [
|
|
90
95
|
"CacheManager",
|
91
96
|
|
92
97
|
# Clients
|
93
|
-
"
|
94
|
-
"
|
98
|
+
"YahooFinanceClient",
|
99
|
+
"CoinPaprikaClient",
|
95
100
|
|
96
101
|
# Database tools
|
97
102
|
"CurrencyDatabaseLoader",
|
@@ -2,10 +2,10 @@
|
|
2
2
|
Currency data clients for fetching rates from external APIs.
|
3
3
|
"""
|
4
4
|
|
5
|
-
from .
|
6
|
-
from .
|
5
|
+
from .yahoo_client import YahooFinanceClient
|
6
|
+
from .coinpaprika_client import CoinPaprikaClient
|
7
7
|
|
8
8
|
__all__ = [
|
9
|
-
'
|
10
|
-
'
|
9
|
+
'YahooFinanceClient',
|
10
|
+
'CoinPaprikaClient'
|
11
11
|
]
|