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
@@ -1,160 +0,0 @@
1
- """
2
- Constance settings integration for Payments app.
3
-
4
- Simplified settings management using Pydantic models and Constance.
5
- """
6
-
7
- from typing import Dict, Any, Optional
8
- from pydantic import BaseModel, Field
9
- from django_cfg.modules.django_logger import get_logger
10
-
11
- logger = get_logger(__name__)
12
-
13
-
14
- class PaymentConstanceSettings(BaseModel):
15
- """
16
- Pydantic model for payments Constance settings.
17
-
18
- Provides type-safe access to dynamic settings with validation.
19
- """
20
-
21
- # Core settings
22
- enabled: bool = Field(default=True, description="Enable payments system")
23
- middleware_enabled: bool = Field(default=True, description="Enable payments middleware")
24
-
25
- # Rate limiting
26
- rate_limiting_enabled: bool = Field(default=True, description="Enable rate limiting")
27
- anonymous_rate_limit: int = Field(default=60, description="Anonymous user rate limit")
28
- authenticated_rate_limit: int = Field(default=300, description="Authenticated user rate limit")
29
-
30
- # Usage tracking
31
- usage_tracking_enabled: bool = Field(default=True, description="Enable usage tracking")
32
- track_anonymous_usage: bool = Field(default=False, description="Track anonymous usage")
33
-
34
- # Cache timeouts
35
- api_key_cache_timeout: int = Field(default=300, description="API key cache timeout")
36
- rate_limit_cache_timeout: int = Field(default=3600, description="Rate limit cache timeout")
37
- session_cache_timeout: int = Field(default=1800, description="Session cache timeout")
38
- default_cache_timeout: int = Field(default=600, description="Default cache timeout")
39
-
40
- # Provider settings
41
- nowpayments_api_key: str = Field(default="", description="NowPayments API key")
42
- nowpayments_ipn_secret: str = Field(default="", description="NowPayments IPN secret")
43
- nowpayments_sandbox_mode: bool = Field(default=True, description="NowPayments sandbox mode")
44
-
45
- # Currency settings
46
- auto_update_rates: bool = Field(default=True, description="Auto update currency rates")
47
- rate_update_interval_hours: int = Field(default=1, description="Rate update interval")
48
-
49
- @classmethod
50
- def from_constance(cls) -> 'PaymentConstanceSettings':
51
- """
52
- Create instance from Constance settings.
53
-
54
- Returns:
55
- PaymentConstanceSettings instance with current Constance values
56
- """
57
- try:
58
- from constance import config
59
- from ..django_cfg_integration import PaymentsConfigManager
60
-
61
- # Get defaults from initialized Pydantic config using PaymentsConfigManager
62
- pydantic_config = PaymentsConfigManager.get_payments_config()
63
-
64
- return cls(
65
- # Static settings from Pydantic (not in Constance anymore)
66
- enabled=pydantic_config.enabled,
67
- middleware_enabled=pydantic_config.middleware_enabled,
68
- rate_limiting_enabled=pydantic_config.rate_limiting_enabled,
69
- anonymous_rate_limit=pydantic_config.default_rate_limits['anonymous'],
70
- authenticated_rate_limit=pydantic_config.default_rate_limits['authenticated'],
71
- usage_tracking_enabled=pydantic_config.usage_tracking_enabled,
72
-
73
- # Dynamic settings from Constance (only what's actually in Constance)
74
- track_anonymous_usage=getattr(config, 'PAYMENTS_TRACK_ANONYMOUS_USAGE', pydantic_config.track_anonymous_usage),
75
-
76
- # Cache timeouts from Pydantic (not in Constance anymore)
77
- api_key_cache_timeout=pydantic_config.cache_timeouts['api_key'],
78
- rate_limit_cache_timeout=pydantic_config.cache_timeouts['rate_limit'],
79
- session_cache_timeout=pydantic_config.cache_timeouts['session'],
80
- default_cache_timeout=pydantic_config.cache_timeouts['default'],
81
-
82
- # Provider settings from Constance (sensitive data)
83
- nowpayments_api_key=getattr(config, 'PAYMENTS_NOWPAYMENTS_API_KEY', ''),
84
- nowpayments_ipn_secret=getattr(config, 'PAYMENTS_NOWPAYMENTS_IPN_SECRET', ''),
85
- nowpayments_sandbox_mode=getattr(config, 'PAYMENTS_NOWPAYMENTS_SANDBOX_MODE', True),
86
-
87
- # Currency settings from Pydantic (automatic now)
88
- auto_update_rates=True, # Always automatic
89
- rate_update_interval_hours=1, # Fixed interval
90
- )
91
- except ImportError:
92
- logger.warning("Constance not available, using default settings")
93
- return cls()
94
-
95
- def to_dict(self) -> Dict[str, Any]:
96
- """Convert to dictionary for easy access."""
97
- return self.model_dump()
98
-
99
- def get_rate_limits(self) -> Dict[str, int]:
100
- """Get rate limits as dictionary."""
101
- return {
102
- 'anonymous': self.anonymous_rate_limit,
103
- 'authenticated': self.authenticated_rate_limit,
104
- }
105
-
106
- def get_cache_timeouts(self) -> Dict[str, int]:
107
- """Get cache timeouts as dictionary."""
108
- return {
109
- 'api_key': self.api_key_cache_timeout,
110
- 'rate_limit': self.rate_limit_cache_timeout,
111
- 'session': self.session_cache_timeout,
112
- 'default': self.default_cache_timeout,
113
- }
114
-
115
- def get_provider_config(self, provider: str) -> Dict[str, Any]:
116
- """Get provider-specific configuration."""
117
- if provider.lower() == 'nowpayments':
118
- # Provider is enabled if it has API key configured
119
- is_enabled = bool(self.nowpayments_api_key and self.nowpayments_api_key.strip())
120
- return {
121
- 'enabled': is_enabled,
122
- 'api_key': self.nowpayments_api_key,
123
- 'ipn_secret': self.nowpayments_ipn_secret,
124
- 'sandbox_mode': self.nowpayments_sandbox_mode,
125
- }
126
- return {}
127
-
128
-
129
- def get_payment_settings() -> PaymentConstanceSettings:
130
- """
131
- Get current payment settings from Constance.
132
-
133
- Returns:
134
- PaymentConstanceSettings instance with current values
135
- """
136
- return PaymentConstanceSettings.from_constance()
137
-
138
-
139
- def is_feature_enabled(feature: str) -> bool:
140
- """
141
- Check if a specific feature is enabled.
142
-
143
- Args:
144
- feature: Feature name (e.g., 'rate_limiting', 'usage_tracking')
145
-
146
- Returns:
147
- True if feature is enabled, False otherwise
148
- """
149
- settings = get_payment_settings()
150
-
151
- feature_map = {
152
- 'payments': settings.enabled,
153
- 'middleware': settings.middleware_enabled,
154
- 'rate_limiting': settings.rate_limiting_enabled,
155
- 'usage_tracking': settings.usage_tracking_enabled,
156
- 'anonymous_tracking': settings.track_anonymous_usage,
157
- 'auto_rates': settings.auto_update_rates,
158
- }
159
-
160
- return feature_map.get(feature, False)
@@ -1,478 +0,0 @@
1
- """
2
- NowPayments provider for the Universal Payment System v2.0.
3
-
4
- Implementation of NowPayments API integration with unified interface.
5
- """
6
-
7
- from typing import Dict, Any, Optional, List
8
- from decimal import Decimal
9
- from datetime import datetime
10
- from pydantic import BaseModel, Field
11
- import hmac
12
- import hashlib
13
- import json
14
-
15
- from .base import BaseProvider, ProviderConfig, PaymentRequest
16
- from ..types import ProviderResponse, ServiceOperationResult, NowPaymentsWebhook
17
- from django_cfg.modules.django_currency import convert_currency
18
-
19
-
20
- class NowPaymentsConfig(ProviderConfig):
21
- """
22
- NowPayments-specific configuration.
23
-
24
- Extends base config with NowPayments-specific fields.
25
- """
26
-
27
- ipn_secret: Optional[str] = Field(None, description="IPN callback secret")
28
-
29
- def __init__(self, **data):
30
- """Initialize with NowPayments defaults."""
31
- # Set NowPayments-specific defaults
32
- if 'provider_name' not in data:
33
- data['provider_name'] = 'nowpayments'
34
-
35
- if 'api_url' not in data:
36
- sandbox = data.get('sandbox', False)
37
- data['api_url'] = (
38
- 'https://api-sandbox.nowpayments.io/v1' if sandbox
39
- else 'https://api.nowpayments.io/v1'
40
- )
41
-
42
- if 'supported_currencies' not in data:
43
- data['supported_currencies'] = [
44
- 'BTC', 'ETH', 'LTC', 'XMR', 'USDT', 'USDC', 'ADA', 'DOT'
45
- ]
46
-
47
- super().__init__(**data)
48
-
49
-
50
- class NowPaymentsProvider(BaseProvider):
51
- """
52
- NowPayments provider implementation.
53
-
54
- Handles payment creation, status checking, and webhook validation for NowPayments.
55
- """
56
-
57
- def __init__(self, config: NowPaymentsConfig):
58
- """Initialize NowPayments provider."""
59
- super().__init__(config)
60
- self.config: NowPaymentsConfig = config
61
-
62
- def create_payment(self, request: PaymentRequest) -> ProviderResponse:
63
- """
64
- Create payment with NowPayments.
65
-
66
- Args:
67
- request: Payment creation request
68
-
69
- Returns:
70
- ProviderResponse: NowPayments response
71
- """
72
- try:
73
- self.logger.info("Creating NowPayments payment", extra={
74
- 'amount_usd': request.amount_usd,
75
- 'currency': request.currency_code,
76
- 'order_id': request.order_id
77
- })
78
-
79
- # Convert USD to crypto amount
80
- try:
81
- crypto_amount = convert_currency(
82
- request.amount_usd,
83
- 'USD',
84
- request.currency_code
85
- )
86
- except Exception as e:
87
- return self._create_provider_response(
88
- success=False,
89
- raw_response={'error': f'Currency conversion failed: {e}'},
90
- error_message=f'Currency conversion failed: {e}'
91
- )
92
-
93
- # Prepare NowPayments request
94
- payment_data = {
95
- 'price_amount': request.amount_usd,
96
- 'price_currency': 'USD',
97
- 'pay_currency': request.currency_code,
98
- 'order_id': request.order_id,
99
- 'order_description': request.description or f'Payment {request.order_id}',
100
- }
101
-
102
- # Add optional fields
103
- if request.callback_url:
104
- payment_data['success_url'] = request.callback_url
105
-
106
- if request.cancel_url:
107
- payment_data['cancel_url'] = request.cancel_url
108
-
109
- if request.customer_email:
110
- payment_data['customer_email'] = request.customer_email
111
-
112
- # Add IPN callback URL (would be configured via webhook service)
113
- if hasattr(self, '_ipn_callback_url'):
114
- payment_data['ipn_callback_url'] = self._ipn_callback_url
115
-
116
- # Make API request
117
- headers = {
118
- 'x-api-key': self.config.api_key
119
- }
120
-
121
- response_data = self._make_request(
122
- method='POST',
123
- endpoint='payment',
124
- data=payment_data,
125
- headers=headers
126
- )
127
-
128
- # Parse NowPayments response
129
- if 'payment_id' in response_data:
130
- # Successful payment creation
131
- payment_url = response_data.get('invoice_url') or response_data.get('pay_url')
132
-
133
- return self._create_provider_response(
134
- success=True,
135
- raw_response=response_data,
136
- provider_payment_id=response_data['payment_id'],
137
- status='waiting', # NowPayments initial status
138
- amount=Decimal(str(crypto_amount)),
139
- currency=request.currency_code,
140
- payment_url=payment_url,
141
- wallet_address=response_data.get('pay_address'),
142
- qr_code_url=response_data.get('qr_code_url'),
143
- expires_at=self._parse_expiry_time(response_data.get('expiration_estimate_date'))
144
- )
145
- else:
146
- # Error response
147
- error_message = response_data.get('message', 'Unknown error')
148
- return self._create_provider_response(
149
- success=False,
150
- raw_response=response_data,
151
- error_message=error_message
152
- )
153
-
154
- except Exception as e:
155
- self.logger.error(f"NowPayments payment creation failed: {e}", extra={
156
- 'order_id': request.order_id
157
- })
158
-
159
- return self._create_provider_response(
160
- success=False,
161
- raw_response={'error': str(e)},
162
- error_message=f'Payment creation failed: {e}'
163
- )
164
-
165
- def get_payment_status(self, provider_payment_id: str) -> ProviderResponse:
166
- """
167
- Get payment status from NowPayments.
168
-
169
- Args:
170
- provider_payment_id: NowPayments payment ID
171
-
172
- Returns:
173
- ProviderResponse: Current payment status
174
- """
175
- try:
176
- self.logger.debug("Getting NowPayments payment status", extra={
177
- 'payment_id': provider_payment_id
178
- })
179
-
180
- headers = {
181
- 'x-api-key': self.config.api_key
182
- }
183
-
184
- response_data = self._make_request(
185
- method='GET',
186
- endpoint=f'payment/{provider_payment_id}',
187
- headers=headers
188
- )
189
-
190
- if 'payment_status' in response_data:
191
- return self._create_provider_response(
192
- success=True,
193
- raw_response=response_data,
194
- provider_payment_id=provider_payment_id,
195
- status=response_data['payment_status'],
196
- amount=Decimal(str(response_data.get('pay_amount', 0))),
197
- currency=response_data.get('pay_currency'),
198
- wallet_address=response_data.get('pay_address')
199
- )
200
- else:
201
- error_message = response_data.get('message', 'Payment not found')
202
- return self._create_provider_response(
203
- success=False,
204
- raw_response=response_data,
205
- error_message=error_message
206
- )
207
-
208
- except Exception as e:
209
- self.logger.error(f"NowPayments status check failed: {e}", extra={
210
- 'payment_id': provider_payment_id
211
- })
212
-
213
- return self._create_provider_response(
214
- success=False,
215
- raw_response={'error': str(e)},
216
- error_message=f'Status check failed: {e}'
217
- )
218
-
219
- def get_supported_currencies(self) -> ServiceOperationResult:
220
- """
221
- Get supported currencies from NowPayments.
222
-
223
- Returns:
224
- ServiceOperationResult: List of supported currencies
225
- """
226
- try:
227
- self.logger.debug("Getting NowPayments supported currencies")
228
-
229
- headers = {
230
- 'x-api-key': self.config.api_key
231
- }
232
-
233
- response_data = self._make_request(
234
- method='GET',
235
- endpoint='currencies',
236
- headers=headers
237
- )
238
-
239
- if 'currencies' in response_data:
240
- currencies = response_data['currencies']
241
-
242
- return ServiceOperationResult(
243
- success=True,
244
- message=f"Retrieved {len(currencies)} supported currencies",
245
- data={
246
- 'currencies': currencies,
247
- 'count': len(currencies),
248
- 'provider': self.name
249
- }
250
- )
251
- else:
252
- return ServiceOperationResult(
253
- success=False,
254
- message="Failed to get currencies from NowPayments",
255
- error_code="currencies_fetch_failed"
256
- )
257
-
258
- except Exception as e:
259
- self.logger.error(f"NowPayments currencies fetch failed: {e}")
260
-
261
- return ServiceOperationResult(
262
- success=False,
263
- message=f"Currencies fetch error: {e}",
264
- error_code="currencies_fetch_error"
265
- )
266
-
267
- def validate_webhook(self, payload: Dict[str, Any], signature: str = None) -> ServiceOperationResult:
268
- """
269
- Validate NowPayments IPN webhook.
270
-
271
- Args:
272
- payload: Webhook payload
273
- signature: HMAC signature (optional)
274
-
275
- Returns:
276
- ServiceOperationResult: Validation result
277
- """
278
- try:
279
- self.logger.debug("Validating NowPayments webhook", extra={
280
- 'has_signature': bool(signature),
281
- 'payment_id': payload.get('payment_id')
282
- })
283
-
284
- # Validate payload structure
285
- try:
286
- webhook_data = NowPaymentsWebhook(**payload)
287
- except Exception as e:
288
- return ServiceOperationResult(
289
- success=False,
290
- message=f"Invalid webhook payload: {e}",
291
- error_code="invalid_payload"
292
- )
293
-
294
- # Validate signature if provided and secret is configured
295
- if signature and self.config.ipn_secret:
296
- is_valid_signature = self._validate_ipn_signature(payload, signature)
297
- if not is_valid_signature:
298
- return ServiceOperationResult(
299
- success=False,
300
- message="Invalid webhook signature",
301
- error_code="invalid_signature"
302
- )
303
-
304
- return ServiceOperationResult(
305
- success=True,
306
- message="Webhook validated successfully",
307
- data={
308
- 'provider': self.name,
309
- 'payment_id': webhook_data.payment_id,
310
- 'status': webhook_data.payment_status,
311
- 'signature_validated': bool(signature and self.config.ipn_secret),
312
- 'webhook_data': webhook_data.model_dump()
313
- }
314
- )
315
-
316
- except Exception as e:
317
- self.logger.error(f"NowPayments webhook validation failed: {e}")
318
-
319
- return ServiceOperationResult(
320
- success=False,
321
- message=f"Webhook validation error: {e}",
322
- error_code="validation_error"
323
- )
324
-
325
- def get_exchange_rate(self, from_currency: str, to_currency: str) -> ServiceOperationResult:
326
- """
327
- Get exchange rate from NowPayments.
328
-
329
- Args:
330
- from_currency: Source currency
331
- to_currency: Target currency
332
-
333
- Returns:
334
- ServiceOperationResult: Exchange rate
335
- """
336
- try:
337
- self.logger.debug("Getting NowPayments exchange rate", extra={
338
- 'from': from_currency,
339
- 'to': to_currency
340
- })
341
-
342
- headers = {
343
- 'x-api-key': self.config.api_key
344
- }
345
-
346
- response_data = self._make_request(
347
- method='GET',
348
- endpoint=f'exchange-amount/{from_currency}-{to_currency}',
349
- headers=headers
350
- )
351
-
352
- if 'estimated_amount' in response_data:
353
- rate = float(response_data['estimated_amount'])
354
-
355
- return ServiceOperationResult(
356
- success=True,
357
- message="Exchange rate retrieved",
358
- data={
359
- 'from_currency': from_currency,
360
- 'to_currency': to_currency,
361
- 'rate': rate,
362
- 'provider': self.name
363
- }
364
- )
365
- else:
366
- return ServiceOperationResult(
367
- success=False,
368
- message="Exchange rate not available",
369
- error_code="rate_not_available"
370
- )
371
-
372
- except Exception as e:
373
- self.logger.error(f"NowPayments exchange rate failed: {e}")
374
-
375
- return ServiceOperationResult(
376
- success=False,
377
- message=f"Exchange rate error: {e}",
378
- error_code="rate_fetch_error"
379
- )
380
-
381
- def _validate_ipn_signature(self, payload: Dict[str, Any], signature: str) -> bool:
382
- """
383
- Validate IPN signature using HMAC-SHA512.
384
-
385
- Args:
386
- payload: Webhook payload
387
- signature: Received signature
388
-
389
- Returns:
390
- bool: True if signature is valid
391
- """
392
- try:
393
- # Sort payload and create canonical string
394
- sorted_payload = json.dumps(payload, separators=(',', ':'), sort_keys=True)
395
-
396
- # Calculate expected signature
397
- expected_signature = hmac.new(
398
- self.config.ipn_secret.encode('utf-8'),
399
- sorted_payload.encode('utf-8'),
400
- hashlib.sha512
401
- ).hexdigest()
402
-
403
- # Compare signatures
404
- return hmac.compare_digest(expected_signature, signature)
405
-
406
- except Exception as e:
407
- self.logger.error(f"Signature validation error: {e}")
408
- return False
409
-
410
- def _parse_expiry_time(self, expiry_str: Optional[str]) -> Optional[datetime]:
411
- """
412
- Parse NowPayments expiry time string.
413
-
414
- Args:
415
- expiry_str: Expiry time string from NowPayments
416
-
417
- Returns:
418
- Optional[datetime]: Parsed expiry time
419
- """
420
- if not expiry_str:
421
- return None
422
-
423
- try:
424
- # NowPayments typically returns ISO format
425
- return datetime.fromisoformat(expiry_str.replace('Z', '+00:00'))
426
- except Exception:
427
- self.logger.warning(f"Failed to parse expiry time: {expiry_str}")
428
- return None
429
-
430
- def set_ipn_callback_url(self, callback_url: str):
431
- """
432
- Set IPN callback URL for payments.
433
-
434
- Args:
435
- callback_url: IPN callback URL
436
- """
437
- self._ipn_callback_url = callback_url
438
- self.logger.info(f"Set IPN callback URL: {callback_url}")
439
-
440
- def health_check(self) -> ServiceOperationResult:
441
- """Perform NowPayments-specific health check."""
442
- try:
443
- # Test API connectivity by getting currencies
444
- currencies_result = self.get_supported_currencies()
445
-
446
- if currencies_result.success:
447
- currency_count = len(currencies_result.data.get('currencies', []))
448
-
449
- return ServiceOperationResult(
450
- success=True,
451
- message="NowPayments provider is healthy",
452
- data={
453
- 'provider': self.name,
454
- 'sandbox': self.is_sandbox,
455
- 'api_url': self.config.api_url,
456
- 'supported_currencies': currency_count,
457
- 'has_ipn_secret': bool(self.config.ipn_secret),
458
- 'api_key_configured': bool(self.config.api_key)
459
- }
460
- )
461
- else:
462
- return ServiceOperationResult(
463
- success=False,
464
- message="NowPayments API connectivity failed",
465
- error_code="api_connectivity_failed",
466
- data={
467
- 'provider': self.name,
468
- 'error': currencies_result.message
469
- }
470
- )
471
-
472
- except Exception as e:
473
- return ServiceOperationResult(
474
- success=False,
475
- message=f"NowPayments health check error: {e}",
476
- error_code="health_check_error",
477
- data={'provider': self.name}
478
- )