django-cfg 1.3.7__py3-none-any.whl → 1.3.11__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 (246) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/__init__.py +24 -8
  3. django_cfg/apps/accounts/admin/activity_admin.py +146 -0
  4. django_cfg/apps/accounts/admin/filters.py +98 -22
  5. django_cfg/apps/accounts/admin/group_admin.py +86 -0
  6. django_cfg/apps/accounts/admin/inlines.py +42 -13
  7. django_cfg/apps/accounts/admin/otp_admin.py +115 -0
  8. django_cfg/apps/accounts/admin/registration_admin.py +173 -0
  9. django_cfg/apps/accounts/admin/resources.py +123 -19
  10. django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
  11. django_cfg/apps/accounts/admin/user_admin.py +362 -0
  12. django_cfg/apps/agents/admin/__init__.py +17 -4
  13. django_cfg/apps/agents/admin/execution_admin.py +204 -183
  14. django_cfg/apps/agents/admin/registry_admin.py +230 -255
  15. django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
  16. django_cfg/apps/agents/core/__init__.py +1 -1
  17. django_cfg/apps/agents/core/django_agent.py +221 -0
  18. django_cfg/apps/agents/core/exceptions.py +14 -0
  19. django_cfg/apps/agents/core/orchestrator.py +18 -3
  20. django_cfg/apps/knowbase/admin/__init__.py +1 -1
  21. django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
  22. django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
  23. django_cfg/apps/knowbase/admin/document_admin.py +269 -262
  24. django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
  25. django_cfg/apps/knowbase/config/settings.py +21 -4
  26. django_cfg/apps/knowbase/views/chat_views.py +3 -0
  27. django_cfg/apps/leads/admin/__init__.py +3 -1
  28. django_cfg/apps/leads/admin/leads_admin.py +235 -35
  29. django_cfg/apps/maintenance/admin/__init__.py +2 -2
  30. django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
  31. django_cfg/apps/maintenance/admin/log_admin.py +143 -61
  32. django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
  33. django_cfg/apps/maintenance/admin/site_admin.py +213 -352
  34. django_cfg/apps/newsletter/admin/__init__.py +29 -2
  35. django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
  36. django_cfg/apps/payments/admin/__init__.py +18 -27
  37. django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
  38. django_cfg/apps/payments/admin/balance_admin.py +166 -632
  39. django_cfg/apps/payments/admin/currencies_admin.py +235 -607
  40. django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
  41. django_cfg/apps/payments/admin/filters.py +83 -3
  42. django_cfg/apps/payments/admin/networks_admin.py +269 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +183 -460
  44. django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
  45. django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
  46. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +153 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  48. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  49. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  50. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  51. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  52. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  53. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  54. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +43 -17
  55. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  56. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  57. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +109 -63
  58. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  59. django_cfg/apps/payments/config/__init__.py +14 -15
  60. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  61. django_cfg/apps/payments/config/helpers.py +8 -13
  62. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  63. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  64. django_cfg/apps/payments/middleware/api_access.py +32 -6
  65. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  66. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  67. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  68. django_cfg/apps/payments/models/balance.py +12 -0
  69. django_cfg/apps/payments/models/currencies.py +106 -32
  70. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  71. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  72. django_cfg/apps/payments/models/payments.py +94 -0
  73. django_cfg/apps/payments/services/core/base.py +4 -4
  74. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  75. django_cfg/apps/payments/services/core/payment_service.py +266 -39
  76. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  77. django_cfg/apps/payments/services/providers/base.py +303 -41
  78. django_cfg/apps/payments/services/providers/models/__init__.py +42 -0
  79. django_cfg/apps/payments/services/providers/models/base.py +145 -0
  80. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  81. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  82. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  83. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  84. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  85. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  86. django_cfg/apps/payments/services/providers/nowpayments/provider.py +557 -0
  87. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  88. django_cfg/apps/payments/services/providers/registry.py +9 -37
  89. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  90. django_cfg/apps/payments/services/types/requests.py +19 -7
  91. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  92. django_cfg/apps/payments/static/payments/js/api-client.js +29 -6
  93. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  94. django_cfg/apps/payments/static/payments/js/payment-form.js +98 -32
  95. django_cfg/apps/payments/tasks/__init__.py +39 -0
  96. django_cfg/apps/payments/tasks/types.py +73 -0
  97. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  98. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  99. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  100. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  101. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  102. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  103. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  104. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  105. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  106. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  107. django_cfg/apps/payments/urls.py +3 -2
  108. django_cfg/apps/payments/urls_admin.py +1 -1
  109. django_cfg/apps/payments/views/api/currencies.py +8 -5
  110. django_cfg/apps/payments/views/overview/services.py +2 -2
  111. django_cfg/apps/payments/views/serializers/currencies.py +22 -8
  112. django_cfg/apps/support/admin/__init__.py +10 -1
  113. django_cfg/apps/support/admin/support_admin.py +338 -141
  114. django_cfg/apps/tasks/admin/__init__.py +11 -0
  115. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  116. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  117. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  118. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  119. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  120. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  121. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  122. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  123. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  124. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  125. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  126. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  127. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  128. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  129. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  130. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  131. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  132. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  133. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  134. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  135. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  136. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  137. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  138. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  139. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  140. django_cfg/apps/tasks/urls.py +2 -2
  141. django_cfg/apps/tasks/urls_admin.py +2 -2
  142. django_cfg/apps/tasks/utils/__init__.py +1 -0
  143. django_cfg/apps/tasks/utils/simulator.py +356 -0
  144. django_cfg/apps/tasks/views/__init__.py +16 -0
  145. django_cfg/apps/tasks/views/api.py +569 -0
  146. django_cfg/apps/tasks/views/dashboard.py +58 -0
  147. django_cfg/config.py +1 -1
  148. django_cfg/core/config.py +10 -5
  149. django_cfg/core/generation.py +1 -1
  150. django_cfg/core/integration/__init__.py +21 -0
  151. django_cfg/management/commands/__init__.py +13 -1
  152. django_cfg/management/commands/migrate_all.py +9 -3
  153. django_cfg/management/commands/migrator.py +11 -6
  154. django_cfg/management/commands/rundramatiq.py +3 -2
  155. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  156. django_cfg/middleware/__init__.py +0 -2
  157. django_cfg/models/api_keys.py +115 -0
  158. django_cfg/models/constance.py +0 -11
  159. django_cfg/models/payments.py +137 -3
  160. django_cfg/modules/django_admin/__init__.py +64 -0
  161. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  162. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  163. django_cfg/modules/django_admin/decorators/display.py +106 -0
  164. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  165. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  166. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  167. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  168. django_cfg/modules/django_admin/models/__init__.py +20 -0
  169. django_cfg/modules/django_admin/models/action_models.py +33 -0
  170. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  171. django_cfg/modules/django_admin/models/base.py +26 -0
  172. django_cfg/modules/django_admin/models/display_models.py +31 -0
  173. django_cfg/modules/django_admin/utils/badges.py +159 -0
  174. django_cfg/modules/django_admin/utils/displays.py +247 -0
  175. django_cfg/modules/django_currency/__init__.py +2 -2
  176. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  177. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  178. django_cfg/modules/django_currency/core/converter.py +12 -12
  179. django_cfg/modules/django_currency/database/__init__.py +2 -2
  180. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  181. django_cfg/modules/django_llm/llm/client.py +10 -2
  182. django_cfg/modules/django_tasks.py +54 -21
  183. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  184. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  185. django_cfg/modules/django_unfold/dashboard.py +14 -13
  186. django_cfg/modules/django_unfold/models/config.py +1 -1
  187. django_cfg/registry/core.py +7 -9
  188. django_cfg/registry/third_party.py +2 -2
  189. django_cfg/template_archive/django_sample.zip +0 -0
  190. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -1
  191. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/RECORD +198 -160
  192. django_cfg/apps/accounts/admin/activity.py +0 -96
  193. django_cfg/apps/accounts/admin/group.py +0 -17
  194. django_cfg/apps/accounts/admin/otp.py +0 -59
  195. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  196. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  197. django_cfg/apps/accounts/admin/user.py +0 -300
  198. django_cfg/apps/agents/core/agent.py +0 -281
  199. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  200. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  201. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  202. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  203. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  204. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  205. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  206. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  207. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  208. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  209. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  210. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  211. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  212. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  213. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  214. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  215. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  216. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  217. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  218. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  219. django_cfg/apps/payments/config/constance/fields.py +0 -69
  220. django_cfg/apps/payments/config/constance/settings.py +0 -160
  221. django_cfg/apps/payments/services/providers/nowpayments.py +0 -478
  222. django_cfg/apps/tasks/admin.py +0 -320
  223. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  224. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  225. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  226. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  227. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  228. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  229. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  230. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  231. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  232. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  233. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  234. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  235. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  236. django_cfg/apps/tasks/views.py +0 -461
  237. django_cfg/management/commands/auto_generate.py +0 -486
  238. django_cfg/middleware/static_nocache.py +0 -55
  239. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  240. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  241. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  242. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  243. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  244. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  245. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  246. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -5,48 +5,13 @@ 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 Dict, Any, Optional, List
8
+ from typing import Dict, Any, Optional
9
9
  from decimal import Decimal
10
- from pydantic import BaseModel, Field
10
+ import requests
11
+ from django.utils import timezone
11
12
  from django_cfg.modules.django_logger import get_logger
12
13
  from ..types import ProviderResponse, ServiceOperationResult
13
-
14
-
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")
33
-
34
-
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")
14
+ from .models import ProviderConfig, PaymentRequest, WithdrawalRequest
50
15
 
51
16
 
52
17
  class BaseProvider(ABC):
@@ -75,7 +40,90 @@ class BaseProvider(ABC):
75
40
  @property
76
41
  def is_sandbox(self) -> bool:
77
42
  """Check if provider is in sandbox mode."""
78
- return self.config.sandbox
43
+ return self.config.sandbox_mode
44
+
45
+ # Provider configuration methods (to be overridden by specific providers)
46
+ def get_fee_percentage(self, currency_code: str = None, currency_type: str = None) -> Decimal:
47
+ """
48
+ Get fee percentage for currency.
49
+
50
+ Args:
51
+ currency_code: Currency code (e.g., 'BTC', 'ETH')
52
+ currency_type: Currency type ('fiat', 'crypto')
53
+
54
+ Returns:
55
+ Fee percentage as decimal (0.005 = 0.5%)
56
+ """
57
+ return Decimal('0.005') # Default 0.5%
58
+
59
+ def get_fixed_fee_usd(self, currency_code: str = None, currency_type: str = None) -> Decimal:
60
+ """
61
+ Get fixed fee in USD for currency.
62
+
63
+ Args:
64
+ currency_code: Currency code (e.g., 'BTC', 'ETH')
65
+ currency_type: Currency type ('fiat', 'crypto')
66
+
67
+ Returns:
68
+ Fixed fee in USD
69
+ """
70
+ return Decimal('0.0') # Default no fixed fee
71
+
72
+ def get_min_amount_usd(self, currency_code: str = None, currency_type: str = None, is_stable: bool = False) -> Decimal:
73
+ """
74
+ Get minimum amount in USD for currency.
75
+
76
+ Args:
77
+ currency_code: Currency code (e.g., 'BTC', 'ETH')
78
+ currency_type: Currency type ('fiat', 'crypto')
79
+ is_stable: Whether currency is a stablecoin
80
+
81
+ Returns:
82
+ Minimum amount in USD
83
+ """
84
+ if currency_type == 'fiat':
85
+ return Decimal('1.0')
86
+ elif is_stable:
87
+ return Decimal('0.01')
88
+ else:
89
+ return Decimal('0.000001')
90
+
91
+ def get_max_amount_usd(self, currency_code: str = None, currency_type: str = None) -> Decimal:
92
+ """
93
+ Get maximum amount in USD for currency.
94
+
95
+ Args:
96
+ currency_code: Currency code (e.g., 'BTC', 'ETH')
97
+ currency_type: Currency type ('fiat', 'crypto')
98
+
99
+ Returns:
100
+ Maximum amount in USD
101
+ """
102
+ return Decimal('1000000.0') # Default 1M USD
103
+
104
+ def get_confirmation_blocks(self, network_code: str) -> int:
105
+ """
106
+ Get confirmation blocks for network.
107
+
108
+ Args:
109
+ network_code: Network code (e.g., 'btc', 'eth')
110
+
111
+ Returns:
112
+ Number of confirmation blocks
113
+ """
114
+ return 1 # Default 1 confirmation
115
+
116
+ def get_network_name(self, network_code: str) -> str:
117
+ """
118
+ Get human-readable network name.
119
+
120
+ Args:
121
+ network_code: Network code (e.g., 'btc', 'eth')
122
+
123
+ Returns:
124
+ Human-readable network name
125
+ """
126
+ return network_code.upper() if network_code else 'Unknown'
79
127
 
80
128
  @abstractmethod
81
129
  def create_payment(self, request: PaymentRequest) -> ProviderResponse:
@@ -103,6 +151,61 @@ class BaseProvider(ABC):
103
151
  """
104
152
  pass
105
153
 
154
+ def refresh_payment_status(self, provider_payment_id: str, force_update: bool = True) -> ProviderResponse:
155
+ """
156
+ Refresh payment status with enhanced error handling and caching control.
157
+
158
+ This method provides additional functionality over get_payment_status:
159
+ - Enhanced error handling
160
+ - Optional caching control
161
+ - Detailed logging
162
+
163
+ Args:
164
+ provider_payment_id: Provider's payment ID
165
+ force_update: Whether to bypass cache and force fresh data
166
+
167
+ Returns:
168
+ ProviderResponse: Current payment status with enhanced metadata
169
+ """
170
+ self.logger.info(f"Refreshing payment status for {provider_payment_id}", extra={
171
+ 'provider': self.name,
172
+ 'payment_id': provider_payment_id,
173
+ 'force_update': force_update
174
+ })
175
+
176
+ try:
177
+ # Call the provider-specific implementation
178
+ result = self.get_payment_status(provider_payment_id)
179
+
180
+ # Add refresh metadata
181
+ if result.success and hasattr(result, 'raw_response') and result.raw_response:
182
+ result.raw_response['_refresh_metadata'] = {
183
+ 'refreshed_at': timezone.now().isoformat(),
184
+ 'provider': self.name,
185
+ 'force_update': force_update
186
+ }
187
+
188
+ self.logger.info(f"Payment status refreshed successfully", extra={
189
+ 'provider': self.name,
190
+ 'payment_id': provider_payment_id,
191
+ 'status': getattr(result, 'status', 'unknown')
192
+ })
193
+
194
+ return result
195
+
196
+ except Exception as e:
197
+ self.logger.error(f"Failed to refresh payment status", extra={
198
+ 'provider': self.name,
199
+ 'payment_id': provider_payment_id,
200
+ 'error': str(e)
201
+ })
202
+
203
+ return ProviderResponse(
204
+ success=False,
205
+ error_message=f"Failed to refresh payment status: {str(e)}",
206
+ raw_response={'error': str(e), 'provider': self.name}
207
+ )
208
+
106
209
  @abstractmethod
107
210
  def get_supported_currencies(self) -> ServiceOperationResult:
108
211
  """
@@ -113,6 +216,16 @@ class BaseProvider(ABC):
113
216
  """
114
217
  pass
115
218
 
219
+ @abstractmethod
220
+ def sync_currencies_to_db(self):
221
+ """
222
+ Sync currencies from provider API to database.
223
+
224
+ Returns:
225
+ CurrencySyncResult: Synchronization result
226
+ """
227
+ pass
228
+
116
229
  @abstractmethod
117
230
  def validate_webhook(self, payload: Dict[str, Any], signature: str = None) -> ServiceOperationResult:
118
231
  """
@@ -127,6 +240,129 @@ class BaseProvider(ABC):
127
240
  """
128
241
  pass
129
242
 
243
+ # Withdrawal/Payout Methods
244
+
245
+ def supports_withdrawals(self) -> bool:
246
+ """
247
+ Check if provider supports withdrawals/payouts.
248
+
249
+ Returns:
250
+ bool: True if provider supports withdrawals
251
+ """
252
+ return False # Default: most payment processors don't support withdrawals
253
+
254
+ def create_withdrawal(self, request: WithdrawalRequest) -> ProviderResponse:
255
+ """
256
+ Create withdrawal/payout request with provider.
257
+
258
+ Args:
259
+ request: Withdrawal creation request
260
+
261
+ Returns:
262
+ ProviderResponse: Provider response with withdrawal details
263
+
264
+ Raises:
265
+ NotImplementedError: If provider doesn't support withdrawals
266
+ """
267
+ if not self.supports_withdrawals():
268
+ return ProviderResponse(
269
+ success=False,
270
+ error_message=f"Provider {self.name} does not support withdrawals",
271
+ raw_response={'error': 'withdrawals_not_supported'}
272
+ )
273
+
274
+ raise NotImplementedError("Subclasses must implement create_withdrawal if they support withdrawals")
275
+
276
+ def get_withdrawal_status(self, provider_withdrawal_id: str) -> ProviderResponse:
277
+ """
278
+ Get withdrawal status from provider.
279
+
280
+ Args:
281
+ provider_withdrawal_id: Provider's withdrawal ID
282
+
283
+ Returns:
284
+ ProviderResponse: Current withdrawal status
285
+
286
+ Raises:
287
+ NotImplementedError: If provider doesn't support withdrawals
288
+ """
289
+ if not self.supports_withdrawals():
290
+ return ProviderResponse(
291
+ success=False,
292
+ error_message=f"Provider {self.name} does not support withdrawals",
293
+ raw_response={'error': 'withdrawals_not_supported'}
294
+ )
295
+
296
+ raise NotImplementedError("Subclasses must implement get_withdrawal_status if they support withdrawals")
297
+
298
+ def cancel_withdrawal(self, provider_withdrawal_id: str) -> ProviderResponse:
299
+ """
300
+ Cancel pending withdrawal.
301
+
302
+ Args:
303
+ provider_withdrawal_id: Provider's withdrawal ID
304
+
305
+ Returns:
306
+ ProviderResponse: Cancellation result
307
+
308
+ Raises:
309
+ NotImplementedError: If provider doesn't support withdrawal cancellation
310
+ """
311
+ if not self.supports_withdrawals():
312
+ return ProviderResponse(
313
+ success=False,
314
+ error_message=f"Provider {self.name} does not support withdrawals",
315
+ raw_response={'error': 'withdrawals_not_supported'}
316
+ )
317
+
318
+ return ProviderResponse(
319
+ success=False,
320
+ error_message=f"Provider {self.name} does not support withdrawal cancellation",
321
+ raw_response={'error': 'withdrawal_cancellation_not_supported'}
322
+ )
323
+
324
+ def get_withdrawal_fees(self, currency_code: str) -> ServiceOperationResult:
325
+ """
326
+ Get withdrawal fees for specific currency.
327
+
328
+ Args:
329
+ currency_code: Currency code
330
+
331
+ Returns:
332
+ ServiceOperationResult: Fee information or not supported
333
+ """
334
+ if not self.supports_withdrawals():
335
+ return ServiceOperationResult(
336
+ success=False,
337
+ message=f"Provider {self.name} does not support withdrawals"
338
+ )
339
+
340
+ return ServiceOperationResult(
341
+ success=False,
342
+ message="Withdrawal fee information not available"
343
+ )
344
+
345
+ def get_minimum_withdrawal_amount(self, currency_code: str) -> ServiceOperationResult:
346
+ """
347
+ Get minimum withdrawal amount for specific currency.
348
+
349
+ Args:
350
+ currency_code: Currency code
351
+
352
+ Returns:
353
+ ServiceOperationResult: Minimum amount or not supported
354
+ """
355
+ if not self.supports_withdrawals():
356
+ return ServiceOperationResult(
357
+ success=False,
358
+ message=f"Provider {self.name} does not support withdrawals"
359
+ )
360
+
361
+ return ServiceOperationResult(
362
+ success=False,
363
+ message="Minimum withdrawal amount information not available"
364
+ )
365
+
130
366
  def get_exchange_rate(self, from_currency: str, to_currency: str) -> ServiceOperationResult:
131
367
  """
132
368
  Get exchange rate from provider (optional).
@@ -257,7 +493,33 @@ class BaseProvider(ABC):
257
493
  })
258
494
 
259
495
  # Handle response
260
- response.raise_for_status()
496
+ try:
497
+ response.raise_for_status()
498
+ except requests.exceptions.HTTPError as e:
499
+ # Log the error with response content for debugging
500
+ error_content = response.text if response.text else "No response content"
501
+ self.logger.error(f"HTTP {response.status_code} error from {self.name}", extra={
502
+ 'status_code': response.status_code,
503
+ 'error_content': error_content[:500], # Limit content length
504
+ 'url': response.url
505
+ })
506
+
507
+ # Handle specific HTTP errors
508
+ if response.status_code == 403:
509
+ if "blocked" in error_content.lower() or "nowpayments.io" in error_content:
510
+ raise Exception(f"IP address blocked by {self.name}. Please contact {self.name} support or use a different IP/VPN.")
511
+ else:
512
+ raise Exception(f"Access forbidden by {self.name}. Check API key and permissions.")
513
+ elif response.status_code == 401:
514
+ raise Exception(f"Authentication failed with {self.name}. Check API key.")
515
+ elif response.status_code == 400:
516
+ raise Exception(f"Bad request to {self.name}: {error_content[:200]}")
517
+ elif response.status_code == 429:
518
+ raise Exception(f"Rate limit exceeded for {self.name}. Please try again later.")
519
+ elif response.status_code >= 500:
520
+ raise Exception(f"{self.name} server error ({response.status_code}). Please try again later.")
521
+ else:
522
+ raise Exception(f"HTTP {response.status_code} error from {self.name}: {error_content[:200]}")
261
523
 
262
524
  try:
263
525
  return response.json()
@@ -0,0 +1,42 @@
1
+ """
2
+ Universal provider models for Universal Payment System v2.0.
3
+
4
+ Common models used across all payment providers.
5
+ """
6
+
7
+ from .base import (
8
+ ProviderConfig,
9
+ PaymentRequest,
10
+ WithdrawalRequest,
11
+ ProviderMetadata,
12
+ ProviderType,
13
+ ProviderStatus
14
+ )
15
+ from .universal import (
16
+ UniversalCurrency,
17
+ UniversalCurrenciesResponse,
18
+ CurrencySyncResult
19
+ )
20
+ from .providers import (
21
+ ProviderEnum,
22
+ PROVIDER_METADATA
23
+ )
24
+
25
+ __all__ = [
26
+ # Base models
27
+ 'ProviderConfig',
28
+ 'PaymentRequest',
29
+ 'WithdrawalRequest',
30
+ 'ProviderMetadata',
31
+ 'ProviderType',
32
+ 'ProviderStatus',
33
+
34
+ # Universal models
35
+ 'UniversalCurrency',
36
+ 'UniversalCurrenciesResponse',
37
+ 'CurrencySyncResult',
38
+
39
+ # Provider definitions
40
+ 'ProviderEnum',
41
+ 'PROVIDER_METADATA'
42
+ ]
@@ -0,0 +1,145 @@
1
+ """
2
+ Base provider models for Universal Payment System v2.0.
3
+
4
+ Core Pydantic models for provider configuration and requests.
5
+ """
6
+
7
+ from typing import Dict, Any, Optional, List
8
+ from decimal import Decimal
9
+ from pydantic import BaseModel, Field, ConfigDict
10
+ from enum import Enum
11
+
12
+
13
+ class ProviderType(str, Enum):
14
+ """Provider type classification."""
15
+
16
+ CRYPTO = "crypto"
17
+ FIAT = "fiat"
18
+ HYBRID = "hybrid" # Supports both fiat and crypto
19
+
20
+
21
+ class ProviderStatus(str, Enum):
22
+ """Provider operational status."""
23
+
24
+ ACTIVE = "active"
25
+ MAINTENANCE = "maintenance"
26
+ DEPRECATED = "deprecated"
27
+ DISABLED = "disabled"
28
+
29
+
30
+ class ProviderConfig(BaseModel):
31
+ """
32
+ Base provider configuration with Pydantic v2.
33
+
34
+ Common configuration fields for all payment providers.
35
+ """
36
+ model_config = ConfigDict(
37
+ validate_assignment=True,
38
+ extra="forbid",
39
+ str_strip_whitespace=True,
40
+ frozen=False
41
+ )
42
+
43
+ provider_name: str = Field(..., description="Provider name")
44
+ api_key: str = Field(..., description="Provider API key")
45
+ api_url: str = Field(..., description="Provider API URL")
46
+ enabled: bool = Field(default=True, description="Whether provider is enabled")
47
+ sandbox_mode: bool = Field(default=False, description="Sandbox mode")
48
+ timeout: int = Field(default=30, ge=5, le=300, description="Request timeout in seconds")
49
+ retry_attempts: int = Field(default=3, ge=0, le=10, description="Number of retry attempts")
50
+ retry_delay: int = Field(default=5, ge=1, le=60, description="Delay between retries in seconds")
51
+ min_amount_usd: float = Field(default=1.0, ge=0.01, description="Minimum amount in USD")
52
+ max_amount_usd: float = Field(default=50000.0, ge=1.0, description="Maximum amount in USD")
53
+ supported_currencies: List[str] = Field(default_factory=list, description="Supported currencies")
54
+ webhook_secret: Optional[str] = Field(None, description="Webhook secret for validation")
55
+
56
+
57
+ class PaymentRequest(BaseModel):
58
+ """
59
+ Universal payment request for providers with Pydantic v2.
60
+
61
+ Standardized payment creation request across all providers.
62
+ """
63
+ model_config = ConfigDict(
64
+ validate_assignment=True,
65
+ extra="forbid",
66
+ str_strip_whitespace=True
67
+ )
68
+
69
+ amount_usd: float = Field(..., gt=0, description="Amount in USD")
70
+ currency_code: str = Field(..., min_length=3, max_length=10, description="Currency code")
71
+ order_id: str = Field(..., min_length=1, max_length=100, description="Internal order/payment ID")
72
+ callback_url: Optional[str] = Field(None, description="Success callback URL")
73
+ cancel_url: Optional[str] = Field(None, description="Cancel URL")
74
+ description: Optional[str] = Field(None, max_length=500, description="Payment description")
75
+ customer_email: Optional[str] = Field(None, description="Customer email")
76
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
77
+
78
+
79
+ class WithdrawalRequest(BaseModel):
80
+ """
81
+ Universal withdrawal request for providers with Pydantic v2.
82
+
83
+ Standardized withdrawal/payout request across all providers.
84
+ """
85
+ model_config = ConfigDict(
86
+ validate_assignment=True,
87
+ extra="forbid",
88
+ str_strip_whitespace=True
89
+ )
90
+
91
+ amount: float = Field(..., gt=0, description="Withdrawal amount in crypto currency")
92
+ currency_code: str = Field(..., min_length=3, max_length=10, description="Cryptocurrency code")
93
+ destination_address: str = Field(..., min_length=10, description="Destination wallet address")
94
+ withdrawal_id: str = Field(..., min_length=1, max_length=100, description="Internal withdrawal ID")
95
+ callback_url: Optional[str] = Field(None, description="Withdrawal status callback URL")
96
+ description: Optional[str] = Field(None, max_length=500, description="Withdrawal description")
97
+ extra_id: Optional[str] = Field(None, description="Extra ID for destination (memo, tag, etc.)")
98
+ priority: Optional[str] = Field(default="normal", description="Transaction priority (low, normal, high)")
99
+
100
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
101
+
102
+
103
+ class ProviderMetadata(BaseModel):
104
+ """
105
+ Provider metadata with classification and features.
106
+
107
+ Contains provider-specific information for routing and display.
108
+ """
109
+ model_config = ConfigDict(
110
+ validate_assignment=True,
111
+ extra="forbid",
112
+ frozen=True # Metadata should be immutable
113
+ )
114
+
115
+ name: str = Field(..., description="Human-readable provider name")
116
+ provider_type: ProviderType = Field(..., description="Provider type (crypto/fiat/hybrid)")
117
+ status: ProviderStatus = Field(default=ProviderStatus.ACTIVE, description="Provider status")
118
+ priority: int = Field(default=100, ge=0, le=1000, description="Provider priority (lower = higher priority)")
119
+
120
+ # Feature flags
121
+ supports_fiat: bool = Field(default=False, description="Supports fiat currencies")
122
+ supports_crypto: bool = Field(default=True, description="Supports cryptocurrencies")
123
+ supports_webhooks: bool = Field(default=True, description="Supports webhook notifications")
124
+ supports_refunds: bool = Field(default=False, description="Supports payment refunds")
125
+ supports_partial_payments: bool = Field(default=False, description="Supports partial payments")
126
+
127
+ # Limits and fees
128
+ min_amount_usd: float = Field(default=1.0, ge=0.01, description="Minimum payment amount in USD")
129
+ max_amount_usd: float = Field(default=50000.0, ge=1.0, description="Maximum payment amount in USD")
130
+ fee_percentage: float = Field(default=0.0, ge=0.0, le=100.0, description="Fee percentage")
131
+ fixed_fee_usd: float = Field(default=0.0, ge=0.0, description="Fixed fee in USD")
132
+
133
+ # Geographic and regulatory
134
+ supported_countries: List[str] = Field(default_factory=list, description="Supported country codes")
135
+ restricted_countries: List[str] = Field(default_factory=list, description="Restricted country codes")
136
+ requires_kyc: bool = Field(default=False, description="Requires KYC verification")
137
+
138
+ # Technical details
139
+ api_version: str = Field(default="v1", description="API version")
140
+ documentation_url: Optional[str] = Field(None, description="API documentation URL")
141
+ status_page_url: Optional[str] = Field(None, description="Provider status page URL")
142
+
143
+ # Additional metadata
144
+ tags: List[str] = Field(default_factory=list, description="Provider tags for categorization")
145
+ description: Optional[str] = Field(None, max_length=500, description="Provider description")
@@ -0,0 +1,87 @@
1
+ """
2
+ Provider enums and metadata for Universal Payment System v2.0.
3
+
4
+ Provider definitions with comprehensive metadata.
5
+ """
6
+
7
+ from enum import Enum
8
+ from typing import List, Dict
9
+ from .base import ProviderMetadata, ProviderType, ProviderStatus
10
+
11
+
12
+ class ProviderEnum(Enum):
13
+ """Enumeration of supported payment providers."""
14
+
15
+ NOWPAYMENTS = "nowpayments"
16
+
17
+ @classmethod
18
+ def get_all_providers(cls) -> List[str]:
19
+ """Get list of all provider values."""
20
+ return [provider.value for provider in cls]
21
+
22
+ @classmethod
23
+ def is_valid_provider(cls, provider_name: str) -> bool:
24
+ """Check if provider name is valid."""
25
+ return provider_name in cls.get_all_providers()
26
+
27
+ @classmethod
28
+ def get_crypto_providers(cls) -> List[str]:
29
+ """Get list of crypto-supporting providers."""
30
+ return [
31
+ provider.value for provider in cls
32
+ if PROVIDER_METADATA[provider.value].supports_crypto
33
+ ]
34
+
35
+ @classmethod
36
+ def get_fiat_providers(cls) -> List[str]:
37
+ """Get list of fiat-supporting providers."""
38
+ return [
39
+ provider.value for provider in cls
40
+ if PROVIDER_METADATA[provider.value].supports_fiat
41
+ ]
42
+
43
+ @classmethod
44
+ def get_active_providers(cls) -> List[str]:
45
+ """Get list of active providers."""
46
+ return [
47
+ provider.value for provider in cls
48
+ if PROVIDER_METADATA[provider.value].status == ProviderStatus.ACTIVE
49
+ ]
50
+
51
+
52
+ # Provider metadata registry
53
+ PROVIDER_METADATA: Dict[str, ProviderMetadata] = {
54
+ ProviderEnum.NOWPAYMENTS.value: ProviderMetadata(
55
+ name="NowPayments",
56
+ provider_type=ProviderType.CRYPTO,
57
+ status=ProviderStatus.ACTIVE,
58
+ priority=10, # High priority
59
+
60
+ # Features
61
+ supports_fiat=False,
62
+ supports_crypto=True,
63
+ supports_webhooks=True,
64
+ supports_refunds=False,
65
+ supports_partial_payments=False,
66
+
67
+ # Limits and fees
68
+ min_amount_usd=1.0,
69
+ max_amount_usd=50000.0,
70
+ fee_percentage=0.5, # 0.5% fee
71
+ fixed_fee_usd=0.0,
72
+
73
+ # Geographic
74
+ supported_countries=[], # Global support
75
+ restricted_countries=["US"], # Example restriction
76
+ requires_kyc=False,
77
+
78
+ # Technical
79
+ api_version="v1",
80
+ documentation_url="https://documenter.getpostman.com/view/7907941/S1a32n38",
81
+ status_page_url="https://status.nowpayments.io/",
82
+
83
+ # Additional
84
+ tags=["crypto", "bitcoin", "ethereum", "altcoins", "instant"],
85
+ description="Cryptocurrency payment processor supporting 300+ cryptocurrencies with instant settlements"
86
+ )
87
+ }