django-cfg 1.2.29__py3-none-any.whl → 1.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/api/health/views.py +4 -2
- django_cfg/apps/knowbase/config/settings.py +16 -15
- django_cfg/apps/payments/README.md +326 -0
- django_cfg/apps/payments/admin/__init__.py +20 -9
- django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
- django_cfg/apps/payments/admin/balance_admin.py +592 -297
- django_cfg/apps/payments/admin/currencies_admin.py +600 -108
- django_cfg/apps/payments/admin/filters.py +306 -199
- django_cfg/apps/payments/admin/payments_admin.py +470 -64
- django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
- django_cfg/apps/payments/admin_interface/__init__.py +18 -0
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
- django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
- django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
- django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
- django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
- django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
- django_cfg/apps/payments/apps.py +34 -9
- django_cfg/apps/payments/config/__init__.py +28 -51
- django_cfg/apps/payments/config/constance/__init__.py +22 -0
- django_cfg/apps/payments/config/constance/config_service.py +123 -0
- django_cfg/apps/payments/config/constance/fields.py +69 -0
- django_cfg/apps/payments/config/constance/settings.py +160 -0
- django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
- django_cfg/apps/payments/config/helpers.py +130 -0
- django_cfg/apps/payments/management/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/manage_currencies.py +381 -0
- django_cfg/apps/payments/management/commands/manage_providers.py +408 -0
- django_cfg/apps/payments/middleware/__init__.py +3 -1
- django_cfg/apps/payments/middleware/api_access.py +329 -222
- django_cfg/apps/payments/middleware/rate_limiting.py +343 -163
- django_cfg/apps/payments/middleware/usage_tracking.py +250 -238
- django_cfg/apps/payments/migrations/0001_initial.py +708 -536
- django_cfg/apps/payments/models/__init__.py +16 -20
- django_cfg/apps/payments/models/api_keys.py +121 -43
- django_cfg/apps/payments/models/balance.py +150 -115
- django_cfg/apps/payments/models/base.py +68 -15
- django_cfg/apps/payments/models/currencies.py +207 -67
- django_cfg/apps/payments/models/managers/__init__.py +44 -0
- django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
- django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
- django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
- django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
- django_cfg/apps/payments/models/payments.py +235 -284
- django_cfg/apps/payments/models/subscriptions.py +257 -177
- django_cfg/apps/payments/models/tariffs.py +147 -40
- django_cfg/apps/payments/services/__init__.py +209 -56
- django_cfg/apps/payments/services/cache/__init__.py +6 -6
- django_cfg/apps/payments/services/cache/{simple_cache.py → cache_service.py} +112 -12
- django_cfg/apps/payments/services/core/__init__.py +10 -6
- django_cfg/apps/payments/services/core/balance_service.py +435 -360
- django_cfg/apps/payments/services/core/base.py +166 -0
- django_cfg/apps/payments/services/core/currency_service.py +478 -0
- django_cfg/apps/payments/services/core/payment_service.py +344 -468
- django_cfg/apps/payments/services/core/subscription_service.py +425 -484
- django_cfg/apps/payments/services/core/webhook_service.py +410 -0
- django_cfg/apps/payments/services/integrations/__init__.py +29 -0
- django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
- django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
- django_cfg/apps/payments/services/providers/__init__.py +9 -14
- django_cfg/apps/payments/services/providers/base.py +232 -71
- django_cfg/apps/payments/services/providers/nowpayments.py +404 -219
- django_cfg/apps/payments/services/providers/registry.py +429 -80
- django_cfg/apps/payments/services/types/__init__.py +78 -0
- django_cfg/apps/payments/services/types/data.py +177 -0
- django_cfg/apps/payments/services/types/requests.py +150 -0
- django_cfg/apps/payments/services/types/responses.py +156 -0
- django_cfg/apps/payments/services/types/webhooks.py +232 -0
- django_cfg/apps/payments/signals/__init__.py +33 -8
- django_cfg/apps/payments/signals/api_key_signals.py +211 -130
- django_cfg/apps/payments/signals/balance_signals.py +174 -0
- django_cfg/apps/payments/signals/payment_signals.py +129 -98
- django_cfg/apps/payments/signals/subscription_signals.py +195 -143
- django_cfg/apps/payments/static/payments/css/components.css +380 -0
- django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
- django_cfg/apps/payments/static/payments/js/components.js +545 -0
- django_cfg/apps/payments/static/payments/js/utils.js +412 -0
- django_cfg/apps/payments/templatetags/__init__.py +1 -1
- django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
- django_cfg/apps/payments/urls.py +46 -47
- django_cfg/apps/payments/urls_admin.py +49 -0
- django_cfg/apps/payments/views/api/__init__.py +101 -0
- django_cfg/apps/payments/views/api/api_keys.py +387 -0
- django_cfg/apps/payments/views/api/balances.py +381 -0
- django_cfg/apps/payments/views/api/base.py +298 -0
- django_cfg/apps/payments/views/api/currencies.py +402 -0
- django_cfg/apps/payments/views/api/payments.py +415 -0
- django_cfg/apps/payments/views/api/subscriptions.py +475 -0
- django_cfg/apps/payments/views/api/webhooks.py +476 -0
- django_cfg/apps/payments/views/serializers/__init__.py +99 -0
- django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
- django_cfg/apps/payments/views/serializers/balances.py +300 -0
- django_cfg/apps/payments/views/serializers/currencies.py +335 -0
- django_cfg/apps/payments/views/serializers/payments.py +387 -0
- django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
- django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
- django_cfg/apps/tasks/urls.py +0 -2
- django_cfg/apps/tasks/urls_admin.py +14 -0
- django_cfg/apps/urls.py +4 -4
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +75 -4
- django_cfg/core/generation.py +25 -4
- django_cfg/core/integration/README.md +363 -0
- django_cfg/core/integration/__init__.py +47 -0
- django_cfg/core/integration/commands_collector.py +239 -0
- django_cfg/core/integration/display/__init__.py +15 -0
- django_cfg/core/integration/display/base.py +157 -0
- django_cfg/core/integration/display/ngrok.py +164 -0
- django_cfg/core/integration/display/startup.py +815 -0
- django_cfg/core/integration/url_integration.py +123 -0
- django_cfg/core/integration/version_checker.py +160 -0
- django_cfg/management/commands/auto_generate.py +4 -0
- django_cfg/management/commands/check_settings.py +6 -0
- django_cfg/management/commands/clear_constance.py +5 -2
- django_cfg/management/commands/create_token.py +6 -0
- django_cfg/management/commands/list_urls.py +6 -0
- django_cfg/management/commands/migrate_all.py +6 -0
- django_cfg/management/commands/migrator.py +3 -0
- django_cfg/management/commands/rundramatiq.py +6 -0
- django_cfg/management/commands/runserver_ngrok.py +51 -29
- django_cfg/management/commands/script.py +6 -0
- django_cfg/management/commands/show_config.py +12 -2
- django_cfg/management/commands/show_urls.py +4 -0
- django_cfg/management/commands/superuser.py +6 -0
- django_cfg/management/commands/task_clear.py +4 -1
- django_cfg/management/commands/task_status.py +3 -1
- django_cfg/management/commands/test_email.py +3 -0
- django_cfg/management/commands/test_telegram.py +6 -0
- django_cfg/management/commands/test_twilio.py +6 -0
- django_cfg/management/commands/tree.py +6 -0
- django_cfg/management/commands/validate_config.py +155 -149
- django_cfg/models/constance.py +31 -11
- django_cfg/models/payments.py +175 -498
- django_cfg/modules/django_currency/__init__.py +16 -11
- django_cfg/modules/django_currency/clients/__init__.py +4 -4
- django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
- django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
- django_cfg/modules/django_currency/core/__init__.py +1 -7
- django_cfg/modules/django_currency/core/converter.py +18 -23
- django_cfg/modules/django_currency/core/models.py +122 -11
- django_cfg/modules/django_currency/database/__init__.py +4 -4
- django_cfg/modules/django_currency/database/database_loader.py +190 -309
- django_cfg/modules/django_logger.py +160 -146
- django_cfg/modules/django_unfold/dashboard.py +65 -12
- django_cfg/registry/core.py +1 -0
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/templates/admin/components/action_grid.html +9 -9
- django_cfg/templates/admin/components/metric_card.html +5 -5
- django_cfg/templates/admin/components/status_badge.html +2 -2
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
- django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
- django_cfg/templates/admin/snippets/components/system_health.html +1 -1
- django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
- django_cfg/utils/smart_defaults.py +222 -571
- django_cfg/utils/toolkit.py +51 -11
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/METADATA +5 -4
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/RECORD +172 -182
- django_cfg/apps/payments/__init__.py +0 -8
- django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
- django_cfg/apps/payments/config/module.py +0 -70
- django_cfg/apps/payments/config/providers.py +0 -105
- django_cfg/apps/payments/config/settings.py +0 -96
- django_cfg/apps/payments/config/utils.py +0 -52
- django_cfg/apps/payments/decorators.py +0 -291
- django_cfg/apps/payments/management/commands/README.md +0 -178
- django_cfg/apps/payments/management/commands/currency_stats.py +0 -323
- django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
- django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
- django_cfg/apps/payments/managers/__init__.py +0 -22
- django_cfg/apps/payments/managers/api_key_manager.py +0 -35
- django_cfg/apps/payments/managers/balance_manager.py +0 -361
- django_cfg/apps/payments/managers/currency_manager.py +0 -83
- django_cfg/apps/payments/managers/payment_manager.py +0 -44
- django_cfg/apps/payments/managers/subscription_manager.py +0 -37
- django_cfg/apps/payments/managers/tariff_manager.py +0 -29
- django_cfg/apps/payments/models/events.py +0 -73
- django_cfg/apps/payments/serializers/__init__.py +0 -56
- django_cfg/apps/payments/serializers/api_keys.py +0 -51
- django_cfg/apps/payments/serializers/balance.py +0 -59
- django_cfg/apps/payments/serializers/currencies.py +0 -55
- django_cfg/apps/payments/serializers/payments.py +0 -62
- django_cfg/apps/payments/serializers/subscriptions.py +0 -71
- django_cfg/apps/payments/serializers/tariffs.py +0 -56
- django_cfg/apps/payments/services/billing/__init__.py +0 -8
- django_cfg/apps/payments/services/cache/base.py +0 -30
- django_cfg/apps/payments/services/core/fallback_service.py +0 -432
- django_cfg/apps/payments/services/internal_types.py +0 -297
- django_cfg/apps/payments/services/middleware/__init__.py +0 -8
- django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
- django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -222
- django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
- django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
- django_cfg/apps/payments/services/providers/cryptomus.py +0 -311
- django_cfg/apps/payments/services/security/__init__.py +0 -34
- django_cfg/apps/payments/services/security/error_handler.py +0 -637
- django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
- django_cfg/apps/payments/services/security/webhook_validator.py +0 -475
- django_cfg/apps/payments/services/validators/__init__.py +0 -8
- django_cfg/apps/payments/static/payments/css/payments.css +0 -340
- django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
- django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
- django_cfg/apps/payments/static/payments/js/theme.js +0 -86
- django_cfg/apps/payments/tasks/__init__.py +0 -12
- django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
- django_cfg/apps/payments/templates/payments/base.html +0 -182
- django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
- django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
- django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -36
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
- django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -27
- django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -144
- django_cfg/apps/payments/templates/payments/dashboard.html +0 -346
- django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
- django_cfg/apps/payments/urls_templates.py +0 -52
- django_cfg/apps/payments/utils/__init__.py +0 -45
- django_cfg/apps/payments/utils/billing_utils.py +0 -342
- django_cfg/apps/payments/utils/config_utils.py +0 -245
- django_cfg/apps/payments/utils/middleware_utils.py +0 -228
- django_cfg/apps/payments/utils/validation_utils.py +0 -94
- django_cfg/apps/payments/views/__init__.py +0 -62
- django_cfg/apps/payments/views/api_key_views.py +0 -164
- django_cfg/apps/payments/views/balance_views.py +0 -75
- django_cfg/apps/payments/views/currency_views.py +0 -111
- django_cfg/apps/payments/views/payment_views.py +0 -149
- django_cfg/apps/payments/views/subscription_views.py +0 -135
- django_cfg/apps/payments/views/tariff_views.py +0 -131
- django_cfg/apps/payments/views/templates/__init__.py +0 -25
- django_cfg/apps/payments/views/templates/ajax.py +0 -312
- django_cfg/apps/payments/views/templates/base.py +0 -204
- django_cfg/apps/payments/views/templates/dashboard.py +0 -60
- django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
- django_cfg/apps/payments/views/templates/payment_management.py +0 -164
- django_cfg/apps/payments/views/templates/qr_code.py +0 -174
- django_cfg/apps/payments/views/templates/stats.py +0 -240
- django_cfg/apps/payments/views/templates/utils.py +0 -181
- django_cfg/apps/payments/views/webhook_views.py +0 -266
- django_cfg/apps/payments/viewsets.py +0 -65
- django_cfg/core/integration.py +0 -160
- django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
- django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
- django_cfg/template_archive/.gitignore +0 -1
- django_cfg/template_archive/__init__.py +0 -0
- django_cfg/urls.py +0 -33
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.29.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/apps/tasks/urls.py
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
"""
|
2
|
+
URLs for Django CFG Tasks app.
|
3
|
+
|
4
|
+
Provides RESTful endpoints for task queue management and monitoring using ViewSets and routers.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from django.urls import path
|
8
|
+
from . import views
|
9
|
+
|
10
|
+
urlpatterns = [
|
11
|
+
|
12
|
+
# Dashboard view
|
13
|
+
path('dashboard/', views.dashboard_view, name='dashboard'),
|
14
|
+
]
|
django_cfg/apps/urls.py
CHANGED
@@ -46,14 +46,14 @@ def get_django_cfg_urlpatterns() -> List[URLPattern]:
|
|
46
46
|
|
47
47
|
# Tasks app - enabled when knowbase or agents are enabled
|
48
48
|
if base_module.should_enable_tasks():
|
49
|
-
patterns.append(path('
|
49
|
+
patterns.append(path('admin/django_cfg_tasks/admin/', include('django_cfg.apps.tasks.urls_admin')))
|
50
50
|
|
51
51
|
# Maintenance app - multi-site maintenance mode with Cloudflare
|
52
|
-
if base_module.is_maintenance_enabled():
|
53
|
-
|
52
|
+
# if base_module.is_maintenance_enabled():
|
53
|
+
# patterns.append(path('admin/django_cfg_maintenance/', include('django_cfg.apps.maintenance.urls_admin')))
|
54
54
|
|
55
55
|
if base_module.is_payments_enabled():
|
56
|
-
patterns.append(path('
|
56
|
+
patterns.append(path('admin/django_cfg_payments/admin/', include('django_cfg.apps.payments.urls_admin')))
|
57
57
|
|
58
58
|
except Exception:
|
59
59
|
# Fallback: include all URLs if config is not available
|
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 = "
|
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"
|