django-cfg 1.2.21__py3-none-any.whl → 1.2.22__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 (51) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/newsletter/signals.py +9 -8
  3. django_cfg/apps/payments/__init__.py +8 -0
  4. django_cfg/apps/payments/apps.py +22 -0
  5. django_cfg/apps/payments/managers/__init__.py +22 -0
  6. django_cfg/apps/payments/managers/api_key_manager.py +35 -0
  7. django_cfg/apps/payments/managers/balance_manager.py +361 -0
  8. django_cfg/apps/payments/managers/currency_manager.py +32 -0
  9. django_cfg/apps/payments/managers/payment_manager.py +44 -0
  10. django_cfg/apps/payments/managers/subscription_manager.py +37 -0
  11. django_cfg/apps/payments/managers/tariff_manager.py +29 -0
  12. django_cfg/apps/payments/middleware/__init__.py +13 -0
  13. django_cfg/apps/payments/migrations/0001_initial.py +982 -0
  14. django_cfg/apps/payments/migrations/__init__.py +1 -0
  15. django_cfg/apps/payments/models/__init__.py +49 -0
  16. django_cfg/apps/payments/models/api_keys.py +96 -0
  17. django_cfg/apps/payments/models/balance.py +209 -0
  18. django_cfg/apps/payments/models/base.py +14 -0
  19. django_cfg/apps/payments/models/currencies.py +138 -0
  20. django_cfg/apps/payments/models/events.py +73 -0
  21. django_cfg/apps/payments/models/payments.py +301 -0
  22. django_cfg/apps/payments/models/subscriptions.py +270 -0
  23. django_cfg/apps/payments/models/tariffs.py +102 -0
  24. django_cfg/apps/payments/serializers/__init__.py +56 -0
  25. django_cfg/apps/payments/serializers/api_keys.py +51 -0
  26. django_cfg/apps/payments/serializers/balance.py +59 -0
  27. django_cfg/apps/payments/serializers/currencies.py +55 -0
  28. django_cfg/apps/payments/serializers/payments.py +62 -0
  29. django_cfg/apps/payments/serializers/subscriptions.py +71 -0
  30. django_cfg/apps/payments/serializers/tariffs.py +56 -0
  31. django_cfg/apps/payments/services/__init__.py +14 -0
  32. django_cfg/apps/payments/services/base.py +68 -0
  33. django_cfg/apps/payments/services/nowpayments.py +78 -0
  34. django_cfg/apps/payments/services/providers.py +77 -0
  35. django_cfg/apps/payments/services/redis_service.py +215 -0
  36. django_cfg/apps/payments/urls.py +78 -0
  37. django_cfg/apps/payments/views/__init__.py +62 -0
  38. django_cfg/apps/payments/views/api_key_views.py +164 -0
  39. django_cfg/apps/payments/views/balance_views.py +75 -0
  40. django_cfg/apps/payments/views/currency_views.py +111 -0
  41. django_cfg/apps/payments/views/payment_views.py +111 -0
  42. django_cfg/apps/payments/views/subscription_views.py +135 -0
  43. django_cfg/apps/payments/views/tariff_views.py +131 -0
  44. django_cfg/core/config.py +6 -0
  45. django_cfg/models/revolution.py +14 -0
  46. django_cfg/modules/base.py +9 -0
  47. {django_cfg-1.2.21.dist-info → django_cfg-1.2.22.dist-info}/METADATA +1 -1
  48. {django_cfg-1.2.21.dist-info → django_cfg-1.2.22.dist-info}/RECORD +51 -10
  49. {django_cfg-1.2.21.dist-info → django_cfg-1.2.22.dist-info}/WHEEL +0 -0
  50. {django_cfg-1.2.21.dist-info → django_cfg-1.2.22.dist-info}/entry_points.txt +0 -0
  51. {django_cfg-1.2.21.dist-info → django_cfg-1.2.22.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,111 @@
1
+ """
2
+ Currency ViewSets.
3
+ """
4
+
5
+ from rest_framework import viewsets, permissions, generics
6
+ from rest_framework.decorators import action
7
+ from rest_framework.response import Response
8
+ from django_filters.rest_framework import DjangoFilterBackend
9
+ from ..models import Currency, CurrencyNetwork
10
+ from ..serializers import (
11
+ CurrencySerializer, CurrencyNetworkSerializer, CurrencyListSerializer
12
+ )
13
+
14
+
15
+ class CurrencyViewSet(viewsets.ReadOnlyModelViewSet):
16
+ """Currency ViewSet: /currencies/"""
17
+
18
+ queryset = Currency.objects.filter(is_active=True)
19
+ serializer_class = CurrencySerializer
20
+ permission_classes = [permissions.IsAuthenticated]
21
+ filter_backends = [DjangoFilterBackend]
22
+ filterset_fields = ['currency_type', 'is_active']
23
+
24
+ def get_serializer_class(self):
25
+ """Use list serializer for list action."""
26
+ if self.action == 'list':
27
+ return CurrencyListSerializer
28
+ return CurrencySerializer
29
+
30
+ @action(detail=False, methods=['get'])
31
+ def crypto(self, request):
32
+ """Get only cryptocurrencies."""
33
+ cryptos = self.get_queryset().filter(currency_type='crypto')
34
+ serializer = CurrencyListSerializer(cryptos, many=True)
35
+ return Response(serializer.data)
36
+
37
+ @action(detail=False, methods=['get'])
38
+ def fiat(self, request):
39
+ """Get only fiat currencies."""
40
+ fiats = self.get_queryset().filter(currency_type='fiat')
41
+ serializer = CurrencyListSerializer(fiats, many=True)
42
+ return Response(serializer.data)
43
+
44
+ @action(detail=True, methods=['get'])
45
+ def networks(self, request, pk=None):
46
+ """Get networks for specific currency."""
47
+ currency = self.get_object()
48
+ networks = CurrencyNetwork.objects.filter(
49
+ currency=currency,
50
+ is_active=True
51
+ )
52
+ serializer = CurrencyNetworkSerializer(networks, many=True)
53
+ return Response(serializer.data)
54
+
55
+
56
+ class CurrencyNetworkViewSet(viewsets.ReadOnlyModelViewSet):
57
+ """Currency Network ViewSet: /currency-networks/"""
58
+
59
+ queryset = CurrencyNetwork.objects.filter(is_active=True)
60
+ serializer_class = CurrencyNetworkSerializer
61
+ permission_classes = [permissions.IsAuthenticated]
62
+ filter_backends = [DjangoFilterBackend]
63
+ filterset_fields = ['currency', 'network_code', 'is_active']
64
+
65
+ @action(detail=False, methods=['get'])
66
+ def by_currency(self, request):
67
+ """Get networks grouped by currency."""
68
+ currency_code = request.query_params.get('currency')
69
+ if not currency_code:
70
+ return Response({'error': 'currency parameter required'}, status=400)
71
+
72
+ try:
73
+ currency = Currency.objects.get(code=currency_code, is_active=True)
74
+ networks = self.get_queryset().filter(currency=currency)
75
+ serializer = self.get_serializer(networks, many=True)
76
+ return Response(serializer.data)
77
+ except Currency.DoesNotExist:
78
+ return Response({'error': 'Currency not found'}, status=404)
79
+
80
+
81
+ # Generic views for specific use cases
82
+ class SupportedCurrenciesView(generics.ListAPIView):
83
+ """Generic view to list supported currencies."""
84
+
85
+ serializer_class = CurrencyListSerializer
86
+ permission_classes = [permissions.IsAuthenticated]
87
+
88
+ def get_queryset(self):
89
+ """Get active currencies."""
90
+ return Currency.objects.filter(is_active=True).order_by('code')
91
+
92
+
93
+ class CurrencyRatesView(generics.GenericAPIView):
94
+ """Generic view to get currency exchange rates."""
95
+
96
+ serializer_class = CurrencySerializer # For schema generation
97
+ permission_classes = [permissions.IsAuthenticated]
98
+
99
+ def get(self, request):
100
+ """Get current exchange rates."""
101
+ currencies = Currency.objects.filter(is_active=True)
102
+
103
+ rates = {}
104
+ for currency in currencies:
105
+ rates[currency.code] = {
106
+ 'usd_rate': currency.usd_rate,
107
+ 'updated_at': currency.rate_updated_at,
108
+ 'type': currency.currency_type,
109
+ }
110
+
111
+ return Response(rates)
@@ -0,0 +1,111 @@
1
+ """
2
+ Payment ViewSets with nested routing.
3
+ """
4
+
5
+ from rest_framework import viewsets, permissions, status, generics
6
+ from rest_framework.decorators import action
7
+ from rest_framework.response import Response
8
+ from django_filters.rest_framework import DjangoFilterBackend
9
+ from django.contrib.auth import get_user_model
10
+ from ..models import UniversalPayment
11
+ from ..serializers import (
12
+ UniversalPaymentSerializer, PaymentCreateSerializer, PaymentListSerializer
13
+ )
14
+
15
+ User = get_user_model()
16
+
17
+
18
+ class UserPaymentViewSet(viewsets.ModelViewSet):
19
+ """Nested ViewSet for user payments: /users/{user_id}/payments/"""
20
+
21
+ serializer_class = UniversalPaymentSerializer
22
+ permission_classes = [permissions.IsAuthenticated]
23
+ filter_backends = [DjangoFilterBackend]
24
+ filterset_fields = ['status', 'provider', 'currency_code']
25
+
26
+ def get_queryset(self):
27
+ """Filter by user from URL."""
28
+ user_id = self.kwargs.get('user_pk')
29
+ return UniversalPayment.objects.filter(user_id=user_id).order_by('-created_at')
30
+
31
+ def get_serializer_class(self):
32
+ """Use different serializers for different actions."""
33
+ if self.action == 'create':
34
+ return PaymentCreateSerializer
35
+ elif self.action == 'list':
36
+ return PaymentListSerializer
37
+ return UniversalPaymentSerializer
38
+
39
+ def perform_create(self, serializer):
40
+ """Set user from URL when creating."""
41
+ user_id = self.kwargs.get('user_pk')
42
+ user = User.objects.get(id=user_id)
43
+ serializer.save(user=user)
44
+
45
+ @action(detail=True, methods=['post'])
46
+ def check_status(self, request, user_pk=None, pk=None):
47
+ """Check payment status via provider API."""
48
+ payment = self.get_object()
49
+ # TODO: Implement provider status check
50
+ return Response({'status': payment.status})
51
+
52
+ @action(detail=False, methods=['get'])
53
+ def summary(self, request, user_pk=None):
54
+ """Get payment summary for user."""
55
+ queryset = self.get_queryset()
56
+
57
+ total_payments = queryset.count()
58
+ total_amount = sum(p.amount_usd for p in queryset if p.status == 'completed')
59
+ pending_amount = sum(p.amount_usd for p in queryset if p.status == 'pending')
60
+
61
+ return Response({
62
+ 'total_payments': total_payments,
63
+ 'total_amount_usd': total_amount,
64
+ 'pending_amount_usd': pending_amount,
65
+ 'completed_payments': queryset.filter(status='completed').count(),
66
+ 'pending_payments': queryset.filter(status='pending').count(),
67
+ })
68
+
69
+
70
+ class UniversalPaymentViewSet(viewsets.ReadOnlyModelViewSet):
71
+ """Global payment ViewSet: /payments/"""
72
+
73
+ queryset = UniversalPayment.objects.all()
74
+ serializer_class = UniversalPaymentSerializer
75
+ permission_classes = [permissions.IsAuthenticated]
76
+ filter_backends = [DjangoFilterBackend]
77
+ filterset_fields = ['status', 'provider', 'currency_code']
78
+
79
+ def get_queryset(self):
80
+ """Filter by current user for security."""
81
+ return UniversalPayment.objects.filter(user=self.request.user).order_by('-created_at')
82
+
83
+ def get_serializer_class(self):
84
+ """Use list serializer for list action."""
85
+ if self.action == 'list':
86
+ return PaymentListSerializer
87
+ return UniversalPaymentSerializer
88
+
89
+
90
+ # Generic views for specific use cases
91
+ class PaymentCreateView(generics.CreateAPIView):
92
+ """Generic view to create payment."""
93
+
94
+ serializer_class = PaymentCreateSerializer
95
+ permission_classes = [permissions.IsAuthenticated]
96
+
97
+ def perform_create(self, serializer):
98
+ """Set current user when creating."""
99
+ serializer.save(user=self.request.user)
100
+
101
+
102
+ class PaymentStatusView(generics.RetrieveAPIView):
103
+ """Generic view to check payment status."""
104
+
105
+ serializer_class = UniversalPaymentSerializer
106
+ permission_classes = [permissions.IsAuthenticated]
107
+ lookup_field = 'internal_payment_id'
108
+
109
+ def get_queryset(self):
110
+ """Filter by current user."""
111
+ return UniversalPayment.objects.filter(user=self.request.user)
@@ -0,0 +1,135 @@
1
+ """
2
+ Subscription ViewSets with nested routing.
3
+ """
4
+
5
+ from rest_framework import viewsets, permissions, status, generics
6
+ from rest_framework.decorators import action
7
+ from rest_framework.response import Response
8
+ from django_filters.rest_framework import DjangoFilterBackend
9
+ from django.contrib.auth import get_user_model
10
+ from ..models import Subscription, EndpointGroup
11
+ from ..serializers import (
12
+ SubscriptionSerializer, SubscriptionCreateSerializer, SubscriptionListSerializer,
13
+ EndpointGroupSerializer
14
+ )
15
+
16
+ User = get_user_model()
17
+
18
+
19
+ class UserSubscriptionViewSet(viewsets.ModelViewSet):
20
+ """Nested ViewSet for user subscriptions: /users/{user_id}/subscriptions/"""
21
+
22
+ serializer_class = SubscriptionSerializer
23
+ permission_classes = [permissions.IsAuthenticated]
24
+ filter_backends = [DjangoFilterBackend]
25
+ filterset_fields = ['status', 'tier', 'endpoint_group']
26
+
27
+ def get_queryset(self):
28
+ """Filter by user from URL."""
29
+ user_id = self.kwargs.get('user_pk')
30
+ return Subscription.objects.filter(user_id=user_id).order_by('-created_at')
31
+
32
+ def get_serializer_class(self):
33
+ """Use different serializers for different actions."""
34
+ if self.action == 'create':
35
+ return SubscriptionCreateSerializer
36
+ elif self.action == 'list':
37
+ return SubscriptionListSerializer
38
+ return SubscriptionSerializer
39
+
40
+ def perform_create(self, serializer):
41
+ """Set user from URL when creating."""
42
+ user_id = self.kwargs.get('user_pk')
43
+ user = User.objects.get(id=user_id)
44
+ serializer.save(user=user)
45
+
46
+ @action(detail=True, methods=['post'])
47
+ def cancel(self, request, user_pk=None, pk=None):
48
+ """Cancel subscription."""
49
+ subscription = self.get_object()
50
+ subscription.status = 'cancelled'
51
+ subscription.save()
52
+
53
+ serializer = self.get_serializer(subscription)
54
+ return Response(serializer.data)
55
+
56
+ @action(detail=True, methods=['post'])
57
+ def renew(self, request, user_pk=None, pk=None):
58
+ """Renew subscription."""
59
+ subscription = self.get_object()
60
+ # TODO: Implement renewal logic
61
+ return Response({'message': 'Subscription renewed'})
62
+
63
+ @action(detail=False, methods=['get'])
64
+ def active(self, request, user_pk=None):
65
+ """Get active subscriptions for user."""
66
+ queryset = self.get_queryset().filter(status='active')
67
+ serializer = SubscriptionListSerializer(queryset, many=True)
68
+ return Response(serializer.data)
69
+
70
+
71
+ class SubscriptionViewSet(viewsets.ReadOnlyModelViewSet):
72
+ """Global subscription ViewSet: /subscriptions/"""
73
+
74
+ queryset = Subscription.objects.all()
75
+ serializer_class = SubscriptionSerializer
76
+ permission_classes = [permissions.IsAuthenticated]
77
+ filter_backends = [DjangoFilterBackend]
78
+ filterset_fields = ['status', 'tier', 'endpoint_group']
79
+
80
+ def get_queryset(self):
81
+ """Filter by current user for security."""
82
+ return Subscription.objects.filter(user=self.request.user).order_by('-created_at')
83
+
84
+ def get_serializer_class(self):
85
+ """Use list serializer for list action."""
86
+ if self.action == 'list':
87
+ return SubscriptionListSerializer
88
+ return SubscriptionSerializer
89
+
90
+
91
+ class EndpointGroupViewSet(viewsets.ReadOnlyModelViewSet):
92
+ """Endpoint groups ViewSet: /endpoint-groups/"""
93
+
94
+ queryset = EndpointGroup.objects.filter(is_active=True)
95
+ serializer_class = EndpointGroupSerializer
96
+ permission_classes = [permissions.IsAuthenticated]
97
+
98
+ @action(detail=True, methods=['get'])
99
+ def pricing(self, request, pk=None):
100
+ """Get pricing for endpoint group."""
101
+ endpoint_group = self.get_object()
102
+ return Response({
103
+ 'basic_price': endpoint_group.basic_price,
104
+ 'premium_price': endpoint_group.premium_price,
105
+ 'enterprise_price': endpoint_group.enterprise_price,
106
+ 'basic_limit': endpoint_group.basic_limit,
107
+ 'premium_limit': endpoint_group.premium_limit,
108
+ 'enterprise_limit': endpoint_group.enterprise_limit,
109
+ })
110
+
111
+
112
+ # Generic views for specific use cases
113
+ class SubscriptionCreateView(generics.CreateAPIView):
114
+ """Generic view to create subscription."""
115
+
116
+ serializer_class = SubscriptionCreateSerializer
117
+ permission_classes = [permissions.IsAuthenticated]
118
+
119
+ def perform_create(self, serializer):
120
+ """Set current user when creating."""
121
+ serializer.save(user=self.request.user)
122
+
123
+
124
+ class ActiveSubscriptionsView(generics.ListAPIView):
125
+ """Generic view to list active subscriptions."""
126
+
127
+ serializer_class = SubscriptionListSerializer
128
+ permission_classes = [permissions.IsAuthenticated]
129
+
130
+ def get_queryset(self):
131
+ """Get active subscriptions for current user."""
132
+ return Subscription.objects.filter(
133
+ user=self.request.user,
134
+ status='active'
135
+ ).order_by('-created_at')
@@ -0,0 +1,131 @@
1
+ """
2
+ Tariff ViewSets.
3
+ """
4
+
5
+ from rest_framework import viewsets, permissions, generics
6
+ from rest_framework.decorators import action
7
+ from rest_framework.response import Response
8
+ from django_filters.rest_framework import DjangoFilterBackend
9
+ from ..models import Tariff, TariffEndpointGroup
10
+ from ..serializers import (
11
+ TariffSerializer, TariffEndpointGroupSerializer, TariffListSerializer
12
+ )
13
+
14
+
15
+ class TariffViewSet(viewsets.ReadOnlyModelViewSet):
16
+ """Tariff ViewSet: /tariffs/"""
17
+
18
+ queryset = Tariff.objects.filter(is_active=True)
19
+ serializer_class = TariffSerializer
20
+ permission_classes = [permissions.IsAuthenticated]
21
+ filter_backends = [DjangoFilterBackend]
22
+ filterset_fields = ['is_active']
23
+
24
+ def get_serializer_class(self):
25
+ """Use list serializer for list action."""
26
+ if self.action == 'list':
27
+ return TariffListSerializer
28
+ return TariffSerializer
29
+
30
+ def get_queryset(self):
31
+ """Order by price."""
32
+ return super().get_queryset().order_by('monthly_price')
33
+
34
+ @action(detail=False, methods=['get'])
35
+ def free(self, request):
36
+ """Get free tariffs."""
37
+ free_tariffs = self.get_queryset().filter(monthly_price=0)
38
+ serializer = TariffListSerializer(free_tariffs, many=True)
39
+ return Response(serializer.data)
40
+
41
+ @action(detail=False, methods=['get'])
42
+ def paid(self, request):
43
+ """Get paid tariffs."""
44
+ paid_tariffs = self.get_queryset().filter(monthly_price__gt=0)
45
+ serializer = TariffListSerializer(paid_tariffs, many=True)
46
+ return Response(serializer.data)
47
+
48
+ @action(detail=True, methods=['get'])
49
+ def endpoint_groups(self, request, pk=None):
50
+ """Get endpoint groups for tariff."""
51
+ tariff = self.get_object()
52
+ endpoint_groups = TariffEndpointGroup.objects.filter(
53
+ tariff=tariff,
54
+ is_enabled=True
55
+ )
56
+ serializer = TariffEndpointGroupSerializer(endpoint_groups, many=True)
57
+ return Response(serializer.data)
58
+
59
+
60
+ class TariffEndpointGroupViewSet(viewsets.ReadOnlyModelViewSet):
61
+ """Tariff Endpoint Group ViewSet: /tariff-endpoint-groups/"""
62
+
63
+ queryset = TariffEndpointGroup.objects.filter(is_enabled=True)
64
+ serializer_class = TariffEndpointGroupSerializer
65
+ permission_classes = [permissions.IsAuthenticated]
66
+ filter_backends = [DjangoFilterBackend]
67
+ filterset_fields = ['tariff', 'endpoint_group', 'is_enabled']
68
+
69
+ @action(detail=False, methods=['get'])
70
+ def by_tariff(self, request):
71
+ """Get endpoint groups by tariff."""
72
+ tariff_id = request.query_params.get('tariff_id')
73
+ if not tariff_id:
74
+ return Response({'error': 'tariff_id parameter required'}, status=400)
75
+
76
+ groups = self.get_queryset().filter(tariff_id=tariff_id)
77
+ serializer = self.get_serializer(groups, many=True)
78
+ return Response(serializer.data)
79
+
80
+ @action(detail=False, methods=['get'])
81
+ def by_endpoint_group(self, request):
82
+ """Get tariffs by endpoint group."""
83
+ endpoint_group_id = request.query_params.get('endpoint_group_id')
84
+ if not endpoint_group_id:
85
+ return Response({'error': 'endpoint_group_id parameter required'}, status=400)
86
+
87
+ groups = self.get_queryset().filter(endpoint_group_id=endpoint_group_id)
88
+ serializer = self.get_serializer(groups, many=True)
89
+ return Response(serializer.data)
90
+
91
+
92
+ # Generic views for specific use cases
93
+ class AvailableTariffsView(generics.ListAPIView):
94
+ """Generic view to list available tariffs."""
95
+
96
+ serializer_class = TariffListSerializer
97
+ permission_classes = [permissions.IsAuthenticated]
98
+
99
+ def get_queryset(self):
100
+ """Get active tariffs ordered by price."""
101
+ return Tariff.objects.filter(is_active=True).order_by('monthly_price')
102
+
103
+
104
+ class TariffComparisonView(generics.GenericAPIView):
105
+ """Generic view to compare tariffs."""
106
+
107
+ serializer_class = TariffSerializer # For schema generation
108
+ permission_classes = [permissions.IsAuthenticated]
109
+
110
+ def get(self, request):
111
+ """Get tariff comparison data."""
112
+ tariffs = Tariff.objects.filter(is_active=True).order_by('monthly_price')
113
+
114
+ comparison = []
115
+ for tariff in tariffs:
116
+ endpoint_groups_count = TariffEndpointGroup.objects.filter(
117
+ tariff=tariff,
118
+ is_enabled=True
119
+ ).count()
120
+
121
+ comparison.append({
122
+ 'id': tariff.id,
123
+ 'name': tariff.name,
124
+ 'display_name': tariff.display_name,
125
+ 'monthly_price': tariff.monthly_price,
126
+ 'request_limit': tariff.request_limit,
127
+ 'is_free': tariff.is_free,
128
+ 'endpoint_groups_count': endpoint_groups_count,
129
+ })
130
+
131
+ return Response(comparison)
django_cfg/core/config.py CHANGED
@@ -158,6 +158,10 @@ class DjangoConfig(BaseModel):
158
158
  default=False,
159
159
  description="Enable django-cfg Maintenance application (multi-site maintenance mode with Cloudflare)",
160
160
  )
161
+ enable_payments: bool = Field(
162
+ default=False,
163
+ description="Enable django-cfg Payments application (universal payment system, subscriptions, API keys, billing)",
164
+ )
161
165
 
162
166
  # === URLs ===
163
167
  site_url: str = Field(default="http://localhost:3000", description="Frontend site URL")
@@ -593,6 +597,8 @@ class DjangoConfig(BaseModel):
593
597
  apps.append("django_cfg.apps.agents")
594
598
  if self.enable_maintenance:
595
599
  apps.append("django_cfg.apps.maintenance")
600
+ if self.enable_payments:
601
+ apps.append("django_cfg.apps.payments")
596
602
 
597
603
  # Auto-enable tasks if needed
598
604
  if self.should_enable_tasks():
@@ -93,6 +93,7 @@ class ExtendedRevolutionConfig(BaseDjangoRevolutionConfig):
93
93
  knowbase_enabled = base_module.is_knowbase_enabled()
94
94
  agents_enabled = base_module.is_agents_enabled()
95
95
  tasks_enabled = base_module.should_enable_tasks()
96
+ payments_enabled = base_module.enable_payments()
96
97
 
97
98
  # Add Support zone if enabled
98
99
  default_support_zone = 'cfg_support'
@@ -177,6 +178,19 @@ class ExtendedRevolutionConfig(BaseDjangoRevolutionConfig):
177
178
  auth_required=True,
178
179
  version="v1",
179
180
  )
181
+
182
+ # Add Payments zone if enabled
183
+ default_payments_zone = 'cfg_payments'
184
+ if payments_enabled and default_payments_zone not in zones:
185
+ zones[default_payments_zone] = ZoneConfig(
186
+ apps=["django_cfg.apps.payments"],
187
+ title="Payments API",
188
+ description="Payments, subscriptions, and billing API",
189
+ public=False,
190
+ auth_required=True,
191
+ version="v1",
192
+ )
193
+
180
194
  except Exception:
181
195
  pass
182
196
 
@@ -175,6 +175,15 @@ class BaseCfgModule(ABC):
175
175
  """
176
176
  return self._get_config_key('enable_maintenance', False)
177
177
 
178
+ def enable_payments(self) -> bool:
179
+ """
180
+ Check if django-cfg Payments is enabled.
181
+
182
+ Returns:
183
+ True if Payments is enabled, False otherwise
184
+ """
185
+ return self._get_config_key('enable_payments', False)
186
+
178
187
 
179
188
  # Export the base class
180
189
  __all__ = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.2.21
3
+ Version: 1.2.22
4
4
  Summary: 🚀 Next-gen Django configuration: type-safety, AI features, blazing-fast setup, and automated best practices — all in one.
5
5
  Project-URL: Homepage, https://djangocfg.com
6
6
  Project-URL: Documentation, https://docs.djangocfg.com
@@ -1,5 +1,5 @@
1
1
  django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- django_cfg/__init__.py,sha256=hdpxqrMF75kn_Gpzz8jqS11Cb75Q3GoMd01Q6Mg6MyU,1631
2
+ django_cfg/__init__.py,sha256=Xr1TpxIGCz8PGP-3n7OZqU3lCSjl9yNS4gakIXX7gqo,1631
3
3
  django_cfg/apps.py,sha256=k84brkeXJI7EgKZLEpTkM9YFZofKI4PzhFOn1cl9Msc,1656
4
4
  django_cfg/config.py,sha256=ME-JKaVzcdmaGhuc1YTkEWoMKSaUasNf1SBlNz-NfrM,1399
5
5
  django_cfg/urls.py,sha256=bpRFjMonQuk4UCUMxx4ueBX3YDNB7HXKFwEghQ3KR3o,793
@@ -246,7 +246,7 @@ django_cfg/apps/newsletter/admin_filters.py,sha256=lPMtHULDOisTsN9_2BMoGu23ICkFE
246
246
  django_cfg/apps/newsletter/apps.py,sha256=TXrULqE2HbmEIHaLomw7z9bkC19le1rKwlUWhK1xrVo,566
247
247
  django_cfg/apps/newsletter/models.py,sha256=tc5IEUJZUAdYbQOhT_XvfiGL86FIb5LvPAuq7TOPrAM,8930
248
248
  django_cfg/apps/newsletter/serializers.py,sha256=Nse8ZmBDNdhzfgZu53LNp_JOR327rI1fSHpgdJtCoV8,4133
249
- django_cfg/apps/newsletter/signals.py,sha256=0wkOxEXnwBnLA1B72sZ_nb_x2ycsvQSQ3jvMwYi92zk,2974
249
+ django_cfg/apps/newsletter/signals.py,sha256=kbjSvnMIy4kwmE_r02YFMneCrHy0oRuc_spjki976dM,3003
250
250
  django_cfg/apps/newsletter/urls.py,sha256=Zfbgj0NZ06dMu503JOn4GGgmKpQ3tOjTIyUmEslaJdE,1609
251
251
  django_cfg/apps/newsletter/admin/__init__.py,sha256=dXCym_3qp9HxT0sUV_LBGgEkKv1k_Mj3a0TTZYnH2yI,290
252
252
  django_cfg/apps/newsletter/admin/filters.py,sha256=lPMtHULDOisTsN9_2BMoGu23ICkFEJDZst5Fhj5Sv5c,3675
@@ -267,6 +267,47 @@ django_cfg/apps/newsletter/views/emails.py,sha256=w-W_egdfFbsbgfW8WjqT0DBJl596lJ
267
267
  django_cfg/apps/newsletter/views/newsletters.py,sha256=gb8skjDYxHd8R2DAVUhSe6z4lnKDCS_e9zbyyHok4CI,1370
268
268
  django_cfg/apps/newsletter/views/subscriptions.py,sha256=marHLbRT6a6e8tMjKVtKG_0K6G_gcjKCU9LmMGihKwg,4774
269
269
  django_cfg/apps/newsletter/views/tracking.py,sha256=EJlAltpASzc_0nslVPJt-6mnIqI_QCEmz3MQEGPKzmU,2481
270
+ django_cfg/apps/payments/__init__.py,sha256=Qb8viLJBKCclV9uuoXjxWXeDevXlbdJ76mV8g5hK7tk,237
271
+ django_cfg/apps/payments/apps.py,sha256=FZgbLQFMeOJPs_892bM6X-3Edt3YPD2CP50HMekH7Sk,551
272
+ django_cfg/apps/payments/urls.py,sha256=aJ4hC3quZvn1c_JJHVWd305DLFAHhtkBHGS-i7XfYCw,3508
273
+ django_cfg/apps/payments/managers/__init__.py,sha256=uYiETCw-K5GXQyAwpcOa2Qxzof8sbtTNc_SghyW45YU,672
274
+ django_cfg/apps/payments/managers/api_key_manager.py,sha256=xhViRKCLC_eR68hankMZQTcbzMgiRJdyr8KYKk01_dQ,928
275
+ django_cfg/apps/payments/managers/balance_manager.py,sha256=Sm_EETlHO7LtlrWIqF65vFPS4FpPSNYo5oTgQwTFAZ4,13279
276
+ django_cfg/apps/payments/managers/currency_manager.py,sha256=-iq_Nhd3cj_n1aUzObBIMhRRHtpOv50Z0phdwOcKv6w,876
277
+ django_cfg/apps/payments/managers/payment_manager.py,sha256=udiTFIIVXsjqmlwzFwcT0ZTkYJZ9HVOSLLFTvAodY3w,1467
278
+ django_cfg/apps/payments/managers/subscription_manager.py,sha256=LHrw5INc1nSqfpw76bn7W2poEA-29FXfF5uhaxmq7l8,968
279
+ django_cfg/apps/payments/managers/tariff_manager.py,sha256=Kor_0ApvROcH0UKlp1-9rYChtHWDeOFgUc1GG3IepxY,799
280
+ django_cfg/apps/payments/middleware/__init__.py,sha256=ryn3emWBq15-kE8eJxX19gd-eTrk7JlpXqV2Cg8N_Wg,294
281
+ django_cfg/apps/payments/migrations/0001_initial.py,sha256=u4as4J5YycFDAs4acQz9girjg2Hp9uTn8Hy7WZEUI6U,39911
282
+ django_cfg/apps/payments/migrations/__init__.py,sha256=OHlzxEGDJYKZz82orZXnh77xy-Okv6FcG3EKMfg9LzU,21
283
+ django_cfg/apps/payments/models/__init__.py,sha256=UV1xEm1osEkWA5XmvBJSS5YU2fddqkvSum6-_15IBH0,882
284
+ django_cfg/apps/payments/models/api_keys.py,sha256=6V9hOSPQT27qEWaygWrtmQPqCeZpgHZv1KAfsgXnS-Q,2476
285
+ django_cfg/apps/payments/models/balance.py,sha256=wn63yjOVIbLbirAjKLAZm03ooXeOe3yOqDGVr_hiLZY,7026
286
+ django_cfg/apps/payments/models/base.py,sha256=T8mQKFXlpLc05xZDmSdOfrOAc8s_mGnKqoSgEhaY9Kk,349
287
+ django_cfg/apps/payments/models/currencies.py,sha256=lz6axNRmfoQa3od-EV78vbZQv3vh2FznxHzGBi20qIU,4106
288
+ django_cfg/apps/payments/models/events.py,sha256=xIrAj8nyBq0NTc0YA-SolJSxG9Mr3ut4gyARsY-QMRM,2364
289
+ django_cfg/apps/payments/models/payments.py,sha256=7CLgRFWPg8K5ajjXjG5_xy8wpACzTAMxOzOufbyIaAI,9540
290
+ django_cfg/apps/payments/models/subscriptions.py,sha256=RuINi31j2DIhI_V_-HNvhdq8qa-woAGrUDeWOJOq3so,8167
291
+ django_cfg/apps/payments/models/tariffs.py,sha256=nqM3RPtu4itAHRvT6jAgZgQEdEIN1KgKnEljkaHGFDc,2749
292
+ django_cfg/apps/payments/serializers/__init__.py,sha256=4l7Tu1NXztXtqceVbC3OxtWJir4n3_dFpVEgRgQ9J4k,1416
293
+ django_cfg/apps/payments/serializers/api_keys.py,sha256=5qG3PEwIVfoNSC6XpbOBbC3l4umgld3kPJC5l_vsYV0,1396
294
+ django_cfg/apps/payments/serializers/balance.py,sha256=QzR1MTRQ_4uTJBRefRB6X-z-0QOkOuHmCyNTojYj_8w,2025
295
+ django_cfg/apps/payments/serializers/currencies.py,sha256=SskSl3rjZBxxLnY_zsaJiFCGfbkz9WyWFHLHiJbbTIM,1821
296
+ django_cfg/apps/payments/serializers/payments.py,sha256=BDhcD9DCea50Cz1PumLNVntgH40dOR55NzCXv4I0YOQ,2258
297
+ django_cfg/apps/payments/serializers/subscriptions.py,sha256=16OileC0yxM90ERK3Uo5Jqe9DeyNf3tBwK-ZqDtEbPw,2795
298
+ django_cfg/apps/payments/serializers/tariffs.py,sha256=54N5tFpc5NBIMyXuN7Re6pDNoQLJc2yZBIb5uVRhn-I,1698
299
+ django_cfg/apps/payments/services/__init__.py,sha256=GigyUFB3pT1qpN1uV1-LZUXVQFPMNkbjh6i6sV4iZsw,280
300
+ django_cfg/apps/payments/services/base.py,sha256=2xX42CnTDObNNc0atXwEWpozI1fAbh7Y4uOgBAoHGzY,2302
301
+ django_cfg/apps/payments/services/nowpayments.py,sha256=K31_KFM9KvkKidbbgYBgnIwg7I49dFBTSkD9upyYkO8,2878
302
+ django_cfg/apps/payments/services/providers.py,sha256=7t335fOtBwwqMa_HLpvuJ-dW3Zyi8AjkKApSurhidTY,2337
303
+ django_cfg/apps/payments/services/redis_service.py,sha256=xarjV3ndkuXXTHy0N9yuLfEvcg0it4pNNQ-yghNwIqo,8193
304
+ django_cfg/apps/payments/views/__init__.py,sha256=00HRO1sDErjnFq2dedFAjBIyIhZZyZmv4DK98lHmx2Q,1613
305
+ django_cfg/apps/payments/views/api_key_views.py,sha256=fG68roqOF9FShde68Qp_CBf0KJuf1chwK41RU0VqUJk,5542
306
+ django_cfg/apps/payments/views/balance_views.py,sha256=rA_OJFizP_UBBr8OdWLQZQ51qlPFYoeJCYUzNXSw4S0,2509
307
+ django_cfg/apps/payments/views/currency_views.py,sha256=B1rjOKDV3KEYRQI6NuPEyEGF8VNumaraoZdI8Qqdu6s,4096
308
+ django_cfg/apps/payments/views/payment_views.py,sha256=zfdHiHJhGby6KHpTe5S9UVeC-pFrJD-RTMhHhkSCl5s,4122
309
+ django_cfg/apps/payments/views/subscription_views.py,sha256=WqBDsaPUQGP6ZCmbheIdRupy-qESPpdj8IgKkaLp3V8,5005
310
+ django_cfg/apps/payments/views/tariff_views.py,sha256=i0uOg6SBk_1sct2M2iwKMw-5YHMr5mhVvm_ljpFRwPQ,4907
270
311
  django_cfg/apps/support/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
271
312
  django_cfg/apps/support/admin.py,sha256=-fcegtqoCcwPiBXwyNLMftcYCTTbTRRBpWItyeNNkDY,8827
272
313
  django_cfg/apps/support/admin_filters.py,sha256=ZpKtetRxppRAMwIr-pwDbXAyh7qouDfTCEZoo1YJfFs,2179
@@ -324,7 +365,7 @@ django_cfg/cli/commands/__init__.py,sha256=EKLXDAx-QttnGmdjsmVANAfhxWplxl2V_2I0S
324
365
  django_cfg/cli/commands/create_project.py,sha256=iuf965j8Yg7zxHcPb0GtFHEj73CYXC45ZJRmd6RbA9E,21025
325
366
  django_cfg/cli/commands/info.py,sha256=o4S1xPJSHv2oEVqmH0X9RTF5f-8Wy9579yHkyd_PC3E,4923
326
367
  django_cfg/core/__init__.py,sha256=eVK57qFOok9kTeHoNEMQ1BplkUOaQ7NB9kP9eQK1vg0,358
327
- django_cfg/core/config.py,sha256=M7rH99nDErty-dT_oHezZOT6HOX8hokjJOubGGKHYZc,28132
368
+ django_cfg/core/config.py,sha256=TS78BFOtzaErZMdEQgZvY2cnMq5PGaC74rOR8dttRuY,28404
328
369
  django_cfg/core/environment.py,sha256=MAoEPqIPsLVhSANT2Bz4nnus2wmbMW0RCOQxhQfDrDc,9106
329
370
  django_cfg/core/exceptions.py,sha256=RTQEoU3PfR8lqqNNv5ayd_HY2yJLs3eioqUy8VM6AG4,10378
330
371
  django_cfg/core/generation.py,sha256=Oa9bnEPsxwEaa0RP71zNl_z2p9NNljHnoCTeRu72X-0,25421
@@ -373,12 +414,12 @@ django_cfg/models/jwt.py,sha256=3R_dpLmVZIcH4zdtwA4qKnuCB8RZQACrgsbbgWY2q4c,9025
373
414
  django_cfg/models/limits.py,sha256=nUfvyPykwKR38ZOlIFqlNGRmfV8RO5hiUrhDH6FCHJw,7449
374
415
  django_cfg/models/logging.py,sha256=4vZF-G9rPmXMxxtUx_ad7Esvgbe8a_5Dl692Yg0fL4A,10636
375
416
  django_cfg/models/ngrok.py,sha256=MVgcKWx0DRSW0QcwhiSx2vVwTSG49vbVrzPkZqDK-zw,3575
376
- django_cfg/models/revolution.py,sha256=n8CLLniMac9PTV5KDcfoO-_8F6U40jLT3DU8-kIIZ4I,7543
417
+ django_cfg/models/revolution.py,sha256=7Rj-EYvNK4EEttMqtE95ejLWzvucz8rFelWNBNTKk3s,8130
377
418
  django_cfg/models/security.py,sha256=Xv19ZVOIenB_-f0wB6fm-Ap4j9kA43bSFaT2XenpSqc,4685
378
419
  django_cfg/models/services.py,sha256=fj9JjrJFrlL4DMnMbx_D8JiiZpz4E5uBqmhquAxau7c,13159
379
420
  django_cfg/models/tasks.py,sha256=2T3apcUFf8zevYJmapbFrh6bWv5PTJPeXR6Bc5vlj4c,15524
380
421
  django_cfg/modules/__init__.py,sha256=Ip9WMpzImEwIAywpFwU056_v0O9oIGG7nCT1YSArxkw,316
381
- django_cfg/modules/base.py,sha256=A-pWUckGKrCAlgfnCfkKmjwtgZjyhc_z-hvJ4ogocpI,5474
422
+ django_cfg/modules/base.py,sha256=_agj4rt9CrEfrDVL29A1vgFYIwZXXg6Wf7aCNFFdXo0,5732
382
423
  django_cfg/modules/django_email.py,sha256=2XXlIKzD6Jao3CT4_zIE2eaM9Cc9ROA1tjp2bJ9z5Lo,16592
383
424
  django_cfg/modules/django_health.py,sha256=7QzuQ6WyjWYj6lecd4auwRvEyrMUL7N6hiAp-tLyoY4,8923
384
425
  django_cfg/modules/django_logger.py,sha256=3oP9jev0lOcFUJ1tYcpbFnK524zIGA2xIOrrAiTwpb8,6331
@@ -503,8 +544,8 @@ django_cfg/utils/path_resolution.py,sha256=C9As6p4Q9l3VeoVkFDRPQWGrzAWf8O8UxLVka
503
544
  django_cfg/utils/smart_defaults.py,sha256=MxbUZwn_xbh48li7uLI6W4D9WCD2P2WO48dv85Fra5E,23057
504
545
  django_cfg/utils/toolkit.py,sha256=Td8_iXNaftonF_xdZP4Y3uO65nuA_4_zditn5Q_Pfcw,23310
505
546
  django_cfg/utils/version_check.py,sha256=jI4v3YMdQriUEeb_TvRl511sDghy6I75iKRDUaNpucs,4800
506
- django_cfg-1.2.21.dist-info/METADATA,sha256=eiujuJP0fSTNmByZnWkfR6nOvjzB2mPxZ-3BPtZNbjc,38434
507
- django_cfg-1.2.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
508
- django_cfg-1.2.21.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
509
- django_cfg-1.2.21.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
510
- django_cfg-1.2.21.dist-info/RECORD,,
547
+ django_cfg-1.2.22.dist-info/METADATA,sha256=VGD-7Y0LSzV1UdNyMYvkZ2w8WIO14V_uk1T0Xe0WbIg,38434
548
+ django_cfg-1.2.22.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
549
+ django_cfg-1.2.22.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
550
+ django_cfg-1.2.22.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
551
+ django_cfg-1.2.22.dist-info/RECORD,,