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,429 @@
1
+ """
2
+ Subscription serializers for the Universal Payment System v2.0.
3
+
4
+ DRF serializers for subscription operations with service integration.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+ from typing import Dict, Any
9
+ from django.contrib.auth import get_user_model
10
+
11
+ from ...models import Subscription, EndpointGroup, Tariff
12
+ from ...services import get_subscription_service, SubscriptionCreateRequest, SubscriptionUpdateRequest
13
+ from django_cfg.modules.django_logger import get_logger
14
+
15
+ User = get_user_model()
16
+ logger = get_logger("subscription_serializers")
17
+
18
+
19
+ class EndpointGroupSerializer(serializers.ModelSerializer):
20
+ """
21
+ Endpoint group serializer for API access management.
22
+
23
+ Used for subscription endpoint group configuration.
24
+ """
25
+
26
+ class Meta:
27
+ model = EndpointGroup
28
+ fields = [
29
+ 'id',
30
+ 'name',
31
+ 'description',
32
+ 'is_active',
33
+ 'created_at',
34
+ 'updated_at',
35
+ ]
36
+ read_only_fields = fields
37
+
38
+
39
+ class TariffSerializer(serializers.ModelSerializer):
40
+ """
41
+ Tariff serializer for subscription pricing.
42
+
43
+ Used for tariff information and selection.
44
+ """
45
+
46
+ endpoint_groups = EndpointGroupSerializer(many=True, read_only=True)
47
+ endpoint_groups_count = serializers.IntegerField(source='endpoint_groups.count', read_only=True)
48
+
49
+ class Meta:
50
+ model = Tariff
51
+ fields = [
52
+ 'id',
53
+ 'name',
54
+ 'description',
55
+ 'monthly_price',
56
+ 'requests_per_month',
57
+ 'requests_per_minute',
58
+ 'is_active',
59
+ 'endpoint_groups',
60
+ 'endpoint_groups_count',
61
+ 'created_at',
62
+ 'updated_at',
63
+ ]
64
+ read_only_fields = fields
65
+
66
+
67
+ class SubscriptionListSerializer(serializers.ModelSerializer):
68
+ """
69
+ Lightweight subscription serializer for lists.
70
+
71
+ Optimized for subscription lists with minimal data.
72
+ """
73
+
74
+ user = serializers.StringRelatedField(read_only=True)
75
+ tariff_name = serializers.CharField(source='tariff.name', read_only=True)
76
+ status_display = serializers.CharField(source='get_status_display', read_only=True)
77
+ is_active = serializers.BooleanField(source='is_active', read_only=True)
78
+ is_expired = serializers.BooleanField(source='is_expired', read_only=True)
79
+
80
+ class Meta:
81
+ model = Subscription
82
+ fields = [
83
+ 'id',
84
+ 'user',
85
+ 'tariff_name',
86
+ 'status',
87
+ 'status_display',
88
+ 'is_active',
89
+ 'is_expired',
90
+ 'expires_at',
91
+ 'created_at',
92
+ ]
93
+ read_only_fields = fields
94
+
95
+
96
+ class SubscriptionSerializer(serializers.ModelSerializer):
97
+ """
98
+ Complete subscription serializer with full details.
99
+
100
+ Used for subscription detail views and updates.
101
+ """
102
+
103
+ user = serializers.StringRelatedField(read_only=True)
104
+ tariff = TariffSerializer(read_only=True)
105
+ endpoint_group = EndpointGroupSerializer(read_only=True)
106
+ status_display = serializers.CharField(source='get_status_display', read_only=True)
107
+ status_color = serializers.CharField(source='status_color', read_only=True)
108
+
109
+ # Status check methods
110
+ is_active = serializers.BooleanField(source='is_active', read_only=True)
111
+ is_expired = serializers.BooleanField(source='is_expired', read_only=True)
112
+ is_trial = serializers.BooleanField(source='is_trial', read_only=True)
113
+ can_be_renewed = serializers.BooleanField(source='can_be_renewed', read_only=True)
114
+ can_be_cancelled = serializers.BooleanField(source='can_be_cancelled', read_only=True)
115
+
116
+ # Usage statistics
117
+ usage_percentage = serializers.FloatField(source='usage_percentage', read_only=True)
118
+ requests_remaining = serializers.IntegerField(source='requests_remaining', read_only=True)
119
+
120
+ class Meta:
121
+ model = Subscription
122
+ fields = [
123
+ 'id',
124
+ 'user',
125
+ 'tariff',
126
+ 'endpoint_group',
127
+ 'status',
128
+ 'status_display',
129
+ 'status_color',
130
+ 'tier',
131
+ 'total_requests',
132
+ 'requests_used',
133
+ 'requests_remaining',
134
+ 'usage_percentage',
135
+ 'last_request_at',
136
+ 'expires_at',
137
+ 'is_active',
138
+ 'is_expired',
139
+ 'is_trial',
140
+ 'can_be_renewed',
141
+ 'can_be_cancelled',
142
+ 'created_at',
143
+ 'updated_at',
144
+ ]
145
+ read_only_fields = [
146
+ 'id',
147
+ 'user',
148
+ 'total_requests',
149
+ 'requests_used',
150
+ 'requests_remaining',
151
+ 'usage_percentage',
152
+ 'last_request_at',
153
+ 'created_at',
154
+ 'updated_at',
155
+ 'status_display',
156
+ 'status_color',
157
+ 'is_active',
158
+ 'is_expired',
159
+ 'is_trial',
160
+ 'can_be_renewed',
161
+ 'can_be_cancelled',
162
+ ]
163
+
164
+
165
+ class SubscriptionCreateSerializer(serializers.Serializer):
166
+ """
167
+ Subscription creation serializer with service integration.
168
+
169
+ Validates input and delegates to SubscriptionService.
170
+ """
171
+
172
+ tariff_id = serializers.IntegerField(
173
+ min_value=1,
174
+ help_text="Tariff ID for the subscription"
175
+ )
176
+ endpoint_group_id = serializers.IntegerField(
177
+ required=False,
178
+ allow_null=True,
179
+ min_value=1,
180
+ help_text="Endpoint group ID (optional)"
181
+ )
182
+ duration_days = serializers.IntegerField(
183
+ default=30,
184
+ min_value=1,
185
+ max_value=365,
186
+ help_text="Subscription duration in days"
187
+ )
188
+
189
+ def validate_tariff_id(self, value: int) -> int:
190
+ """Validate tariff exists and is active."""
191
+ if not Tariff.objects.filter(id=value, is_active=True).exists():
192
+ raise serializers.ValidationError(f"Tariff {value} not found or inactive")
193
+ return value
194
+
195
+ def validate_endpoint_group_id(self, value: int) -> int:
196
+ """Validate endpoint group exists and is active."""
197
+ if value and not EndpointGroup.objects.filter(id=value, is_active=True).exists():
198
+ raise serializers.ValidationError(f"Endpoint group {value} not found or inactive")
199
+ return value
200
+
201
+ def validate(self, attrs: Dict[str, Any]) -> Dict[str, Any]:
202
+ """Validate subscription creation data."""
203
+ try:
204
+ # Get user from context
205
+ user_id = self.context.get('user_pk') or self.context['request'].user.id
206
+
207
+ # Create Pydantic request for validation
208
+ request = SubscriptionCreateRequest(
209
+ user_id=user_id,
210
+ **attrs
211
+ )
212
+
213
+ # Store validated request for create method
214
+ self._validated_request = request
215
+ return attrs
216
+
217
+ except Exception as e:
218
+ logger.error(f"Subscription validation failed: {e}")
219
+ raise serializers.ValidationError(f"Invalid subscription data: {e}")
220
+
221
+ def create(self, validated_data: Dict[str, Any]) -> Subscription:
222
+ """Create subscription using SubscriptionService."""
223
+ try:
224
+ subscription_service = get_subscription_service()
225
+ result = subscription_service.create_subscription(self._validated_request)
226
+
227
+ if result.success:
228
+ # Get the created subscription from database
229
+ subscription = Subscription.objects.get(id=result.subscription_id)
230
+
231
+ logger.info(f"Subscription created successfully", extra={
232
+ 'subscription_id': result.subscription_id,
233
+ 'user_id': self._validated_request.user_id,
234
+ 'tariff_id': self._validated_request.tariff_id
235
+ })
236
+
237
+ return subscription
238
+ else:
239
+ logger.error(f"Subscription creation failed: {result.message}")
240
+ raise serializers.ValidationError(result.message)
241
+
242
+ except Exception as e:
243
+ logger.error(f"Subscription creation error: {e}")
244
+ raise serializers.ValidationError(f"Subscription creation failed: {e}")
245
+
246
+ def to_representation(self, instance: Subscription) -> Dict[str, Any]:
247
+ """Return full subscription data after creation."""
248
+ return SubscriptionSerializer(instance, context=self.context).data
249
+
250
+
251
+ class SubscriptionUpdateSerializer(serializers.Serializer):
252
+ """
253
+ Subscription update serializer with service integration.
254
+
255
+ Handles subscription modifications through SubscriptionService.
256
+ """
257
+
258
+ action = serializers.ChoiceField(
259
+ choices=[
260
+ ('activate', 'Activate'),
261
+ ('suspend', 'Suspend'),
262
+ ('cancel', 'Cancel'),
263
+ ('renew', 'Renew'),
264
+ ],
265
+ help_text="Action to perform on subscription"
266
+ )
267
+ reason = serializers.CharField(
268
+ required=False,
269
+ allow_blank=True,
270
+ max_length=500,
271
+ help_text="Reason for the action"
272
+ )
273
+ duration_days = serializers.IntegerField(
274
+ required=False,
275
+ min_value=1,
276
+ max_value=365,
277
+ help_text="Duration for renewal (required for renew action)"
278
+ )
279
+
280
+ def validate(self, attrs: Dict[str, Any]) -> Dict[str, Any]:
281
+ """Validate subscription update data."""
282
+ action = attrs.get('action')
283
+ duration_days = attrs.get('duration_days')
284
+
285
+ if action == 'renew' and not duration_days:
286
+ raise serializers.ValidationError("duration_days is required for renew action")
287
+
288
+ return attrs
289
+
290
+ def save(self) -> Dict[str, Any]:
291
+ """Update subscription using SubscriptionService."""
292
+ try:
293
+ subscription_id = self.context.get('subscription_id')
294
+ if not subscription_id:
295
+ raise serializers.ValidationError("Subscription ID is required")
296
+
297
+ subscription_service = get_subscription_service()
298
+ action = self.validated_data['action']
299
+ reason = self.validated_data.get('reason')
300
+ duration_days = self.validated_data.get('duration_days')
301
+
302
+ # Call appropriate service method based on action
303
+ if action == 'activate':
304
+ result = subscription_service.activate_subscription(subscription_id)
305
+ elif action == 'suspend':
306
+ result = subscription_service.suspend_subscription(subscription_id, reason)
307
+ elif action == 'cancel':
308
+ result = subscription_service.cancel_subscription(subscription_id, reason)
309
+ elif action == 'renew':
310
+ result = subscription_service.renew_subscription(subscription_id, duration_days)
311
+ else:
312
+ raise serializers.ValidationError(f"Unknown action: {action}")
313
+
314
+ if result.success:
315
+ # Get updated subscription
316
+ subscription = Subscription.objects.get(id=result.subscription_id)
317
+
318
+ return {
319
+ 'success': True,
320
+ 'message': result.message,
321
+ 'subscription': SubscriptionSerializer(subscription, context=self.context).data
322
+ }
323
+ else:
324
+ return {
325
+ 'success': False,
326
+ 'error': result.message,
327
+ 'error_code': result.error_code
328
+ }
329
+
330
+ except Exception as e:
331
+ logger.error(f"Subscription update error: {e}")
332
+ return {
333
+ 'success': False,
334
+ 'error': f"Subscription update failed: {e}",
335
+ 'error_code': 'subscription_update_error'
336
+ }
337
+
338
+
339
+ class SubscriptionUsageSerializer(serializers.Serializer):
340
+ """
341
+ Subscription usage tracking serializer.
342
+
343
+ Used for incrementing usage counters.
344
+ """
345
+
346
+ endpoint = serializers.CharField(
347
+ required=False,
348
+ allow_blank=True,
349
+ max_length=200,
350
+ help_text="API endpoint used"
351
+ )
352
+
353
+ def save(self) -> Dict[str, Any]:
354
+ """Increment subscription usage using SubscriptionService."""
355
+ try:
356
+ subscription_id = self.context.get('subscription_id')
357
+ if not subscription_id:
358
+ raise serializers.ValidationError("Subscription ID is required")
359
+
360
+ subscription_service = get_subscription_service()
361
+ result = subscription_service.increment_usage(
362
+ subscription_id=subscription_id,
363
+ endpoint=self.validated_data.get('endpoint')
364
+ )
365
+
366
+ if result.success:
367
+ return {
368
+ 'success': True,
369
+ 'message': result.message,
370
+ 'usage': result.data
371
+ }
372
+ else:
373
+ return {
374
+ 'success': False,
375
+ 'error': result.message,
376
+ 'error_code': result.error_code
377
+ }
378
+
379
+ except Exception as e:
380
+ logger.error(f"Usage increment error: {e}")
381
+ return {
382
+ 'success': False,
383
+ 'error': f"Usage increment failed: {e}",
384
+ 'error_code': 'usage_increment_error'
385
+ }
386
+
387
+
388
+ class SubscriptionStatsSerializer(serializers.Serializer):
389
+ """
390
+ Subscription statistics serializer.
391
+
392
+ Used for subscription analytics and reporting.
393
+ """
394
+
395
+ days = serializers.IntegerField(
396
+ default=30,
397
+ min_value=1,
398
+ max_value=365,
399
+ help_text="Number of days to analyze"
400
+ )
401
+
402
+ def save(self) -> Dict[str, Any]:
403
+ """Get subscription statistics using SubscriptionService."""
404
+ try:
405
+ subscription_service = get_subscription_service()
406
+ result = subscription_service.get_subscription_stats(
407
+ days=self.validated_data['days']
408
+ )
409
+
410
+ if result.success:
411
+ return {
412
+ 'success': True,
413
+ 'stats': result.data,
414
+ 'message': result.message
415
+ }
416
+ else:
417
+ return {
418
+ 'success': False,
419
+ 'error': result.message,
420
+ 'error_code': result.error_code
421
+ }
422
+
423
+ except Exception as e:
424
+ logger.error(f"Subscription stats error: {e}")
425
+ return {
426
+ 'success': False,
427
+ 'error': f"Stats generation failed: {e}",
428
+ 'error_code': 'stats_error'
429
+ }
@@ -0,0 +1,137 @@
1
+ """
2
+ Webhook serializers for the Universal Payment System v2.0.
3
+
4
+ DRF serializers for webhook endpoints and data validation.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+ from django_cfg.modules.django_logger import get_logger
9
+
10
+ logger = get_logger(__name__)
11
+
12
+
13
+ class WebhookSerializer(serializers.Serializer):
14
+ """
15
+ Generic webhook serializer.
16
+
17
+ Base serializer for all webhook types.
18
+ """
19
+
20
+ provider = serializers.CharField(max_length=50, help_text="Payment provider name")
21
+ success = serializers.BooleanField(help_text="Processing success status")
22
+ message = serializers.CharField(max_length=500, help_text="Processing message")
23
+
24
+ class Meta:
25
+ fields = ['provider', 'success', 'message']
26
+
27
+
28
+ class WebhookDataSerializer(serializers.Serializer):
29
+ """
30
+ Serializer for incoming webhook data.
31
+
32
+ Generic webhook data structure for all providers.
33
+ """
34
+
35
+ provider = serializers.CharField(max_length=50, help_text="Payment provider name")
36
+ payment_id = serializers.CharField(max_length=256, help_text="Provider payment ID")
37
+ status = serializers.CharField(max_length=50, help_text="Payment status")
38
+ amount = serializers.DecimalField(max_digits=20, decimal_places=8, required=False, help_text="Payment amount")
39
+ currency = serializers.CharField(max_length=10, required=False, help_text="Payment currency")
40
+ transaction_hash = serializers.CharField(max_length=256, required=False, help_text="Blockchain transaction hash")
41
+ confirmations = serializers.IntegerField(required=False, help_text="Number of confirmations")
42
+
43
+ # Raw webhook data
44
+ raw_data = serializers.JSONField(help_text="Raw webhook payload")
45
+
46
+ class Meta:
47
+ fields = [
48
+ 'provider', 'payment_id', 'status', 'amount',
49
+ 'currency', 'transaction_hash', 'confirmations', 'raw_data'
50
+ ]
51
+
52
+
53
+ class WebhookResponseSerializer(serializers.Serializer):
54
+ """
55
+ Serializer for webhook processing response.
56
+
57
+ Standard response format for all webhook endpoints.
58
+ """
59
+
60
+ success = serializers.BooleanField(help_text="Whether webhook was processed successfully")
61
+ message = serializers.CharField(max_length=500, help_text="Processing result message")
62
+ payment_id = serializers.CharField(max_length=256, required=False, help_text="Internal payment ID")
63
+ provider_payment_id = serializers.CharField(max_length=256, required=False, help_text="Provider payment ID")
64
+ processed_at = serializers.DateTimeField(required=False, help_text="Processing timestamp")
65
+
66
+ class Meta:
67
+ fields = ['success', 'message', 'payment_id', 'provider_payment_id', 'processed_at']
68
+
69
+
70
+ class WebhookHealthSerializer(serializers.Serializer):
71
+ """
72
+ Serializer for webhook health check response.
73
+ """
74
+
75
+ status = serializers.CharField(max_length=20, help_text="Health status")
76
+ timestamp = serializers.DateTimeField(help_text="Check timestamp")
77
+ providers = serializers.JSONField(help_text="Provider health status")
78
+
79
+ class Meta:
80
+ fields = ['status', 'timestamp', 'providers']
81
+
82
+
83
+ class WebhookStatsSerializer(serializers.Serializer):
84
+ """
85
+ Serializer for webhook statistics response.
86
+ """
87
+
88
+ total_webhooks = serializers.IntegerField(help_text="Total webhooks processed")
89
+ successful_webhooks = serializers.IntegerField(help_text="Successfully processed webhooks")
90
+ failed_webhooks = serializers.IntegerField(help_text="Failed webhook processing attempts")
91
+ success_rate = serializers.FloatField(help_text="Success rate percentage")
92
+ providers = serializers.JSONField(help_text="Per-provider statistics")
93
+
94
+ class Meta:
95
+ fields = ['total_webhooks', 'successful_webhooks', 'failed_webhooks', 'success_rate', 'providers']
96
+
97
+
98
+ class SupportedProvidersSerializer(serializers.Serializer):
99
+ """
100
+ Serializer for supported providers response.
101
+ """
102
+
103
+ success = serializers.BooleanField(help_text="Request success status")
104
+ providers = serializers.JSONField(help_text="List of supported providers")
105
+ total_count = serializers.IntegerField(help_text="Total number of providers")
106
+ timestamp = serializers.DateTimeField(help_text="Response timestamp")
107
+
108
+ class Meta:
109
+ fields = ['success', 'providers', 'total_count', 'timestamp']
110
+
111
+
112
+ class NowPaymentsWebhookSerializer(serializers.Serializer):
113
+ """
114
+ Serializer for NowPayments webhook data.
115
+
116
+ Specific to NowPayments IPN format.
117
+ """
118
+
119
+ payment_id = serializers.CharField(max_length=256, help_text="NowPayments payment ID")
120
+ payment_status = serializers.CharField(max_length=50, help_text="Payment status")
121
+ pay_address = serializers.CharField(max_length=256, required=False, help_text="Payment address")
122
+ price_amount = serializers.DecimalField(max_digits=20, decimal_places=8, required=False, help_text="Price amount")
123
+ price_currency = serializers.CharField(max_length=10, required=False, help_text="Price currency")
124
+ pay_amount = serializers.DecimalField(max_digits=20, decimal_places=8, required=False, help_text="Pay amount")
125
+ pay_currency = serializers.CharField(max_length=10, required=False, help_text="Pay currency")
126
+ order_id = serializers.CharField(max_length=256, required=False, help_text="Order ID")
127
+ order_description = serializers.CharField(max_length=500, required=False, help_text="Order description")
128
+ purchase_id = serializers.CharField(max_length=256, required=False, help_text="Purchase ID")
129
+ outcome_amount = serializers.DecimalField(max_digits=20, decimal_places=8, required=False, help_text="Outcome amount")
130
+ outcome_currency = serializers.CharField(max_length=10, required=False, help_text="Outcome currency")
131
+
132
+ class Meta:
133
+ fields = [
134
+ 'payment_id', 'payment_status', 'pay_address', 'price_amount', 'price_currency',
135
+ 'pay_amount', 'pay_currency', 'order_id', 'order_description', 'purchase_id',
136
+ 'outcome_amount', 'outcome_currency'
137
+ ]
django_cfg/config.py CHANGED
@@ -10,7 +10,7 @@ from .modules.django_unfold.models.dropdown import SiteDropdownItem
10
10
 
11
11
 
12
12
  # Library configuration
13
- LIB_NAME = "Django CFG"
13
+ LIB_NAME = "django-cfg"
14
14
  LIB_SITE_URL = "https://djangocfg.com"
15
15
  LIB_DOCS_URL = "https://docs.djangocfg.com"
16
16
  LIB_GITHUB_URL = "https://github.com/django-cfg/django-cfg"
django_cfg/core/config.py CHANGED
@@ -79,6 +79,13 @@ class EnvironmentMode(str, Enum):
79
79
  return cls.DEVELOPMENT if debug else cls.PRODUCTION
80
80
 
81
81
 
82
+ class StartupInfoMode(str, Enum):
83
+ """Startup information display mode."""
84
+ NONE = "none" # Minimal info only (version, environment, critical errors)
85
+ SHORT = "short" # Essential info (apps, endpoints, status, updates)
86
+ FULL = "full" # Complete info (everything from old system)
87
+
88
+
82
89
  class DjangoConfig(BaseModel):
83
90
  """
84
91
  Base configuration class for Django projects.
@@ -150,6 +157,11 @@ class DjangoConfig(BaseModel):
150
157
  )
151
158
 
152
159
  # === Django CFG Features ===
160
+ startup_info_mode: StartupInfoMode = Field(
161
+ default=StartupInfoMode.FULL,
162
+ description="Startup information display mode: none (minimal), short (essential), full (complete)",
163
+ )
164
+
153
165
  enable_support: bool = Field(
154
166
  default=True,
155
167
  description="Enable django-cfg Support application (tickets, messages, chat interface)",
@@ -601,16 +613,40 @@ class DjangoConfig(BaseModel):
601
613
  if self.enable_knowbase or self.enable_agents:
602
614
  return True
603
615
 
604
- # Check if payments module requires tasks
605
- if self.payments and self.payments.should_enable_tasks():
606
- return True
607
-
608
616
  # Check if agents module requires tasks
609
617
  if self.enable_agents:
610
618
  return True
611
619
 
612
620
  return False
613
621
 
622
+ def get_enabled_apps(self) -> List[str]:
623
+ """
624
+ Get list of enabled django-cfg apps.
625
+ """
626
+
627
+ apps = [
628
+ "django_cfg.apps.api.health",
629
+ "django_cfg.apps.api.commands",
630
+ ]
631
+
632
+ if self.enable_support:
633
+ apps.append("django_cfg.apps.support")
634
+ if self.enable_accounts:
635
+ apps.append("django_cfg.apps.accounts")
636
+ if self.enable_newsletter:
637
+ apps.append("django_cfg.apps.newsletter")
638
+ if self.enable_leads:
639
+ apps.append("django_cfg.apps.leads")
640
+ if self.enable_knowbase:
641
+ apps.append("django_cfg.apps.knowbase")
642
+ if self.enable_agents:
643
+ apps.append("django_cfg.apps.agents")
644
+ if self.enable_maintenance:
645
+ apps.append("django_cfg.apps.maintenance")
646
+ if self.payments and self.payments.enabled:
647
+ apps.append("django_cfg.apps.payments")
648
+ return apps
649
+
614
650
  def get_installed_apps(self) -> List[str]:
615
651
  """
616
652
  Get complete list of installed Django apps.
@@ -114,13 +114,34 @@ class SettingsGenerator:
114
114
 
115
115
  # Add templates configuration
116
116
  django_cfg_templates = Path(__file__).parent.parent / "templates"
117
+
118
+ # Collect all django-cfg template directories
119
+ template_dirs = [
120
+ config.base_dir / "templates",
121
+ django_cfg_templates, # Add django_cfg templates
122
+ ]
123
+
124
+ # Auto-discover app template directories
125
+ django_cfg_dir = Path(__file__).parent.parent
126
+ apps_dir = django_cfg_dir / 'apps'
127
+ if apps_dir.exists():
128
+ for app_dir in apps_dir.iterdir():
129
+ if app_dir.is_dir() and not app_dir.name.startswith(('@', '_', '.')):
130
+ # Look for common template directory patterns
131
+ possible_template_dirs = [
132
+ app_dir / 'templates',
133
+ app_dir / 'admin_interface' / 'templates',
134
+ app_dir / 'frontend' / 'templates',
135
+ ]
136
+
137
+ for template_dir in possible_template_dirs:
138
+ if template_dir.exists():
139
+ template_dirs.append(template_dir)
140
+
117
141
  settings["TEMPLATES"] = [
118
142
  {
119
143
  "BACKEND": "django.template.backends.django.DjangoTemplates",
120
- "DIRS": [
121
- config.base_dir / "templates",
122
- django_cfg_templates, # Add django_cfg templates
123
- ],
144
+ "DIRS": template_dirs,
124
145
  "APP_DIRS": True,
125
146
  "OPTIONS": {
126
147
  "context_processors": [