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.
Files changed (125) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/knowbase/tasks/archive_tasks.py +6 -6
  3. django_cfg/apps/knowbase/tasks/document_processing.py +3 -3
  4. django_cfg/apps/knowbase/tasks/external_data_tasks.py +2 -2
  5. django_cfg/apps/knowbase/tasks/maintenance.py +3 -3
  6. django_cfg/apps/payments/admin/__init__.py +23 -0
  7. django_cfg/apps/payments/admin/api_keys_admin.py +347 -0
  8. django_cfg/apps/payments/admin/balance_admin.py +434 -0
  9. django_cfg/apps/payments/admin/currencies_admin.py +186 -0
  10. django_cfg/apps/payments/admin/filters.py +259 -0
  11. django_cfg/apps/payments/admin/payments_admin.py +142 -0
  12. django_cfg/apps/payments/admin/subscriptions_admin.py +227 -0
  13. django_cfg/apps/payments/admin/tariffs_admin.py +199 -0
  14. django_cfg/apps/payments/config/__init__.py +65 -0
  15. django_cfg/apps/payments/config/module.py +70 -0
  16. django_cfg/apps/payments/config/providers.py +115 -0
  17. django_cfg/apps/payments/config/settings.py +96 -0
  18. django_cfg/apps/payments/config/utils.py +52 -0
  19. django_cfg/apps/payments/decorators.py +291 -0
  20. django_cfg/apps/payments/management/__init__.py +3 -0
  21. django_cfg/apps/payments/management/commands/README.md +178 -0
  22. django_cfg/apps/payments/management/commands/__init__.py +3 -0
  23. django_cfg/apps/payments/management/commands/currency_stats.py +323 -0
  24. django_cfg/apps/payments/management/commands/populate_currencies.py +246 -0
  25. django_cfg/apps/payments/management/commands/update_currencies.py +336 -0
  26. django_cfg/apps/payments/managers/currency_manager.py +65 -14
  27. django_cfg/apps/payments/middleware/api_access.py +294 -0
  28. django_cfg/apps/payments/middleware/rate_limiting.py +216 -0
  29. django_cfg/apps/payments/middleware/usage_tracking.py +296 -0
  30. django_cfg/apps/payments/migrations/0001_initial.py +125 -11
  31. django_cfg/apps/payments/models/__init__.py +18 -0
  32. django_cfg/apps/payments/models/api_keys.py +2 -2
  33. django_cfg/apps/payments/models/balance.py +2 -2
  34. django_cfg/apps/payments/models/base.py +16 -0
  35. django_cfg/apps/payments/models/events.py +2 -2
  36. django_cfg/apps/payments/models/payments.py +112 -2
  37. django_cfg/apps/payments/models/subscriptions.py +2 -2
  38. django_cfg/apps/payments/services/__init__.py +64 -7
  39. django_cfg/apps/payments/services/billing/__init__.py +8 -0
  40. django_cfg/apps/payments/services/cache/__init__.py +15 -0
  41. django_cfg/apps/payments/services/cache/base.py +30 -0
  42. django_cfg/apps/payments/services/cache/simple_cache.py +135 -0
  43. django_cfg/apps/payments/services/core/__init__.py +17 -0
  44. django_cfg/apps/payments/services/core/balance_service.py +447 -0
  45. django_cfg/apps/payments/services/core/fallback_service.py +432 -0
  46. django_cfg/apps/payments/services/core/payment_service.py +576 -0
  47. django_cfg/apps/payments/services/core/subscription_service.py +614 -0
  48. django_cfg/apps/payments/services/internal_types.py +297 -0
  49. django_cfg/apps/payments/services/middleware/__init__.py +8 -0
  50. django_cfg/apps/payments/services/monitoring/__init__.py +22 -0
  51. django_cfg/apps/payments/services/monitoring/api_schemas.py +222 -0
  52. django_cfg/apps/payments/services/monitoring/provider_health.py +372 -0
  53. django_cfg/apps/payments/services/providers/__init__.py +22 -0
  54. django_cfg/apps/payments/services/providers/base.py +137 -0
  55. django_cfg/apps/payments/services/providers/cryptapi.py +273 -0
  56. django_cfg/apps/payments/services/providers/cryptomus.py +310 -0
  57. django_cfg/apps/payments/services/providers/nowpayments.py +293 -0
  58. django_cfg/apps/payments/services/providers/registry.py +103 -0
  59. django_cfg/apps/payments/services/security/__init__.py +34 -0
  60. django_cfg/apps/payments/services/security/error_handler.py +637 -0
  61. django_cfg/apps/payments/services/security/payment_notifications.py +342 -0
  62. django_cfg/apps/payments/services/security/webhook_validator.py +475 -0
  63. django_cfg/apps/payments/services/validators/__init__.py +8 -0
  64. django_cfg/apps/payments/signals/__init__.py +13 -0
  65. django_cfg/apps/payments/signals/api_key_signals.py +160 -0
  66. django_cfg/apps/payments/signals/payment_signals.py +128 -0
  67. django_cfg/apps/payments/signals/subscription_signals.py +196 -0
  68. django_cfg/apps/payments/tasks/__init__.py +12 -0
  69. django_cfg/apps/payments/tasks/webhook_processing.py +177 -0
  70. django_cfg/apps/payments/urls.py +5 -5
  71. django_cfg/apps/payments/utils/__init__.py +45 -0
  72. django_cfg/apps/payments/utils/billing_utils.py +342 -0
  73. django_cfg/apps/payments/utils/config_utils.py +245 -0
  74. django_cfg/apps/payments/utils/middleware_utils.py +228 -0
  75. django_cfg/apps/payments/utils/validation_utils.py +94 -0
  76. django_cfg/apps/payments/views/payment_views.py +40 -2
  77. django_cfg/apps/payments/views/webhook_views.py +266 -0
  78. django_cfg/apps/payments/viewsets.py +65 -0
  79. django_cfg/apps/support/signals.py +16 -4
  80. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  81. django_cfg/cli/README.md +2 -2
  82. django_cfg/cli/commands/create_project.py +1 -1
  83. django_cfg/cli/commands/info.py +1 -1
  84. django_cfg/cli/main.py +1 -1
  85. django_cfg/cli/utils.py +5 -5
  86. django_cfg/core/config.py +18 -4
  87. django_cfg/models/payments.py +546 -0
  88. django_cfg/models/revolution.py +1 -1
  89. django_cfg/models/tasks.py +51 -2
  90. django_cfg/modules/base.py +12 -6
  91. django_cfg/modules/django_currency/README.md +104 -269
  92. django_cfg/modules/django_currency/__init__.py +99 -41
  93. django_cfg/modules/django_currency/clients/__init__.py +11 -0
  94. django_cfg/modules/django_currency/clients/coingecko_client.py +257 -0
  95. django_cfg/modules/django_currency/clients/yfinance_client.py +246 -0
  96. django_cfg/modules/django_currency/core/__init__.py +42 -0
  97. django_cfg/modules/django_currency/core/converter.py +169 -0
  98. django_cfg/modules/django_currency/core/exceptions.py +28 -0
  99. django_cfg/modules/django_currency/core/models.py +54 -0
  100. django_cfg/modules/django_currency/database/__init__.py +25 -0
  101. django_cfg/modules/django_currency/database/database_loader.py +507 -0
  102. django_cfg/modules/django_currency/utils/__init__.py +9 -0
  103. django_cfg/modules/django_currency/utils/cache.py +92 -0
  104. django_cfg/modules/django_email.py +42 -4
  105. django_cfg/modules/django_unfold/dashboard.py +20 -0
  106. django_cfg/registry/core.py +10 -0
  107. django_cfg/template_archive/__init__.py +0 -0
  108. django_cfg/template_archive/django_sample.zip +0 -0
  109. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/METADATA +11 -6
  110. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/RECORD +113 -50
  111. django_cfg/apps/agents/examples/__init__.py +0 -3
  112. django_cfg/apps/agents/examples/simple_example.py +0 -161
  113. django_cfg/apps/knowbase/examples/__init__.py +0 -3
  114. django_cfg/apps/knowbase/examples/external_data_usage.py +0 -191
  115. django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +0 -199
  116. django_cfg/apps/payments/services/base.py +0 -68
  117. django_cfg/apps/payments/services/nowpayments.py +0 -78
  118. django_cfg/apps/payments/services/providers.py +0 -77
  119. django_cfg/apps/payments/services/redis_service.py +0 -215
  120. django_cfg/modules/django_currency/cache.py +0 -430
  121. django_cfg/modules/django_currency/converter.py +0 -324
  122. django_cfg/modules/django_currency/service.py +0 -277
  123. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/WHEEL +0 -0
  124. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/entry_points.txt +0 -0
  125. {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 TimestampedModel
10
+ from .base import UUIDTimestampedModel
11
11
 
12
12
  User = get_user_model()
13
13
 
14
14
 
15
15
 
16
- class UniversalPayment(TimestampedModel):
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(TimestampedModel):
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 payment services.
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
- from .base import PaymentProvider, PaymentService
6
- from .nowpayments import NowPaymentsProvider
7
- from .redis_service import RedisService
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
- 'PaymentProvider',
11
- 'PaymentService',
34
+ # Core services
35
+ 'PaymentService',
36
+ 'BalanceService',
37
+ 'SubscriptionService',
38
+
39
+ # Provider services
40
+ 'ProviderRegistry',
12
41
  'NowPaymentsProvider',
13
- 'RedisService',
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,8 @@
1
+ """
2
+ Billing services for payments module.
3
+
4
+ TODO: Implement billing services when needed.
5
+ """
6
+
7
+ # Placeholder for future billing services
8
+ __all__ = []
@@ -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
+ ]