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,103 +1,452 @@
|
|
1
1
|
"""
|
2
|
-
Provider registry for
|
2
|
+
Provider registry for the Universal Payment System v2.0.
|
3
3
|
|
4
|
-
|
4
|
+
Centralized management of payment providers with health monitoring and fallbacks.
|
5
5
|
"""
|
6
6
|
|
7
|
-
import
|
8
|
-
from typing import Optional,
|
9
|
-
|
10
|
-
|
7
|
+
from enum import Enum
|
8
|
+
from typing import Dict, List, Optional, Type, Any
|
9
|
+
from django_cfg.modules.django_logger import get_logger
|
10
|
+
# ConfigService removed - using direct Constance access
|
11
|
+
from ..types import ServiceOperationResult
|
12
|
+
from .base import BaseProvider, ProviderConfig
|
11
13
|
from .nowpayments import NowPaymentsProvider, NowPaymentsConfig
|
12
|
-
from .cryptapi import CryptAPIProvider, CryptAPIConfig
|
13
|
-
from .cryptomus import CryptomusProvider, CryptomusConfig
|
14
14
|
|
15
|
-
logger =
|
15
|
+
logger = get_logger("provider_registry")
|
16
|
+
|
17
|
+
|
18
|
+
# Provider enums
|
19
|
+
class ProviderEnum(Enum):
|
20
|
+
NOWPAYMENTS = "nowpayments"
|
21
|
+
CRYPTAPI = "cryptapi"
|
22
|
+
STRIPE = "stripe"
|
23
|
+
CRYPTOMUS = "cryptomus"
|
16
24
|
|
25
|
+
@classmethod
|
26
|
+
def get_crypto_providers(cls):
|
27
|
+
"""Get list of crypto provider values."""
|
28
|
+
return [cls.NOWPAYMENTS.value, cls.CRYPTAPI.value, cls.CRYPTOMUS.value]
|
29
|
+
|
30
|
+
@classmethod
|
31
|
+
def get_fiat_providers(cls):
|
32
|
+
"""Get list of fiat provider values."""
|
33
|
+
return [cls.STRIPE.value]
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
def get_priority_order(cls):
|
37
|
+
"""Get list of provider values in priority order."""
|
38
|
+
return [
|
39
|
+
cls.NOWPAYMENTS.value,
|
40
|
+
cls.CRYPTAPI.value,
|
41
|
+
cls.STRIPE.value,
|
42
|
+
cls.CRYPTOMUS.value
|
43
|
+
]
|
17
44
|
|
18
45
|
class ProviderRegistry:
|
19
|
-
"""
|
46
|
+
"""
|
47
|
+
Registry for managing payment providers.
|
48
|
+
|
49
|
+
Provides centralized access to providers with health monitoring,
|
50
|
+
configuration management, and fallback mechanisms.
|
51
|
+
"""
|
20
52
|
|
21
53
|
def __init__(self):
|
22
|
-
"""Initialize registry
|
23
|
-
|
24
|
-
|
25
|
-
|
54
|
+
"""Initialize provider registry."""
|
55
|
+
# Use PaymentConfigService for configuration
|
56
|
+
from ...config.constance import get_payment_config_service
|
57
|
+
|
58
|
+
self.config_service = get_payment_config_service()
|
59
|
+
self._providers: Dict[str, BaseProvider] = {}
|
60
|
+
|
61
|
+
self._provider_classes: Dict[str, Type[BaseProvider]] = {
|
62
|
+
ProviderEnum.NOWPAYMENTS.value: NowPaymentsProvider,
|
63
|
+
}
|
64
|
+
self._provider_configs: Dict[str, Type[ProviderConfig]] = {
|
65
|
+
ProviderEnum.NOWPAYMENTS.value: NowPaymentsConfig,
|
66
|
+
}
|
67
|
+
|
68
|
+
self._health_status: Dict[str, bool] = {}
|
69
|
+
self._initialized = False
|
26
70
|
|
27
|
-
def
|
28
|
-
"""
|
71
|
+
def initialize(self) -> ServiceOperationResult:
|
72
|
+
"""
|
73
|
+
Initialize all configured providers.
|
74
|
+
|
75
|
+
Returns:
|
76
|
+
ServiceOperationResult: Initialization result
|
77
|
+
"""
|
29
78
|
try:
|
30
|
-
|
31
|
-
|
79
|
+
logger.info("Initializing provider registry")
|
80
|
+
|
81
|
+
# Get all provider configurations
|
82
|
+
try:
|
83
|
+
provider_configs = self.config_service.get_all_provider_configs()
|
84
|
+
except Exception as e:
|
85
|
+
return ServiceOperationResult(
|
86
|
+
success=False,
|
87
|
+
message=f"Failed to get provider configurations: {e}",
|
88
|
+
error_code="config_failed"
|
89
|
+
)
|
90
|
+
|
91
|
+
# provider_configs is already a dict from get_all_provider_configs()
|
92
|
+
initialized_count = 0
|
93
|
+
failed_providers = []
|
32
94
|
|
33
|
-
|
34
|
-
for provider_name,
|
35
|
-
if
|
36
|
-
|
95
|
+
# Initialize each configured provider
|
96
|
+
for provider_name, config_data in provider_configs.items():
|
97
|
+
if not config_data.get('enabled', False):
|
98
|
+
logger.debug(f"Skipping disabled provider: {provider_name}")
|
99
|
+
continue
|
100
|
+
|
101
|
+
try:
|
102
|
+
provider = self._create_provider(provider_name, config_data)
|
103
|
+
if provider:
|
104
|
+
self._providers[provider_name] = provider
|
105
|
+
initialized_count += 1
|
106
|
+
logger.info(f"Initialized provider: {provider_name}")
|
107
|
+
else:
|
108
|
+
failed_providers.append(provider_name)
|
109
|
+
|
110
|
+
except Exception as e:
|
111
|
+
logger.error(f"Failed to initialize provider {provider_name}: {e}")
|
112
|
+
failed_providers.append(provider_name)
|
113
|
+
|
114
|
+
self._initialized = True
|
115
|
+
|
116
|
+
# Perform initial health check
|
117
|
+
self.health_check_all()
|
118
|
+
|
119
|
+
result_message = f"Initialized {initialized_count} providers"
|
120
|
+
if failed_providers:
|
121
|
+
result_message += f", failed: {', '.join(failed_providers)}"
|
122
|
+
|
123
|
+
return ServiceOperationResult(
|
124
|
+
success=True,
|
125
|
+
message=result_message,
|
126
|
+
data={
|
127
|
+
'initialized_providers': list(self._providers.keys()),
|
128
|
+
'failed_providers': failed_providers,
|
129
|
+
'total_configured': len(provider_configs),
|
130
|
+
'initialized_count': initialized_count
|
131
|
+
}
|
132
|
+
)
|
133
|
+
|
134
|
+
except Exception as e:
|
135
|
+
logger.error(f"Provider registry initialization failed: {e}")
|
136
|
+
return ServiceOperationResult(
|
137
|
+
success=False,
|
138
|
+
message=f"Registry initialization failed: {e}",
|
139
|
+
error_code="initialization_failed"
|
140
|
+
)
|
141
|
+
|
142
|
+
def get_provider(self, provider_name: str) -> Optional[BaseProvider]:
|
143
|
+
"""
|
144
|
+
Get provider by name.
|
145
|
+
|
146
|
+
Args:
|
147
|
+
provider_name: Provider name
|
148
|
+
|
149
|
+
Returns:
|
150
|
+
Optional[BaseProvider]: Provider instance or None
|
151
|
+
"""
|
152
|
+
if not self._initialized:
|
153
|
+
logger.warning("Registry not initialized, initializing now")
|
154
|
+
self.initialize()
|
155
|
+
|
156
|
+
provider = self._providers.get(provider_name)
|
157
|
+
if not provider:
|
158
|
+
logger.warning(f"Provider not found: {provider_name}")
|
159
|
+
return None
|
160
|
+
|
161
|
+
# Check if provider is healthy
|
162
|
+
if not self._health_status.get(provider_name, False):
|
163
|
+
logger.warning(f"Provider {provider_name} is unhealthy")
|
164
|
+
|
165
|
+
return provider
|
166
|
+
|
167
|
+
def get_available_providers(self) -> List[str]:
|
168
|
+
"""
|
169
|
+
Get list of available (healthy) providers.
|
170
|
+
|
171
|
+
Returns:
|
172
|
+
List[str]: List of available provider names
|
173
|
+
"""
|
174
|
+
if not self._initialized:
|
175
|
+
self.initialize()
|
176
|
+
|
177
|
+
return [
|
178
|
+
name for name, provider in self._providers.items()
|
179
|
+
if self._health_status.get(name, False)
|
180
|
+
]
|
181
|
+
|
182
|
+
def get_primary_provider(self) -> Optional[BaseProvider]:
|
183
|
+
"""
|
184
|
+
Get primary (preferred) provider.
|
185
|
+
|
186
|
+
Returns:
|
187
|
+
Optional[BaseProvider]: Primary provider or None
|
188
|
+
"""
|
189
|
+
available_providers = self.get_available_providers()
|
190
|
+
|
191
|
+
if not available_providers:
|
192
|
+
logger.warning("No healthy providers available")
|
193
|
+
return None
|
194
|
+
|
195
|
+
# Priority order: nowpayments first, then others
|
196
|
+
priority_order = ProviderEnum.get_priority_order()
|
197
|
+
|
198
|
+
for provider_name in priority_order:
|
199
|
+
if provider_name in available_providers:
|
200
|
+
return self._providers[provider_name]
|
201
|
+
|
202
|
+
# Fallback to first available
|
203
|
+
return self._providers[available_providers[0]]
|
204
|
+
|
205
|
+
def get_provider_for_currency(self, currency_code: str) -> Optional[BaseProvider]:
|
206
|
+
"""
|
207
|
+
Get best provider for specific currency.
|
208
|
+
|
209
|
+
Args:
|
210
|
+
currency_code: Currency code
|
211
|
+
|
212
|
+
Returns:
|
213
|
+
Optional[BaseProvider]: Best provider for currency or None
|
214
|
+
"""
|
215
|
+
available_providers = self.get_available_providers()
|
216
|
+
|
217
|
+
# Find providers that support the currency
|
218
|
+
supporting_providers = []
|
219
|
+
for provider_name in available_providers:
|
220
|
+
provider = self._providers[provider_name]
|
221
|
+
if currency_code in provider.config.supported_currencies:
|
222
|
+
supporting_providers.append(provider)
|
223
|
+
|
224
|
+
if not supporting_providers:
|
225
|
+
logger.warning(f"No providers support currency: {currency_code}")
|
226
|
+
return None
|
227
|
+
|
228
|
+
# Return first supporting provider (could be enhanced with more logic)
|
229
|
+
return supporting_providers[0]
|
230
|
+
|
231
|
+
def health_check_all(self) -> ServiceOperationResult:
|
232
|
+
"""
|
233
|
+
Perform health check on all providers.
|
234
|
+
|
235
|
+
Returns:
|
236
|
+
ServiceOperationResult: Overall health status
|
237
|
+
"""
|
238
|
+
try:
|
239
|
+
logger.debug("Performing health check on all providers")
|
240
|
+
|
241
|
+
if not self._providers:
|
242
|
+
return ServiceOperationResult(
|
243
|
+
success=False,
|
244
|
+
message="No providers initialized",
|
245
|
+
error_code="no_providers"
|
246
|
+
)
|
247
|
+
|
248
|
+
health_results = {}
|
249
|
+
healthy_count = 0
|
250
|
+
|
251
|
+
for provider_name, provider in self._providers.items():
|
252
|
+
try:
|
253
|
+
health_result = provider.health_check()
|
254
|
+
is_healthy = health_result.success
|
255
|
+
|
256
|
+
self._health_status[provider_name] = is_healthy
|
257
|
+
health_results[provider_name] = {
|
258
|
+
'healthy': is_healthy,
|
259
|
+
'message': health_result.message,
|
260
|
+
'data': health_result.data
|
261
|
+
}
|
262
|
+
|
263
|
+
if is_healthy:
|
264
|
+
healthy_count += 1
|
37
265
|
|
266
|
+
logger.debug(f"Provider {provider_name} health: {is_healthy}")
|
267
|
+
|
268
|
+
except Exception as e:
|
269
|
+
logger.error(f"Health check failed for {provider_name}: {e}")
|
270
|
+
self._health_status[provider_name] = False
|
271
|
+
health_results[provider_name] = {
|
272
|
+
'healthy': False,
|
273
|
+
'message': f"Health check error: {e}",
|
274
|
+
'data': {}
|
275
|
+
}
|
276
|
+
|
277
|
+
overall_healthy = healthy_count > 0
|
278
|
+
|
279
|
+
return ServiceOperationResult(
|
280
|
+
success=overall_healthy,
|
281
|
+
message=f"{healthy_count}/{len(self._providers)} providers healthy",
|
282
|
+
data={
|
283
|
+
'total_providers': len(self._providers),
|
284
|
+
'healthy_providers': healthy_count,
|
285
|
+
'unhealthy_providers': len(self._providers) - healthy_count,
|
286
|
+
'provider_health': health_results,
|
287
|
+
'available_providers': self.get_available_providers()
|
288
|
+
}
|
289
|
+
)
|
290
|
+
|
291
|
+
except Exception as e:
|
292
|
+
logger.error(f"Health check failed: {e}")
|
293
|
+
return ServiceOperationResult(
|
294
|
+
success=False,
|
295
|
+
message=f"Health check error: {e}",
|
296
|
+
error_code="health_check_error"
|
297
|
+
)
|
298
|
+
|
299
|
+
def refresh_configurations(self) -> ServiceOperationResult:
|
300
|
+
"""
|
301
|
+
Refresh provider configurations from config service.
|
302
|
+
|
303
|
+
Returns:
|
304
|
+
ServiceOperationResult: Refresh result
|
305
|
+
"""
|
306
|
+
try:
|
307
|
+
logger.info("Refreshing provider configurations")
|
308
|
+
|
309
|
+
# Clear current providers
|
310
|
+
self._providers.clear()
|
311
|
+
self._health_status.clear()
|
312
|
+
self._initialized = False
|
313
|
+
|
314
|
+
# Refresh config service
|
315
|
+
self.config_service.refresh_configuration()
|
316
|
+
|
317
|
+
# Re-initialize providers
|
318
|
+
return self.initialize()
|
319
|
+
|
320
|
+
except Exception as e:
|
321
|
+
logger.error(f"Configuration refresh failed: {e}")
|
322
|
+
return ServiceOperationResult(
|
323
|
+
success=False,
|
324
|
+
message=f"Configuration refresh failed: {e}",
|
325
|
+
error_code="refresh_failed"
|
326
|
+
)
|
327
|
+
|
328
|
+
def get_registry_stats(self) -> ServiceOperationResult:
|
329
|
+
"""
|
330
|
+
Get registry statistics.
|
331
|
+
|
332
|
+
Returns:
|
333
|
+
ServiceOperationResult: Registry statistics
|
334
|
+
"""
|
335
|
+
try:
|
336
|
+
stats = {
|
337
|
+
'initialized': self._initialized,
|
338
|
+
'total_providers': len(self._providers),
|
339
|
+
'healthy_providers': sum(1 for h in self._health_status.values() if h),
|
340
|
+
'available_provider_classes': list(self._provider_classes.keys()),
|
341
|
+
'configured_providers': list(self._providers.keys()),
|
342
|
+
'health_status': dict(self._health_status)
|
343
|
+
}
|
344
|
+
|
345
|
+
return ServiceOperationResult(
|
346
|
+
success=True,
|
347
|
+
message="Registry statistics",
|
348
|
+
data=stats
|
349
|
+
)
|
350
|
+
|
38
351
|
except Exception as e:
|
39
|
-
logger.
|
40
|
-
|
352
|
+
logger.error(f"Failed to get registry stats: {e}")
|
353
|
+
return ServiceOperationResult(
|
354
|
+
success=False,
|
355
|
+
message=f"Stats error: {e}",
|
356
|
+
error_code="stats_error"
|
357
|
+
)
|
41
358
|
|
42
|
-
def _create_provider(self,
|
43
|
-
"""
|
359
|
+
def _create_provider(self, provider_name: str, config_data: Dict[str, Any]) -> Optional[BaseProvider]:
|
360
|
+
"""
|
361
|
+
Create provider instance from configuration.
|
362
|
+
|
363
|
+
Args:
|
364
|
+
provider_name: Provider name
|
365
|
+
config_data: Provider configuration data
|
366
|
+
|
367
|
+
Returns:
|
368
|
+
Optional[BaseProvider]: Provider instance or None
|
369
|
+
"""
|
44
370
|
try:
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
config = CryptAPIConfig(**config_dict)
|
50
|
-
return CryptAPIProvider(config)
|
51
|
-
elif name == 'cryptomus':
|
52
|
-
config = CryptomusConfig(**config_dict)
|
53
|
-
return CryptomusProvider(config)
|
54
|
-
elif name == 'stripe':
|
55
|
-
# TODO: Implement StripeProvider with StripeConfig
|
371
|
+
# Get provider class
|
372
|
+
provider_class = self._provider_classes.get(provider_name)
|
373
|
+
if not provider_class:
|
374
|
+
logger.error(f"Unknown provider class: {provider_name}")
|
56
375
|
return None
|
57
|
-
|
58
|
-
|
376
|
+
|
377
|
+
# Get config class
|
378
|
+
config_class = self._provider_configs.get(provider_name)
|
379
|
+
if not config_class:
|
380
|
+
logger.error(f"Unknown provider config class: {provider_name}")
|
59
381
|
return None
|
60
|
-
|
382
|
+
|
383
|
+
# Create configuration
|
384
|
+
config = config_class(**config_data)
|
385
|
+
|
386
|
+
# Create provider
|
387
|
+
provider = provider_class(config)
|
388
|
+
|
389
|
+
logger.debug(f"Created provider: {provider}")
|
390
|
+
return provider
|
391
|
+
|
61
392
|
except Exception as e:
|
62
|
-
logger.error(f"Failed to create provider {
|
393
|
+
logger.error(f"Failed to create provider {provider_name}: {e}")
|
63
394
|
return None
|
64
395
|
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
396
|
+
def register_provider_class(
|
397
|
+
self,
|
398
|
+
provider_name: str,
|
399
|
+
provider_class: Type[BaseProvider],
|
400
|
+
config_class: Type[ProviderConfig]
|
401
|
+
):
|
402
|
+
"""
|
403
|
+
Register new provider class.
|
404
|
+
|
405
|
+
Args:
|
406
|
+
provider_name: Provider name
|
407
|
+
provider_class: Provider class
|
408
|
+
config_class: Provider config class
|
409
|
+
"""
|
410
|
+
self._provider_classes[provider_name] = provider_class
|
411
|
+
self._provider_configs[provider_name] = config_class
|
412
|
+
logger.info(f"Registered provider class: {provider_name}")
|
413
|
+
|
414
|
+
def __len__(self) -> int:
|
415
|
+
"""Get number of initialized providers."""
|
416
|
+
return len(self._providers)
|
417
|
+
|
418
|
+
def __contains__(self, provider_name: str) -> bool:
|
419
|
+
"""Check if provider is initialized."""
|
420
|
+
return provider_name in self._providers
|
421
|
+
|
422
|
+
def __iter__(self):
|
423
|
+
"""Iterate over provider names."""
|
424
|
+
return iter(self._providers.keys())
|
425
|
+
|
426
|
+
|
427
|
+
# Global registry instance
|
428
|
+
_global_registry: Optional[ProviderRegistry] = None
|
429
|
+
|
430
|
+
|
431
|
+
def get_provider_registry() -> ProviderRegistry:
|
432
|
+
"""
|
433
|
+
Get global provider registry instance.
|
434
|
+
|
435
|
+
Returns:
|
436
|
+
ProviderRegistry: Global registry instance
|
437
|
+
"""
|
438
|
+
global _global_registry
|
439
|
+
if _global_registry is None:
|
440
|
+
_global_registry = ProviderRegistry()
|
441
|
+
return _global_registry
|
442
|
+
|
443
|
+
|
444
|
+
def initialize_providers() -> ServiceOperationResult:
|
445
|
+
"""
|
446
|
+
Initialize global provider registry.
|
447
|
+
|
448
|
+
Returns:
|
449
|
+
ServiceOperationResult: Initialization result
|
450
|
+
"""
|
451
|
+
registry = get_provider_registry()
|
452
|
+
return registry.initialize()
|
@@ -0,0 +1,78 @@
|
|
1
|
+
"""
|
2
|
+
Pydantic types for the Universal Payment System v2.0.
|
3
|
+
|
4
|
+
Type-safe models for inter-service communication following data typing requirements.
|
5
|
+
Uses Pydantic 2 for service layer validation and business logic.
|
6
|
+
"""
|
7
|
+
|
8
|
+
# Request types
|
9
|
+
from .requests import (
|
10
|
+
PaymentCreateRequest,
|
11
|
+
PaymentStatusRequest,
|
12
|
+
BalanceUpdateRequest,
|
13
|
+
SubscriptionCreateRequest,
|
14
|
+
SubscriptionUpdateRequest,
|
15
|
+
CurrencyConversionRequest,
|
16
|
+
WebhookValidationRequest,
|
17
|
+
)
|
18
|
+
|
19
|
+
# Response types
|
20
|
+
from .responses import (
|
21
|
+
PaymentResult,
|
22
|
+
ProviderResponse,
|
23
|
+
BalanceResult,
|
24
|
+
SubscriptionResult,
|
25
|
+
CurrencyConversionResult,
|
26
|
+
ServiceOperationResult,
|
27
|
+
)
|
28
|
+
|
29
|
+
# Data types
|
30
|
+
from .data import (
|
31
|
+
PaymentData,
|
32
|
+
BalanceData,
|
33
|
+
SubscriptionData,
|
34
|
+
TransactionData,
|
35
|
+
CurrencyData,
|
36
|
+
ProviderData,
|
37
|
+
)
|
38
|
+
|
39
|
+
# Webhook types
|
40
|
+
from .webhooks import (
|
41
|
+
WebhookData,
|
42
|
+
NowPaymentsWebhook,
|
43
|
+
WebhookProcessingResult,
|
44
|
+
WebhookSignature,
|
45
|
+
)
|
46
|
+
|
47
|
+
__all__ = [
|
48
|
+
# Request types
|
49
|
+
'PaymentCreateRequest',
|
50
|
+
'PaymentStatusRequest',
|
51
|
+
'BalanceUpdateRequest',
|
52
|
+
'SubscriptionCreateRequest',
|
53
|
+
'SubscriptionUpdateRequest',
|
54
|
+
'CurrencyConversionRequest',
|
55
|
+
'WebhookValidationRequest',
|
56
|
+
|
57
|
+
# Response types
|
58
|
+
'PaymentResult',
|
59
|
+
'ProviderResponse',
|
60
|
+
'BalanceResult',
|
61
|
+
'SubscriptionResult',
|
62
|
+
'CurrencyConversionResult',
|
63
|
+
'ServiceOperationResult',
|
64
|
+
|
65
|
+
# Data types
|
66
|
+
'PaymentData',
|
67
|
+
'BalanceData',
|
68
|
+
'SubscriptionData',
|
69
|
+
'TransactionData',
|
70
|
+
'CurrencyData',
|
71
|
+
'ProviderData',
|
72
|
+
|
73
|
+
# Webhook types
|
74
|
+
'WebhookData',
|
75
|
+
'NowPaymentsWebhook',
|
76
|
+
'WebhookProcessingResult',
|
77
|
+
'WebhookSignature',
|
78
|
+
]
|