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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/api/health/views.py +4 -2
  3. django_cfg/apps/knowbase/config/settings.py +16 -15
  4. django_cfg/apps/payments/README.md +326 -0
  5. django_cfg/apps/payments/admin/__init__.py +20 -10
  6. django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
  7. django_cfg/apps/payments/admin/balance_admin.py +592 -297
  8. django_cfg/apps/payments/admin/currencies_admin.py +526 -222
  9. django_cfg/apps/payments/admin/filters.py +306 -199
  10. django_cfg/apps/payments/admin/payments_admin.py +465 -70
  11. django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
  12. django_cfg/apps/payments/admin_interface/__init__.py +18 -0
  13. django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
  14. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
  23. django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
  24. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
  25. django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
  26. django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
  27. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
  28. django_cfg/apps/payments/apps.py +34 -9
  29. django_cfg/apps/payments/config/__init__.py +28 -51
  30. django_cfg/apps/payments/config/constance/__init__.py +22 -0
  31. django_cfg/apps/payments/config/constance/config_service.py +123 -0
  32. django_cfg/apps/payments/config/constance/fields.py +69 -0
  33. django_cfg/apps/payments/config/constance/settings.py +160 -0
  34. django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
  35. django_cfg/apps/payments/config/helpers.py +130 -0
  36. django_cfg/apps/payments/management/__init__.py +1 -3
  37. django_cfg/apps/payments/management/commands/__init__.py +1 -3
  38. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +419 -0
  39. django_cfg/apps/payments/management/commands/currency_stats.py +297 -225
  40. django_cfg/apps/payments/management/commands/manage_currencies.py +303 -151
  41. django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
  42. django_cfg/apps/payments/management/commands/process_pending_payments.py +357 -0
  43. django_cfg/apps/payments/management/commands/test_providers.py +434 -0
  44. django_cfg/apps/payments/middleware/__init__.py +3 -1
  45. django_cfg/apps/payments/middleware/api_access.py +329 -222
  46. django_cfg/apps/payments/middleware/rate_limiting.py +342 -152
  47. django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
  48. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  49. django_cfg/apps/payments/models/__init__.py +13 -18
  50. django_cfg/apps/payments/models/api_keys.py +121 -43
  51. django_cfg/apps/payments/models/balance.py +153 -115
  52. django_cfg/apps/payments/models/base.py +68 -15
  53. django_cfg/apps/payments/models/currencies.py +172 -148
  54. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  55. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  56. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  57. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  58. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  59. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  60. django_cfg/apps/payments/models/payments.py +235 -285
  61. django_cfg/apps/payments/models/subscriptions.py +257 -177
  62. django_cfg/apps/payments/models/tariffs.py +147 -40
  63. django_cfg/apps/payments/services/__init__.py +209 -56
  64. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  65. django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
  66. django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
  67. django_cfg/apps/payments/services/{cache/base.py → cache_service/interfaces.py} +3 -1
  68. django_cfg/apps/payments/services/cache_service/keys.py +49 -0
  69. django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
  70. django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
  71. django_cfg/apps/payments/services/core/__init__.py +10 -6
  72. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  73. django_cfg/apps/payments/services/core/base.py +166 -0
  74. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  75. django_cfg/apps/payments/services/core/payment_service.py +371 -465
  76. django_cfg/apps/payments/services/core/subscription_service.py +425 -481
  77. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  78. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  79. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  80. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  81. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  82. django_cfg/apps/payments/services/providers/base.py +234 -174
  83. django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
  84. django_cfg/apps/payments/services/providers/registry.py +367 -301
  85. django_cfg/apps/payments/services/types/__init__.py +78 -0
  86. django_cfg/apps/payments/services/types/data.py +177 -0
  87. django_cfg/apps/payments/services/types/requests.py +150 -0
  88. django_cfg/apps/payments/services/types/responses.py +156 -0
  89. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  90. django_cfg/apps/payments/signals/__init__.py +33 -8
  91. django_cfg/apps/payments/signals/api_key_signals.py +210 -129
  92. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  93. django_cfg/apps/payments/signals/payment_signals.py +128 -103
  94. django_cfg/apps/payments/signals/subscription_signals.py +194 -142
  95. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  96. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  97. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  98. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  99. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  100. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  101. django_cfg/apps/payments/urls.py +45 -48
  102. django_cfg/apps/payments/urls_admin.py +33 -42
  103. django_cfg/apps/payments/views/api/__init__.py +101 -0
  104. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  105. django_cfg/apps/payments/views/api/balances.py +381 -0
  106. django_cfg/apps/payments/views/api/base.py +298 -0
  107. django_cfg/apps/payments/views/api/currencies.py +402 -0
  108. django_cfg/apps/payments/views/api/payments.py +415 -0
  109. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  110. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  111. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  112. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  113. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  114. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  115. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  116. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  117. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  118. django_cfg/config.py +1 -1
  119. django_cfg/core/config.py +40 -4
  120. django_cfg/core/generation.py +25 -4
  121. django_cfg/core/integration/README.md +363 -0
  122. django_cfg/core/integration/__init__.py +47 -0
  123. django_cfg/core/integration/commands_collector.py +239 -0
  124. django_cfg/core/integration/display/__init__.py +15 -0
  125. django_cfg/core/integration/display/base.py +157 -0
  126. django_cfg/core/integration/display/ngrok.py +164 -0
  127. django_cfg/core/integration/display/startup.py +815 -0
  128. django_cfg/core/integration/url_integration.py +123 -0
  129. django_cfg/core/integration/version_checker.py +160 -0
  130. django_cfg/management/commands/auto_generate.py +4 -0
  131. django_cfg/management/commands/check_settings.py +6 -0
  132. django_cfg/management/commands/clear_constance.py +5 -2
  133. django_cfg/management/commands/create_token.py +6 -0
  134. django_cfg/management/commands/list_urls.py +6 -0
  135. django_cfg/management/commands/migrate_all.py +6 -0
  136. django_cfg/management/commands/migrator.py +3 -0
  137. django_cfg/management/commands/rundramatiq.py +6 -0
  138. django_cfg/management/commands/runserver_ngrok.py +51 -29
  139. django_cfg/management/commands/script.py +6 -0
  140. django_cfg/management/commands/show_config.py +12 -2
  141. django_cfg/management/commands/show_urls.py +4 -0
  142. django_cfg/management/commands/superuser.py +6 -0
  143. django_cfg/management/commands/task_clear.py +4 -1
  144. django_cfg/management/commands/task_status.py +3 -1
  145. django_cfg/management/commands/test_email.py +3 -0
  146. django_cfg/management/commands/test_telegram.py +6 -0
  147. django_cfg/management/commands/test_twilio.py +6 -0
  148. django_cfg/management/commands/tree.py +6 -0
  149. django_cfg/management/commands/validate_config.py +155 -149
  150. django_cfg/models/constance.py +31 -11
  151. django_cfg/models/payments.py +175 -492
  152. django_cfg/modules/django_logger.py +160 -146
  153. django_cfg/modules/django_unfold/dashboard.py +64 -16
  154. django_cfg/registry/core.py +1 -0
  155. django_cfg/template_archive/django_sample.zip +0 -0
  156. django_cfg/utils/smart_defaults.py +227 -570
  157. django_cfg/utils/toolkit.py +51 -11
  158. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/METADATA +4 -1
  159. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/RECORD +162 -185
  160. django_cfg/apps/payments/__init__.py +0 -8
  161. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  162. django_cfg/apps/payments/config/module.py +0 -70
  163. django_cfg/apps/payments/config/providers.py +0 -105
  164. django_cfg/apps/payments/config/settings.py +0 -96
  165. django_cfg/apps/payments/config/utils.py +0 -52
  166. django_cfg/apps/payments/decorators.py +0 -291
  167. django_cfg/apps/payments/management/commands/README.md +0 -146
  168. django_cfg/apps/payments/managers/__init__.py +0 -23
  169. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  170. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  171. django_cfg/apps/payments/managers/currency_manager.py +0 -306
  172. django_cfg/apps/payments/managers/payment_manager.py +0 -192
  173. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  174. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  175. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
  176. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
  177. django_cfg/apps/payments/models/events.py +0 -73
  178. django_cfg/apps/payments/serializers/__init__.py +0 -57
  179. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  180. django_cfg/apps/payments/serializers/balance.py +0 -59
  181. django_cfg/apps/payments/serializers/currencies.py +0 -63
  182. django_cfg/apps/payments/serializers/payments.py +0 -62
  183. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  184. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  185. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  186. django_cfg/apps/payments/services/cache/simple_cache.py +0 -135
  187. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  188. django_cfg/apps/payments/services/internal_types.py +0 -461
  189. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  190. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  191. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
  192. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  193. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
  194. django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
  195. django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
  196. django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
  197. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
  198. django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
  199. django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
  200. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
  201. django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
  202. django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
  203. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
  204. django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
  205. django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
  206. django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
  207. django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
  208. django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
  209. django_cfg/apps/payments/services/security/__init__.py +0 -34
  210. django_cfg/apps/payments/services/security/error_handler.py +0 -635
  211. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  212. django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
  213. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  214. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  215. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  216. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  217. django_cfg/apps/payments/tasks/__init__.py +0 -12
  218. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  219. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
  220. django_cfg/apps/payments/templates/payments/base.html +0 -182
  221. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  222. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  223. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
  224. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  225. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
  226. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
  227. django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
  228. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
  229. django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
  230. django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
  231. django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
  232. django_cfg/apps/payments/templates/payments/stats.html +0 -261
  233. django_cfg/apps/payments/templates/payments/test.html +0 -213
  234. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  235. django_cfg/apps/payments/utils/__init__.py +0 -43
  236. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  237. django_cfg/apps/payments/utils/config_utils.py +0 -239
  238. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  239. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  240. django_cfg/apps/payments/views/__init__.py +0 -63
  241. django_cfg/apps/payments/views/api_key_views.py +0 -164
  242. django_cfg/apps/payments/views/balance_views.py +0 -75
  243. django_cfg/apps/payments/views/currency_views.py +0 -122
  244. django_cfg/apps/payments/views/payment_views.py +0 -149
  245. django_cfg/apps/payments/views/subscription_views.py +0 -135
  246. django_cfg/apps/payments/views/tariff_views.py +0 -131
  247. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  248. django_cfg/apps/payments/views/templates/ajax.py +0 -451
  249. django_cfg/apps/payments/views/templates/base.py +0 -212
  250. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  251. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  252. django_cfg/apps/payments/views/templates/payment_management.py +0 -158
  253. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  254. django_cfg/apps/payments/views/templates/stats.py +0 -244
  255. django_cfg/apps/payments/views/templates/utils.py +0 -181
  256. django_cfg/apps/payments/views/webhook_views.py +0 -266
  257. django_cfg/apps/payments/viewsets.py +0 -66
  258. django_cfg/core/integration.py +0 -160
  259. django_cfg/template_archive/.gitignore +0 -1
  260. django_cfg/template_archive/__init__.py +0 -0
  261. django_cfg/urls.py +0 -33
  262. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/WHEEL +0 -0
  263. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/entry_points.txt +0 -0
  264. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,402 @@
1
+ """
2
+ Currency ViewSets for the Universal Payment System v2.0.
3
+
4
+ DRF ViewSets for currency management with service integration.
5
+ """
6
+
7
+ from rest_framework import viewsets, permissions, status, generics
8
+ from rest_framework.decorators import action
9
+ from rest_framework.response import Response
10
+ from django_filters.rest_framework import DjangoFilterBackend
11
+ from django.utils import timezone
12
+
13
+ from .base import ReadOnlyPaymentViewSet
14
+ from ...models import Currency, Network, ProviderCurrency
15
+ from ...services import get_currency_service
16
+ from ..serializers.currencies import (
17
+ CurrencySerializer,
18
+ CurrencyListSerializer,
19
+ NetworkSerializer,
20
+ ProviderCurrencySerializer,
21
+ CurrencyConversionSerializer,
22
+ CurrencyRatesSerializer,
23
+ SupportedCurrenciesSerializer,
24
+ )
25
+ from django_cfg.modules.django_logger import get_logger
26
+
27
+ logger = get_logger("currency_viewsets")
28
+
29
+
30
+ class CurrencyViewSet(ReadOnlyPaymentViewSet):
31
+ """
32
+ Currency ViewSet: /api/currencies/
33
+
34
+ Read-only access to currency information with conversion capabilities.
35
+ """
36
+
37
+ queryset = Currency.objects.filter(is_active=True)
38
+ serializer_class = CurrencySerializer
39
+ permission_classes = [permissions.IsAuthenticated]
40
+ filterset_fields = ['currency_type', 'is_active']
41
+ search_fields = ['code', 'name', 'symbol']
42
+ ordering_fields = ['code', 'name', 'created_at']
43
+
44
+ serializer_classes = {
45
+ 'list': CurrencyListSerializer,
46
+ 'retrieve': CurrencySerializer,
47
+ }
48
+
49
+ @action(detail=False, methods=['get'])
50
+ def crypto(self, request):
51
+ """
52
+ Get only cryptocurrencies.
53
+
54
+ GET /api/currencies/crypto/
55
+ """
56
+ cryptos = self.get_queryset().filter(currency_type='crypto')
57
+ serializer = CurrencyListSerializer(cryptos, many=True)
58
+
59
+ return Response({
60
+ 'currencies': serializer.data,
61
+ 'count': len(serializer.data),
62
+ 'type': 'crypto',
63
+ 'generated_at': timezone.now().isoformat()
64
+ })
65
+
66
+ @action(detail=False, methods=['get'])
67
+ def fiat(self, request):
68
+ """
69
+ Get only fiat currencies.
70
+
71
+ GET /api/currencies/fiat/
72
+ """
73
+ fiats = self.get_queryset().filter(currency_type='fiat')
74
+ serializer = CurrencyListSerializer(fiats, many=True)
75
+
76
+ return Response({
77
+ 'currencies': serializer.data,
78
+ 'count': len(serializer.data),
79
+ 'type': 'fiat',
80
+ 'generated_at': timezone.now().isoformat()
81
+ })
82
+
83
+ @action(detail=False, methods=['get'])
84
+ def stable(self, request):
85
+ """
86
+ Get only stablecoins.
87
+
88
+ GET /api/currencies/stable/
89
+ """
90
+ stables = self.get_queryset().filter(
91
+ currency_type='crypto',
92
+ code__in=['USDT', 'USDC', 'DAI', 'BUSD', 'TUSD']
93
+ )
94
+ serializer = CurrencyListSerializer(stables, many=True)
95
+
96
+ return Response({
97
+ 'currencies': serializer.data,
98
+ 'count': len(serializer.data),
99
+ 'type': 'stable',
100
+ 'generated_at': timezone.now().isoformat()
101
+ })
102
+
103
+ @action(detail=True, methods=['get'])
104
+ def networks(self, request, pk=None):
105
+ """
106
+ Get networks for specific currency.
107
+
108
+ GET /api/currencies/{id}/networks/
109
+ """
110
+ currency = self.get_object()
111
+ networks = Network.objects.filter(currency=currency, is_active=True)
112
+ serializer = NetworkSerializer(networks, many=True)
113
+
114
+ return Response({
115
+ 'currency': CurrencyListSerializer(currency).data,
116
+ 'networks': serializer.data,
117
+ 'count': len(serializer.data),
118
+ 'generated_at': timezone.now().isoformat()
119
+ })
120
+
121
+ @action(detail=True, methods=['get'])
122
+ def providers(self, request, pk=None):
123
+ """
124
+ Get providers supporting specific currency.
125
+
126
+ GET /api/currencies/{id}/providers/
127
+ """
128
+ currency = self.get_object()
129
+ provider_currencies = ProviderCurrency.objects.filter(
130
+ currency=currency,
131
+ is_active=True
132
+ )
133
+ serializer = ProviderCurrencySerializer(provider_currencies, many=True)
134
+
135
+ return Response({
136
+ 'currency': CurrencyListSerializer(currency).data,
137
+ 'providers': serializer.data,
138
+ 'count': len(serializer.data),
139
+ 'generated_at': timezone.now().isoformat()
140
+ })
141
+
142
+ @action(detail=False, methods=['post'])
143
+ def convert(self, request):
144
+ """
145
+ Convert between currencies.
146
+
147
+ POST /api/currencies/convert/
148
+ """
149
+ serializer = CurrencyConversionSerializer(data=request.data)
150
+
151
+ if serializer.is_valid():
152
+ result = serializer.save()
153
+ return Response(result)
154
+
155
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
156
+
157
+ @action(detail=False, methods=['get'])
158
+ def rates(self, request):
159
+ """
160
+ Get current exchange rates.
161
+
162
+ GET /api/currencies/rates/?base_currency=USD&currencies=BTC,ETH
163
+ """
164
+ serializer = CurrencyRatesSerializer(data=request.query_params)
165
+
166
+ if serializer.is_valid():
167
+ result = serializer.save()
168
+ return Response(result)
169
+
170
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
171
+
172
+ @action(detail=False, methods=['get'])
173
+ def supported(self, request):
174
+ """
175
+ Get supported currencies from providers.
176
+
177
+ GET /api/currencies/supported/?provider=nowpayments&currency_type=crypto
178
+ """
179
+ serializer = SupportedCurrenciesSerializer(data=request.query_params)
180
+
181
+ if serializer.is_valid():
182
+ result = serializer.save()
183
+ return Response(result)
184
+
185
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
186
+
187
+
188
+ class NetworkViewSet(ReadOnlyPaymentViewSet):
189
+ """
190
+ Network ViewSet: /api/networks/
191
+
192
+ Read-only access to blockchain network information.
193
+ """
194
+
195
+ queryset = Network.objects.filter(is_active=True)
196
+ serializer_class = NetworkSerializer
197
+ permission_classes = [permissions.IsAuthenticated]
198
+ filterset_fields = ['currency', 'is_active']
199
+ search_fields = ['name', 'code']
200
+ ordering_fields = ['name', 'code', 'created_at']
201
+
202
+ def get_queryset(self):
203
+ """Optimize queryset with related objects."""
204
+ return super().get_queryset().select_related('currency')
205
+
206
+ @action(detail=False, methods=['get'])
207
+ def by_currency(self, request):
208
+ """
209
+ Get networks grouped by currency.
210
+
211
+ GET /api/networks/by_currency/
212
+ """
213
+ try:
214
+ queryset = self.filter_queryset(self.get_queryset())
215
+
216
+ networks_by_currency = {}
217
+ for network in queryset:
218
+ currency_code = network.currency.code
219
+
220
+ if currency_code not in networks_by_currency:
221
+ networks_by_currency[currency_code] = {
222
+ 'currency': CurrencyListSerializer(network.currency).data,
223
+ 'networks': []
224
+ }
225
+
226
+ networks_by_currency[currency_code]['networks'].append(
227
+ NetworkSerializer(network).data
228
+ )
229
+
230
+ return Response({
231
+ 'networks_by_currency': networks_by_currency,
232
+ 'total_currencies': len(networks_by_currency),
233
+ 'generated_at': timezone.now().isoformat()
234
+ })
235
+
236
+ except Exception as e:
237
+ logger.error(f"Networks by currency failed: {e}")
238
+ return Response(
239
+ {'error': f'Networks by currency failed: {e}'},
240
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR
241
+ )
242
+
243
+
244
+ class ProviderCurrencyViewSet(ReadOnlyPaymentViewSet):
245
+ """
246
+ Provider Currency ViewSet: /api/provider-currencies/
247
+
248
+ Read-only access to provider-specific currency information.
249
+ """
250
+
251
+ queryset = ProviderCurrency.objects.filter(is_enabled=True)
252
+ serializer_class = ProviderCurrencySerializer
253
+ permission_classes = [permissions.IsAuthenticated]
254
+ filterset_fields = ['provider', 'currency', 'network', 'is_enabled']
255
+ search_fields = ['provider_currency_code']
256
+ ordering_fields = ['provider', 'created_at', 'min_amount', 'max_amount']
257
+
258
+ def get_queryset(self):
259
+ """Optimize queryset with related objects."""
260
+ return super().get_queryset().select_related('currency', 'network')
261
+
262
+ @action(detail=False, methods=['get'])
263
+ def by_provider(self, request):
264
+ """
265
+ Get provider currencies grouped by provider.
266
+
267
+ GET /api/provider-currencies/by_provider/
268
+ """
269
+ try:
270
+ queryset = self.filter_queryset(self.get_queryset())
271
+
272
+ currencies_by_provider = {}
273
+ for provider_currency in queryset:
274
+ provider = provider_currency.provider
275
+
276
+ if provider not in currencies_by_provider:
277
+ currencies_by_provider[provider] = {
278
+ 'provider': provider,
279
+ 'currencies': []
280
+ }
281
+
282
+ currencies_by_provider[provider]['currencies'].append(
283
+ ProviderCurrencySerializer(provider_currency).data
284
+ )
285
+
286
+ return Response({
287
+ 'currencies_by_provider': currencies_by_provider,
288
+ 'total_providers': len(currencies_by_provider),
289
+ 'generated_at': timezone.now().isoformat()
290
+ })
291
+
292
+ except Exception as e:
293
+ logger.error(f"Provider currencies by provider failed: {e}")
294
+ return Response(
295
+ {'error': f'Provider currencies by provider failed: {e}'},
296
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR
297
+ )
298
+
299
+ @action(detail=False, methods=['get'])
300
+ def limits(self, request):
301
+ """
302
+ Get currency limits by provider.
303
+
304
+ GET /api/provider-currencies/limits/?provider=nowpayments
305
+ """
306
+ try:
307
+ queryset = self.filter_queryset(self.get_queryset())
308
+
309
+ provider_filter = request.query_params.get('provider')
310
+ if provider_filter:
311
+ queryset = queryset.filter(provider=provider_filter)
312
+
313
+ limits = {}
314
+ for provider_currency in queryset:
315
+ currency_code = provider_currency.currency.code
316
+ provider = provider_currency.provider
317
+
318
+ key = f"{provider}_{currency_code}"
319
+ limits[key] = {
320
+ 'provider': provider,
321
+ 'currency': currency_code,
322
+ 'min_amount': float(provider_currency.min_amount or 0),
323
+ 'max_amount': float(provider_currency.max_amount or 0),
324
+ 'fee_percentage': float(provider_currency.fee_percentage or 0),
325
+ }
326
+
327
+ return Response({
328
+ 'limits': limits,
329
+ 'count': len(limits),
330
+ 'generated_at': timezone.now().isoformat()
331
+ })
332
+
333
+ except Exception as e:
334
+ logger.error(f"Currency limits failed: {e}")
335
+ return Response(
336
+ {'error': f'Currency limits failed: {e}'},
337
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR
338
+ )
339
+
340
+
341
+ # Standalone views for common operations
342
+ class CurrencyConversionView(generics.GenericAPIView):
343
+ """
344
+ Standalone currency conversion endpoint: /api/currencies/convert/
345
+
346
+ Simplified endpoint for currency conversion.
347
+ """
348
+
349
+ serializer_class = CurrencyConversionSerializer
350
+ permission_classes = [permissions.IsAuthenticated]
351
+
352
+ def post(self, request, *args, **kwargs):
353
+ """Convert between currencies."""
354
+ serializer = self.get_serializer(data=request.data)
355
+
356
+ if serializer.is_valid():
357
+ result = serializer.save()
358
+ return Response(result)
359
+
360
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
361
+
362
+
363
+ class CurrencyRatesView(generics.GenericAPIView):
364
+ """
365
+ Standalone currency rates endpoint: /api/currencies/rates/
366
+
367
+ Simplified endpoint for getting exchange rates.
368
+ """
369
+
370
+ serializer_class = CurrencyRatesSerializer
371
+ permission_classes = [permissions.IsAuthenticated]
372
+
373
+ def get(self, request, *args, **kwargs):
374
+ """Get current exchange rates."""
375
+ serializer = self.get_serializer(data=request.query_params)
376
+
377
+ if serializer.is_valid():
378
+ result = serializer.save()
379
+ return Response(result)
380
+
381
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
382
+
383
+
384
+ class SupportedCurrenciesView(generics.GenericAPIView):
385
+ """
386
+ Standalone supported currencies endpoint: /api/currencies/supported/
387
+
388
+ Simplified endpoint for getting supported currencies.
389
+ """
390
+
391
+ serializer_class = SupportedCurrenciesSerializer
392
+ permission_classes = [permissions.IsAuthenticated]
393
+
394
+ def get(self, request, *args, **kwargs):
395
+ """Get supported currencies from providers."""
396
+ serializer = self.get_serializer(data=request.query_params)
397
+
398
+ if serializer.is_valid():
399
+ result = serializer.save()
400
+ return Response(result)
401
+
402
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)