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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/api/health/views.py +4 -2
  3. django_cfg/apps/knowbase/config/settings.py +16 -15
  4. django_cfg/apps/payments/README.md +326 -0
  5. django_cfg/apps/payments/admin/__init__.py +20 -9
  6. django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
  7. django_cfg/apps/payments/admin/balance_admin.py +592 -297
  8. django_cfg/apps/payments/admin/currencies_admin.py +600 -108
  9. django_cfg/apps/payments/admin/filters.py +306 -199
  10. django_cfg/apps/payments/admin/payments_admin.py +470 -64
  11. django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
  12. django_cfg/apps/payments/admin_interface/__init__.py +18 -0
  13. django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
  14. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
  23. django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
  24. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
  25. django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
  26. django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
  27. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
  28. django_cfg/apps/payments/apps.py +34 -9
  29. django_cfg/apps/payments/config/__init__.py +28 -51
  30. django_cfg/apps/payments/config/constance/__init__.py +22 -0
  31. django_cfg/apps/payments/config/constance/config_service.py +123 -0
  32. django_cfg/apps/payments/config/constance/fields.py +69 -0
  33. django_cfg/apps/payments/config/constance/settings.py +160 -0
  34. django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
  35. django_cfg/apps/payments/config/helpers.py +130 -0
  36. django_cfg/apps/payments/management/__init__.py +1 -3
  37. django_cfg/apps/payments/management/commands/__init__.py +1 -3
  38. django_cfg/apps/payments/management/commands/manage_currencies.py +381 -0
  39. django_cfg/apps/payments/management/commands/manage_providers.py +408 -0
  40. django_cfg/apps/payments/middleware/__init__.py +3 -1
  41. django_cfg/apps/payments/middleware/api_access.py +329 -222
  42. django_cfg/apps/payments/middleware/rate_limiting.py +343 -163
  43. django_cfg/apps/payments/middleware/usage_tracking.py +250 -238
  44. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  45. django_cfg/apps/payments/models/__init__.py +16 -20
  46. django_cfg/apps/payments/models/api_keys.py +121 -43
  47. django_cfg/apps/payments/models/balance.py +150 -115
  48. django_cfg/apps/payments/models/base.py +68 -15
  49. django_cfg/apps/payments/models/currencies.py +207 -67
  50. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  51. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  52. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  53. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  54. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  55. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  56. django_cfg/apps/payments/models/payments.py +235 -284
  57. django_cfg/apps/payments/models/subscriptions.py +257 -177
  58. django_cfg/apps/payments/models/tariffs.py +147 -40
  59. django_cfg/apps/payments/services/__init__.py +209 -56
  60. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  61. django_cfg/apps/payments/services/cache/{simple_cache.py → cache_service.py} +112 -12
  62. django_cfg/apps/payments/services/core/__init__.py +10 -6
  63. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  64. django_cfg/apps/payments/services/core/base.py +166 -0
  65. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  66. django_cfg/apps/payments/services/core/payment_service.py +344 -468
  67. django_cfg/apps/payments/services/core/subscription_service.py +425 -484
  68. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  69. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  70. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  71. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  72. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  73. django_cfg/apps/payments/services/providers/base.py +232 -71
  74. django_cfg/apps/payments/services/providers/nowpayments.py +404 -219
  75. django_cfg/apps/payments/services/providers/registry.py +429 -80
  76. django_cfg/apps/payments/services/types/__init__.py +78 -0
  77. django_cfg/apps/payments/services/types/data.py +177 -0
  78. django_cfg/apps/payments/services/types/requests.py +150 -0
  79. django_cfg/apps/payments/services/types/responses.py +156 -0
  80. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  81. django_cfg/apps/payments/signals/__init__.py +33 -8
  82. django_cfg/apps/payments/signals/api_key_signals.py +211 -130
  83. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  84. django_cfg/apps/payments/signals/payment_signals.py +129 -98
  85. django_cfg/apps/payments/signals/subscription_signals.py +195 -143
  86. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  87. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  88. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  89. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  90. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  91. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  92. django_cfg/apps/payments/urls.py +46 -47
  93. django_cfg/apps/payments/urls_admin.py +49 -0
  94. django_cfg/apps/payments/views/api/__init__.py +101 -0
  95. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  96. django_cfg/apps/payments/views/api/balances.py +381 -0
  97. django_cfg/apps/payments/views/api/base.py +298 -0
  98. django_cfg/apps/payments/views/api/currencies.py +402 -0
  99. django_cfg/apps/payments/views/api/payments.py +415 -0
  100. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  101. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  102. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  103. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  104. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  105. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  106. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  107. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  108. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  109. django_cfg/apps/tasks/urls.py +0 -2
  110. django_cfg/apps/tasks/urls_admin.py +14 -0
  111. django_cfg/apps/urls.py +4 -4
  112. django_cfg/config.py +1 -1
  113. django_cfg/core/config.py +75 -4
  114. django_cfg/core/generation.py +25 -4
  115. django_cfg/core/integration/README.md +363 -0
  116. django_cfg/core/integration/__init__.py +47 -0
  117. django_cfg/core/integration/commands_collector.py +239 -0
  118. django_cfg/core/integration/display/__init__.py +15 -0
  119. django_cfg/core/integration/display/base.py +157 -0
  120. django_cfg/core/integration/display/ngrok.py +164 -0
  121. django_cfg/core/integration/display/startup.py +815 -0
  122. django_cfg/core/integration/url_integration.py +123 -0
  123. django_cfg/core/integration/version_checker.py +160 -0
  124. django_cfg/management/commands/auto_generate.py +4 -0
  125. django_cfg/management/commands/check_settings.py +6 -0
  126. django_cfg/management/commands/clear_constance.py +5 -2
  127. django_cfg/management/commands/create_token.py +6 -0
  128. django_cfg/management/commands/list_urls.py +6 -0
  129. django_cfg/management/commands/migrate_all.py +6 -0
  130. django_cfg/management/commands/migrator.py +3 -0
  131. django_cfg/management/commands/rundramatiq.py +6 -0
  132. django_cfg/management/commands/runserver_ngrok.py +51 -29
  133. django_cfg/management/commands/script.py +6 -0
  134. django_cfg/management/commands/show_config.py +12 -2
  135. django_cfg/management/commands/show_urls.py +4 -0
  136. django_cfg/management/commands/superuser.py +6 -0
  137. django_cfg/management/commands/task_clear.py +4 -1
  138. django_cfg/management/commands/task_status.py +3 -1
  139. django_cfg/management/commands/test_email.py +3 -0
  140. django_cfg/management/commands/test_telegram.py +6 -0
  141. django_cfg/management/commands/test_twilio.py +6 -0
  142. django_cfg/management/commands/tree.py +6 -0
  143. django_cfg/management/commands/validate_config.py +155 -149
  144. django_cfg/models/constance.py +31 -11
  145. django_cfg/models/payments.py +175 -498
  146. django_cfg/modules/django_currency/__init__.py +16 -11
  147. django_cfg/modules/django_currency/clients/__init__.py +4 -4
  148. django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
  149. django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
  150. django_cfg/modules/django_currency/core/__init__.py +1 -7
  151. django_cfg/modules/django_currency/core/converter.py +18 -23
  152. django_cfg/modules/django_currency/core/models.py +122 -11
  153. django_cfg/modules/django_currency/database/__init__.py +4 -4
  154. django_cfg/modules/django_currency/database/database_loader.py +190 -309
  155. django_cfg/modules/django_logger.py +160 -146
  156. django_cfg/modules/django_unfold/dashboard.py +65 -12
  157. django_cfg/registry/core.py +1 -0
  158. django_cfg/template_archive/django_sample.zip +0 -0
  159. django_cfg/templates/admin/components/action_grid.html +9 -9
  160. django_cfg/templates/admin/components/metric_card.html +5 -5
  161. django_cfg/templates/admin/components/status_badge.html +2 -2
  162. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
  163. django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
  164. django_cfg/templates/admin/snippets/components/system_health.html +1 -1
  165. django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
  166. django_cfg/utils/smart_defaults.py +222 -571
  167. django_cfg/utils/toolkit.py +51 -11
  168. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/METADATA +5 -4
  169. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/RECORD +172 -182
  170. django_cfg/apps/payments/__init__.py +0 -8
  171. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  172. django_cfg/apps/payments/config/module.py +0 -70
  173. django_cfg/apps/payments/config/providers.py +0 -105
  174. django_cfg/apps/payments/config/settings.py +0 -96
  175. django_cfg/apps/payments/config/utils.py +0 -52
  176. django_cfg/apps/payments/decorators.py +0 -291
  177. django_cfg/apps/payments/management/commands/README.md +0 -178
  178. django_cfg/apps/payments/management/commands/currency_stats.py +0 -323
  179. django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
  180. django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
  181. django_cfg/apps/payments/managers/__init__.py +0 -22
  182. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  183. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  184. django_cfg/apps/payments/managers/currency_manager.py +0 -83
  185. django_cfg/apps/payments/managers/payment_manager.py +0 -44
  186. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  187. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  188. django_cfg/apps/payments/models/events.py +0 -73
  189. django_cfg/apps/payments/serializers/__init__.py +0 -56
  190. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  191. django_cfg/apps/payments/serializers/balance.py +0 -59
  192. django_cfg/apps/payments/serializers/currencies.py +0 -55
  193. django_cfg/apps/payments/serializers/payments.py +0 -62
  194. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  195. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  196. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  197. django_cfg/apps/payments/services/cache/base.py +0 -30
  198. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  199. django_cfg/apps/payments/services/internal_types.py +0 -297
  200. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  201. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  202. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -222
  203. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  204. django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
  205. django_cfg/apps/payments/services/providers/cryptomus.py +0 -311
  206. django_cfg/apps/payments/services/security/__init__.py +0 -34
  207. django_cfg/apps/payments/services/security/error_handler.py +0 -637
  208. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  209. django_cfg/apps/payments/services/security/webhook_validator.py +0 -475
  210. django_cfg/apps/payments/services/validators/__init__.py +0 -8
  211. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  212. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  213. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  214. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  215. django_cfg/apps/payments/tasks/__init__.py +0 -12
  216. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  217. django_cfg/apps/payments/templates/payments/base.html +0 -182
  218. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  219. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  220. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -36
  221. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  222. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -27
  223. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -144
  224. django_cfg/apps/payments/templates/payments/dashboard.html +0 -346
  225. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  226. django_cfg/apps/payments/urls_templates.py +0 -52
  227. django_cfg/apps/payments/utils/__init__.py +0 -45
  228. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  229. django_cfg/apps/payments/utils/config_utils.py +0 -245
  230. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  231. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  232. django_cfg/apps/payments/views/__init__.py +0 -62
  233. django_cfg/apps/payments/views/api_key_views.py +0 -164
  234. django_cfg/apps/payments/views/balance_views.py +0 -75
  235. django_cfg/apps/payments/views/currency_views.py +0 -111
  236. django_cfg/apps/payments/views/payment_views.py +0 -149
  237. django_cfg/apps/payments/views/subscription_views.py +0 -135
  238. django_cfg/apps/payments/views/tariff_views.py +0 -131
  239. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  240. django_cfg/apps/payments/views/templates/ajax.py +0 -312
  241. django_cfg/apps/payments/views/templates/base.py +0 -204
  242. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  243. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  244. django_cfg/apps/payments/views/templates/payment_management.py +0 -164
  245. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  246. django_cfg/apps/payments/views/templates/stats.py +0 -240
  247. django_cfg/apps/payments/views/templates/utils.py +0 -181
  248. django_cfg/apps/payments/views/webhook_views.py +0 -266
  249. django_cfg/apps/payments/viewsets.py +0 -65
  250. django_cfg/core/integration.py +0 -160
  251. django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
  252. django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
  253. django_cfg/template_archive/.gitignore +0 -1
  254. django_cfg/template_archive/__init__.py +0 -0
  255. django_cfg/urls.py +0 -33
  256. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
  257. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
  258. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,387 @@
1
+ """
2
+ Payment serializers for the Universal Payment System v2.0.
3
+
4
+ DRF serializers with Pydantic integration and service layer validation.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+ from decimal import Decimal
9
+ from typing import Dict, Any
10
+ from django.contrib.auth import get_user_model
11
+
12
+ from ...models import UniversalPayment
13
+ from ...services import get_payment_service, PaymentCreateRequest, PaymentStatusRequest
14
+ from django_cfg.modules.django_logger import get_logger
15
+
16
+ User = get_user_model()
17
+ logger = get_logger("payment_serializers")
18
+
19
+
20
+ class PaymentListSerializer(serializers.ModelSerializer):
21
+ """
22
+ Lightweight serializer for payment lists.
23
+
24
+ Optimized for list views with minimal data.
25
+ """
26
+
27
+ status_display = serializers.CharField(source='get_status_display', read_only=True)
28
+ amount_display = serializers.CharField(source='amount_display', read_only=True)
29
+ crypto_amount_display = serializers.CharField(source='crypto_amount_display', read_only=True)
30
+
31
+ class Meta:
32
+ model = UniversalPayment
33
+ fields = [
34
+ 'id',
35
+ 'amount_usd',
36
+ 'crypto_amount',
37
+ 'currency_code',
38
+ 'provider',
39
+ 'status',
40
+ 'status_display',
41
+ 'amount_display',
42
+ 'crypto_amount_display',
43
+ 'created_at',
44
+ 'expires_at',
45
+ ]
46
+ read_only_fields = fields
47
+
48
+
49
+ class PaymentSerializer(serializers.ModelSerializer):
50
+ """
51
+ Complete payment serializer with full details.
52
+
53
+ Used for detail views and updates.
54
+ """
55
+
56
+ user = serializers.StringRelatedField(read_only=True)
57
+ status_display = serializers.CharField(source='get_status_display', read_only=True)
58
+ status_color = serializers.CharField(source='status_color', read_only=True)
59
+ amount_display = serializers.CharField(source='amount_display', read_only=True)
60
+ crypto_amount_display = serializers.CharField(source='crypto_amount_display', read_only=True)
61
+
62
+ # Status check methods
63
+ is_pending = serializers.BooleanField(source='is_pending', read_only=True)
64
+ is_completed = serializers.BooleanField(source='is_completed', read_only=True)
65
+ is_failed = serializers.BooleanField(source='is_failed', read_only=True)
66
+ is_expired = serializers.BooleanField(source='is_expired', read_only=True)
67
+ can_be_cancelled = serializers.BooleanField(source='can_be_cancelled', read_only=True)
68
+ can_be_refunded = serializers.BooleanField(source='can_be_refunded', read_only=True)
69
+
70
+ class Meta:
71
+ model = UniversalPayment
72
+ fields = [
73
+ 'id',
74
+ 'user',
75
+ 'amount_usd',
76
+ 'crypto_amount',
77
+ 'currency_code',
78
+ 'provider',
79
+ 'status',
80
+ 'status_display',
81
+ 'status_color',
82
+ 'amount_display',
83
+ 'crypto_amount_display',
84
+ 'provider_payment_id',
85
+ 'payment_url',
86
+ 'qr_code_url',
87
+ 'wallet_address',
88
+ 'callback_url',
89
+ 'cancel_url',
90
+ 'description',
91
+ 'metadata',
92
+ 'transaction_hash',
93
+ 'confirmation_blocks',
94
+ 'created_at',
95
+ 'updated_at',
96
+ 'expires_at',
97
+ 'completed_at',
98
+ # Status methods
99
+ 'is_pending',
100
+ 'is_completed',
101
+ 'is_failed',
102
+ 'is_expired',
103
+ 'can_be_cancelled',
104
+ 'can_be_refunded',
105
+ ]
106
+ read_only_fields = [
107
+ 'id',
108
+ 'user',
109
+ 'crypto_amount',
110
+ 'provider_payment_id',
111
+ 'payment_url',
112
+ 'qr_code_url',
113
+ 'wallet_address',
114
+ 'transaction_hash',
115
+ 'confirmation_blocks',
116
+ 'created_at',
117
+ 'updated_at',
118
+ 'completed_at',
119
+ 'status_display',
120
+ 'status_color',
121
+ 'amount_display',
122
+ 'crypto_amount_display',
123
+ 'is_pending',
124
+ 'is_completed',
125
+ 'is_failed',
126
+ 'is_expired',
127
+ 'can_be_cancelled',
128
+ 'can_be_refunded',
129
+ ]
130
+
131
+
132
+ class PaymentCreateSerializer(serializers.Serializer):
133
+ """
134
+ Payment creation serializer with Pydantic integration.
135
+
136
+ Validates input and delegates to PaymentService.
137
+ """
138
+
139
+ amount_usd = serializers.FloatField(
140
+ min_value=1.0,
141
+ max_value=50000.0,
142
+ help_text="Amount in USD (1.00 - 50,000.00)"
143
+ )
144
+ currency_code = serializers.ChoiceField(
145
+ choices=[
146
+ ('BTC', 'Bitcoin'),
147
+ ('ETH', 'Ethereum'),
148
+ ('LTC', 'Litecoin'),
149
+ ('XMR', 'Monero'),
150
+ ('USDT', 'Tether'),
151
+ ('USDC', 'USD Coin'),
152
+ ('ADA', 'Cardano'),
153
+ ('DOT', 'Polkadot'),
154
+ ],
155
+ help_text="Cryptocurrency to receive"
156
+ )
157
+ provider = serializers.ChoiceField(
158
+ choices=[('nowpayments', 'NowPayments')],
159
+ default='nowpayments',
160
+ help_text="Payment provider"
161
+ )
162
+ callback_url = serializers.URLField(
163
+ required=False,
164
+ allow_blank=True,
165
+ help_text="Success callback URL"
166
+ )
167
+ cancel_url = serializers.URLField(
168
+ required=False,
169
+ allow_blank=True,
170
+ help_text="Cancellation URL"
171
+ )
172
+ description = serializers.CharField(
173
+ required=False,
174
+ allow_blank=True,
175
+ max_length=500,
176
+ help_text="Payment description"
177
+ )
178
+ metadata = serializers.JSONField(
179
+ required=False,
180
+ default=dict,
181
+ help_text="Additional metadata"
182
+ )
183
+
184
+ def validate(self, attrs: Dict[str, Any]) -> Dict[str, Any]:
185
+ """Validate payment creation data."""
186
+ try:
187
+ # Create Pydantic request for validation
188
+ request = PaymentCreateRequest(
189
+ user_id=self.context['request'].user.id,
190
+ **attrs
191
+ )
192
+
193
+ # Store validated request for create method
194
+ self._validated_request = request
195
+ return attrs
196
+
197
+ except Exception as e:
198
+ logger.error(f"Payment validation failed: {e}")
199
+ raise serializers.ValidationError(f"Invalid payment data: {e}")
200
+
201
+ def create(self, validated_data: Dict[str, Any]) -> UniversalPayment:
202
+ """Create payment using PaymentService."""
203
+ try:
204
+ payment_service = get_payment_service()
205
+
206
+ # Use pre-validated Pydantic request
207
+ result = payment_service.create_payment(self._validated_request)
208
+
209
+ if result.success:
210
+ # Get the created payment from database
211
+ payment = UniversalPayment.objects.get(id=result.payment_id)
212
+
213
+ logger.info(f"Payment created successfully", extra={
214
+ 'payment_id': result.payment_id,
215
+ 'user_id': self._validated_request.user_id,
216
+ 'amount_usd': self._validated_request.amount_usd
217
+ })
218
+
219
+ return payment
220
+ else:
221
+ logger.error(f"Payment creation failed: {result.message}")
222
+ raise serializers.ValidationError(result.message)
223
+
224
+ except Exception as e:
225
+ logger.error(f"Payment creation error: {e}")
226
+ raise serializers.ValidationError(f"Payment creation failed: {e}")
227
+
228
+ def to_representation(self, instance: UniversalPayment) -> Dict[str, Any]:
229
+ """Return full payment data after creation."""
230
+ return PaymentSerializer(instance, context=self.context).data
231
+
232
+
233
+ class PaymentStatusSerializer(serializers.Serializer):
234
+ """
235
+ Payment status check serializer.
236
+
237
+ Used for checking payment status via provider API.
238
+ """
239
+
240
+ force_provider_check = serializers.BooleanField(
241
+ default=False,
242
+ help_text="Force check with payment provider"
243
+ )
244
+
245
+ def validate(self, attrs: Dict[str, Any]) -> Dict[str, Any]:
246
+ """Validate status check request."""
247
+ payment_id = self.context.get('payment_id')
248
+ if not payment_id:
249
+ raise serializers.ValidationError("Payment ID is required")
250
+
251
+ user = self.context['request'].user
252
+
253
+ # Create Pydantic request
254
+ self._status_request = PaymentStatusRequest(
255
+ payment_id=payment_id,
256
+ user_id=user.id,
257
+ force_provider_check=attrs.get('force_provider_check', False)
258
+ )
259
+
260
+ return attrs
261
+
262
+ def save(self) -> Dict[str, Any]:
263
+ """Check payment status using PaymentService."""
264
+ try:
265
+ payment_service = get_payment_service()
266
+ result = payment_service.get_payment_status(self._status_request)
267
+
268
+ if result.success:
269
+ # Get updated payment from database
270
+ payment = UniversalPayment.objects.get(id=result.payment_id)
271
+
272
+ return {
273
+ 'success': True,
274
+ 'payment': PaymentSerializer(payment, context=self.context).data,
275
+ 'provider_checked': self._status_request.force_provider_check,
276
+ 'message': result.message
277
+ }
278
+ else:
279
+ return {
280
+ 'success': False,
281
+ 'error': result.message,
282
+ 'error_code': result.error_code
283
+ }
284
+
285
+ except Exception as e:
286
+ logger.error(f"Payment status check error: {e}")
287
+ return {
288
+ 'success': False,
289
+ 'error': f"Status check failed: {e}",
290
+ 'error_code': 'status_check_error'
291
+ }
292
+
293
+
294
+ class PaymentCancelSerializer(serializers.Serializer):
295
+ """
296
+ Payment cancellation serializer.
297
+
298
+ Used for cancelling payments.
299
+ """
300
+
301
+ reason = serializers.CharField(
302
+ required=False,
303
+ allow_blank=True,
304
+ max_length=500,
305
+ help_text="Cancellation reason"
306
+ )
307
+
308
+ def save(self) -> Dict[str, Any]:
309
+ """Cancel payment using PaymentService."""
310
+ try:
311
+ payment_id = self.context.get('payment_id')
312
+ if not payment_id:
313
+ raise serializers.ValidationError("Payment ID is required")
314
+
315
+ payment_service = get_payment_service()
316
+ result = payment_service.cancel_payment(
317
+ payment_id=payment_id,
318
+ reason=self.validated_data.get('reason')
319
+ )
320
+
321
+ if result.success:
322
+ # Get updated payment
323
+ payment = UniversalPayment.objects.get(id=result.payment_id)
324
+
325
+ return {
326
+ 'success': True,
327
+ 'payment': PaymentSerializer(payment, context=self.context).data,
328
+ 'message': result.message
329
+ }
330
+ else:
331
+ return {
332
+ 'success': False,
333
+ 'error': result.message,
334
+ 'error_code': result.error_code
335
+ }
336
+
337
+ except Exception as e:
338
+ logger.error(f"Payment cancellation error: {e}")
339
+ return {
340
+ 'success': False,
341
+ 'error': f"Cancellation failed: {e}",
342
+ 'error_code': 'cancellation_error'
343
+ }
344
+
345
+
346
+ class PaymentStatsSerializer(serializers.Serializer):
347
+ """
348
+ Payment statistics serializer.
349
+
350
+ Used for payment analytics and reporting.
351
+ """
352
+
353
+ days = serializers.IntegerField(
354
+ default=30,
355
+ min_value=1,
356
+ max_value=365,
357
+ help_text="Number of days to analyze"
358
+ )
359
+
360
+ def save(self) -> Dict[str, Any]:
361
+ """Get payment statistics using PaymentService."""
362
+ try:
363
+ payment_service = get_payment_service()
364
+ result = payment_service.get_payment_stats(
365
+ days=self.validated_data['days']
366
+ )
367
+
368
+ if result.success:
369
+ return {
370
+ 'success': True,
371
+ 'stats': result.data,
372
+ 'message': result.message
373
+ }
374
+ else:
375
+ return {
376
+ 'success': False,
377
+ 'error': result.message,
378
+ 'error_code': result.error_code
379
+ }
380
+
381
+ except Exception as e:
382
+ logger.error(f"Payment stats error: {e}")
383
+ return {
384
+ 'success': False,
385
+ 'error': f"Stats generation failed: {e}",
386
+ 'error_code': 'stats_error'
387
+ }