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,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": [