django-cfg 1.3.1__py3-none-any.whl → 1.3.5__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 (115) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin_interface/old/payments/base.html +175 -0
  3. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +125 -0
  4. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +113 -0
  5. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +35 -0
  6. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +309 -0
  7. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +303 -0
  8. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +382 -0
  9. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +518 -0
  10. django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/components.css +248 -9
  11. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +163 -0
  12. django_cfg/apps/payments/admin_interface/serializers/__init__.py +39 -0
  13. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +149 -0
  14. django_cfg/apps/payments/admin_interface/serializers/webhook_serializers.py +114 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/base.html +55 -90
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/dialog.html +81 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_help_dialog.html +112 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_status.html +175 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +21 -17
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +123 -250
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +170 -269
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +152 -355
  23. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +202 -551
  24. django_cfg/apps/payments/admin_interface/views/__init__.py +25 -14
  25. django_cfg/apps/payments/admin_interface/views/api/__init__.py +20 -0
  26. django_cfg/apps/payments/admin_interface/views/api/payments.py +191 -0
  27. django_cfg/apps/payments/admin_interface/views/api/stats.py +206 -0
  28. django_cfg/apps/payments/admin_interface/views/api/users.py +60 -0
  29. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +257 -0
  30. django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +70 -0
  31. django_cfg/apps/payments/admin_interface/views/base.py +114 -0
  32. django_cfg/apps/payments/admin_interface/views/dashboard.py +60 -0
  33. django_cfg/apps/payments/admin_interface/views/forms.py +94 -0
  34. django_cfg/apps/payments/config/helpers.py +2 -2
  35. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +429 -0
  36. django_cfg/apps/payments/management/commands/currency_stats.py +443 -0
  37. django_cfg/apps/payments/management/commands/manage_currencies.py +9 -20
  38. django_cfg/apps/payments/management/commands/manage_providers.py +5 -5
  39. django_cfg/apps/payments/management/commands/process_pending_payments.py +357 -0
  40. django_cfg/apps/payments/management/commands/test_providers.py +434 -0
  41. django_cfg/apps/payments/middleware/api_access.py +35 -34
  42. django_cfg/apps/payments/migrations/0001_initial.py +1 -1
  43. django_cfg/apps/payments/models/balance.py +5 -2
  44. django_cfg/apps/payments/models/managers/api_key_managers.py +6 -2
  45. django_cfg/apps/payments/models/managers/balance_managers.py +3 -3
  46. django_cfg/apps/payments/models/managers/payment_managers.py +5 -0
  47. django_cfg/apps/payments/models/managers/subscription_managers.py +3 -3
  48. django_cfg/apps/payments/models/subscriptions.py +0 -24
  49. django_cfg/apps/payments/services/cache/__init__.py +1 -1
  50. django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
  51. django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
  52. django_cfg/apps/payments/services/cache_service/interfaces.py +32 -0
  53. django_cfg/apps/payments/services/cache_service/keys.py +49 -0
  54. django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
  55. django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
  56. django_cfg/apps/payments/services/core/balance_service.py +13 -2
  57. django_cfg/apps/payments/services/core/payment_service.py +49 -22
  58. django_cfg/apps/payments/services/integrations/ngrok_service.py +3 -3
  59. django_cfg/apps/payments/services/providers/registry.py +20 -0
  60. django_cfg/apps/payments/signals/api_key_signals.py +2 -2
  61. django_cfg/apps/payments/signals/balance_signals.py +8 -5
  62. django_cfg/apps/payments/static/payments/js/api-client.js +385 -0
  63. django_cfg/apps/payments/static/payments/js/ngrok-status.js +58 -0
  64. django_cfg/apps/payments/static/payments/js/payment-dashboard.js +50 -0
  65. django_cfg/apps/payments/static/payments/js/payment-form.js +175 -0
  66. django_cfg/apps/payments/static/payments/js/payment-list.js +95 -0
  67. django_cfg/apps/payments/static/payments/js/webhook-dashboard.js +154 -0
  68. django_cfg/apps/payments/urls.py +4 -0
  69. django_cfg/apps/payments/urls_admin.py +37 -18
  70. django_cfg/apps/payments/views/api/api_keys.py +14 -0
  71. django_cfg/apps/payments/views/api/base.py +1 -0
  72. django_cfg/apps/payments/views/api/currencies.py +2 -2
  73. django_cfg/apps/payments/views/api/payments.py +11 -5
  74. django_cfg/apps/payments/views/api/subscriptions.py +36 -31
  75. django_cfg/apps/payments/views/overview/__init__.py +40 -0
  76. django_cfg/apps/payments/views/overview/serializers.py +205 -0
  77. django_cfg/apps/payments/views/overview/services.py +439 -0
  78. django_cfg/apps/payments/views/overview/urls.py +27 -0
  79. django_cfg/apps/payments/views/overview/views.py +231 -0
  80. django_cfg/apps/payments/views/serializers/api_keys.py +20 -6
  81. django_cfg/apps/payments/views/serializers/balances.py +5 -8
  82. django_cfg/apps/payments/views/serializers/currencies.py +2 -6
  83. django_cfg/apps/payments/views/serializers/payments.py +37 -32
  84. django_cfg/apps/payments/views/serializers/subscriptions.py +4 -26
  85. django_cfg/apps/urls.py +2 -1
  86. django_cfg/core/config.py +25 -15
  87. django_cfg/core/generation.py +12 -12
  88. django_cfg/core/integration/display/startup.py +1 -1
  89. django_cfg/core/validation.py +4 -4
  90. django_cfg/management/commands/show_config.py +2 -2
  91. django_cfg/management/commands/tree.py +1 -3
  92. django_cfg/middleware/__init__.py +2 -0
  93. django_cfg/middleware/static_nocache.py +55 -0
  94. django_cfg/models/payments.py +13 -15
  95. django_cfg/models/security.py +15 -0
  96. django_cfg/modules/django_ngrok.py +6 -0
  97. django_cfg/modules/django_unfold/dashboard.py +1 -3
  98. django_cfg/utils/smart_defaults.py +51 -5
  99. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/METADATA +1 -1
  100. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/RECORD +111 -69
  101. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +0 -38
  102. django_cfg/apps/payments/admin_interface/views/payment_views.py +0 -259
  103. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +0 -37
  104. django_cfg/apps/payments/services/cache/cache_service.py +0 -235
  105. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/loading_spinner.html +0 -0
  106. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/notification.html +0 -0
  107. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/provider_card.html +0 -0
  108. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/currency_converter.html +0 -0
  109. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/payment_status.html +0 -0
  110. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/dashboard.css +0 -0
  111. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/components.js +0 -0
  112. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/utils.js +0 -0
  113. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/WHEEL +0 -0
  114. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/entry_points.txt +0 -0
  115. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/licenses/LICENSE +0 -0
@@ -102,30 +102,6 @@ class SubscriptionQuerySet(models.QuerySet):
102
102
  return self.filter(user=user)
103
103
 
104
104
 
105
- class SubscriptionManager(models.Manager):
106
- """Manager for subscription operations."""
107
-
108
- def get_queryset(self):
109
- """Return optimized queryset by default."""
110
- return SubscriptionQuerySet(self.model, using=self._db)
111
-
112
- def optimized(self):
113
- """Get optimized queryset."""
114
- return self.get_queryset().optimized()
115
-
116
- def active(self):
117
- """Get active subscriptions."""
118
- return self.get_queryset().active()
119
-
120
- def expired(self):
121
- """Get expired subscriptions."""
122
- return self.get_queryset().expired()
123
-
124
- def by_tier(self, tier):
125
- """Get subscriptions by tier."""
126
- return self.get_queryset().by_tier(tier)
127
-
128
-
129
105
  class Subscription(UUIDTimestampedModel):
130
106
  """
131
107
  User subscription model for API access control.
@@ -4,7 +4,7 @@ Cache services for the Universal Payment System v2.0.
4
4
  Redis-backed caching with type safety and automatic key management.
5
5
  """
6
6
 
7
- from .cache_service import CacheService, get_cache_service, SimpleCache, ApiKeyCache, RateLimitCache
7
+ from ..cache_service import CacheService, get_cache_service, SimpleCache, ApiKeyCache, RateLimitCache
8
8
 
9
9
  __all__ = [
10
10
  'CacheService',
@@ -0,0 +1,143 @@
1
+ """
2
+ Cache services for the Universal Payment System v2.0.
3
+
4
+ Main entry point for cache functionality.
5
+ Based on proven solutions from payments_old with improvements.
6
+ ONLY for API access control - NOT payment data!
7
+ """
8
+
9
+ from typing import Dict, Any
10
+ from django.core.cache import cache
11
+ from django_cfg.modules.django_logger import get_logger
12
+
13
+ from .interfaces import CacheInterface
14
+ from .simple_cache import SimpleCache
15
+ from .api_key_cache import ApiKeyCache
16
+ from .rate_limit_cache import RateLimitCache
17
+ from .keys import CacheKeys
18
+
19
+ logger = get_logger(__name__)
20
+
21
+
22
+ class CacheService:
23
+ """
24
+ Main cache service providing access to specialized caches.
25
+
26
+ Provides centralized access to different cache types.
27
+ """
28
+
29
+ def __init__(self):
30
+ """Initialize cache service with specialized caches."""
31
+ self.simple_cache = SimpleCache()
32
+ self.api_key_cache = ApiKeyCache()
33
+ self.rate_limit_cache = RateLimitCache()
34
+ # Backward compatibility attributes
35
+ self.default_timeout = 300
36
+ self.key_prefix = "payments"
37
+
38
+ # Backward compatibility methods - delegate to simple_cache
39
+ def get(self, key: str):
40
+ """Get value from cache."""
41
+ return self.simple_cache.get(key)
42
+
43
+ def set(self, key: str, value, timeout=None):
44
+ """Set value in cache."""
45
+ return self.simple_cache.set(key, value, timeout)
46
+
47
+ def delete(self, key: str):
48
+ """Delete value from cache."""
49
+ return self.simple_cache.delete(key)
50
+
51
+ def exists(self, key: str):
52
+ """Check if key exists in cache."""
53
+ return self.simple_cache.exists(key)
54
+
55
+ def get_or_set(self, key: str, default, timeout=None):
56
+ """Get value or set default if not exists."""
57
+ value = self.get(key)
58
+ if value is None:
59
+ if callable(default):
60
+ value = default()
61
+ else:
62
+ value = default
63
+ self.set(key, value, timeout)
64
+ return value
65
+
66
+ def set_many(self, data: dict, timeout=None):
67
+ """Set multiple values."""
68
+ for key, value in data.items():
69
+ self.set(key, value, timeout)
70
+
71
+ def get_many(self, keys: list):
72
+ """Get multiple values."""
73
+ result = {}
74
+ for key in keys:
75
+ value = self.get(key)
76
+ if value is not None:
77
+ result[key] = value
78
+ return result
79
+
80
+ def delete_many(self, keys: list):
81
+ """Delete multiple values."""
82
+ for key in keys:
83
+ self.delete(key)
84
+
85
+ def clear(self):
86
+ """Clear all cache (not implemented for safety)."""
87
+ # For safety, we don't implement cache.clear()
88
+ # as it would clear the entire cache backend
89
+ # Instead, we clear Django's cache which is safe for tests
90
+ from django.core.cache import cache
91
+ cache.clear()
92
+
93
+ def health_check(self) -> Dict[str, Any]:
94
+ """Check cache health."""
95
+ try:
96
+ test_key = "health_check"
97
+ test_value = "ok"
98
+
99
+ # Test set/get/delete
100
+ self.simple_cache.set(test_key, test_value, 10)
101
+ retrieved = self.simple_cache.get(test_key)
102
+ self.simple_cache.delete(test_key)
103
+
104
+ is_healthy = retrieved == test_value
105
+
106
+ return {
107
+ 'healthy': is_healthy,
108
+ 'backend': cache.__class__.__name__,
109
+ 'simple_cache': True,
110
+ 'api_key_cache': True,
111
+ 'rate_limit_cache': True
112
+ }
113
+ except Exception as e:
114
+ logger.error(f"Cache health check failed: {e}")
115
+ return {
116
+ 'healthy': False,
117
+ 'error': str(e),
118
+ 'backend': cache.__class__.__name__
119
+ }
120
+
121
+
122
+ # Global cache service instance
123
+ _cache_service = None
124
+
125
+
126
+ def get_cache_service() -> CacheService:
127
+ """Get global cache service instance."""
128
+ global _cache_service
129
+ if _cache_service is None:
130
+ _cache_service = CacheService()
131
+ return _cache_service
132
+
133
+
134
+ # Export main classes for backward compatibility
135
+ __all__ = [
136
+ 'CacheService',
137
+ 'CacheInterface',
138
+ 'SimpleCache',
139
+ 'ApiKeyCache',
140
+ 'RateLimitCache',
141
+ 'CacheKeys',
142
+ 'get_cache_service'
143
+ ]
@@ -0,0 +1,37 @@
1
+ """
2
+ API Key cache implementation for the Universal Payment System v2.0.
3
+
4
+ Specialized caching for API key operations and validation.
5
+ """
6
+
7
+ from typing import Optional
8
+ from .simple_cache import SimpleCache
9
+
10
+
11
+ class ApiKeyCache:
12
+ """Specialized cache for API key operations."""
13
+
14
+ def __init__(self):
15
+ self.cache = SimpleCache("api_keys")
16
+ self.default_timeout = self._get_cache_timeout('api_key')
17
+
18
+ def _get_cache_timeout(self, cache_type: str) -> int:
19
+ """Get cache timeout from PaymentsConfig."""
20
+ try:
21
+ from django_cfg.models.payments import PaymentsConfig
22
+ config = PaymentsConfig.get_current_config()
23
+ return config.cache_timeouts.get(cache_type, 300)
24
+ except Exception:
25
+ return 300 # 5 minutes default
26
+
27
+ def get_api_key_data(self, api_key: str) -> Optional[dict]:
28
+ """Get cached API key data."""
29
+ return self.cache.get(f"key:{api_key}")
30
+
31
+ def cache_api_key_data(self, api_key: str, data: dict) -> bool:
32
+ """Cache API key data."""
33
+ return self.cache.set(f"key:{api_key}", data, self.default_timeout)
34
+
35
+ def invalidate_api_key(self, api_key: str) -> bool:
36
+ """Invalidate cached API key."""
37
+ return self.cache.delete(f"key:{api_key}")
@@ -0,0 +1,32 @@
1
+ """
2
+ Cache interfaces for the Universal Payment System v2.0.
3
+
4
+ Abstract interfaces for cache implementations.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from typing import Optional, Any
9
+
10
+
11
+ class CacheInterface(ABC):
12
+ """Abstract cache interface."""
13
+
14
+ @abstractmethod
15
+ def get(self, key: str) -> Optional[Any]:
16
+ """Get value from cache."""
17
+ pass
18
+
19
+ @abstractmethod
20
+ def set(self, key: str, value: Any, timeout: Optional[int] = None) -> bool:
21
+ """Set value in cache."""
22
+ pass
23
+
24
+ @abstractmethod
25
+ def delete(self, key: str) -> bool:
26
+ """Delete value from cache."""
27
+ pass
28
+
29
+ @abstractmethod
30
+ def exists(self, key: str) -> bool:
31
+ """Check if key exists in cache."""
32
+ pass
@@ -0,0 +1,49 @@
1
+ """
2
+ Cache key generation utilities for the Universal Payment System v2.0.
3
+
4
+ Centralized cache key generation for consistency and testing.
5
+ """
6
+
7
+
8
+ class CacheKeys:
9
+ """Cache key generation utilities."""
10
+
11
+ @staticmethod
12
+ def user_balance(user_id: int) -> str:
13
+ """Generate cache key for user balance."""
14
+ return f"payments:user_balance:{user_id}"
15
+
16
+ @staticmethod
17
+ def payment(payment_id: str) -> str:
18
+ """Generate cache key for payment data."""
19
+ return f"payments:payment:{payment_id}"
20
+
21
+ @staticmethod
22
+ def payment_status(payment_id: str) -> str:
23
+ """Generate cache key for payment status."""
24
+ return f"payments:payment_status:{payment_id}"
25
+
26
+ @staticmethod
27
+ def currency_rates(from_currency: str, to_currency: str) -> str:
28
+ """Generate cache key for currency exchange rates."""
29
+ return f"payments:currency_rates:{from_currency}:{to_currency}"
30
+
31
+ @staticmethod
32
+ def provider_currencies(provider: str) -> str:
33
+ """Generate cache key for provider supported currencies."""
34
+ return f"payments:provider_currencies:{provider}"
35
+
36
+ @staticmethod
37
+ def api_key_validation(api_key: str) -> str:
38
+ """Generate cache key for API key validation data."""
39
+ return f"payments:api_key_validation:{api_key}"
40
+
41
+ @staticmethod
42
+ def user_subscription(user_id: int) -> str:
43
+ """Generate cache key for user subscription data."""
44
+ return f"payments:user_subscription:{user_id}"
45
+
46
+ @staticmethod
47
+ def rate_limit(user_id: int, action: str) -> str:
48
+ """Generate cache key for rate limiting."""
49
+ return f"payments:rate_limit:{user_id}:{action}"
@@ -0,0 +1,47 @@
1
+ """
2
+ Rate limiting cache implementation for the Universal Payment System v2.0.
3
+
4
+ Specialized caching for API rate limiting and usage tracking.
5
+ """
6
+
7
+ from typing import Optional
8
+ from .simple_cache import SimpleCache
9
+
10
+
11
+ class RateLimitCache:
12
+ """Specialized cache for rate limiting."""
13
+
14
+ def __init__(self):
15
+ self.cache = SimpleCache("rate_limit")
16
+
17
+ def get_usage_count(self, user_id: int, endpoint_group: str, window: str = "hour") -> int:
18
+ """Get current usage count for rate limiting."""
19
+ key = f"usage:{user_id}:{endpoint_group}:{window}"
20
+ count = self.cache.get(key)
21
+ return count if count is not None else 0
22
+
23
+ def increment_usage(self, user_id: int, endpoint_group: str, window: str = "hour", ttl: Optional[int] = None) -> int:
24
+ """Increment usage count and return new count."""
25
+ key = f"usage:{user_id}:{endpoint_group}:{window}"
26
+
27
+ # Get current count
28
+ current = self.get_usage_count(user_id, endpoint_group, window)
29
+ new_count = current + 1
30
+
31
+ # Get TTL from config or use defaults
32
+ if ttl is None:
33
+ try:
34
+ from django_cfg.models.payments import PaymentsConfig
35
+ config = PaymentsConfig.get_current_config()
36
+ ttl = config.cache_timeouts.get('rate_limit', 3600)
37
+ except Exception:
38
+ ttl = 3600 if window == "hour" else 86400 # 1 hour or 1 day
39
+
40
+ # Set new count with TTL
41
+ self.cache.set(key, new_count, ttl)
42
+ return new_count
43
+
44
+ def reset_usage(self, user_id: int, endpoint_group: str, window: str = "hour") -> bool:
45
+ """Reset usage count."""
46
+ key = f"usage:{user_id}:{endpoint_group}:{window}"
47
+ return self.cache.delete(key)
@@ -0,0 +1,101 @@
1
+ """
2
+ Simple cache implementation for the Universal Payment System v2.0.
3
+
4
+ Basic cache functionality with graceful fallback.
5
+ """
6
+
7
+ from typing import Optional, Any
8
+ from django.core.cache import cache
9
+ from django_cfg.modules.django_logger import get_logger
10
+
11
+ from .interfaces import CacheInterface
12
+
13
+ logger = get_logger(__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
+ Based on proven solution from payments_old.
22
+ """
23
+
24
+ def __init__(self, prefix: str = "payments"):
25
+ self.prefix = prefix
26
+ self.enabled = self._is_cache_enabled()
27
+
28
+ def _is_cache_enabled(self) -> bool:
29
+ """Check if cache is enabled via PaymentsConfig."""
30
+ from django.conf import settings
31
+ import sys
32
+
33
+ # For tests, always enable cache (detect test environment)
34
+ if 'test' in sys.argv or hasattr(settings, 'TESTING'):
35
+ return True
36
+
37
+ # For development, enable by default
38
+ if settings.DEBUG:
39
+ return True
40
+
41
+ try:
42
+ from django_cfg.models.payments import PaymentsConfig
43
+ config = PaymentsConfig.get_current_config()
44
+ return config.enabled # Cache enabled if payments enabled
45
+ except Exception as e:
46
+ logger.debug(f"PaymentsConfig not available, enabling cache by default: {e}")
47
+ return True # Default to enabled with graceful fallback
48
+
49
+ def _make_key(self, key: str) -> str:
50
+ """Create prefixed cache key."""
51
+ return f"{self.prefix}:{key}"
52
+
53
+ def get(self, key: str) -> Optional[Any]:
54
+ """Get value from cache."""
55
+ if not self.enabled:
56
+ return None
57
+
58
+ try:
59
+ cache_key = self._make_key(key)
60
+ return cache.get(cache_key)
61
+ except Exception as e:
62
+ logger.warning(f"Cache get failed for key {key}: {e}")
63
+ return None
64
+
65
+ def set(self, key: str, value: Any, timeout: Optional[int] = None) -> bool:
66
+ """Set value in cache."""
67
+ if not self.enabled:
68
+ return False
69
+
70
+ try:
71
+ cache_key = self._make_key(key)
72
+ cache.set(cache_key, value, timeout)
73
+ return True
74
+ except Exception as e:
75
+ logger.warning(f"Cache set failed for key {key}: {e}")
76
+ return False
77
+
78
+ def delete(self, key: str) -> bool:
79
+ """Delete value from cache."""
80
+ if not self.enabled:
81
+ return False
82
+
83
+ try:
84
+ cache_key = self._make_key(key)
85
+ cache.delete(cache_key)
86
+ return True
87
+ except Exception as e:
88
+ logger.warning(f"Cache delete failed for key {key}: {e}")
89
+ return False
90
+
91
+ def exists(self, key: str) -> bool:
92
+ """Check if key exists in cache."""
93
+ if not self.enabled:
94
+ return False
95
+
96
+ try:
97
+ cache_key = self._make_key(key)
98
+ return cache.get(cache_key) is not None
99
+ except Exception as e:
100
+ logger.warning(f"Cache exists check failed for key {key}: {e}")
101
+ return False
@@ -5,10 +5,12 @@ Handles user balance operations and transaction management.
5
5
  """
6
6
 
7
7
  from typing import Optional, Dict, Any, List
8
- from django.contrib.auth.models import User
8
+ from django.contrib.auth import get_user_model
9
9
  from django.db import models
10
10
  from django.utils import timezone
11
11
 
12
+ User = get_user_model()
13
+
12
14
  from .base import BaseService
13
15
  from ..types import (
14
16
  BalanceUpdateRequest, BalanceResult, TransactionData,
@@ -141,7 +143,16 @@ class BalanceService(BaseService):
141
143
 
142
144
  # Convert to response data
143
145
  balance_data = BalanceData.model_validate(balance)
144
- transaction_data = TransactionData.model_validate(transaction)
146
+ transaction_data = TransactionData(
147
+ id=str(transaction.id),
148
+ user_id=transaction.user_id,
149
+ amount=float(transaction.amount_usd),
150
+ transaction_type=transaction.transaction_type,
151
+ description=transaction.description,
152
+ payment_id=transaction.payment_id,
153
+ metadata=transaction.metadata or {},
154
+ created_at=transaction.created_at
155
+ )
145
156
 
146
157
  self._log_operation(
147
158
  "update_balance",
@@ -6,19 +6,20 @@ Handles payment creation, status checking, and lifecycle management.
6
6
 
7
7
  from typing import Optional, Dict, Any
8
8
  from decimal import Decimal
9
- from django.contrib.auth.models import User
9
+ from django.contrib.auth import get_user_model
10
+ from django.db import models
10
11
  from django.utils import timezone
11
12
 
13
+ from django_cfg.modules.django_currency import convert_currency, get_exchange_rate
12
14
  from .base import BaseService
13
15
  from ..types import (
14
16
  PaymentCreateRequest, PaymentStatusRequest, PaymentResult,
15
17
  PaymentData, ServiceOperationResult
16
18
  )
17
19
  from ...models import UniversalPayment, Currency, ProviderCurrency
18
- from django_cfg.modules.django_currency import convert_currency, get_exchange_rate
19
- # ConfigService removed - using direct Constance access
20
20
  from ..providers import ProviderRegistry, get_provider_registry
21
21
 
22
+ User = get_user_model()
22
23
 
23
24
  class PaymentService(BaseService):
24
25
  """
@@ -85,16 +86,17 @@ class PaymentService(BaseService):
85
86
 
86
87
  # Create payment in database first
87
88
  def create_payment_transaction():
89
+ currency = currency_result.data['currency']
88
90
  payment = UniversalPayment.objects.create(
89
91
  user=user,
90
92
  amount_usd=request.amount_usd,
91
- currency_code=request.currency_code,
93
+ currency=currency,
94
+ network=currency.native_networks.first(), # Use first native network
92
95
  provider=request.provider,
93
96
  status=UniversalPayment.PaymentStatus.PENDING,
94
97
  callback_url=request.callback_url,
95
98
  cancel_url=request.cancel_url,
96
99
  description=request.description,
97
- metadata=request.metadata,
98
100
  expires_at=timezone.now() + timezone.timedelta(hours=1) # 1 hour expiry
99
101
  )
100
102
  return payment
@@ -137,8 +139,8 @@ class PaymentService(BaseService):
137
139
  error_code="provider_creation_failed"
138
140
  )
139
141
 
140
- # Convert to PaymentData for response
141
- payment_data = PaymentData.model_validate(payment)
142
+ # Convert to PaymentData using our helper method
143
+ payment_data = self._convert_payment_to_data(payment)
142
144
 
143
145
  self._log_operation(
144
146
  "create_payment",
@@ -154,8 +156,9 @@ class PaymentService(BaseService):
154
156
  payment_id=str(payment.id),
155
157
  status=payment.status,
156
158
  amount_usd=payment.amount_usd,
157
- crypto_amount=payment.crypto_amount,
158
- currency_code=payment.currency_code,
159
+ crypto_amount=payment.pay_amount,
160
+ currency_code=payment.currency.code,
161
+ payment_url=payment.payment_url,
159
162
  expires_at=payment.expires_at,
160
163
  data={'payment': payment_data.model_dump()}
161
164
  )
@@ -211,8 +214,8 @@ class PaymentService(BaseService):
211
214
  # Reload payment if status was updated
212
215
  payment.refresh_from_db()
213
216
 
214
- # Convert to PaymentData
215
- payment_data = PaymentData.model_validate(payment)
217
+ # Convert to PaymentData using from_attributes
218
+ payment_data = self._convert_payment_to_data(payment)
216
219
 
217
220
  return PaymentResult(
218
221
  success=True,
@@ -220,12 +223,12 @@ class PaymentService(BaseService):
220
223
  payment_id=str(payment.id),
221
224
  status=payment.status,
222
225
  amount_usd=payment.amount_usd,
223
- crypto_amount=payment.crypto_amount,
224
- currency_code=payment.currency_code,
226
+ crypto_amount=payment.pay_amount,
227
+ currency_code=payment.currency.code,
225
228
  provider_payment_id=payment.provider_payment_id,
226
229
  payment_url=payment.payment_url,
227
- qr_code_url=payment.qr_code_url,
228
- wallet_address=payment.wallet_address,
230
+ qr_code_url=getattr(payment, 'qr_code_url', None),
231
+ wallet_address=payment.pay_address,
229
232
  expires_at=payment.expires_at,
230
233
  data={'payment': payment_data.model_dump()}
231
234
  )
@@ -279,7 +282,7 @@ class PaymentService(BaseService):
279
282
 
280
283
  if success:
281
284
  payment.refresh_from_db()
282
- payment_data = PaymentData.model_validate(payment)
285
+ payment_data = self._convert_payment_to_data(payment)
283
286
 
284
287
  self._log_operation(
285
288
  "cancel_payment",
@@ -311,7 +314,7 @@ class PaymentService(BaseService):
311
314
  def _validate_currency(self, currency_code: str) -> ServiceOperationResult:
312
315
  """Validate currency is supported."""
313
316
  try:
314
- currency = Currency.objects.get(code=currency_code, is_enabled=True)
317
+ currency = Currency.objects.get(code=currency_code, is_active=True)
315
318
 
316
319
  # Check if currency is supported by any provider
317
320
  provider_currency = ProviderCurrency.objects.filter(
@@ -327,7 +330,7 @@ class PaymentService(BaseService):
327
330
 
328
331
  return self._create_success_result(
329
332
  "Currency is valid",
330
- {'currency': currency_code}
333
+ {'currency': currency} # Wrap in dict for Pydantic
331
334
  )
332
335
 
333
336
  except Currency.DoesNotExist:
@@ -391,10 +394,10 @@ class PaymentService(BaseService):
391
394
  total_count = queryset.count()
392
395
  payments = queryset.order_by('-created_at')[offset:offset + limit]
393
396
 
394
- payment_data = [
395
- PaymentData.model_validate(payment).model_dump()
396
- for payment in payments
397
- ]
397
+ payment_data = []
398
+ for payment in payments:
399
+ payment_obj = self._convert_payment_to_data(payment)
400
+ payment_data.append(payment_obj.model_dump())
398
401
 
399
402
  return self._create_success_result(
400
403
  f"Retrieved {len(payment_data)} payments",
@@ -413,6 +416,30 @@ class PaymentService(BaseService):
413
416
  user_id=user_id
414
417
  )
415
418
 
419
+ def _convert_payment_to_data(self, payment: UniversalPayment) -> PaymentData:
420
+ """Convert Django UniversalPayment to PaymentData."""
421
+ return PaymentData(
422
+ id=str(payment.id),
423
+ user_id=payment.user_id,
424
+ amount_usd=float(payment.amount_usd),
425
+ crypto_amount=payment.pay_amount,
426
+ currency_code=payment.currency.code,
427
+ provider=payment.provider,
428
+ status=payment.status,
429
+ provider_payment_id=payment.provider_payment_id,
430
+ payment_url=payment.payment_url,
431
+ qr_code_url=getattr(payment, 'qr_code_url', None),
432
+ wallet_address=payment.pay_address,
433
+ callback_url=payment.callback_url,
434
+ cancel_url=payment.cancel_url,
435
+ description=payment.description,
436
+ metadata={},
437
+ created_at=payment.created_at,
438
+ updated_at=payment.updated_at,
439
+ expires_at=payment.expires_at,
440
+ completed_at=getattr(payment, 'completed_at', None)
441
+ )
442
+
416
443
  def get_payment_stats(self, days: int = 30) -> ServiceOperationResult:
417
444
  """Get payment statistics."""
418
445
  try:
@@ -39,9 +39,9 @@ def get_api_base_url() -> str:
39
39
 
40
40
 
41
41
  def is_ngrok_available() -> bool:
42
- """Check if ngrok tunnel is available."""
42
+ """Check if ngrok tunnel is actually active."""
43
43
  try:
44
- from django_cfg.modules.django_ngrok import get_tunnel_url
45
- return get_tunnel_url() is not None
44
+ from django_cfg.modules.django_ngrok import is_tunnel_active
45
+ return is_tunnel_active()
46
46
  except ImportError:
47
47
  return False