django-cfg 1.2.31__py3-none-any.whl → 1.3.3__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 (264) 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/cleanup_expired_data.py +419 -0
  39. django_cfg/apps/payments/management/commands/currency_stats.py +297 -225
  40. django_cfg/apps/payments/management/commands/manage_currencies.py +303 -151
  41. django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
  42. django_cfg/apps/payments/management/commands/process_pending_payments.py +357 -0
  43. django_cfg/apps/payments/management/commands/test_providers.py +434 -0
  44. django_cfg/apps/payments/middleware/__init__.py +3 -1
  45. django_cfg/apps/payments/middleware/api_access.py +329 -222
  46. django_cfg/apps/payments/middleware/rate_limiting.py +342 -152
  47. django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
  48. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  49. django_cfg/apps/payments/models/__init__.py +13 -18
  50. django_cfg/apps/payments/models/api_keys.py +121 -43
  51. django_cfg/apps/payments/models/balance.py +153 -115
  52. django_cfg/apps/payments/models/base.py +68 -15
  53. django_cfg/apps/payments/models/currencies.py +172 -148
  54. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  55. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  56. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  57. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  58. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  59. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  60. django_cfg/apps/payments/models/payments.py +235 -285
  61. django_cfg/apps/payments/models/subscriptions.py +257 -177
  62. django_cfg/apps/payments/models/tariffs.py +147 -40
  63. django_cfg/apps/payments/services/__init__.py +209 -56
  64. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  65. django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
  66. django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
  67. django_cfg/apps/payments/services/{cache/base.py → cache_service/interfaces.py} +3 -1
  68. django_cfg/apps/payments/services/cache_service/keys.py +49 -0
  69. django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
  70. django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
  71. django_cfg/apps/payments/services/core/__init__.py +10 -6
  72. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  73. django_cfg/apps/payments/services/core/base.py +166 -0
  74. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  75. django_cfg/apps/payments/services/core/payment_service.py +371 -465
  76. django_cfg/apps/payments/services/core/subscription_service.py +425 -481
  77. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  78. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  79. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  80. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  81. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  82. django_cfg/apps/payments/services/providers/base.py +234 -174
  83. django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
  84. django_cfg/apps/payments/services/providers/registry.py +367 -301
  85. django_cfg/apps/payments/services/types/__init__.py +78 -0
  86. django_cfg/apps/payments/services/types/data.py +177 -0
  87. django_cfg/apps/payments/services/types/requests.py +150 -0
  88. django_cfg/apps/payments/services/types/responses.py +156 -0
  89. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  90. django_cfg/apps/payments/signals/__init__.py +33 -8
  91. django_cfg/apps/payments/signals/api_key_signals.py +210 -129
  92. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  93. django_cfg/apps/payments/signals/payment_signals.py +128 -103
  94. django_cfg/apps/payments/signals/subscription_signals.py +194 -142
  95. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  96. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  97. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  98. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  99. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  100. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  101. django_cfg/apps/payments/urls.py +45 -48
  102. django_cfg/apps/payments/urls_admin.py +33 -42
  103. django_cfg/apps/payments/views/api/__init__.py +101 -0
  104. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  105. django_cfg/apps/payments/views/api/balances.py +381 -0
  106. django_cfg/apps/payments/views/api/base.py +298 -0
  107. django_cfg/apps/payments/views/api/currencies.py +402 -0
  108. django_cfg/apps/payments/views/api/payments.py +415 -0
  109. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  110. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  111. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  112. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  113. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  114. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  115. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  116. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  117. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  118. django_cfg/config.py +1 -1
  119. django_cfg/core/config.py +40 -4
  120. django_cfg/core/generation.py +25 -4
  121. django_cfg/core/integration/README.md +363 -0
  122. django_cfg/core/integration/__init__.py +47 -0
  123. django_cfg/core/integration/commands_collector.py +239 -0
  124. django_cfg/core/integration/display/__init__.py +15 -0
  125. django_cfg/core/integration/display/base.py +157 -0
  126. django_cfg/core/integration/display/ngrok.py +164 -0
  127. django_cfg/core/integration/display/startup.py +815 -0
  128. django_cfg/core/integration/url_integration.py +123 -0
  129. django_cfg/core/integration/version_checker.py +160 -0
  130. django_cfg/management/commands/auto_generate.py +4 -0
  131. django_cfg/management/commands/check_settings.py +6 -0
  132. django_cfg/management/commands/clear_constance.py +5 -2
  133. django_cfg/management/commands/create_token.py +6 -0
  134. django_cfg/management/commands/list_urls.py +6 -0
  135. django_cfg/management/commands/migrate_all.py +6 -0
  136. django_cfg/management/commands/migrator.py +3 -0
  137. django_cfg/management/commands/rundramatiq.py +6 -0
  138. django_cfg/management/commands/runserver_ngrok.py +51 -29
  139. django_cfg/management/commands/script.py +6 -0
  140. django_cfg/management/commands/show_config.py +12 -2
  141. django_cfg/management/commands/show_urls.py +4 -0
  142. django_cfg/management/commands/superuser.py +6 -0
  143. django_cfg/management/commands/task_clear.py +4 -1
  144. django_cfg/management/commands/task_status.py +3 -1
  145. django_cfg/management/commands/test_email.py +3 -0
  146. django_cfg/management/commands/test_telegram.py +6 -0
  147. django_cfg/management/commands/test_twilio.py +6 -0
  148. django_cfg/management/commands/tree.py +6 -0
  149. django_cfg/management/commands/validate_config.py +155 -149
  150. django_cfg/models/constance.py +31 -11
  151. django_cfg/models/payments.py +175 -492
  152. django_cfg/modules/django_logger.py +160 -146
  153. django_cfg/modules/django_unfold/dashboard.py +64 -16
  154. django_cfg/registry/core.py +1 -0
  155. django_cfg/template_archive/django_sample.zip +0 -0
  156. django_cfg/utils/smart_defaults.py +227 -570
  157. django_cfg/utils/toolkit.py +51 -11
  158. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/METADATA +4 -1
  159. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/RECORD +162 -185
  160. django_cfg/apps/payments/__init__.py +0 -8
  161. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  162. django_cfg/apps/payments/config/module.py +0 -70
  163. django_cfg/apps/payments/config/providers.py +0 -105
  164. django_cfg/apps/payments/config/settings.py +0 -96
  165. django_cfg/apps/payments/config/utils.py +0 -52
  166. django_cfg/apps/payments/decorators.py +0 -291
  167. django_cfg/apps/payments/management/commands/README.md +0 -146
  168. django_cfg/apps/payments/managers/__init__.py +0 -23
  169. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  170. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  171. django_cfg/apps/payments/managers/currency_manager.py +0 -306
  172. django_cfg/apps/payments/managers/payment_manager.py +0 -192
  173. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  174. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  175. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
  176. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
  177. django_cfg/apps/payments/models/events.py +0 -73
  178. django_cfg/apps/payments/serializers/__init__.py +0 -57
  179. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  180. django_cfg/apps/payments/serializers/balance.py +0 -59
  181. django_cfg/apps/payments/serializers/currencies.py +0 -63
  182. django_cfg/apps/payments/serializers/payments.py +0 -62
  183. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  184. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  185. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  186. django_cfg/apps/payments/services/cache/simple_cache.py +0 -135
  187. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  188. django_cfg/apps/payments/services/internal_types.py +0 -461
  189. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  190. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  191. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
  192. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  193. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
  194. django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
  195. django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
  196. django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
  197. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
  198. django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
  199. django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
  200. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
  201. django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
  202. django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
  203. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
  204. django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
  205. django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
  206. django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
  207. django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
  208. django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
  209. django_cfg/apps/payments/services/security/__init__.py +0 -34
  210. django_cfg/apps/payments/services/security/error_handler.py +0 -635
  211. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  212. django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
  213. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  214. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  215. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  216. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  217. django_cfg/apps/payments/tasks/__init__.py +0 -12
  218. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  219. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
  220. django_cfg/apps/payments/templates/payments/base.html +0 -182
  221. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  222. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  223. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
  224. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  225. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
  226. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
  227. django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
  228. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
  229. django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
  230. django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
  231. django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
  232. django_cfg/apps/payments/templates/payments/stats.html +0 -261
  233. django_cfg/apps/payments/templates/payments/test.html +0 -213
  234. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  235. django_cfg/apps/payments/utils/__init__.py +0 -43
  236. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  237. django_cfg/apps/payments/utils/config_utils.py +0 -239
  238. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  239. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  240. django_cfg/apps/payments/views/__init__.py +0 -63
  241. django_cfg/apps/payments/views/api_key_views.py +0 -164
  242. django_cfg/apps/payments/views/balance_views.py +0 -75
  243. django_cfg/apps/payments/views/currency_views.py +0 -122
  244. django_cfg/apps/payments/views/payment_views.py +0 -149
  245. django_cfg/apps/payments/views/subscription_views.py +0 -135
  246. django_cfg/apps/payments/views/tariff_views.py +0 -131
  247. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  248. django_cfg/apps/payments/views/templates/ajax.py +0 -451
  249. django_cfg/apps/payments/views/templates/base.py +0 -212
  250. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  251. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  252. django_cfg/apps/payments/views/templates/payment_management.py +0 -158
  253. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  254. django_cfg/apps/payments/views/templates/stats.py +0 -244
  255. django_cfg/apps/payments/views/templates/utils.py +0 -181
  256. django_cfg/apps/payments/views/webhook_views.py +0 -266
  257. django_cfg/apps/payments/viewsets.py +0 -66
  258. django_cfg/core/integration.py +0 -160
  259. django_cfg/template_archive/.gitignore +0 -1
  260. django_cfg/template_archive/__init__.py +0 -0
  261. django_cfg/urls.py +0 -33
  262. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/WHEEL +0 -0
  263. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/entry_points.txt +0 -0
  264. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.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}')"