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

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