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.
Files changed (258) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/api/health/views.py +4 -2
  3. django_cfg/apps/knowbase/config/settings.py +16 -15
  4. django_cfg/apps/payments/README.md +326 -0
  5. django_cfg/apps/payments/admin/__init__.py +20 -9
  6. django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
  7. django_cfg/apps/payments/admin/balance_admin.py +592 -297
  8. django_cfg/apps/payments/admin/currencies_admin.py +600 -108
  9. django_cfg/apps/payments/admin/filters.py +306 -199
  10. django_cfg/apps/payments/admin/payments_admin.py +470 -64
  11. django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
  12. django_cfg/apps/payments/admin_interface/__init__.py +18 -0
  13. django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
  14. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
  23. django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
  24. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
  25. django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
  26. django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
  27. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
  28. django_cfg/apps/payments/apps.py +34 -9
  29. django_cfg/apps/payments/config/__init__.py +28 -51
  30. django_cfg/apps/payments/config/constance/__init__.py +22 -0
  31. django_cfg/apps/payments/config/constance/config_service.py +123 -0
  32. django_cfg/apps/payments/config/constance/fields.py +69 -0
  33. django_cfg/apps/payments/config/constance/settings.py +160 -0
  34. django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
  35. django_cfg/apps/payments/config/helpers.py +130 -0
  36. django_cfg/apps/payments/management/__init__.py +1 -3
  37. django_cfg/apps/payments/management/commands/__init__.py +1 -3
  38. django_cfg/apps/payments/management/commands/manage_currencies.py +381 -0
  39. django_cfg/apps/payments/management/commands/manage_providers.py +408 -0
  40. django_cfg/apps/payments/middleware/__init__.py +3 -1
  41. django_cfg/apps/payments/middleware/api_access.py +329 -222
  42. django_cfg/apps/payments/middleware/rate_limiting.py +343 -163
  43. django_cfg/apps/payments/middleware/usage_tracking.py +250 -238
  44. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  45. django_cfg/apps/payments/models/__init__.py +16 -20
  46. django_cfg/apps/payments/models/api_keys.py +121 -43
  47. django_cfg/apps/payments/models/balance.py +150 -115
  48. django_cfg/apps/payments/models/base.py +68 -15
  49. django_cfg/apps/payments/models/currencies.py +207 -67
  50. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  51. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  52. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  53. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  54. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  55. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  56. django_cfg/apps/payments/models/payments.py +235 -284
  57. django_cfg/apps/payments/models/subscriptions.py +257 -177
  58. django_cfg/apps/payments/models/tariffs.py +147 -40
  59. django_cfg/apps/payments/services/__init__.py +209 -56
  60. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  61. django_cfg/apps/payments/services/cache/{simple_cache.py → cache_service.py} +112 -12
  62. django_cfg/apps/payments/services/core/__init__.py +10 -6
  63. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  64. django_cfg/apps/payments/services/core/base.py +166 -0
  65. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  66. django_cfg/apps/payments/services/core/payment_service.py +344 -468
  67. django_cfg/apps/payments/services/core/subscription_service.py +425 -484
  68. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  69. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  70. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  71. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  72. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  73. django_cfg/apps/payments/services/providers/base.py +232 -71
  74. django_cfg/apps/payments/services/providers/nowpayments.py +404 -219
  75. django_cfg/apps/payments/services/providers/registry.py +429 -80
  76. django_cfg/apps/payments/services/types/__init__.py +78 -0
  77. django_cfg/apps/payments/services/types/data.py +177 -0
  78. django_cfg/apps/payments/services/types/requests.py +150 -0
  79. django_cfg/apps/payments/services/types/responses.py +156 -0
  80. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  81. django_cfg/apps/payments/signals/__init__.py +33 -8
  82. django_cfg/apps/payments/signals/api_key_signals.py +211 -130
  83. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  84. django_cfg/apps/payments/signals/payment_signals.py +129 -98
  85. django_cfg/apps/payments/signals/subscription_signals.py +195 -143
  86. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  87. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  88. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  89. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  90. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  91. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  92. django_cfg/apps/payments/urls.py +46 -47
  93. django_cfg/apps/payments/urls_admin.py +49 -0
  94. django_cfg/apps/payments/views/api/__init__.py +101 -0
  95. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  96. django_cfg/apps/payments/views/api/balances.py +381 -0
  97. django_cfg/apps/payments/views/api/base.py +298 -0
  98. django_cfg/apps/payments/views/api/currencies.py +402 -0
  99. django_cfg/apps/payments/views/api/payments.py +415 -0
  100. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  101. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  102. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  103. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  104. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  105. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  106. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  107. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  108. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  109. django_cfg/apps/tasks/urls.py +0 -2
  110. django_cfg/apps/tasks/urls_admin.py +14 -0
  111. django_cfg/apps/urls.py +4 -4
  112. django_cfg/config.py +1 -1
  113. django_cfg/core/config.py +75 -4
  114. django_cfg/core/generation.py +25 -4
  115. django_cfg/core/integration/README.md +363 -0
  116. django_cfg/core/integration/__init__.py +47 -0
  117. django_cfg/core/integration/commands_collector.py +239 -0
  118. django_cfg/core/integration/display/__init__.py +15 -0
  119. django_cfg/core/integration/display/base.py +157 -0
  120. django_cfg/core/integration/display/ngrok.py +164 -0
  121. django_cfg/core/integration/display/startup.py +815 -0
  122. django_cfg/core/integration/url_integration.py +123 -0
  123. django_cfg/core/integration/version_checker.py +160 -0
  124. django_cfg/management/commands/auto_generate.py +4 -0
  125. django_cfg/management/commands/check_settings.py +6 -0
  126. django_cfg/management/commands/clear_constance.py +5 -2
  127. django_cfg/management/commands/create_token.py +6 -0
  128. django_cfg/management/commands/list_urls.py +6 -0
  129. django_cfg/management/commands/migrate_all.py +6 -0
  130. django_cfg/management/commands/migrator.py +3 -0
  131. django_cfg/management/commands/rundramatiq.py +6 -0
  132. django_cfg/management/commands/runserver_ngrok.py +51 -29
  133. django_cfg/management/commands/script.py +6 -0
  134. django_cfg/management/commands/show_config.py +12 -2
  135. django_cfg/management/commands/show_urls.py +4 -0
  136. django_cfg/management/commands/superuser.py +6 -0
  137. django_cfg/management/commands/task_clear.py +4 -1
  138. django_cfg/management/commands/task_status.py +3 -1
  139. django_cfg/management/commands/test_email.py +3 -0
  140. django_cfg/management/commands/test_telegram.py +6 -0
  141. django_cfg/management/commands/test_twilio.py +6 -0
  142. django_cfg/management/commands/tree.py +6 -0
  143. django_cfg/management/commands/validate_config.py +155 -149
  144. django_cfg/models/constance.py +31 -11
  145. django_cfg/models/payments.py +175 -498
  146. django_cfg/modules/django_currency/__init__.py +16 -11
  147. django_cfg/modules/django_currency/clients/__init__.py +4 -4
  148. django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
  149. django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
  150. django_cfg/modules/django_currency/core/__init__.py +1 -7
  151. django_cfg/modules/django_currency/core/converter.py +18 -23
  152. django_cfg/modules/django_currency/core/models.py +122 -11
  153. django_cfg/modules/django_currency/database/__init__.py +4 -4
  154. django_cfg/modules/django_currency/database/database_loader.py +190 -309
  155. django_cfg/modules/django_logger.py +160 -146
  156. django_cfg/modules/django_unfold/dashboard.py +65 -12
  157. django_cfg/registry/core.py +1 -0
  158. django_cfg/template_archive/django_sample.zip +0 -0
  159. django_cfg/templates/admin/components/action_grid.html +9 -9
  160. django_cfg/templates/admin/components/metric_card.html +5 -5
  161. django_cfg/templates/admin/components/status_badge.html +2 -2
  162. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
  163. django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
  164. django_cfg/templates/admin/snippets/components/system_health.html +1 -1
  165. django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
  166. django_cfg/utils/smart_defaults.py +222 -571
  167. django_cfg/utils/toolkit.py +51 -11
  168. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/METADATA +5 -4
  169. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/RECORD +172 -182
  170. django_cfg/apps/payments/__init__.py +0 -8
  171. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  172. django_cfg/apps/payments/config/module.py +0 -70
  173. django_cfg/apps/payments/config/providers.py +0 -105
  174. django_cfg/apps/payments/config/settings.py +0 -96
  175. django_cfg/apps/payments/config/utils.py +0 -52
  176. django_cfg/apps/payments/decorators.py +0 -291
  177. django_cfg/apps/payments/management/commands/README.md +0 -178
  178. django_cfg/apps/payments/management/commands/currency_stats.py +0 -323
  179. django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
  180. django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
  181. django_cfg/apps/payments/managers/__init__.py +0 -22
  182. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  183. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  184. django_cfg/apps/payments/managers/currency_manager.py +0 -83
  185. django_cfg/apps/payments/managers/payment_manager.py +0 -44
  186. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  187. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  188. django_cfg/apps/payments/models/events.py +0 -73
  189. django_cfg/apps/payments/serializers/__init__.py +0 -56
  190. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  191. django_cfg/apps/payments/serializers/balance.py +0 -59
  192. django_cfg/apps/payments/serializers/currencies.py +0 -55
  193. django_cfg/apps/payments/serializers/payments.py +0 -62
  194. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  195. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  196. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  197. django_cfg/apps/payments/services/cache/base.py +0 -30
  198. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  199. django_cfg/apps/payments/services/internal_types.py +0 -297
  200. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  201. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  202. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -222
  203. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  204. django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
  205. django_cfg/apps/payments/services/providers/cryptomus.py +0 -311
  206. django_cfg/apps/payments/services/security/__init__.py +0 -34
  207. django_cfg/apps/payments/services/security/error_handler.py +0 -637
  208. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  209. django_cfg/apps/payments/services/security/webhook_validator.py +0 -475
  210. django_cfg/apps/payments/services/validators/__init__.py +0 -8
  211. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  212. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  213. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  214. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  215. django_cfg/apps/payments/tasks/__init__.py +0 -12
  216. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  217. django_cfg/apps/payments/templates/payments/base.html +0 -182
  218. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  219. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  220. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -36
  221. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  222. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -27
  223. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -144
  224. django_cfg/apps/payments/templates/payments/dashboard.html +0 -346
  225. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  226. django_cfg/apps/payments/urls_templates.py +0 -52
  227. django_cfg/apps/payments/utils/__init__.py +0 -45
  228. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  229. django_cfg/apps/payments/utils/config_utils.py +0 -245
  230. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  231. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  232. django_cfg/apps/payments/views/__init__.py +0 -62
  233. django_cfg/apps/payments/views/api_key_views.py +0 -164
  234. django_cfg/apps/payments/views/balance_views.py +0 -75
  235. django_cfg/apps/payments/views/currency_views.py +0 -111
  236. django_cfg/apps/payments/views/payment_views.py +0 -149
  237. django_cfg/apps/payments/views/subscription_views.py +0 -135
  238. django_cfg/apps/payments/views/tariff_views.py +0 -131
  239. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  240. django_cfg/apps/payments/views/templates/ajax.py +0 -312
  241. django_cfg/apps/payments/views/templates/base.py +0 -204
  242. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  243. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  244. django_cfg/apps/payments/views/templates/payment_management.py +0 -164
  245. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  246. django_cfg/apps/payments/views/templates/stats.py +0 -240
  247. django_cfg/apps/payments/views/templates/utils.py +0 -181
  248. django_cfg/apps/payments/views/webhook_views.py +0 -266
  249. django_cfg/apps/payments/viewsets.py +0 -65
  250. django_cfg/core/integration.py +0 -160
  251. django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
  252. django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
  253. django_cfg/template_archive/.gitignore +0 -1
  254. django_cfg/template_archive/__init__.py +0 -0
  255. django_cfg/urls.py +0 -33
  256. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
  257. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
  258. {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 managing payment providers.
2
+ Provider registry for the Universal Payment System v2.0.
3
3
 
4
- Central registry with lazy loading and typed configuration.
4
+ Centralized management of payment providers with health monitoring and fallbacks.
5
5
  """
6
6
 
7
- import logging
8
- from typing import Optional, List
9
-
10
- from .base import PaymentProvider
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 = logging.getLogger(__name__)
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
- """Central registry for payment providers with typed configs."""
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 with lazy loading."""
23
- self._providers: dict[str, PaymentProvider] = {}
24
- self._provider_configs: dict[str, dict] = {}
25
- self._load_configurations()
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 _load_configurations(self) -> None:
28
- """Load provider configurations."""
71
+ def initialize(self) -> ServiceOperationResult:
72
+ """
73
+ Initialize all configured providers.
74
+
75
+ Returns:
76
+ ServiceOperationResult: Initialization result
77
+ """
29
78
  try:
30
- from ...utils.config_utils import get_payments_config
31
- config = get_payments_config()
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
- self._provider_configs = {}
34
- for provider_name, provider_config in config.providers.items():
35
- if provider_config.enabled:
36
- self._provider_configs[provider_name] = provider_config.get_config_dict()
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.warning(f"Failed to load provider configurations: {e}")
40
- self._provider_configs = {}
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, name: str, config_dict: dict) -> Optional[PaymentProvider]:
43
- """Create provider instance from configuration with typed config."""
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
- if name == 'nowpayments':
46
- config = NowPaymentsConfig(**config_dict)
47
- return NowPaymentsProvider(config)
48
- elif name == 'cryptapi':
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
- else:
58
- logger.warning(f"Unknown provider type: {name}")
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 {name}: {e}")
393
+ logger.error(f"Failed to create provider {provider_name}: {e}")
63
394
  return None
64
395
 
65
- def register_provider(self, name: str, provider: PaymentProvider) -> None:
66
- """Register a payment provider instance."""
67
- self._providers[name] = provider
68
-
69
- def get_provider(self, name: str) -> Optional[PaymentProvider]:
70
- """Get provider by name with lazy loading."""
71
- # Check if already loaded
72
- if name in self._providers:
73
- return self._providers[name]
74
-
75
- # Try to load from configuration
76
- if name in self._provider_configs:
77
- provider = self._create_provider(name, self._provider_configs[name])
78
- if provider:
79
- self._providers[name] = provider
80
- return provider
81
-
82
- return None
83
-
84
- def list_providers(self) -> List[str]:
85
- """Get list of available providers."""
86
- available = set(self._providers.keys())
87
- available.update(self._provider_configs.keys())
88
- return list(available)
89
-
90
- def get_active_providers(self) -> List[str]:
91
- """Get list of active providers."""
92
- active = []
93
- for name in self.list_providers():
94
- provider = self.get_provider(name)
95
- if provider and provider.is_enabled():
96
- active.append(name)
97
- return active
98
-
99
- def reload_providers(self) -> None:
100
- """Reload all providers from configuration."""
101
- logger.info("Reloading providers from configuration")
102
- self._providers.clear()
103
- self._load_configurations()
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
+ ]