django-cfg 1.2.21__py3-none-any.whl → 1.2.23__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/newsletter/signals.py +9 -8
- django_cfg/apps/payments/__init__.py +8 -0
- django_cfg/apps/payments/admin/__init__.py +23 -0
- django_cfg/apps/payments/admin/api_keys_admin.py +347 -0
- django_cfg/apps/payments/admin/balance_admin.py +434 -0
- django_cfg/apps/payments/admin/currencies_admin.py +186 -0
- django_cfg/apps/payments/admin/filters.py +259 -0
- django_cfg/apps/payments/admin/payments_admin.py +142 -0
- django_cfg/apps/payments/admin/subscriptions_admin.py +227 -0
- django_cfg/apps/payments/admin/tariffs_admin.py +199 -0
- django_cfg/apps/payments/apps.py +22 -0
- django_cfg/apps/payments/config/__init__.py +87 -0
- django_cfg/apps/payments/config/module.py +162 -0
- django_cfg/apps/payments/config/providers.py +93 -0
- django_cfg/apps/payments/config/settings.py +136 -0
- django_cfg/apps/payments/config/utils.py +198 -0
- django_cfg/apps/payments/decorators.py +291 -0
- django_cfg/apps/payments/managers/__init__.py +22 -0
- django_cfg/apps/payments/managers/api_key_manager.py +35 -0
- django_cfg/apps/payments/managers/balance_manager.py +361 -0
- django_cfg/apps/payments/managers/currency_manager.py +32 -0
- django_cfg/apps/payments/managers/payment_manager.py +44 -0
- django_cfg/apps/payments/managers/subscription_manager.py +37 -0
- django_cfg/apps/payments/managers/tariff_manager.py +29 -0
- django_cfg/apps/payments/middleware/__init__.py +13 -0
- django_cfg/apps/payments/middleware/api_access.py +261 -0
- django_cfg/apps/payments/middleware/rate_limiting.py +216 -0
- django_cfg/apps/payments/middleware/usage_tracking.py +296 -0
- django_cfg/apps/payments/migrations/0001_initial.py +1003 -0
- django_cfg/apps/payments/migrations/__init__.py +1 -0
- django_cfg/apps/payments/models/__init__.py +67 -0
- django_cfg/apps/payments/models/api_keys.py +96 -0
- django_cfg/apps/payments/models/balance.py +209 -0
- django_cfg/apps/payments/models/base.py +30 -0
- django_cfg/apps/payments/models/currencies.py +138 -0
- django_cfg/apps/payments/models/events.py +73 -0
- django_cfg/apps/payments/models/payments.py +301 -0
- django_cfg/apps/payments/models/subscriptions.py +270 -0
- django_cfg/apps/payments/models/tariffs.py +102 -0
- django_cfg/apps/payments/serializers/__init__.py +56 -0
- django_cfg/apps/payments/serializers/api_keys.py +51 -0
- django_cfg/apps/payments/serializers/balance.py +59 -0
- django_cfg/apps/payments/serializers/currencies.py +55 -0
- django_cfg/apps/payments/serializers/payments.py +62 -0
- django_cfg/apps/payments/serializers/subscriptions.py +71 -0
- django_cfg/apps/payments/serializers/tariffs.py +56 -0
- django_cfg/apps/payments/services/__init__.py +65 -0
- django_cfg/apps/payments/services/billing/__init__.py +8 -0
- django_cfg/apps/payments/services/cache/__init__.py +15 -0
- django_cfg/apps/payments/services/cache/base.py +30 -0
- django_cfg/apps/payments/services/cache/simple_cache.py +135 -0
- django_cfg/apps/payments/services/core/__init__.py +17 -0
- django_cfg/apps/payments/services/core/balance_service.py +449 -0
- django_cfg/apps/payments/services/core/payment_service.py +393 -0
- django_cfg/apps/payments/services/core/subscription_service.py +616 -0
- django_cfg/apps/payments/services/internal_types.py +266 -0
- django_cfg/apps/payments/services/middleware/__init__.py +8 -0
- django_cfg/apps/payments/services/providers/__init__.py +19 -0
- django_cfg/apps/payments/services/providers/base.py +137 -0
- django_cfg/apps/payments/services/providers/cryptapi.py +262 -0
- django_cfg/apps/payments/services/providers/nowpayments.py +293 -0
- django_cfg/apps/payments/services/providers/registry.py +99 -0
- django_cfg/apps/payments/services/validators/__init__.py +8 -0
- django_cfg/apps/payments/signals/__init__.py +13 -0
- django_cfg/apps/payments/signals/api_key_signals.py +150 -0
- django_cfg/apps/payments/signals/payment_signals.py +127 -0
- django_cfg/apps/payments/signals/subscription_signals.py +196 -0
- django_cfg/apps/payments/urls.py +78 -0
- django_cfg/apps/payments/utils/__init__.py +42 -0
- django_cfg/apps/payments/utils/config_utils.py +243 -0
- django_cfg/apps/payments/utils/middleware_utils.py +228 -0
- django_cfg/apps/payments/utils/validation_utils.py +94 -0
- django_cfg/apps/payments/views/__init__.py +62 -0
- django_cfg/apps/payments/views/api_key_views.py +164 -0
- django_cfg/apps/payments/views/balance_views.py +75 -0
- django_cfg/apps/payments/views/currency_views.py +111 -0
- django_cfg/apps/payments/views/payment_views.py +111 -0
- django_cfg/apps/payments/views/subscription_views.py +135 -0
- django_cfg/apps/payments/views/tariff_views.py +131 -0
- django_cfg/apps/support/signals.py +16 -4
- django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
- django_cfg/core/config.py +6 -0
- django_cfg/models/revolution.py +14 -0
- django_cfg/modules/base.py +9 -0
- django_cfg/modules/django_email.py +42 -4
- django_cfg/modules/django_unfold/dashboard.py +20 -0
- {django_cfg-1.2.21.dist-info → django_cfg-1.2.23.dist-info}/METADATA +2 -1
- {django_cfg-1.2.21.dist-info → django_cfg-1.2.23.dist-info}/RECORD +92 -14
- {django_cfg-1.2.21.dist-info → django_cfg-1.2.23.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.21.dist-info → django_cfg-1.2.23.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.21.dist-info → django_cfg-1.2.23.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,199 @@
|
|
1
|
+
"""
|
2
|
+
Admin interface for tariffs.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from django.contrib import admin
|
6
|
+
from django.utils.html import format_html
|
7
|
+
from django.contrib.humanize.templatetags.humanize import naturaltime
|
8
|
+
from unfold.admin import ModelAdmin
|
9
|
+
from unfold.decorators import display
|
10
|
+
|
11
|
+
from ..models import Tariff, TariffEndpointGroup
|
12
|
+
|
13
|
+
|
14
|
+
@admin.register(Tariff)
|
15
|
+
class TariffAdmin(ModelAdmin):
|
16
|
+
"""Admin interface for tariffs."""
|
17
|
+
|
18
|
+
list_display = [
|
19
|
+
'tariff_display',
|
20
|
+
'price_display',
|
21
|
+
'limit_display',
|
22
|
+
'status_display',
|
23
|
+
'endpoint_groups_count',
|
24
|
+
'created_at_display'
|
25
|
+
]
|
26
|
+
|
27
|
+
list_display_links = ['tariff_display']
|
28
|
+
|
29
|
+
search_fields = ['name', 'display_name', 'description']
|
30
|
+
|
31
|
+
list_filter = ['is_active', 'created_at']
|
32
|
+
|
33
|
+
readonly_fields = ['created_at', 'updated_at']
|
34
|
+
|
35
|
+
fieldsets = [
|
36
|
+
('Tariff Information', {
|
37
|
+
'fields': ['name', 'display_name', 'description']
|
38
|
+
}),
|
39
|
+
('Pricing & Limits', {
|
40
|
+
'fields': ['monthly_price', 'request_limit']
|
41
|
+
}),
|
42
|
+
('Settings', {
|
43
|
+
'fields': ['is_active']
|
44
|
+
}),
|
45
|
+
('Timestamps', {
|
46
|
+
'fields': ['created_at', 'updated_at'],
|
47
|
+
'classes': ['collapse']
|
48
|
+
})
|
49
|
+
]
|
50
|
+
|
51
|
+
@display(description="Tariff")
|
52
|
+
def tariff_display(self, obj):
|
53
|
+
"""Display tariff name and description."""
|
54
|
+
return format_html(
|
55
|
+
'<strong>{}</strong><br><small>{}</small>',
|
56
|
+
obj.display_name,
|
57
|
+
obj.description[:50] + '...' if len(obj.description) > 50 else obj.description
|
58
|
+
)
|
59
|
+
|
60
|
+
@display(description="Price")
|
61
|
+
def price_display(self, obj):
|
62
|
+
"""Display price with free indicator."""
|
63
|
+
if obj.monthly_price == 0:
|
64
|
+
return format_html(
|
65
|
+
'<span style="background: #28a745; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;">FREE</span>'
|
66
|
+
)
|
67
|
+
else:
|
68
|
+
return format_html(
|
69
|
+
'<strong>${:.2f}</strong>/month',
|
70
|
+
obj.monthly_price
|
71
|
+
)
|
72
|
+
|
73
|
+
@display(description="Request Limit")
|
74
|
+
def limit_display(self, obj):
|
75
|
+
"""Display request limit."""
|
76
|
+
if obj.request_limit == 0:
|
77
|
+
return format_html(
|
78
|
+
'<span style="color: #28a745; font-weight: bold;">Unlimited</span>'
|
79
|
+
)
|
80
|
+
else:
|
81
|
+
return format_html(
|
82
|
+
'<strong>{:,}</strong>/month',
|
83
|
+
obj.request_limit
|
84
|
+
)
|
85
|
+
|
86
|
+
@display(description="Status")
|
87
|
+
def status_display(self, obj):
|
88
|
+
"""Display status badge."""
|
89
|
+
if obj.is_active:
|
90
|
+
return format_html(
|
91
|
+
'<span style="background: #28a745; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;">Active</span>'
|
92
|
+
)
|
93
|
+
else:
|
94
|
+
return format_html(
|
95
|
+
'<span style="background: #dc3545; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;">Inactive</span>'
|
96
|
+
)
|
97
|
+
|
98
|
+
@display(description="Endpoint Groups")
|
99
|
+
def endpoint_groups_count(self, obj):
|
100
|
+
"""Display count of endpoint groups."""
|
101
|
+
count = obj.endpoint_groups.filter(is_enabled=True).count()
|
102
|
+
total = obj.endpoint_groups.count()
|
103
|
+
|
104
|
+
if total == 0:
|
105
|
+
return format_html('<span style="color: #6c757d;">No groups</span>')
|
106
|
+
|
107
|
+
return format_html(
|
108
|
+
'<strong>{}</strong> active<br><small>{} total</small>',
|
109
|
+
count,
|
110
|
+
total
|
111
|
+
)
|
112
|
+
|
113
|
+
@display(description="Created")
|
114
|
+
def created_at_display(self, obj):
|
115
|
+
"""Display creation date."""
|
116
|
+
return naturaltime(obj.created_at)
|
117
|
+
|
118
|
+
|
119
|
+
@admin.register(TariffEndpointGroup)
|
120
|
+
class TariffEndpointGroupAdmin(ModelAdmin):
|
121
|
+
"""Admin interface for tariff endpoint group associations."""
|
122
|
+
|
123
|
+
list_display = [
|
124
|
+
'association_display',
|
125
|
+
'tariff_display',
|
126
|
+
'endpoint_group_display',
|
127
|
+
'status_display',
|
128
|
+
'created_at_display'
|
129
|
+
]
|
130
|
+
|
131
|
+
list_display_links = ['association_display']
|
132
|
+
|
133
|
+
search_fields = [
|
134
|
+
'tariff__name',
|
135
|
+
'tariff__display_name',
|
136
|
+
'endpoint_group__name',
|
137
|
+
'endpoint_group__display_name'
|
138
|
+
]
|
139
|
+
|
140
|
+
list_filter = ['is_enabled', 'tariff', 'endpoint_group', 'created_at']
|
141
|
+
|
142
|
+
readonly_fields = ['created_at', 'updated_at']
|
143
|
+
|
144
|
+
fieldsets = [
|
145
|
+
('Association', {
|
146
|
+
'fields': ['tariff', 'endpoint_group', 'is_enabled']
|
147
|
+
}),
|
148
|
+
('Timestamps', {
|
149
|
+
'fields': ['created_at', 'updated_at'],
|
150
|
+
'classes': ['collapse']
|
151
|
+
})
|
152
|
+
]
|
153
|
+
|
154
|
+
@display(description="Association")
|
155
|
+
def association_display(self, obj):
|
156
|
+
"""Display association ID."""
|
157
|
+
return format_html(
|
158
|
+
'<strong>#{}</strong><br><small>{} → {}</small>',
|
159
|
+
str(obj.id)[:8],
|
160
|
+
obj.tariff.name,
|
161
|
+
obj.endpoint_group.name
|
162
|
+
)
|
163
|
+
|
164
|
+
@display(description="Tariff")
|
165
|
+
def tariff_display(self, obj):
|
166
|
+
"""Display tariff information."""
|
167
|
+
price_text = 'FREE' if obj.tariff.monthly_price == 0 else f'${obj.tariff.monthly_price:.2f}/mo'
|
168
|
+
|
169
|
+
return format_html(
|
170
|
+
'<strong>{}</strong><br><small>{}</small>',
|
171
|
+
obj.tariff.display_name,
|
172
|
+
price_text
|
173
|
+
)
|
174
|
+
|
175
|
+
@display(description="Endpoint Group")
|
176
|
+
def endpoint_group_display(self, obj):
|
177
|
+
"""Display endpoint group information."""
|
178
|
+
return format_html(
|
179
|
+
'<strong>{}</strong><br><small>{}</small>',
|
180
|
+
obj.endpoint_group.display_name,
|
181
|
+
obj.endpoint_group.description[:30] + '...' if len(obj.endpoint_group.description) > 30 else obj.endpoint_group.description
|
182
|
+
)
|
183
|
+
|
184
|
+
@display(description="Status")
|
185
|
+
def status_display(self, obj):
|
186
|
+
"""Display status badge."""
|
187
|
+
if obj.is_enabled:
|
188
|
+
return format_html(
|
189
|
+
'<span style="background: #28a745; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;">Enabled</span>'
|
190
|
+
)
|
191
|
+
else:
|
192
|
+
return format_html(
|
193
|
+
'<span style="background: #dc3545; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;">Disabled</span>'
|
194
|
+
)
|
195
|
+
|
196
|
+
@display(description="Created")
|
197
|
+
def created_at_display(self, obj):
|
198
|
+
"""Display creation date."""
|
199
|
+
return naturaltime(obj.created_at)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
"""
|
2
|
+
Django app configuration for universal payments.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from django.apps import AppConfig
|
6
|
+
|
7
|
+
|
8
|
+
class PaymentsConfig(AppConfig):
|
9
|
+
"""Universal payments app configuration."""
|
10
|
+
|
11
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
12
|
+
name = 'django_cfg.apps.payments'
|
13
|
+
label = 'django_cfg_payments'
|
14
|
+
verbose_name = 'Universal Payments'
|
15
|
+
|
16
|
+
def ready(self):
|
17
|
+
"""Called when the app is ready."""
|
18
|
+
# Import signals if any
|
19
|
+
try:
|
20
|
+
from . import signals # noqa
|
21
|
+
except ImportError:
|
22
|
+
pass
|
@@ -0,0 +1,87 @@
|
|
1
|
+
"""
|
2
|
+
Universal Payment Configuration.
|
3
|
+
|
4
|
+
Modular configuration system for the payments module.
|
5
|
+
"""
|
6
|
+
|
7
|
+
# Core configuration classes
|
8
|
+
from .settings import (
|
9
|
+
PaymentsSettings,
|
10
|
+
SecuritySettings,
|
11
|
+
RateLimitSettings,
|
12
|
+
BillingSettings,
|
13
|
+
CacheSettings,
|
14
|
+
NotificationSettings
|
15
|
+
)
|
16
|
+
|
17
|
+
# Provider configurations
|
18
|
+
from .providers import (
|
19
|
+
PaymentProviderConfig,
|
20
|
+
NowPaymentsConfig,
|
21
|
+
StripeConfig,
|
22
|
+
CryptAPIConfig,
|
23
|
+
get_provider_config_class,
|
24
|
+
PROVIDER_CONFIGS
|
25
|
+
)
|
26
|
+
|
27
|
+
# Configuration module
|
28
|
+
from .module import PaymentsCfgModule
|
29
|
+
|
30
|
+
# Utility functions
|
31
|
+
from .utils import (
|
32
|
+
get_payments_config,
|
33
|
+
get_provider_config,
|
34
|
+
get_nowpayments_config,
|
35
|
+
get_stripe_config,
|
36
|
+
is_payments_enabled,
|
37
|
+
is_feature_enabled,
|
38
|
+
get_enabled_providers,
|
39
|
+
get_provider_settings,
|
40
|
+
validate_provider_config,
|
41
|
+
get_rate_limit_settings,
|
42
|
+
get_cache_settings,
|
43
|
+
get_billing_settings,
|
44
|
+
reset_config_cache,
|
45
|
+
reload_config
|
46
|
+
)
|
47
|
+
|
48
|
+
# Backwards compatibility exports
|
49
|
+
payments_config = PaymentsCfgModule()
|
50
|
+
|
51
|
+
__all__ = [
|
52
|
+
# Core settings
|
53
|
+
'PaymentsSettings',
|
54
|
+
'SecuritySettings',
|
55
|
+
'RateLimitSettings',
|
56
|
+
'BillingSettings',
|
57
|
+
'CacheSettings',
|
58
|
+
'NotificationSettings',
|
59
|
+
|
60
|
+
# Provider configurations
|
61
|
+
'PaymentProviderConfig',
|
62
|
+
'NowPaymentsConfig',
|
63
|
+
'StripeConfig',
|
64
|
+
'CryptAPIConfig',
|
65
|
+
'get_provider_config_class',
|
66
|
+
'PROVIDER_CONFIGS',
|
67
|
+
|
68
|
+
# Configuration module
|
69
|
+
'PaymentsCfgModule',
|
70
|
+
'payments_config',
|
71
|
+
|
72
|
+
# Utility functions
|
73
|
+
'get_payments_config',
|
74
|
+
'get_provider_config',
|
75
|
+
'get_nowpayments_config',
|
76
|
+
'get_stripe_config',
|
77
|
+
'is_payments_enabled',
|
78
|
+
'is_feature_enabled',
|
79
|
+
'get_enabled_providers',
|
80
|
+
'get_provider_settings',
|
81
|
+
'validate_provider_config',
|
82
|
+
'get_rate_limit_settings',
|
83
|
+
'get_cache_settings',
|
84
|
+
'get_billing_settings',
|
85
|
+
'reset_config_cache',
|
86
|
+
'reload_config',
|
87
|
+
]
|
@@ -0,0 +1,162 @@
|
|
1
|
+
"""
|
2
|
+
Payment configuration module.
|
3
|
+
|
4
|
+
Handles loading and managing payment configurations from project settings.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Optional
|
8
|
+
from pydantic import SecretStr
|
9
|
+
import logging
|
10
|
+
|
11
|
+
from django_cfg.modules.base import BaseCfgModule
|
12
|
+
from .settings import PaymentsSettings
|
13
|
+
from .providers import NowPaymentsConfig, StripeConfig, CryptAPIConfig, get_provider_config_class
|
14
|
+
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
|
18
|
+
class PaymentsCfgModule(BaseCfgModule):
|
19
|
+
"""Payment configuration module for django-cfg."""
|
20
|
+
|
21
|
+
def __init__(self):
|
22
|
+
super().__init__()
|
23
|
+
self.settings_class = PaymentsSettings
|
24
|
+
self._settings_cache = None
|
25
|
+
|
26
|
+
def get_config(self) -> PaymentsSettings:
|
27
|
+
"""Get payments configuration."""
|
28
|
+
if self._settings_cache is None:
|
29
|
+
project_config = super().get_config()
|
30
|
+
if project_config:
|
31
|
+
self._settings_cache = self.load_from_project_config(project_config)
|
32
|
+
else:
|
33
|
+
self._settings_cache = PaymentsSettings()
|
34
|
+
return self._settings_cache
|
35
|
+
|
36
|
+
def reset_cache(self):
|
37
|
+
"""Reset configuration cache."""
|
38
|
+
self._settings_cache = None
|
39
|
+
|
40
|
+
def load_from_project_config(self, config) -> PaymentsSettings:
|
41
|
+
"""Load payments configuration from main project config."""
|
42
|
+
# Get base settings
|
43
|
+
settings_dict = {}
|
44
|
+
|
45
|
+
# Load from django-cfg config if available
|
46
|
+
if hasattr(config, 'enable_payments'):
|
47
|
+
settings_dict['enabled'] = config.enable_payments
|
48
|
+
|
49
|
+
if hasattr(config, 'debug'):
|
50
|
+
settings_dict['debug_mode'] = config.debug
|
51
|
+
|
52
|
+
# Load provider configurations from environment/config
|
53
|
+
providers = {}
|
54
|
+
|
55
|
+
# Load NowPayments configuration
|
56
|
+
if self._has_provider_config(config, 'nowpayments'):
|
57
|
+
nowpayments_config = self._get_provider_config(config, 'nowpayments')
|
58
|
+
if nowpayments_config:
|
59
|
+
providers['nowpayments'] = nowpayments_config
|
60
|
+
|
61
|
+
# Load Stripe configuration
|
62
|
+
if self._has_provider_config(config, 'stripe'):
|
63
|
+
stripe_config = self._get_provider_config(config, 'stripe')
|
64
|
+
if stripe_config:
|
65
|
+
providers['stripe'] = stripe_config
|
66
|
+
|
67
|
+
# Load CryptAPI configuration
|
68
|
+
if self._has_provider_config(config, 'cryptapi'):
|
69
|
+
cryptapi_config = self._get_provider_config(config, 'cryptapi')
|
70
|
+
if cryptapi_config:
|
71
|
+
providers['cryptapi'] = cryptapi_config
|
72
|
+
|
73
|
+
|
74
|
+
settings_dict['providers'] = providers
|
75
|
+
|
76
|
+
return PaymentsSettings(**settings_dict)
|
77
|
+
|
78
|
+
def _has_provider_config(self, config, provider_name: str) -> bool:
|
79
|
+
"""Check if provider configuration exists."""
|
80
|
+
return (
|
81
|
+
hasattr(config, 'api_keys') and
|
82
|
+
hasattr(config.api_keys, provider_name)
|
83
|
+
)
|
84
|
+
|
85
|
+
def _get_provider_config(self, config, provider_name: str) -> Optional[object]:
|
86
|
+
"""Get provider configuration."""
|
87
|
+
try:
|
88
|
+
if provider_name == 'nowpayments':
|
89
|
+
return self._load_nowpayments_config(config)
|
90
|
+
elif provider_name == 'stripe':
|
91
|
+
return self._load_stripe_config(config)
|
92
|
+
elif provider_name == 'cryptapi':
|
93
|
+
return self._load_cryptapi_config(config)
|
94
|
+
else:
|
95
|
+
logger.warning(f"Unknown provider: {provider_name}")
|
96
|
+
return None
|
97
|
+
except Exception as e:
|
98
|
+
logger.error(f"Error loading {provider_name} config: {e}")
|
99
|
+
return None
|
100
|
+
|
101
|
+
def _load_nowpayments_config(self, config) -> Optional[NowPaymentsConfig]:
|
102
|
+
"""Load NowPayments configuration."""
|
103
|
+
nowpayments_config = config.api_keys.nowpayments
|
104
|
+
if not hasattr(nowpayments_config, 'api_key'):
|
105
|
+
return None
|
106
|
+
|
107
|
+
return NowPaymentsConfig(
|
108
|
+
api_key=SecretStr(nowpayments_config.api_key),
|
109
|
+
public_key=SecretStr(nowpayments_config.public_key) if hasattr(nowpayments_config, 'public_key') else None,
|
110
|
+
sandbox=getattr(config, 'debug', True),
|
111
|
+
callback_url=self._build_callback_url(config, 'nowpayments'),
|
112
|
+
success_url=self._build_success_url(config),
|
113
|
+
cancel_url=self._build_cancel_url(config)
|
114
|
+
)
|
115
|
+
|
116
|
+
def _load_stripe_config(self, config) -> Optional[StripeConfig]:
|
117
|
+
"""Load Stripe configuration."""
|
118
|
+
stripe_config = config.api_keys.stripe
|
119
|
+
if not hasattr(stripe_config, 'secret_key'):
|
120
|
+
return None
|
121
|
+
|
122
|
+
return StripeConfig(
|
123
|
+
api_key=SecretStr(stripe_config.secret_key),
|
124
|
+
publishable_key=SecretStr(stripe_config.publishable_key) if hasattr(stripe_config, 'publishable_key') else None,
|
125
|
+
webhook_secret=SecretStr(stripe_config.webhook_secret) if hasattr(stripe_config, 'webhook_secret') else None,
|
126
|
+
sandbox=getattr(config, 'debug', True)
|
127
|
+
)
|
128
|
+
|
129
|
+
def _load_cryptapi_config(self, config) -> Optional[CryptAPIConfig]:
|
130
|
+
"""Load CryptAPI configuration."""
|
131
|
+
cryptapi_config = config.api_keys.cryptapi
|
132
|
+
if not hasattr(cryptapi_config, 'own_address'):
|
133
|
+
return None
|
134
|
+
|
135
|
+
return CryptAPIConfig(
|
136
|
+
api_key=SecretStr('dummy'), # CryptAPI doesn't require API key
|
137
|
+
own_address=cryptapi_config.own_address,
|
138
|
+
callback_url=self._build_callback_url(config, 'cryptapi'),
|
139
|
+
convert_payments=getattr(cryptapi_config, 'convert_payments', True),
|
140
|
+
multi_token=getattr(cryptapi_config, 'multi_token', True),
|
141
|
+
priority=getattr(cryptapi_config, 'priority', 'default'),
|
142
|
+
sandbox=getattr(config, 'debug', True)
|
143
|
+
)
|
144
|
+
|
145
|
+
|
146
|
+
def _build_callback_url(self, config, provider: str) -> Optional[str]:
|
147
|
+
"""Build webhook callback URL for provider."""
|
148
|
+
if hasattr(config, 'api_url') and config.api_url:
|
149
|
+
return f"{config.api_url}/api/payments/webhook/{provider}/"
|
150
|
+
return None
|
151
|
+
|
152
|
+
def _build_success_url(self, config) -> Optional[str]:
|
153
|
+
"""Build payment success URL."""
|
154
|
+
if hasattr(config, 'site_url') and config.site_url:
|
155
|
+
return f"{config.site_url}/payments/success/"
|
156
|
+
return None
|
157
|
+
|
158
|
+
def _build_cancel_url(self, config) -> Optional[str]:
|
159
|
+
"""Build payment cancel URL."""
|
160
|
+
if hasattr(config, 'site_url') and config.site_url:
|
161
|
+
return f"{config.site_url}/payments/cancel/"
|
162
|
+
return None
|
@@ -0,0 +1,93 @@
|
|
1
|
+
"""
|
2
|
+
Payment provider configurations.
|
3
|
+
|
4
|
+
Defines configuration classes for different payment providers.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Dict, Any, Optional
|
8
|
+
from pydantic import BaseModel, Field, SecretStr
|
9
|
+
|
10
|
+
|
11
|
+
class PaymentProviderConfig(BaseModel):
|
12
|
+
"""Base configuration for payment providers."""
|
13
|
+
enabled: bool = True
|
14
|
+
sandbox: bool = Field(default=True, description="Use sandbox mode")
|
15
|
+
api_key: SecretStr = Field(description="Provider API key")
|
16
|
+
timeout: int = Field(default=30, description="Request timeout in seconds")
|
17
|
+
max_retries: int = Field(default=3, description="Maximum retry attempts")
|
18
|
+
|
19
|
+
def get_config_dict(self) -> Dict[str, Any]:
|
20
|
+
"""Get configuration as dictionary for provider initialization."""
|
21
|
+
return {
|
22
|
+
'api_key': self.api_key.get_secret_value(),
|
23
|
+
'sandbox': self.sandbox,
|
24
|
+
'timeout': self.timeout,
|
25
|
+
'max_retries': self.max_retries
|
26
|
+
}
|
27
|
+
|
28
|
+
|
29
|
+
class NowPaymentsConfig(PaymentProviderConfig):
|
30
|
+
"""NowPayments provider configuration."""
|
31
|
+
public_key: Optional[SecretStr] = Field(default=None, description="NowPayments public key")
|
32
|
+
callback_url: Optional[str] = Field(default=None, description="Webhook callback URL")
|
33
|
+
success_url: Optional[str] = Field(default=None, description="Payment success URL")
|
34
|
+
cancel_url: Optional[str] = Field(default=None, description="Payment cancel URL")
|
35
|
+
|
36
|
+
def get_config_dict(self) -> Dict[str, Any]:
|
37
|
+
"""Get configuration as dictionary for provider initialization."""
|
38
|
+
config = super().get_config_dict()
|
39
|
+
config.update({
|
40
|
+
'callback_url': self.callback_url,
|
41
|
+
'success_url': self.success_url,
|
42
|
+
'cancel_url': self.cancel_url,
|
43
|
+
})
|
44
|
+
return config
|
45
|
+
|
46
|
+
|
47
|
+
class StripeConfig(PaymentProviderConfig):
|
48
|
+
"""Stripe provider configuration."""
|
49
|
+
publishable_key: Optional[SecretStr] = Field(default=None, description="Stripe publishable key")
|
50
|
+
webhook_secret: Optional[SecretStr] = Field(default=None, description="Stripe webhook secret")
|
51
|
+
|
52
|
+
def get_config_dict(self) -> Dict[str, Any]:
|
53
|
+
"""Get configuration as dictionary for provider initialization."""
|
54
|
+
config = super().get_config_dict()
|
55
|
+
config.update({
|
56
|
+
'publishable_key': self.publishable_key.get_secret_value() if self.publishable_key else None,
|
57
|
+
'webhook_secret': self.webhook_secret.get_secret_value() if self.webhook_secret else None,
|
58
|
+
})
|
59
|
+
return config
|
60
|
+
|
61
|
+
|
62
|
+
class CryptAPIConfig(PaymentProviderConfig):
|
63
|
+
"""CryptAPI provider configuration."""
|
64
|
+
own_address: str = Field(description="Your crypto address where funds will be sent")
|
65
|
+
callback_url: Optional[str] = Field(default=None, description="Webhook callback URL")
|
66
|
+
convert_payments: bool = Field(default=True, description="Auto-convert payments to your address currency")
|
67
|
+
multi_token: bool = Field(default=True, description="Enable multi-token support")
|
68
|
+
priority: str = Field(default='default', description="Transaction priority (default, economic, priority)")
|
69
|
+
|
70
|
+
def get_config_dict(self) -> Dict[str, Any]:
|
71
|
+
"""Get configuration as dictionary for provider initialization."""
|
72
|
+
config = super().get_config_dict()
|
73
|
+
config.update({
|
74
|
+
'own_address': self.own_address,
|
75
|
+
'callback_url': self.callback_url,
|
76
|
+
'convert_payments': self.convert_payments,
|
77
|
+
'multi_token': self.multi_token,
|
78
|
+
'priority': self.priority,
|
79
|
+
})
|
80
|
+
return config
|
81
|
+
|
82
|
+
|
83
|
+
# Provider registry for easy access
|
84
|
+
PROVIDER_CONFIGS = {
|
85
|
+
'nowpayments': NowPaymentsConfig,
|
86
|
+
'stripe': StripeConfig,
|
87
|
+
'cryptapi': CryptAPIConfig,
|
88
|
+
}
|
89
|
+
|
90
|
+
|
91
|
+
def get_provider_config_class(provider_name: str) -> Optional[type]:
|
92
|
+
"""Get provider configuration class by name."""
|
93
|
+
return PROVIDER_CONFIGS.get(provider_name)
|