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.
- 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 -10
- 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 +526 -222
- django_cfg/apps/payments/admin/filters.py +306 -199
- django_cfg/apps/payments/admin/payments_admin.py +465 -70
- 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 +303 -151
- django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
- 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 +342 -152
- django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
- django_cfg/apps/payments/migrations/0001_initial.py +708 -536
- django_cfg/apps/payments/models/__init__.py +13 -18
- 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 +172 -148
- 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 -285
- 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 +346 -467
- django_cfg/apps/payments/services/core/subscription_service.py +425 -481
- 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 +234 -174
- django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
- django_cfg/apps/payments/services/providers/registry.py +367 -301
- 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 +210 -129
- django_cfg/apps/payments/signals/balance_signals.py +174 -0
- django_cfg/apps/payments/signals/payment_signals.py +128 -103
- django_cfg/apps/payments/signals/subscription_signals.py +194 -142
- 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 +45 -48
- django_cfg/apps/payments/urls_admin.py +33 -42
- 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/config.py +1 -1
- django_cfg/core/config.py +40 -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 -492
- django_cfg/modules/django_logger.py +160 -146
- django_cfg/modules/django_unfold/dashboard.py +64 -16
- django_cfg/registry/core.py +1 -0
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/utils/smart_defaults.py +222 -571
- django_cfg/utils/toolkit.py +51 -11
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/METADATA +4 -1
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/RECORD +153 -185
- 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 -146
- django_cfg/apps/payments/management/commands/currency_stats.py +0 -304
- django_cfg/apps/payments/managers/__init__.py +0 -23
- 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 -306
- django_cfg/apps/payments/managers/payment_manager.py +0 -192
- django_cfg/apps/payments/managers/subscription_manager.py +0 -37
- django_cfg/apps/payments/managers/tariff_manager.py +0 -29
- django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
- django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
- django_cfg/apps/payments/models/events.py +0 -73
- django_cfg/apps/payments/serializers/__init__.py +0 -57
- 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 -63
- 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 -461
- 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 -76
- django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
- django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
- django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
- django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
- django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
- django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
- django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
- django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
- django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
- django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
- django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
- django_cfg/apps/payments/services/security/__init__.py +0 -34
- django_cfg/apps/payments/services/security/error_handler.py +0 -635
- django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
- django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
- 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/admin/payments/currency/change_list.html +0 -50
- 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 -43
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
- django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
- django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
- django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
- django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
- django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
- django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
- django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
- django_cfg/apps/payments/templates/payments/stats.html +0 -261
- django_cfg/apps/payments/templates/payments/test.html +0 -213
- django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
- django_cfg/apps/payments/utils/__init__.py +0 -43
- django_cfg/apps/payments/utils/billing_utils.py +0 -342
- django_cfg/apps/payments/utils/config_utils.py +0 -239
- 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 -63
- 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 -122
- 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 -451
- django_cfg/apps/payments/views/templates/base.py +0 -212
- 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 -158
- django_cfg/apps/payments/views/templates/qr_code.py +0 -174
- django_cfg/apps/payments/views/templates/stats.py +0 -244
- 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 -66
- django_cfg/core/integration.py +0 -160
- 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.31.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,102 +1,209 @@
|
|
1
1
|
"""
|
2
|
-
Tariff models for the
|
2
|
+
Tariff models for the Universal Payment System v2.0.
|
3
|
+
|
4
|
+
Handles pricing tiers and endpoint group associations.
|
3
5
|
"""
|
4
6
|
|
5
7
|
from django.db import models
|
6
|
-
from django.core.validators import MinValueValidator
|
8
|
+
from django.core.validators import MinValueValidator, MaxValueValidator
|
9
|
+
from django.core.exceptions import ValidationError
|
7
10
|
from .base import TimestampedModel
|
11
|
+
from .subscriptions import EndpointGroup
|
8
12
|
|
9
13
|
|
10
14
|
class Tariff(TimestampedModel):
|
11
|
-
"""
|
15
|
+
"""
|
16
|
+
Tariff model for subscription pricing tiers.
|
17
|
+
|
18
|
+
Defines pricing and limits for different subscription levels.
|
19
|
+
"""
|
12
20
|
|
13
21
|
name = models.CharField(
|
14
22
|
max_length=100,
|
15
23
|
unique=True,
|
16
|
-
help_text="Tariff name"
|
24
|
+
help_text="Tariff name (e.g., 'Free', 'Basic', 'Pro')"
|
17
25
|
)
|
18
|
-
|
19
|
-
|
20
|
-
|
26
|
+
|
27
|
+
code = models.CharField(
|
28
|
+
max_length=50,
|
29
|
+
unique=True,
|
30
|
+
help_text="Tariff code (e.g., 'free', 'basic', 'pro')"
|
21
31
|
)
|
32
|
+
|
22
33
|
description = models.TextField(
|
23
34
|
blank=True,
|
24
|
-
help_text="
|
35
|
+
help_text="Detailed description of what this tariff includes"
|
25
36
|
)
|
26
37
|
|
27
38
|
# Pricing
|
28
|
-
|
29
|
-
default=0.0,
|
39
|
+
monthly_price_usd = models.FloatField(
|
30
40
|
validators=[MinValueValidator(0.0)],
|
31
41
|
help_text="Monthly price in USD"
|
32
42
|
)
|
33
43
|
|
34
|
-
|
35
|
-
|
44
|
+
yearly_price_usd = models.FloatField(
|
45
|
+
null=True,
|
46
|
+
blank=True,
|
47
|
+
validators=[MinValueValidator(0.0)],
|
48
|
+
help_text="Yearly price in USD (optional discount)"
|
49
|
+
)
|
50
|
+
|
51
|
+
# Rate limits
|
52
|
+
requests_per_hour = models.PositiveIntegerField(
|
53
|
+
default=100,
|
54
|
+
validators=[MinValueValidator(1), MaxValueValidator(100000)],
|
55
|
+
help_text="API requests allowed per hour"
|
56
|
+
)
|
57
|
+
|
58
|
+
requests_per_day = models.PositiveIntegerField(
|
36
59
|
default=1000,
|
37
|
-
|
60
|
+
validators=[MinValueValidator(1), MaxValueValidator(1000000)],
|
61
|
+
help_text="API requests allowed per day"
|
62
|
+
)
|
63
|
+
|
64
|
+
requests_per_month = models.PositiveIntegerField(
|
65
|
+
default=30000,
|
66
|
+
validators=[MinValueValidator(1), MaxValueValidator(10000000)],
|
67
|
+
help_text="API requests allowed per month"
|
68
|
+
)
|
69
|
+
|
70
|
+
# Features
|
71
|
+
max_api_keys = models.PositiveIntegerField(
|
72
|
+
default=1,
|
73
|
+
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
74
|
+
help_text="Maximum number of API keys allowed"
|
75
|
+
)
|
76
|
+
|
77
|
+
supports_webhooks = models.BooleanField(
|
78
|
+
default=True,
|
79
|
+
help_text="Whether webhooks are supported"
|
80
|
+
)
|
81
|
+
|
82
|
+
priority_support = models.BooleanField(
|
83
|
+
default=False,
|
84
|
+
help_text="Whether priority support is included"
|
38
85
|
)
|
39
86
|
|
40
|
-
#
|
87
|
+
# Availability
|
41
88
|
is_active = models.BooleanField(
|
42
89
|
default=True,
|
43
|
-
help_text="
|
90
|
+
help_text="Whether this tariff is available for new subscriptions"
|
44
91
|
)
|
45
92
|
|
46
|
-
|
47
|
-
|
48
|
-
|
93
|
+
is_public = models.BooleanField(
|
94
|
+
default=True,
|
95
|
+
help_text="Whether this tariff is publicly visible"
|
96
|
+
)
|
97
|
+
|
98
|
+
# Ordering
|
99
|
+
sort_order = models.PositiveIntegerField(
|
100
|
+
default=0,
|
101
|
+
help_text="Sort order for display (lower numbers first)"
|
102
|
+
)
|
49
103
|
|
50
104
|
class Meta:
|
51
|
-
db_table = '
|
52
|
-
verbose_name =
|
53
|
-
verbose_name_plural =
|
105
|
+
db_table = 'payments_tariffs'
|
106
|
+
verbose_name = 'Tariff'
|
107
|
+
verbose_name_plural = 'Tariffs'
|
108
|
+
ordering = ['sort_order', 'monthly_price_usd']
|
54
109
|
indexes = [
|
55
|
-
models.Index(fields=['is_active']),
|
56
|
-
models.Index(fields=['
|
110
|
+
models.Index(fields=['is_active', 'is_public']),
|
111
|
+
models.Index(fields=['sort_order']),
|
57
112
|
]
|
58
|
-
ordering = ['monthly_price']
|
59
113
|
|
60
114
|
def __str__(self):
|
61
|
-
return f"{self.
|
115
|
+
return f"{self.name} - ${self.monthly_price_usd:.2f}/month"
|
116
|
+
|
117
|
+
def clean(self):
|
118
|
+
"""Validate tariff data."""
|
119
|
+
if self.code:
|
120
|
+
self.code = self.code.lower().replace(' ', '_')
|
121
|
+
|
122
|
+
# Validate rate limits hierarchy
|
123
|
+
if self.requests_per_day < self.requests_per_hour:
|
124
|
+
raise ValidationError("Daily limit cannot be less than hourly limit")
|
125
|
+
|
126
|
+
if self.requests_per_month < self.requests_per_day:
|
127
|
+
raise ValidationError("Monthly limit cannot be less than daily limit")
|
128
|
+
|
129
|
+
# Validate yearly pricing
|
130
|
+
if self.yearly_price_usd and self.yearly_price_usd >= (self.monthly_price_usd * 12):
|
131
|
+
raise ValidationError("Yearly price should be less than 12x monthly price")
|
62
132
|
|
63
133
|
@property
|
64
134
|
def is_free(self) -> bool:
|
65
135
|
"""Check if this is a free tariff."""
|
66
|
-
return self.
|
136
|
+
return self.monthly_price_usd == 0.0
|
137
|
+
|
138
|
+
@property
|
139
|
+
def yearly_discount_percentage(self) -> float:
|
140
|
+
"""Calculate yearly discount percentage."""
|
141
|
+
if not self.yearly_price_usd or self.monthly_price_usd == 0:
|
142
|
+
return 0.0
|
143
|
+
|
144
|
+
yearly_equivalent = self.monthly_price_usd * 12
|
145
|
+
discount = yearly_equivalent - self.yearly_price_usd
|
146
|
+
return (discount / yearly_equivalent) * 100
|
147
|
+
|
148
|
+
@property
|
149
|
+
def price_display(self) -> str:
|
150
|
+
"""Formatted price display."""
|
151
|
+
if self.is_free:
|
152
|
+
return "Free"
|
153
|
+
return f"${self.monthly_price_usd:.2f}/month"
|
154
|
+
|
155
|
+
@property
|
156
|
+
def yearly_price_display(self) -> str:
|
157
|
+
"""Formatted yearly price display."""
|
158
|
+
if not self.yearly_price_usd:
|
159
|
+
return "N/A"
|
160
|
+
if self.yearly_price_usd == 0:
|
161
|
+
return "Free"
|
162
|
+
return f"${self.yearly_price_usd:.2f}/year"
|
67
163
|
|
68
164
|
|
69
165
|
class TariffEndpointGroup(TimestampedModel):
|
70
|
-
"""
|
166
|
+
"""
|
167
|
+
Association between tariffs and endpoint groups.
|
168
|
+
|
169
|
+
Defines which API endpoints are available for each tariff.
|
170
|
+
"""
|
71
171
|
|
72
172
|
tariff = models.ForeignKey(
|
73
173
|
Tariff,
|
74
174
|
on_delete=models.CASCADE,
|
75
|
-
related_name='endpoint_groups'
|
76
|
-
help_text="Tariff plan"
|
175
|
+
related_name='endpoint_groups'
|
77
176
|
)
|
78
|
-
|
177
|
+
|
79
178
|
endpoint_group = models.ForeignKey(
|
80
179
|
EndpointGroup,
|
81
180
|
on_delete=models.CASCADE,
|
82
|
-
related_name='tariffs'
|
83
|
-
|
181
|
+
related_name='tariffs'
|
182
|
+
)
|
183
|
+
|
184
|
+
# Override default rate limits for this specific combination
|
185
|
+
custom_rate_limit = models.PositiveIntegerField(
|
186
|
+
null=True,
|
187
|
+
blank=True,
|
188
|
+
help_text="Custom rate limit for this endpoint group (overrides tariff default)"
|
84
189
|
)
|
85
190
|
|
86
191
|
is_enabled = models.BooleanField(
|
87
192
|
default=True,
|
88
|
-
help_text="
|
193
|
+
help_text="Whether this endpoint group is enabled for this tariff"
|
89
194
|
)
|
90
195
|
|
91
|
-
# Import and assign manager
|
92
|
-
from ..managers import TariffEndpointGroupManager
|
93
|
-
objects = TariffEndpointGroupManager()
|
94
|
-
|
95
196
|
class Meta:
|
96
|
-
db_table = '
|
97
|
-
verbose_name =
|
98
|
-
verbose_name_plural =
|
197
|
+
db_table = 'payments_tariff_endpoint_groups'
|
198
|
+
verbose_name = 'Tariff Endpoint Group'
|
199
|
+
verbose_name_plural = 'Tariff Endpoint Groups'
|
99
200
|
unique_together = [['tariff', 'endpoint_group']]
|
201
|
+
ordering = ['tariff__sort_order', 'endpoint_group__name']
|
100
202
|
|
101
203
|
def __str__(self):
|
102
204
|
return f"{self.tariff.name} - {self.endpoint_group.name}"
|
205
|
+
|
206
|
+
@property
|
207
|
+
def effective_rate_limit(self) -> int:
|
208
|
+
"""Get effective rate limit (custom or tariff default)."""
|
209
|
+
return self.custom_rate_limit or self.tariff.requests_per_hour
|
@@ -1,71 +1,224 @@
|
|
1
1
|
"""
|
2
|
-
Universal Payment
|
2
|
+
Services package for the Universal Payment System v2.0.
|
3
3
|
|
4
|
-
|
5
|
-
Uses Django ORM for data persistence and DRF for API responses.
|
4
|
+
Business logic services with Pydantic validation and type safety.
|
6
5
|
"""
|
7
6
|
|
8
7
|
# Core services
|
9
|
-
from .core
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
#
|
19
|
-
from .
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
8
|
+
from .core import (
|
9
|
+
BaseService,
|
10
|
+
PaymentService,
|
11
|
+
BalanceService,
|
12
|
+
SubscriptionService,
|
13
|
+
WebhookService,
|
14
|
+
CurrencyService,
|
15
|
+
)
|
16
|
+
|
17
|
+
# Service types
|
18
|
+
from .types import (
|
19
|
+
# Request types
|
20
|
+
PaymentCreateRequest,
|
21
|
+
PaymentStatusRequest,
|
22
|
+
BalanceUpdateRequest,
|
23
|
+
SubscriptionCreateRequest,
|
24
|
+
SubscriptionUpdateRequest,
|
25
|
+
CurrencyConversionRequest,
|
26
|
+
|
27
|
+
# Response types
|
28
|
+
PaymentResult,
|
29
|
+
BalanceResult,
|
30
|
+
SubscriptionResult,
|
31
|
+
CurrencyConversionResult,
|
32
|
+
ServiceOperationResult,
|
33
|
+
|
34
|
+
# Data types
|
35
|
+
PaymentData,
|
36
|
+
BalanceData,
|
37
|
+
SubscriptionData,
|
38
|
+
TransactionData,
|
39
|
+
CurrencyData,
|
40
|
+
|
41
|
+
# Webhook types
|
42
|
+
WebhookData,
|
43
|
+
NowPaymentsWebhook,
|
44
|
+
WebhookProcessingResult,
|
31
45
|
)
|
32
46
|
|
33
47
|
__all__ = [
|
34
48
|
# Core services
|
49
|
+
'BaseService',
|
35
50
|
'PaymentService',
|
36
|
-
'BalanceService',
|
51
|
+
'BalanceService',
|
37
52
|
'SubscriptionService',
|
53
|
+
'WebhookService',
|
54
|
+
'CurrencyService',
|
38
55
|
|
39
|
-
#
|
40
|
-
'
|
41
|
-
'
|
42
|
-
'
|
43
|
-
|
44
|
-
|
45
|
-
'
|
46
|
-
'ApiKeyCache',
|
47
|
-
'RateLimitCache',
|
56
|
+
# Request types
|
57
|
+
'PaymentCreateRequest',
|
58
|
+
'PaymentStatusRequest',
|
59
|
+
'BalanceUpdateRequest',
|
60
|
+
'SubscriptionCreateRequest',
|
61
|
+
'SubscriptionUpdateRequest',
|
62
|
+
'CurrencyConversionRequest',
|
48
63
|
|
49
|
-
#
|
50
|
-
'
|
51
|
-
'
|
64
|
+
# Response types
|
65
|
+
'PaymentResult',
|
66
|
+
'BalanceResult',
|
67
|
+
'SubscriptionResult',
|
68
|
+
'CurrencyConversionResult',
|
52
69
|
'ServiceOperationResult',
|
53
|
-
|
54
|
-
|
55
|
-
'
|
56
|
-
|
57
|
-
|
58
|
-
'
|
59
|
-
'
|
60
|
-
|
61
|
-
|
62
|
-
'
|
63
|
-
'
|
64
|
-
'
|
65
|
-
'SubscriptionInfo',
|
66
|
-
'SubscriptionAnalytics',
|
67
|
-
|
68
|
-
# Additional response models
|
69
|
-
'PaymentHistoryItem',
|
70
|
-
'ProviderInfo',
|
70
|
+
|
71
|
+
# Data types
|
72
|
+
'PaymentData',
|
73
|
+
'BalanceData',
|
74
|
+
'SubscriptionData',
|
75
|
+
'TransactionData',
|
76
|
+
'CurrencyData',
|
77
|
+
|
78
|
+
# Webhook types
|
79
|
+
'WebhookData',
|
80
|
+
'NowPaymentsWebhook',
|
81
|
+
'WebhookProcessingResult',
|
71
82
|
]
|
83
|
+
|
84
|
+
|
85
|
+
# Service registry for dependency injection and health checks
|
86
|
+
class ServiceRegistry:
|
87
|
+
"""
|
88
|
+
Service registry for managing service instances.
|
89
|
+
|
90
|
+
Provides singleton access to services and health monitoring.
|
91
|
+
"""
|
92
|
+
|
93
|
+
_instances = {}
|
94
|
+
|
95
|
+
@classmethod
|
96
|
+
def get_payment_service(cls) -> PaymentService:
|
97
|
+
"""Get PaymentService instance."""
|
98
|
+
if 'payment' not in cls._instances:
|
99
|
+
cls._instances['payment'] = PaymentService()
|
100
|
+
return cls._instances['payment']
|
101
|
+
|
102
|
+
@classmethod
|
103
|
+
def get_balance_service(cls) -> BalanceService:
|
104
|
+
"""Get BalanceService instance."""
|
105
|
+
if 'balance' not in cls._instances:
|
106
|
+
cls._instances['balance'] = BalanceService()
|
107
|
+
return cls._instances['balance']
|
108
|
+
|
109
|
+
@classmethod
|
110
|
+
def get_subscription_service(cls) -> SubscriptionService:
|
111
|
+
"""Get SubscriptionService instance."""
|
112
|
+
if 'subscription' not in cls._instances:
|
113
|
+
cls._instances['subscription'] = SubscriptionService()
|
114
|
+
return cls._instances['subscription']
|
115
|
+
|
116
|
+
@classmethod
|
117
|
+
def get_webhook_service(cls) -> WebhookService:
|
118
|
+
"""Get WebhookService instance."""
|
119
|
+
if 'webhook' not in cls._instances:
|
120
|
+
cls._instances['webhook'] = WebhookService()
|
121
|
+
return cls._instances['webhook']
|
122
|
+
|
123
|
+
@classmethod
|
124
|
+
def get_currency_service(cls) -> CurrencyService:
|
125
|
+
"""Get CurrencyService instance."""
|
126
|
+
if 'currency' not in cls._instances:
|
127
|
+
cls._instances['currency'] = CurrencyService()
|
128
|
+
return cls._instances['currency']
|
129
|
+
|
130
|
+
@classmethod
|
131
|
+
def get_all_services(cls) -> dict:
|
132
|
+
"""Get all service instances."""
|
133
|
+
return {
|
134
|
+
'payment': cls.get_payment_service(),
|
135
|
+
'balance': cls.get_balance_service(),
|
136
|
+
'subscription': cls.get_subscription_service(),
|
137
|
+
'webhook': cls.get_webhook_service(),
|
138
|
+
'currency': cls.get_currency_service(),
|
139
|
+
}
|
140
|
+
|
141
|
+
@classmethod
|
142
|
+
def health_check_all(cls) -> dict:
|
143
|
+
"""Perform health check on all services."""
|
144
|
+
services = cls.get_all_services()
|
145
|
+
results = {}
|
146
|
+
|
147
|
+
for name, service in services.items():
|
148
|
+
try:
|
149
|
+
health_result = service.health_check()
|
150
|
+
results[name] = {
|
151
|
+
'healthy': health_result.success,
|
152
|
+
'message': health_result.message,
|
153
|
+
'data': health_result.data
|
154
|
+
}
|
155
|
+
except Exception as e:
|
156
|
+
results[name] = {
|
157
|
+
'healthy': False,
|
158
|
+
'message': f"Health check failed: {e}",
|
159
|
+
'data': {}
|
160
|
+
}
|
161
|
+
|
162
|
+
return results
|
163
|
+
|
164
|
+
@classmethod
|
165
|
+
def clear_cache_all(cls):
|
166
|
+
"""Clear cache for all services."""
|
167
|
+
for service in cls._instances.values():
|
168
|
+
if hasattr(service, '_cache_clear'):
|
169
|
+
service._cache_clear()
|
170
|
+
|
171
|
+
@classmethod
|
172
|
+
def get_stats_all(cls) -> dict:
|
173
|
+
"""Get statistics from all services."""
|
174
|
+
services = cls.get_all_services()
|
175
|
+
stats = {}
|
176
|
+
|
177
|
+
for name, service in services.items():
|
178
|
+
try:
|
179
|
+
service_stats = service.get_service_stats()
|
180
|
+
stats[name] = service_stats
|
181
|
+
except Exception as e:
|
182
|
+
stats[name] = {
|
183
|
+
'error': str(e),
|
184
|
+
'service_name': service.__class__.__name__
|
185
|
+
}
|
186
|
+
|
187
|
+
return stats
|
188
|
+
|
189
|
+
|
190
|
+
# Convenience functions for direct service access
|
191
|
+
def get_payment_service() -> PaymentService:
|
192
|
+
"""Get PaymentService instance."""
|
193
|
+
return ServiceRegistry.get_payment_service()
|
194
|
+
|
195
|
+
|
196
|
+
def get_balance_service() -> BalanceService:
|
197
|
+
"""Get BalanceService instance."""
|
198
|
+
return ServiceRegistry.get_balance_service()
|
199
|
+
|
200
|
+
|
201
|
+
def get_subscription_service() -> SubscriptionService:
|
202
|
+
"""Get SubscriptionService instance."""
|
203
|
+
return ServiceRegistry.get_subscription_service()
|
204
|
+
|
205
|
+
|
206
|
+
def get_webhook_service() -> WebhookService:
|
207
|
+
"""Get WebhookService instance."""
|
208
|
+
return ServiceRegistry.get_webhook_service()
|
209
|
+
|
210
|
+
|
211
|
+
def get_currency_service() -> CurrencyService:
|
212
|
+
"""Get CurrencyService instance."""
|
213
|
+
return ServiceRegistry.get_currency_service()
|
214
|
+
|
215
|
+
|
216
|
+
# Add convenience functions to __all__
|
217
|
+
__all__.extend([
|
218
|
+
'ServiceRegistry',
|
219
|
+
'get_payment_service',
|
220
|
+
'get_balance_service',
|
221
|
+
'get_subscription_service',
|
222
|
+
'get_webhook_service',
|
223
|
+
'get_currency_service',
|
224
|
+
])
|
@@ -1,15 +1,15 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
Cache services for the Universal Payment System v2.0.
|
3
3
|
|
4
|
-
|
4
|
+
Redis-backed caching with type safety and automatic key management.
|
5
5
|
"""
|
6
6
|
|
7
|
-
from .
|
8
|
-
from .simple_cache import SimpleCache, ApiKeyCache, RateLimitCache
|
7
|
+
from .cache_service import CacheService, get_cache_service, SimpleCache, ApiKeyCache, RateLimitCache
|
9
8
|
|
10
9
|
__all__ = [
|
11
|
-
'
|
12
|
-
'
|
10
|
+
'CacheService',
|
11
|
+
'get_cache_service',
|
13
12
|
'ApiKeyCache',
|
14
13
|
'RateLimitCache',
|
14
|
+
'SimpleCache',
|
15
15
|
]
|