django-cfg 1.2.22__py3-none-any.whl → 1.2.25__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/knowbase/tasks/archive_tasks.py +6 -6
- django_cfg/apps/knowbase/tasks/document_processing.py +3 -3
- django_cfg/apps/knowbase/tasks/external_data_tasks.py +2 -2
- django_cfg/apps/knowbase/tasks/maintenance.py +3 -3
- 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/config/__init__.py +65 -0
- django_cfg/apps/payments/config/module.py +70 -0
- django_cfg/apps/payments/config/providers.py +115 -0
- django_cfg/apps/payments/config/settings.py +96 -0
- django_cfg/apps/payments/config/utils.py +52 -0
- django_cfg/apps/payments/decorators.py +291 -0
- django_cfg/apps/payments/management/__init__.py +3 -0
- django_cfg/apps/payments/management/commands/README.md +178 -0
- django_cfg/apps/payments/management/commands/__init__.py +3 -0
- django_cfg/apps/payments/management/commands/currency_stats.py +323 -0
- django_cfg/apps/payments/management/commands/populate_currencies.py +246 -0
- django_cfg/apps/payments/management/commands/update_currencies.py +336 -0
- django_cfg/apps/payments/managers/currency_manager.py +65 -14
- django_cfg/apps/payments/middleware/api_access.py +294 -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 +125 -11
- django_cfg/apps/payments/models/__init__.py +18 -0
- django_cfg/apps/payments/models/api_keys.py +2 -2
- django_cfg/apps/payments/models/balance.py +2 -2
- django_cfg/apps/payments/models/base.py +16 -0
- django_cfg/apps/payments/models/events.py +2 -2
- django_cfg/apps/payments/models/payments.py +112 -2
- django_cfg/apps/payments/models/subscriptions.py +2 -2
- django_cfg/apps/payments/services/__init__.py +64 -7
- 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 +447 -0
- django_cfg/apps/payments/services/core/fallback_service.py +432 -0
- django_cfg/apps/payments/services/core/payment_service.py +576 -0
- django_cfg/apps/payments/services/core/subscription_service.py +614 -0
- django_cfg/apps/payments/services/internal_types.py +297 -0
- django_cfg/apps/payments/services/middleware/__init__.py +8 -0
- django_cfg/apps/payments/services/monitoring/__init__.py +22 -0
- django_cfg/apps/payments/services/monitoring/api_schemas.py +222 -0
- django_cfg/apps/payments/services/monitoring/provider_health.py +372 -0
- django_cfg/apps/payments/services/providers/__init__.py +22 -0
- django_cfg/apps/payments/services/providers/base.py +137 -0
- django_cfg/apps/payments/services/providers/cryptapi.py +273 -0
- django_cfg/apps/payments/services/providers/cryptomus.py +310 -0
- django_cfg/apps/payments/services/providers/nowpayments.py +293 -0
- django_cfg/apps/payments/services/providers/registry.py +103 -0
- django_cfg/apps/payments/services/security/__init__.py +34 -0
- django_cfg/apps/payments/services/security/error_handler.py +637 -0
- django_cfg/apps/payments/services/security/payment_notifications.py +342 -0
- django_cfg/apps/payments/services/security/webhook_validator.py +475 -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 +160 -0
- django_cfg/apps/payments/signals/payment_signals.py +128 -0
- django_cfg/apps/payments/signals/subscription_signals.py +196 -0
- django_cfg/apps/payments/tasks/__init__.py +12 -0
- django_cfg/apps/payments/tasks/webhook_processing.py +177 -0
- django_cfg/apps/payments/urls.py +5 -5
- django_cfg/apps/payments/utils/__init__.py +45 -0
- django_cfg/apps/payments/utils/billing_utils.py +342 -0
- django_cfg/apps/payments/utils/config_utils.py +245 -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/payment_views.py +40 -2
- django_cfg/apps/payments/views/webhook_views.py +266 -0
- django_cfg/apps/payments/viewsets.py +65 -0
- django_cfg/apps/support/signals.py +16 -4
- django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
- django_cfg/cli/README.md +2 -2
- django_cfg/cli/commands/create_project.py +1 -1
- django_cfg/cli/commands/info.py +1 -1
- django_cfg/cli/main.py +1 -1
- django_cfg/cli/utils.py +5 -5
- django_cfg/core/config.py +18 -4
- django_cfg/models/payments.py +546 -0
- django_cfg/models/revolution.py +1 -1
- django_cfg/models/tasks.py +51 -2
- django_cfg/modules/base.py +12 -6
- django_cfg/modules/django_currency/README.md +104 -269
- django_cfg/modules/django_currency/__init__.py +99 -41
- django_cfg/modules/django_currency/clients/__init__.py +11 -0
- django_cfg/modules/django_currency/clients/coingecko_client.py +257 -0
- django_cfg/modules/django_currency/clients/yfinance_client.py +246 -0
- django_cfg/modules/django_currency/core/__init__.py +42 -0
- django_cfg/modules/django_currency/core/converter.py +169 -0
- django_cfg/modules/django_currency/core/exceptions.py +28 -0
- django_cfg/modules/django_currency/core/models.py +54 -0
- django_cfg/modules/django_currency/database/__init__.py +25 -0
- django_cfg/modules/django_currency/database/database_loader.py +507 -0
- django_cfg/modules/django_currency/utils/__init__.py +9 -0
- django_cfg/modules/django_currency/utils/cache.py +92 -0
- django_cfg/modules/django_email.py +42 -4
- django_cfg/modules/django_unfold/dashboard.py +20 -0
- django_cfg/registry/core.py +10 -0
- django_cfg/template_archive/__init__.py +0 -0
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/METADATA +11 -6
- {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/RECORD +113 -50
- django_cfg/apps/agents/examples/__init__.py +0 -3
- django_cfg/apps/agents/examples/simple_example.py +0 -161
- django_cfg/apps/knowbase/examples/__init__.py +0 -3
- django_cfg/apps/knowbase/examples/external_data_usage.py +0 -191
- django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +0 -199
- django_cfg/apps/payments/services/base.py +0 -68
- django_cfg/apps/payments/services/nowpayments.py +0 -78
- django_cfg/apps/payments/services/providers.py +0 -77
- django_cfg/apps/payments/services/redis_service.py +0 -215
- django_cfg/modules/django_currency/cache.py +0 -430
- django_cfg/modules/django_currency/converter.py +0 -324
- django_cfg/modules/django_currency/service.py +0 -277
- {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/licenses/LICENSE +0 -0
@@ -7,13 +7,13 @@ from django.contrib.auth import get_user_model
|
|
7
7
|
from django.core.validators import MinValueValidator
|
8
8
|
from django.core.exceptions import ValidationError
|
9
9
|
from django.utils import timezone
|
10
|
-
from .base import
|
10
|
+
from .base import UUIDTimestampedModel
|
11
11
|
|
12
12
|
User = get_user_model()
|
13
13
|
|
14
14
|
|
15
15
|
|
16
|
-
class UniversalPayment(
|
16
|
+
class UniversalPayment(UUIDTimestampedModel):
|
17
17
|
"""Universal payment model for all providers."""
|
18
18
|
|
19
19
|
class PaymentStatus(models.TextChoices):
|
@@ -28,6 +28,8 @@ class UniversalPayment(TimestampedModel):
|
|
28
28
|
|
29
29
|
class PaymentProvider(models.TextChoices):
|
30
30
|
NOWPAYMENTS = "nowpayments", "NowPayments"
|
31
|
+
CRYPTAPI = "cryptapi", "CryptAPI"
|
32
|
+
CRYPTOMUS = "cryptomus", "Cryptomus"
|
31
33
|
STRIPE = "stripe", "Stripe"
|
32
34
|
INTERNAL = "internal", "Internal"
|
33
35
|
|
@@ -138,6 +140,57 @@ class UniversalPayment(TimestampedModel):
|
|
138
140
|
help_text="Raw webhook data from provider"
|
139
141
|
)
|
140
142
|
|
143
|
+
# Universal Security fields (used by all providers)
|
144
|
+
security_nonce = models.CharField(
|
145
|
+
max_length=64,
|
146
|
+
null=True,
|
147
|
+
blank=True,
|
148
|
+
db_index=True,
|
149
|
+
help_text="Security nonce for replay attack protection (CryptAPI, Cryptomus, etc.)"
|
150
|
+
)
|
151
|
+
provider_callback_url = models.CharField(
|
152
|
+
max_length=512,
|
153
|
+
null=True,
|
154
|
+
blank=True,
|
155
|
+
help_text="Full callback URL with security parameters"
|
156
|
+
)
|
157
|
+
|
158
|
+
# Universal Transaction fields (crypto providers)
|
159
|
+
transaction_hash = models.CharField(
|
160
|
+
max_length=256,
|
161
|
+
null=True,
|
162
|
+
blank=True,
|
163
|
+
db_index=True,
|
164
|
+
help_text="Main transaction hash/ID (txid_in for CryptAPI, hash for Cryptomus)"
|
165
|
+
)
|
166
|
+
confirmation_hash = models.CharField(
|
167
|
+
max_length=256,
|
168
|
+
null=True,
|
169
|
+
blank=True,
|
170
|
+
help_text="Secondary transaction hash (txid_out for CryptAPI, confirmation for others)"
|
171
|
+
)
|
172
|
+
sender_address = models.CharField(
|
173
|
+
max_length=200,
|
174
|
+
null=True,
|
175
|
+
blank=True,
|
176
|
+
help_text="Sender address (address_in for CryptAPI, from_address for Cryptomus)"
|
177
|
+
)
|
178
|
+
receiver_address = models.CharField(
|
179
|
+
max_length=200,
|
180
|
+
null=True,
|
181
|
+
blank=True,
|
182
|
+
help_text="Receiver address (address_out for CryptAPI, to_address for Cryptomus)"
|
183
|
+
)
|
184
|
+
crypto_amount = models.FloatField(
|
185
|
+
null=True,
|
186
|
+
blank=True,
|
187
|
+
help_text="Amount in cryptocurrency units (value_coin for CryptAPI, amount for Cryptomus)"
|
188
|
+
)
|
189
|
+
confirmations_count = models.PositiveIntegerField(
|
190
|
+
default=0,
|
191
|
+
help_text="Number of blockchain confirmations"
|
192
|
+
)
|
193
|
+
|
141
194
|
# Timestamps
|
142
195
|
expires_at = models.DateTimeField(
|
143
196
|
null=True,
|
@@ -172,6 +225,11 @@ class UniversalPayment(TimestampedModel):
|
|
172
225
|
models.Index(fields=['currency_code']),
|
173
226
|
models.Index(fields=['created_at']),
|
174
227
|
models.Index(fields=['processed_at']),
|
228
|
+
# Universal crypto provider indexes
|
229
|
+
models.Index(fields=['security_nonce']),
|
230
|
+
models.Index(fields=['transaction_hash']),
|
231
|
+
models.Index(fields=['confirmations_count']),
|
232
|
+
models.Index(fields=['provider', 'status', 'confirmations_count']),
|
175
233
|
]
|
176
234
|
ordering = ['-created_at']
|
177
235
|
|
@@ -207,6 +265,28 @@ class UniversalPayment(TimestampedModel):
|
|
207
265
|
"""Check if this is a cryptocurrency payment."""
|
208
266
|
return self.provider == self.PaymentProvider.NOWPAYMENTS
|
209
267
|
|
268
|
+
@property
|
269
|
+
def is_crypto_provider_payment(self) -> bool:
|
270
|
+
"""Check if this is a crypto provider payment (CryptAPI, Cryptomus, etc.)."""
|
271
|
+
crypto_providers = ['cryptapi', 'cryptomus', 'nowpayments']
|
272
|
+
return self.provider in crypto_providers or (self.security_nonce is not None)
|
273
|
+
|
274
|
+
@property
|
275
|
+
def has_sufficient_confirmations(self) -> bool:
|
276
|
+
"""Check if payment has sufficient confirmations (3+ for most cryptos)."""
|
277
|
+
required_confirmations = 3 # Can be made configurable per currency
|
278
|
+
return self.confirmations_count >= required_confirmations
|
279
|
+
|
280
|
+
@property
|
281
|
+
def is_security_nonce_valid(self) -> bool:
|
282
|
+
"""Check if security nonce is present for crypto provider payments."""
|
283
|
+
return bool(self.security_nonce) if self.is_crypto_provider_payment else True
|
284
|
+
|
285
|
+
@property
|
286
|
+
def has_transaction_hash(self) -> bool:
|
287
|
+
"""Check if payment has a transaction hash."""
|
288
|
+
return bool(self.transaction_hash)
|
289
|
+
|
210
290
|
def get_payment_url(self) -> str:
|
211
291
|
"""Get payment URL for QR code or direct payment."""
|
212
292
|
if self.pay_address and self.pay_amount:
|
@@ -243,6 +323,36 @@ class UniversalPayment(TimestampedModel):
|
|
243
323
|
|
244
324
|
if 'payment_id' in webhook_data:
|
245
325
|
self.provider_payment_id = webhook_data['payment_id']
|
326
|
+
|
327
|
+
# Universal crypto provider webhook fields
|
328
|
+
# CryptAPI format
|
329
|
+
if 'txid_in' in webhook_data:
|
330
|
+
self.transaction_hash = webhook_data['txid_in']
|
331
|
+
if 'txid_out' in webhook_data:
|
332
|
+
self.confirmation_hash = webhook_data['txid_out']
|
333
|
+
if 'address_in' in webhook_data:
|
334
|
+
self.sender_address = webhook_data['address_in']
|
335
|
+
if 'address_out' in webhook_data:
|
336
|
+
self.receiver_address = webhook_data['address_out']
|
337
|
+
if 'value_coin' in webhook_data:
|
338
|
+
self.crypto_amount = float(str(webhook_data['value_coin']))
|
339
|
+
|
340
|
+
# Cryptomus format
|
341
|
+
if 'hash' in webhook_data:
|
342
|
+
self.transaction_hash = webhook_data['hash']
|
343
|
+
if 'from_address' in webhook_data:
|
344
|
+
self.sender_address = webhook_data['from_address']
|
345
|
+
if 'to_address' in webhook_data:
|
346
|
+
self.receiver_address = webhook_data['to_address']
|
347
|
+
if 'amount' in webhook_data and isinstance(webhook_data['amount'], (int, float, str)):
|
348
|
+
try:
|
349
|
+
self.crypto_amount = float(str(webhook_data['amount']))
|
350
|
+
except (ValueError, TypeError):
|
351
|
+
pass
|
352
|
+
|
353
|
+
# Universal confirmations field
|
354
|
+
if 'confirmations' in webhook_data:
|
355
|
+
self.confirmations_count = int(webhook_data['confirmations'])
|
246
356
|
|
247
357
|
self.save()
|
248
358
|
|
@@ -7,7 +7,7 @@ from django.contrib.auth import get_user_model
|
|
7
7
|
from django.core.validators import MinValueValidator
|
8
8
|
from django.utils import timezone
|
9
9
|
from datetime import timedelta
|
10
|
-
from .base import TimestampedModel
|
10
|
+
from .base import UUIDTimestampedModel, TimestampedModel
|
11
11
|
|
12
12
|
User = get_user_model()
|
13
13
|
|
@@ -106,7 +106,7 @@ class EndpointGroup(TimestampedModel):
|
|
106
106
|
return tier_limits.get(tier, 0)
|
107
107
|
|
108
108
|
|
109
|
-
class Subscription(
|
109
|
+
class Subscription(UUIDTimestampedModel):
|
110
110
|
"""User subscriptions to endpoint groups."""
|
111
111
|
|
112
112
|
class SubscriptionStatus(models.TextChoices):
|
@@ -1,14 +1,71 @@
|
|
1
1
|
"""
|
2
|
-
Universal
|
2
|
+
Universal Payment Services.
|
3
|
+
|
4
|
+
Modular architecture with minimal Pydantic typing for inter-service communication.
|
5
|
+
Uses Django ORM for data persistence and DRF for API responses.
|
3
6
|
"""
|
4
7
|
|
5
|
-
|
6
|
-
from .
|
7
|
-
from .
|
8
|
+
# Core services
|
9
|
+
from .core.payment_service import PaymentService
|
10
|
+
from .core.balance_service import BalanceService
|
11
|
+
from .core.subscription_service import SubscriptionService
|
12
|
+
|
13
|
+
# Provider services
|
14
|
+
from .providers.registry import ProviderRegistry
|
15
|
+
from .providers.nowpayments import NowPaymentsProvider
|
16
|
+
from .providers.cryptapi import CryptAPIProvider
|
17
|
+
|
18
|
+
# Cache services
|
19
|
+
from .cache import SimpleCache, ApiKeyCache, RateLimitCache
|
20
|
+
|
21
|
+
# Internal types for inter-service communication
|
22
|
+
from .internal_types import (
|
23
|
+
ProviderResponse, WebhookData, ServiceOperationResult,
|
24
|
+
BalanceUpdateRequest, AccessCheckRequest, AccessCheckResult,
|
25
|
+
# Service response models
|
26
|
+
PaymentCreationResult, WebhookProcessingResult, PaymentStatusResult,
|
27
|
+
UserBalanceResult, TransferResult, TransactionInfo,
|
28
|
+
EndpointGroupInfo, SubscriptionInfo, SubscriptionAnalytics,
|
29
|
+
# Additional response models
|
30
|
+
PaymentHistoryItem, ProviderInfo
|
31
|
+
)
|
8
32
|
|
9
33
|
__all__ = [
|
10
|
-
|
11
|
-
'PaymentService',
|
34
|
+
# Core services
|
35
|
+
'PaymentService',
|
36
|
+
'BalanceService',
|
37
|
+
'SubscriptionService',
|
38
|
+
|
39
|
+
# Provider services
|
40
|
+
'ProviderRegistry',
|
12
41
|
'NowPaymentsProvider',
|
13
|
-
'
|
42
|
+
'CryptAPIProvider',
|
43
|
+
|
44
|
+
# Cache services
|
45
|
+
'SimpleCache',
|
46
|
+
'ApiKeyCache',
|
47
|
+
'RateLimitCache',
|
48
|
+
|
49
|
+
# Internal types
|
50
|
+
'ProviderResponse',
|
51
|
+
'WebhookData',
|
52
|
+
'ServiceOperationResult',
|
53
|
+
'BalanceUpdateRequest',
|
54
|
+
'AccessCheckRequest',
|
55
|
+
'AccessCheckResult',
|
56
|
+
|
57
|
+
# Service response models
|
58
|
+
'PaymentCreationResult',
|
59
|
+
'WebhookProcessingResult',
|
60
|
+
'PaymentStatusResult',
|
61
|
+
'UserBalanceResult',
|
62
|
+
'TransferResult',
|
63
|
+
'TransactionInfo',
|
64
|
+
'EndpointGroupInfo',
|
65
|
+
'SubscriptionInfo',
|
66
|
+
'SubscriptionAnalytics',
|
67
|
+
|
68
|
+
# Additional response models
|
69
|
+
'PaymentHistoryItem',
|
70
|
+
'ProviderInfo',
|
14
71
|
]
|
@@ -0,0 +1,15 @@
|
|
1
|
+
"""
|
2
|
+
Simple caching for API key access control and rate limiting.
|
3
|
+
|
4
|
+
ONLY for API key caching - NOT for payment data!
|
5
|
+
"""
|
6
|
+
|
7
|
+
from .base import CacheInterface
|
8
|
+
from .simple_cache import SimpleCache, ApiKeyCache, RateLimitCache
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
'CacheInterface',
|
12
|
+
'SimpleCache',
|
13
|
+
'ApiKeyCache',
|
14
|
+
'RateLimitCache',
|
15
|
+
]
|
@@ -0,0 +1,30 @@
|
|
1
|
+
"""
|
2
|
+
Base cache interface for payments module.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from abc import ABC, abstractmethod
|
6
|
+
from typing import Optional, Any
|
7
|
+
|
8
|
+
|
9
|
+
class CacheInterface(ABC):
|
10
|
+
"""Abstract cache interface."""
|
11
|
+
|
12
|
+
@abstractmethod
|
13
|
+
def get(self, key: str) -> Optional[Any]:
|
14
|
+
"""Get value from cache."""
|
15
|
+
pass
|
16
|
+
|
17
|
+
@abstractmethod
|
18
|
+
def set(self, key: str, value: Any, timeout: Optional[int] = None) -> bool:
|
19
|
+
"""Set value in cache."""
|
20
|
+
pass
|
21
|
+
|
22
|
+
@abstractmethod
|
23
|
+
def delete(self, key: str) -> bool:
|
24
|
+
"""Delete value from cache."""
|
25
|
+
pass
|
26
|
+
|
27
|
+
@abstractmethod
|
28
|
+
def exists(self, key: str) -> bool:
|
29
|
+
"""Check if key exists in cache."""
|
30
|
+
pass
|
@@ -0,0 +1,135 @@
|
|
1
|
+
"""
|
2
|
+
Simple cache implementation for API keys and rate limiting.
|
3
|
+
ONLY for API access control - NOT payment data!
|
4
|
+
"""
|
5
|
+
|
6
|
+
import logging
|
7
|
+
from typing import Optional, Any
|
8
|
+
from django.core.cache import cache
|
9
|
+
|
10
|
+
from .base import CacheInterface
|
11
|
+
from ...utils.config_utils import CacheConfigHelper
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
class SimpleCache(CacheInterface):
|
17
|
+
"""
|
18
|
+
Simple cache implementation using Django's cache framework.
|
19
|
+
|
20
|
+
Falls back gracefully when cache is unavailable.
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(self, prefix: str = "payments"):
|
24
|
+
self.prefix = prefix
|
25
|
+
# Use config helper to check if cache is enabled
|
26
|
+
self.enabled = CacheConfigHelper.is_cache_enabled()
|
27
|
+
|
28
|
+
def _make_key(self, key: str) -> str:
|
29
|
+
"""Create prefixed cache key."""
|
30
|
+
return f"{self.prefix}:{key}"
|
31
|
+
|
32
|
+
def get(self, key: str) -> Optional[Any]:
|
33
|
+
"""Get value from cache."""
|
34
|
+
if not self.enabled:
|
35
|
+
return None
|
36
|
+
|
37
|
+
try:
|
38
|
+
cache_key = self._make_key(key)
|
39
|
+
return cache.get(cache_key)
|
40
|
+
except Exception as e:
|
41
|
+
logger.warning(f"Cache get failed for key {key}: {e}")
|
42
|
+
return None
|
43
|
+
|
44
|
+
def set(self, key: str, value: Any, timeout: Optional[int] = None) -> bool:
|
45
|
+
"""Set value in cache."""
|
46
|
+
if not self.enabled:
|
47
|
+
return False
|
48
|
+
|
49
|
+
try:
|
50
|
+
cache_key = self._make_key(key)
|
51
|
+
cache.set(cache_key, value, timeout)
|
52
|
+
return True
|
53
|
+
except Exception as e:
|
54
|
+
logger.warning(f"Cache set failed for key {key}: {e}")
|
55
|
+
return False
|
56
|
+
|
57
|
+
def delete(self, key: str) -> bool:
|
58
|
+
"""Delete value from cache."""
|
59
|
+
if not self.enabled:
|
60
|
+
return False
|
61
|
+
|
62
|
+
try:
|
63
|
+
cache_key = self._make_key(key)
|
64
|
+
cache.delete(cache_key)
|
65
|
+
return True
|
66
|
+
except Exception as e:
|
67
|
+
logger.warning(f"Cache delete failed for key {key}: {e}")
|
68
|
+
return False
|
69
|
+
|
70
|
+
def exists(self, key: str) -> bool:
|
71
|
+
"""Check if key exists in cache."""
|
72
|
+
if not self.enabled:
|
73
|
+
return False
|
74
|
+
|
75
|
+
try:
|
76
|
+
cache_key = self._make_key(key)
|
77
|
+
return cache.get(cache_key) is not None
|
78
|
+
except Exception as e:
|
79
|
+
logger.warning(f"Cache exists check failed for key {key}: {e}")
|
80
|
+
return False
|
81
|
+
|
82
|
+
|
83
|
+
class ApiKeyCache:
|
84
|
+
"""Specialized cache for API key operations."""
|
85
|
+
|
86
|
+
def __init__(self):
|
87
|
+
self.cache = SimpleCache("api_keys")
|
88
|
+
# Get timeout from config
|
89
|
+
self.default_timeout = CacheConfigHelper.get_cache_timeout('api_key')
|
90
|
+
|
91
|
+
def get_api_key_data(self, api_key: str) -> Optional[dict]:
|
92
|
+
"""Get cached API key data."""
|
93
|
+
return self.cache.get(f"key:{api_key}")
|
94
|
+
|
95
|
+
def cache_api_key_data(self, api_key: str, data: dict) -> bool:
|
96
|
+
"""Cache API key data."""
|
97
|
+
return self.cache.set(f"key:{api_key}", data, self.default_timeout)
|
98
|
+
|
99
|
+
def invalidate_api_key(self, api_key: str) -> bool:
|
100
|
+
"""Invalidate cached API key."""
|
101
|
+
return self.cache.delete(f"key:{api_key}")
|
102
|
+
|
103
|
+
|
104
|
+
class RateLimitCache:
|
105
|
+
"""Specialized cache for rate limiting."""
|
106
|
+
|
107
|
+
def __init__(self):
|
108
|
+
self.cache = SimpleCache("rate_limit")
|
109
|
+
|
110
|
+
def get_usage_count(self, user_id: int, endpoint_group: str, window: str = "hour") -> int:
|
111
|
+
"""Get current usage count for rate limiting."""
|
112
|
+
key = f"usage:{user_id}:{endpoint_group}:{window}"
|
113
|
+
count = self.cache.get(key)
|
114
|
+
return count if count is not None else 0
|
115
|
+
|
116
|
+
def increment_usage(self, user_id: int, endpoint_group: str, window: str = "hour", ttl: Optional[int] = None) -> int:
|
117
|
+
"""Increment usage count and return new count."""
|
118
|
+
key = f"usage:{user_id}:{endpoint_group}:{window}"
|
119
|
+
|
120
|
+
# Get current count
|
121
|
+
current = self.get_usage_count(user_id, endpoint_group, window)
|
122
|
+
new_count = current + 1
|
123
|
+
|
124
|
+
# Use config helper for TTL if not provided
|
125
|
+
if ttl is None:
|
126
|
+
ttl = CacheConfigHelper.get_cache_timeout('rate_limit')
|
127
|
+
|
128
|
+
# Set new count with TTL
|
129
|
+
self.cache.set(key, new_count, ttl)
|
130
|
+
return new_count
|
131
|
+
|
132
|
+
def reset_usage(self, user_id: int, endpoint_group: str, window: str = "hour") -> bool:
|
133
|
+
"""Reset usage count."""
|
134
|
+
key = f"usage:{user_id}:{endpoint_group}:{window}"
|
135
|
+
return self.cache.delete(key)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
"""
|
2
|
+
Core payment services.
|
3
|
+
|
4
|
+
Main business logic services for the payment system.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from .payment_service import PaymentService
|
8
|
+
from .balance_service import BalanceService
|
9
|
+
from .subscription_service import SubscriptionService
|
10
|
+
# Core services only - no legacy adapters
|
11
|
+
|
12
|
+
__all__ = [
|
13
|
+
'PaymentService',
|
14
|
+
'BalanceService',
|
15
|
+
'SubscriptionService',
|
16
|
+
# No legacy services
|
17
|
+
]
|