django-cfg 1.2.31__py3-none-any.whl → 1.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) 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 -10
  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 +526 -222
  9. django_cfg/apps/payments/admin/filters.py +306 -199
  10. django_cfg/apps/payments/admin/payments_admin.py +465 -70
  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 +303 -151
  39. django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
  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 +342 -152
  43. django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
  44. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  45. django_cfg/apps/payments/models/__init__.py +13 -18
  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 +172 -148
  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 -285
  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 +346 -467
  67. django_cfg/apps/payments/services/core/subscription_service.py +425 -481
  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 +234 -174
  74. django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
  75. django_cfg/apps/payments/services/providers/registry.py +367 -301
  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 +210 -129
  83. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  84. django_cfg/apps/payments/signals/payment_signals.py +128 -103
  85. django_cfg/apps/payments/signals/subscription_signals.py +194 -142
  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 +45 -48
  93. django_cfg/apps/payments/urls_admin.py +33 -42
  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/config.py +1 -1
  110. django_cfg/core/config.py +40 -4
  111. django_cfg/core/generation.py +25 -4
  112. django_cfg/core/integration/README.md +363 -0
  113. django_cfg/core/integration/__init__.py +47 -0
  114. django_cfg/core/integration/commands_collector.py +239 -0
  115. django_cfg/core/integration/display/__init__.py +15 -0
  116. django_cfg/core/integration/display/base.py +157 -0
  117. django_cfg/core/integration/display/ngrok.py +164 -0
  118. django_cfg/core/integration/display/startup.py +815 -0
  119. django_cfg/core/integration/url_integration.py +123 -0
  120. django_cfg/core/integration/version_checker.py +160 -0
  121. django_cfg/management/commands/auto_generate.py +4 -0
  122. django_cfg/management/commands/check_settings.py +6 -0
  123. django_cfg/management/commands/clear_constance.py +5 -2
  124. django_cfg/management/commands/create_token.py +6 -0
  125. django_cfg/management/commands/list_urls.py +6 -0
  126. django_cfg/management/commands/migrate_all.py +6 -0
  127. django_cfg/management/commands/migrator.py +3 -0
  128. django_cfg/management/commands/rundramatiq.py +6 -0
  129. django_cfg/management/commands/runserver_ngrok.py +51 -29
  130. django_cfg/management/commands/script.py +6 -0
  131. django_cfg/management/commands/show_config.py +12 -2
  132. django_cfg/management/commands/show_urls.py +4 -0
  133. django_cfg/management/commands/superuser.py +6 -0
  134. django_cfg/management/commands/task_clear.py +4 -1
  135. django_cfg/management/commands/task_status.py +3 -1
  136. django_cfg/management/commands/test_email.py +3 -0
  137. django_cfg/management/commands/test_telegram.py +6 -0
  138. django_cfg/management/commands/test_twilio.py +6 -0
  139. django_cfg/management/commands/tree.py +6 -0
  140. django_cfg/management/commands/validate_config.py +155 -149
  141. django_cfg/models/constance.py +31 -11
  142. django_cfg/models/payments.py +175 -492
  143. django_cfg/modules/django_logger.py +160 -146
  144. django_cfg/modules/django_unfold/dashboard.py +64 -16
  145. django_cfg/registry/core.py +1 -0
  146. django_cfg/template_archive/django_sample.zip +0 -0
  147. django_cfg/utils/smart_defaults.py +222 -571
  148. django_cfg/utils/toolkit.py +51 -11
  149. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/METADATA +4 -1
  150. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/RECORD +153 -185
  151. django_cfg/apps/payments/__init__.py +0 -8
  152. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  153. django_cfg/apps/payments/config/module.py +0 -70
  154. django_cfg/apps/payments/config/providers.py +0 -105
  155. django_cfg/apps/payments/config/settings.py +0 -96
  156. django_cfg/apps/payments/config/utils.py +0 -52
  157. django_cfg/apps/payments/decorators.py +0 -291
  158. django_cfg/apps/payments/management/commands/README.md +0 -146
  159. django_cfg/apps/payments/management/commands/currency_stats.py +0 -304
  160. django_cfg/apps/payments/managers/__init__.py +0 -23
  161. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  162. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  163. django_cfg/apps/payments/managers/currency_manager.py +0 -306
  164. django_cfg/apps/payments/managers/payment_manager.py +0 -192
  165. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  166. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  167. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
  168. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
  169. django_cfg/apps/payments/models/events.py +0 -73
  170. django_cfg/apps/payments/serializers/__init__.py +0 -57
  171. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  172. django_cfg/apps/payments/serializers/balance.py +0 -59
  173. django_cfg/apps/payments/serializers/currencies.py +0 -63
  174. django_cfg/apps/payments/serializers/payments.py +0 -62
  175. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  176. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  177. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  178. django_cfg/apps/payments/services/cache/base.py +0 -30
  179. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  180. django_cfg/apps/payments/services/internal_types.py +0 -461
  181. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  182. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  183. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
  184. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  185. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
  186. django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
  187. django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
  188. django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
  189. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
  190. django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
  191. django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
  192. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
  193. django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
  194. django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
  195. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
  196. django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
  197. django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
  198. django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
  199. django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
  200. django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
  201. django_cfg/apps/payments/services/security/__init__.py +0 -34
  202. django_cfg/apps/payments/services/security/error_handler.py +0 -635
  203. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  204. django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
  205. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  206. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  207. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  208. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  209. django_cfg/apps/payments/tasks/__init__.py +0 -12
  210. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  211. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
  212. django_cfg/apps/payments/templates/payments/base.html +0 -182
  213. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  214. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  215. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
  216. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  217. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
  218. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
  219. django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
  220. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
  221. django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
  222. django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
  223. django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
  224. django_cfg/apps/payments/templates/payments/stats.html +0 -261
  225. django_cfg/apps/payments/templates/payments/test.html +0 -213
  226. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  227. django_cfg/apps/payments/utils/__init__.py +0 -43
  228. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  229. django_cfg/apps/payments/utils/config_utils.py +0 -239
  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 -63
  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 -122
  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 -451
  241. django_cfg/apps/payments/views/templates/base.py +0 -212
  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 -158
  245. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  246. django_cfg/apps/payments/views/templates/stats.py +0 -244
  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 -66
  250. django_cfg/core/integration.py +0 -160
  251. django_cfg/template_archive/.gitignore +0 -1
  252. django_cfg/template_archive/__init__.py +0 -0
  253. django_cfg/urls.py +0 -33
  254. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
  255. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
  256. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,238 +1,298 @@
1
1
  """
2
- Base payment provider interface.
2
+ Base provider class for the Universal Payment System v2.0.
3
3
 
4
- Abstract base class for all payment providers.
4
+ Abstract base class for all payment providers with unified interface.
5
5
  """
6
6
 
7
7
  from abc import ABC, abstractmethod
8
- from typing import Optional, List, Dict
9
- from django.db.models import QuerySet
8
+ from typing import Dict, Any, Optional, List
10
9
  from decimal import Decimal
10
+ from pydantic import BaseModel, Field
11
+ from django_cfg.modules.django_logger import get_logger
12
+ from ..types import ProviderResponse, ServiceOperationResult
11
13
 
12
- from django.db import models, transaction
13
- from cachetools import TTLCache
14
14
 
15
- from ..internal_types import ProviderResponse, WebhookData, PaymentAmountEstimate, ProviderInfo, UniversalCurrency, UniversalCurrenciesResponse, ProviderSyncResult
16
- from ...models import Currency, Network, ProviderCurrency
17
- from django_cfg.modules.django_logger import get_logger
15
+ class ProviderConfig(BaseModel):
16
+ """
17
+ Base provider configuration.
18
+
19
+ Common configuration fields for all payment providers.
20
+ """
21
+
22
+ provider_name: str = Field(description="Provider name")
23
+ api_key: str = Field(description="Provider API key")
24
+ api_url: str = Field(description="Provider API URL")
25
+ sandbox: bool = Field(default=False, description="Sandbox mode")
26
+ timeout: int = Field(default=30, description="Request timeout in seconds")
27
+ retry_attempts: int = Field(default=3, description="Number of retry attempts")
28
+ retry_delay: int = Field(default=5, description="Delay between retries in seconds")
29
+ min_amount_usd: float = Field(default=1.0, description="Minimum amount in USD")
30
+ max_amount_usd: float = Field(default=50000.0, description="Maximum amount in USD")
31
+ supported_currencies: List[str] = Field(default_factory=list, description="Supported currencies")
32
+ webhook_secret: Optional[str] = Field(None, description="Webhook secret for validation")
18
33
 
19
34
 
20
- logger = get_logger('base_provider')
35
+ class PaymentRequest(BaseModel):
36
+ """
37
+ Universal payment request for providers.
38
+
39
+ Standardized payment creation request across all providers.
40
+ """
41
+
42
+ amount_usd: float = Field(gt=0, description="Amount in USD")
43
+ currency_code: str = Field(description="Cryptocurrency code")
44
+ order_id: str = Field(description="Internal order/payment ID")
45
+ callback_url: Optional[str] = Field(None, description="Success callback URL")
46
+ cancel_url: Optional[str] = Field(None, description="Cancel URL")
47
+ description: Optional[str] = Field(None, description="Payment description")
48
+ customer_email: Optional[str] = Field(None, description="Customer email")
49
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
50
+
21
51
 
22
- class PaymentProvider(ABC):
23
- """Abstract base class for payment providers."""
52
+ class BaseProvider(ABC):
53
+ """
54
+ Abstract base class for payment providers.
24
55
 
25
- # Class-level cache for all providers (5 min TTL)
26
- _api_cache = TTLCache(maxsize=100, ttl=300)
56
+ Defines the unified interface that all providers must implement.
57
+ """
27
58
 
28
- def __init__(self, config):
29
- """Initialize provider with config."""
30
- self.config = config
31
- self.name = self.__class__.__name__.lower().replace('provider', '')
32
- self.logger = get_logger(f"payment.{self.name}")
59
+ def __init__(self, config: ProviderConfig):
60
+ """
61
+ Initialize provider with configuration.
33
62
 
34
- # Handle both dict and Pydantic model configs
35
- if hasattr(config, 'enabled'):
36
- self.enabled = config.enabled
37
- elif hasattr(config, 'get'):
38
- self.enabled = config.get('enabled', True)
39
- else:
40
- self.enabled = getattr(config, 'enabled', True)
63
+ Args:
64
+ config: Provider configuration
65
+ """
66
+ self.config = config
67
+ self.logger = get_logger(f"providers.{config.provider_name}")
68
+ self._session = None
69
+
70
+ @property
71
+ def name(self) -> str:
72
+ """Get provider name."""
73
+ return self.config.provider_name
74
+
75
+ @property
76
+ def is_sandbox(self) -> bool:
77
+ """Check if provider is in sandbox mode."""
78
+ return self.config.sandbox
41
79
 
42
80
  @abstractmethod
43
- def create_payment(self, payment_data: dict) -> ProviderResponse:
81
+ def create_payment(self, request: PaymentRequest) -> ProviderResponse:
44
82
  """
45
- Create a payment request.
83
+ Create payment with provider.
46
84
 
47
85
  Args:
48
- amount: Payment amount
49
- currency: Payment currency
50
- **kwargs: Additional parameters (order_id, description, etc.)
86
+ request: Payment creation request
51
87
 
52
88
  Returns:
53
- Dict with payment creation result
89
+ ProviderResponse: Provider response with payment details
54
90
  """
55
91
  pass
56
92
 
57
93
  @abstractmethod
58
- def check_payment_status(self, payment_id: str) -> ProviderResponse:
94
+ def get_payment_status(self, provider_payment_id: str) -> ProviderResponse:
59
95
  """
60
- Check payment status.
96
+ Get payment status from provider.
61
97
 
62
98
  Args:
63
- payment_id: Payment ID from provider
99
+ provider_payment_id: Provider's payment ID
64
100
 
65
101
  Returns:
66
- Dict with payment status
102
+ ProviderResponse: Current payment status
67
103
  """
68
104
  pass
69
105
 
70
106
  @abstractmethod
71
- def process_webhook(self, payload: dict) -> WebhookData:
107
+ def get_supported_currencies(self) -> ServiceOperationResult:
72
108
  """
73
- Process webhook payload.
109
+ Get list of supported currencies from provider.
74
110
 
75
- Args:
76
- payload: Webhook data from provider
77
-
78
111
  Returns:
79
- Dict with processed webhook data
112
+ ServiceOperationResult: List of supported currencies
80
113
  """
81
114
  pass
82
115
 
83
- @abstractmethod
84
- def get_parsed_currencies(self) -> UniversalCurrenciesResponse:
116
+ @abstractmethod
117
+ def validate_webhook(self, payload: Dict[str, Any], signature: str = None) -> ServiceOperationResult:
85
118
  """
86
- Get parsed and normalized currencies ready for database sync.
87
-
88
- This method should:
89
- 1. Fetch data from provider API
90
- 2. Parse provider codes into base_currency + network
91
- 3. Return universal format
119
+ Validate webhook from provider.
92
120
 
121
+ Args:
122
+ payload: Webhook payload
123
+ signature: Webhook signature (if any)
124
+
93
125
  Returns:
94
- UniversalCurrenciesResponse with parsed data
126
+ ServiceOperationResult: Validation result
95
127
  """
96
128
  pass
97
129
 
98
-
99
-
100
- def validate_webhook(self, payload: dict, headers: Optional[dict] = None) -> bool:
130
+ def get_exchange_rate(self, from_currency: str, to_currency: str) -> ServiceOperationResult:
101
131
  """
102
- Validate webhook signature and data.
132
+ Get exchange rate from provider (optional).
103
133
 
104
134
  Args:
105
- payload: Webhook data
106
- signature: Webhook signature (if applicable)
135
+ from_currency: Source currency
136
+ to_currency: Target currency
107
137
 
108
138
  Returns:
109
- True if webhook is valid
139
+ ServiceOperationResult: Exchange rate or not supported
110
140
  """
111
- # Default implementation - providers can override
112
- return True
113
-
141
+ return ServiceOperationResult(
142
+ success=False,
143
+ message=f"Exchange rates not supported by {self.name}",
144
+ error_code="not_supported"
145
+ )
114
146
 
115
- def check_api_status(self) -> bool:
147
+ def health_check(self) -> ServiceOperationResult:
116
148
  """
117
- Check if provider API is available.
149
+ Perform provider health check.
118
150
 
119
151
  Returns:
120
- True if API is available
152
+ ServiceOperationResult: Health check result
121
153
  """
122
- # Optional method - providers can override
123
- return True
124
-
125
- def is_enabled(self) -> bool:
126
- """Check if provider is enabled."""
127
- return self.enabled
154
+ try:
155
+ # Basic connectivity test - can be overridden by providers
156
+ result = self.get_supported_currencies()
157
+
158
+ if result.success:
159
+ return ServiceOperationResult(
160
+ success=True,
161
+ message=f"{self.name} provider is healthy",
162
+ data={
163
+ 'provider': self.name,
164
+ 'sandbox': self.is_sandbox,
165
+ 'api_url': self.config.api_url,
166
+ 'supported_currencies_count': len(result.data.get('currencies', []))
167
+ }
168
+ )
169
+ else:
170
+ return ServiceOperationResult(
171
+ success=False,
172
+ message=f"{self.name} provider health check failed",
173
+ error_code="health_check_failed",
174
+ data={'provider': self.name, 'error': result.message}
175
+ )
176
+
177
+ except Exception as e:
178
+ self.logger.error(f"Health check failed for {self.name}: {e}")
179
+ return ServiceOperationResult(
180
+ success=False,
181
+ message=f"{self.name} provider health check error: {e}",
182
+ error_code="health_check_error",
183
+ data={'provider': self.name}
184
+ )
128
185
 
129
- def sync_currencies_to_db(self) -> ProviderSyncResult:
186
+ def _make_request(
187
+ self,
188
+ method: str,
189
+ endpoint: str,
190
+ data: Optional[Dict[str, Any]] = None,
191
+ headers: Optional[Dict[str, str]] = None
192
+ ) -> Dict[str, Any]:
130
193
  """
131
- Synchronize provider currencies with clean architecture.
132
- Uses get_parsed_currencies() to get normalized data.
194
+ Make HTTP request to provider API.
195
+
196
+ Args:
197
+ method: HTTP method
198
+ endpoint: API endpoint
199
+ data: Request data
200
+ headers: Request headers
201
+
202
+ Returns:
203
+ Dict[str, Any]: Response data
204
+
205
+ Raises:
206
+ Exception: If request fails
133
207
  """
134
- result = ProviderSyncResult()
208
+ import requests
209
+ from requests.adapters import HTTPAdapter
210
+ from urllib3.util.retry import Retry
135
211
 
136
- # Get parsed data from provider
137
- try:
138
- parsed_response = self.get_parsed_currencies()
139
- except Exception as e:
140
- result.errors.append(f"Failed to get parsed currencies: {str(e)}")
141
- return result
142
-
143
- logger.info(f"Processing {len(parsed_response.currencies)} currencies from {self.name}")
144
-
145
- with transaction.atomic():
146
- for universal_currency in parsed_response.currencies:
147
- try:
148
- # 1. Create/update base Currency using normalized manager
149
- currency, created = Currency.objects.get_or_create_normalized(
150
- code=universal_currency.base_currency_code,
151
- defaults={
152
- 'name': universal_currency.name,
153
- 'currency_type': universal_currency.currency_type
154
- }
155
- )
156
-
157
- if created:
158
- result.currencies_created += 1
159
- logger.debug(f"Created currency: {universal_currency.base_currency_code}")
160
- else:
161
- result.currencies_updated += 1
162
-
163
- # 2. Create/update Network (if needed) using normalized manager
164
- network = None
165
- if universal_currency.network_code:
166
- network, created = Network.objects.get_or_create_normalized(
167
- code=universal_currency.network_code,
168
- defaults={
169
- 'name': universal_currency.network_code.title()
170
- }
171
- )
172
-
173
- if created:
174
- result.networks_created += 1
175
- logger.debug(f"Created network: {universal_currency.network_code}")
176
- else:
177
- result.networks_updated += 1
178
-
179
- # 3. Create/update ProviderCurrency mapping
180
- provider_currency, created = ProviderCurrency.objects.get_or_create(
181
- provider_name=self.name,
182
- provider_currency_code=universal_currency.provider_currency_code,
183
- defaults={
184
- 'base_currency': currency,
185
- 'network': network,
186
- 'is_enabled': universal_currency.is_enabled,
187
- 'is_popular': universal_currency.is_popular,
188
- 'is_stable': universal_currency.is_stable,
189
- 'priority': universal_currency.priority,
190
- 'logo_url': universal_currency.logo_url,
191
- 'available_for_payment': universal_currency.available_for_payment,
192
- 'available_for_payout': universal_currency.available_for_payout,
193
- 'min_amount': universal_currency.min_amount,
194
- 'max_amount': universal_currency.max_amount,
195
- 'metadata': universal_currency.raw_data
196
- }
197
- )
198
-
199
- if created:
200
- result.provider_currencies_created += 1
201
- logger.debug(f"Created provider currency: {universal_currency.provider_currency_code}")
202
- else:
203
- # Update existing record
204
- provider_currency.is_enabled = universal_currency.is_enabled
205
- provider_currency.is_popular = universal_currency.is_popular
206
- provider_currency.is_stable = universal_currency.is_stable
207
- provider_currency.priority = universal_currency.priority
208
- provider_currency.logo_url = universal_currency.logo_url
209
- provider_currency.save()
210
- result.provider_currencies_updated += 1
211
-
212
- except Exception as e:
213
- error_msg = f"Error processing {universal_currency.provider_currency_code}: {str(e)}"
214
- result.errors.append(error_msg)
215
- logger.error(error_msg)
212
+ # Create session if not exists
213
+ if self._session is None:
214
+ self._session = requests.Session()
215
+
216
+ # Configure retries
217
+ retry_strategy = Retry(
218
+ total=self.config.retry_attempts,
219
+ backoff_factor=self.config.retry_delay,
220
+ status_forcelist=[429, 500, 502, 503, 504],
221
+ )
222
+ adapter = HTTPAdapter(max_retries=retry_strategy)
223
+ self._session.mount("http://", adapter)
224
+ self._session.mount("https://", adapter)
225
+
226
+ # Build URL
227
+ url = f"{self.config.api_url.rstrip('/')}/{endpoint.lstrip('/')}"
228
+
229
+ # Prepare headers
230
+ request_headers = {
231
+ 'Content-Type': 'application/json',
232
+ 'User-Agent': f'django-cfg-payments/2.0 ({self.name})',
233
+ }
234
+ if headers:
235
+ request_headers.update(headers)
236
+
237
+ # Log request
238
+ self.logger.debug(f"Making {method} request to {url}", extra={
239
+ 'method': method,
240
+ 'url': url,
241
+ 'has_data': bool(data)
242
+ })
243
+
244
+ # Make request
245
+ response = self._session.request(
246
+ method=method,
247
+ url=url,
248
+ json=data if data else None,
249
+ headers=request_headers,
250
+ timeout=self.config.timeout
251
+ )
252
+
253
+ # Log response
254
+ self.logger.debug(f"Received response: {response.status_code}", extra={
255
+ 'status_code': response.status_code,
256
+ 'response_size': len(response.content)
257
+ })
258
+
259
+ # Handle response
260
+ response.raise_for_status()
216
261
 
217
- logger.info(f"Sync completed: {result}")
218
- return result
219
-
220
- def get_provider_info(self) -> ProviderInfo:
221
- """Get provider information using parsed currencies."""
222
262
  try:
223
- parsed_response = self.get_parsed_currencies()
224
- supported_currencies = [c.base_currency_code for c in parsed_response.currencies]
225
- except Exception:
226
- supported_currencies = []
227
-
228
- return ProviderInfo(
229
- name=self.name,
230
- display_name=self.name.title(),
231
- supported_currencies=supported_currencies,
232
- is_active=self.enabled and self.check_api_status(),
233
- features={
234
- 'supports_networks': True,
235
- 'supports_webhooks': True,
236
- 'supports_refunds': True
237
- }
263
+ return response.json()
264
+ except ValueError:
265
+ # Non-JSON response
266
+ return {'raw_response': response.text, 'status_code': response.status_code}
267
+
268
+ def _create_provider_response(
269
+ self,
270
+ success: bool,
271
+ raw_response: Dict[str, Any],
272
+ **kwargs
273
+ ) -> ProviderResponse:
274
+ """
275
+ Create standardized provider response.
276
+
277
+ Args:
278
+ success: Operation success
279
+ raw_response: Raw provider response
280
+ **kwargs: Additional response fields
281
+
282
+ Returns:
283
+ ProviderResponse: Standardized response
284
+ """
285
+ return ProviderResponse(
286
+ provider=self.name,
287
+ success=success,
288
+ raw_response=raw_response,
289
+ **kwargs
238
290
  )
291
+
292
+ def __str__(self) -> str:
293
+ """String representation."""
294
+ return f"{self.name}Provider(sandbox={self.is_sandbox})"
295
+
296
+ def __repr__(self) -> str:
297
+ """Detailed representation."""
298
+ return f"{self.__class__.__name__}(name='{self.name}', sandbox={self.is_sandbox}, api_url='{self.config.api_url}')"