django-cfg 1.3.3__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 (101) 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 +16 -6
  36. django_cfg/apps/payments/management/commands/currency_stats.py +72 -5
  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/middleware/api_access.py +35 -34
  40. django_cfg/apps/payments/migrations/0001_initial.py +1 -1
  41. django_cfg/apps/payments/models/managers/api_key_managers.py +4 -0
  42. django_cfg/apps/payments/models/managers/payment_managers.py +5 -0
  43. django_cfg/apps/payments/models/subscriptions.py +0 -24
  44. django_cfg/apps/payments/services/cache/__init__.py +1 -1
  45. django_cfg/apps/payments/services/core/balance_service.py +13 -2
  46. django_cfg/apps/payments/services/integrations/ngrok_service.py +3 -3
  47. django_cfg/apps/payments/services/providers/registry.py +20 -0
  48. django_cfg/apps/payments/signals/balance_signals.py +7 -4
  49. django_cfg/apps/payments/static/payments/js/api-client.js +385 -0
  50. django_cfg/apps/payments/static/payments/js/ngrok-status.js +58 -0
  51. django_cfg/apps/payments/static/payments/js/payment-dashboard.js +50 -0
  52. django_cfg/apps/payments/static/payments/js/payment-form.js +175 -0
  53. django_cfg/apps/payments/static/payments/js/payment-list.js +95 -0
  54. django_cfg/apps/payments/static/payments/js/webhook-dashboard.js +154 -0
  55. django_cfg/apps/payments/urls.py +4 -0
  56. django_cfg/apps/payments/urls_admin.py +37 -18
  57. django_cfg/apps/payments/views/api/api_keys.py +14 -0
  58. django_cfg/apps/payments/views/api/base.py +1 -0
  59. django_cfg/apps/payments/views/api/currencies.py +2 -2
  60. django_cfg/apps/payments/views/api/payments.py +11 -5
  61. django_cfg/apps/payments/views/api/subscriptions.py +36 -31
  62. django_cfg/apps/payments/views/overview/__init__.py +40 -0
  63. django_cfg/apps/payments/views/overview/serializers.py +205 -0
  64. django_cfg/apps/payments/views/overview/services.py +439 -0
  65. django_cfg/apps/payments/views/overview/urls.py +27 -0
  66. django_cfg/apps/payments/views/overview/views.py +231 -0
  67. django_cfg/apps/payments/views/serializers/api_keys.py +20 -6
  68. django_cfg/apps/payments/views/serializers/balances.py +5 -8
  69. django_cfg/apps/payments/views/serializers/currencies.py +2 -6
  70. django_cfg/apps/payments/views/serializers/payments.py +37 -32
  71. django_cfg/apps/payments/views/serializers/subscriptions.py +4 -26
  72. django_cfg/apps/urls.py +2 -1
  73. django_cfg/core/config.py +25 -15
  74. django_cfg/core/generation.py +12 -12
  75. django_cfg/core/integration/display/startup.py +1 -1
  76. django_cfg/core/validation.py +4 -4
  77. django_cfg/management/commands/show_config.py +2 -2
  78. django_cfg/management/commands/tree.py +1 -3
  79. django_cfg/middleware/__init__.py +2 -0
  80. django_cfg/middleware/static_nocache.py +55 -0
  81. django_cfg/models/payments.py +13 -15
  82. django_cfg/models/security.py +15 -0
  83. django_cfg/modules/django_ngrok.py +6 -0
  84. django_cfg/modules/django_unfold/dashboard.py +1 -3
  85. django_cfg/utils/smart_defaults.py +41 -1
  86. {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/METADATA +1 -1
  87. {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/RECORD +98 -65
  88. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +0 -38
  89. django_cfg/apps/payments/admin_interface/views/payment_views.py +0 -259
  90. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +0 -37
  91. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/loading_spinner.html +0 -0
  92. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/notification.html +0 -0
  93. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/provider_card.html +0 -0
  94. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/currency_converter.html +0 -0
  95. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/payment_status.html +0 -0
  96. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/dashboard.css +0 -0
  97. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/components.js +0 -0
  98. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/utils.js +0 -0
  99. {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/WHEEL +0 -0
  100. {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/entry_points.txt +0 -0
  101. {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,231 @@
1
+ """
2
+ 💰 Payments Overview Dashboard Views
3
+
4
+ API views for payments dashboard data using existing models.
5
+ """
6
+ from rest_framework import viewsets, status
7
+ from rest_framework.decorators import action
8
+ from rest_framework.response import Response
9
+ from rest_framework.permissions import IsAuthenticated
10
+ from drf_spectacular.utils import extend_schema, extend_schema_view
11
+ from drf_spectacular.openapi import OpenApiParameter
12
+
13
+ from .services import (
14
+ PaymentsDashboardMetricsService,
15
+ PaymentsUsageChartService,
16
+ RecentPaymentsService,
17
+ PaymentsAnalyticsService,
18
+ )
19
+ from .serializers import (
20
+ PaymentsDashboardOverviewSerializer,
21
+ PaymentsMetricsSerializer,
22
+ PaymentsChartResponseSerializer,
23
+ TimePeriodSerializer,
24
+ RecentPaymentSerializer,
25
+ RecentTransactionSerializer,
26
+ BalanceOverviewSerializer,
27
+ SubscriptionOverviewSerializer,
28
+ APIKeysOverviewSerializer,
29
+ )
30
+
31
+
32
+ @extend_schema_view(
33
+ overview=extend_schema(
34
+ summary="Payments Dashboard Overview",
35
+ description="Get complete payments dashboard overview with metrics, recent payments, and analytics",
36
+ responses={200: PaymentsDashboardOverviewSerializer}
37
+ ),
38
+ metrics=extend_schema(
39
+ summary="Payments Dashboard Metrics",
40
+ description="Get payments dashboard metrics including balance, subscriptions, API keys, and payments",
41
+ responses={200: PaymentsMetricsSerializer}
42
+ ),
43
+ chart_data=extend_schema(
44
+ summary="Payments Chart Data",
45
+ description="Get chart data for payments visualization",
46
+ parameters=[
47
+ OpenApiParameter(
48
+ name='period',
49
+ description='Time period for chart data',
50
+ required=False,
51
+ type=str,
52
+ enum=['7d', '30d', '90d', '1y'],
53
+ default='30d'
54
+ )
55
+ ],
56
+ responses={200: PaymentsChartResponseSerializer}
57
+ ),
58
+ recent_payments=extend_schema(
59
+ summary="Recent Payments",
60
+ description="Get recent payments for the user",
61
+ parameters=[
62
+ OpenApiParameter(
63
+ name='limit',
64
+ description='Number of payments to return',
65
+ required=False,
66
+ type=int,
67
+ default=10
68
+ )
69
+ ],
70
+ responses={200: RecentPaymentSerializer(many=True)}
71
+ ),
72
+ recent_transactions=extend_schema(
73
+ summary="Recent Transactions",
74
+ description="Get recent balance transactions for the user",
75
+ parameters=[
76
+ OpenApiParameter(
77
+ name='limit',
78
+ description='Number of transactions to return',
79
+ required=False,
80
+ type=int,
81
+ default=10
82
+ )
83
+ ],
84
+ responses={200: RecentTransactionSerializer(many=True)}
85
+ ),
86
+ payment_analytics=extend_schema(
87
+ summary="Payment Analytics",
88
+ description="Get analytics for payments by currency and provider",
89
+ parameters=[
90
+ OpenApiParameter(
91
+ name='limit',
92
+ description='Number of analytics items to return',
93
+ required=False,
94
+ type=int,
95
+ default=10
96
+ )
97
+ ],
98
+ responses={200: 'object'} # Generic object since it's a dict with currency_analytics and provider_analytics
99
+ ),
100
+ balance_overview=extend_schema(
101
+ summary="Balance Overview",
102
+ description="Get user balance overview",
103
+ responses={200: BalanceOverviewSerializer}
104
+ ),
105
+ subscription_overview=extend_schema(
106
+ summary="Subscription Overview",
107
+ description="Get current subscription overview",
108
+ responses={200: SubscriptionOverviewSerializer}
109
+ ),
110
+ api_keys_overview=extend_schema(
111
+ summary="API Keys Overview",
112
+ description="Get API keys overview",
113
+ responses={200: APIKeysOverviewSerializer}
114
+ )
115
+ )
116
+ class PaymentsDashboardViewSet(viewsets.GenericViewSet):
117
+ """
118
+ Payments dashboard data endpoints
119
+ """
120
+ permission_classes = [IsAuthenticated]
121
+
122
+ @action(detail=False, methods=['get'])
123
+ def overview(self, request):
124
+ """
125
+ Get complete payments dashboard overview
126
+ """
127
+ # Initialize services
128
+ metrics_service = PaymentsDashboardMetricsService(request.user)
129
+ chart_service = PaymentsUsageChartService(request.user)
130
+ payments_service = RecentPaymentsService(request.user)
131
+
132
+ # Get all data
133
+ data = {
134
+ 'metrics': metrics_service.get_dashboard_metrics(),
135
+ 'recent_payments': payments_service.get_recent_payments(limit=10),
136
+ 'recent_transactions': payments_service.get_recent_transactions(limit=10),
137
+ 'chart_data': chart_service.get_chart_data(period='30d')
138
+ }
139
+
140
+ return Response(data)
141
+
142
+ @action(detail=False, methods=['get'])
143
+ def metrics(self, request):
144
+ """
145
+ Get payments dashboard metrics only
146
+ """
147
+ service = PaymentsDashboardMetricsService(request.user)
148
+ metrics = service.get_dashboard_metrics()
149
+ return Response(metrics)
150
+
151
+ @action(detail=False, methods=['get'])
152
+ def chart_data(self, request):
153
+ """
154
+ Get payments usage chart data
155
+ """
156
+ period = request.query_params.get('period', '30d')
157
+
158
+ # Validate period
159
+ serializer = TimePeriodSerializer(data={'period': period})
160
+ if not serializer.is_valid():
161
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
162
+
163
+ service = PaymentsUsageChartService(request.user)
164
+ data = service.get_chart_data(period)
165
+ return Response(data)
166
+
167
+ @action(detail=False, methods=['get'])
168
+ def recent_payments(self, request):
169
+ """
170
+ Get recent payments
171
+ """
172
+ limit = int(request.query_params.get('limit', 10))
173
+ limit = min(limit, 50) # Max 50 payments
174
+
175
+ service = RecentPaymentsService(request.user)
176
+ payments = service.get_recent_payments(limit)
177
+ return Response(payments)
178
+
179
+ @action(detail=False, methods=['get'])
180
+ def recent_transactions(self, request):
181
+ """
182
+ Get recent balance transactions
183
+ """
184
+ limit = int(request.query_params.get('limit', 10))
185
+ limit = min(limit, 50) # Max 50 transactions
186
+
187
+ service = RecentPaymentsService(request.user)
188
+ transactions = service.get_recent_transactions(limit)
189
+ return Response(transactions)
190
+
191
+ @action(detail=False, methods=['get'])
192
+ def payment_analytics(self, request):
193
+ """
194
+ Get payment analytics
195
+ """
196
+ limit = int(request.query_params.get('limit', 10))
197
+ limit = min(limit, 20) # Max 20 analytics items
198
+
199
+ service = PaymentsAnalyticsService(request.user)
200
+ analytics = {
201
+ 'currency_analytics': service.get_payment_analytics(limit),
202
+ 'provider_analytics': service.get_provider_analytics(),
203
+ }
204
+ return Response(analytics)
205
+
206
+ @action(detail=False, methods=['get'])
207
+ def balance_overview(self, request):
208
+ """
209
+ Get balance overview only
210
+ """
211
+ service = PaymentsDashboardMetricsService(request.user)
212
+ balance = service.get_balance_overview()
213
+ return Response(balance)
214
+
215
+ @action(detail=False, methods=['get'])
216
+ def subscription_overview(self, request):
217
+ """
218
+ Get subscription overview only
219
+ """
220
+ service = PaymentsDashboardMetricsService(request.user)
221
+ subscription = service.get_subscription_overview()
222
+ return Response(subscription)
223
+
224
+ @action(detail=False, methods=['get'])
225
+ def api_keys_overview(self, request):
226
+ """
227
+ Get API keys overview only
228
+ """
229
+ service = PaymentsDashboardMetricsService(request.user)
230
+ api_keys = service.get_api_keys_overview()
231
+ return Response(api_keys)
@@ -23,8 +23,8 @@ class APIKeyListSerializer(serializers.ModelSerializer):
23
23
  """
24
24
 
25
25
  user = serializers.StringRelatedField(read_only=True)
26
- is_expired = serializers.BooleanField(source='is_expired', read_only=True)
27
- is_valid = serializers.BooleanField(source='is_valid', read_only=True)
26
+ is_expired = serializers.BooleanField(read_only=True)
27
+ is_valid = serializers.BooleanField(read_only=True)
28
28
 
29
29
  class Meta:
30
30
  model = APIKey
@@ -51,10 +51,10 @@ class APIKeySerializer(serializers.ModelSerializer):
51
51
  """
52
52
 
53
53
  user = serializers.StringRelatedField(read_only=True)
54
- key_preview = serializers.CharField(source='key_preview', read_only=True)
55
- is_expired = serializers.BooleanField(source='is_expired', read_only=True)
56
- is_valid = serializers.BooleanField(source='is_valid', read_only=True)
57
- days_until_expiry = serializers.IntegerField(source='days_until_expiry', read_only=True)
54
+ key_preview = serializers.CharField(read_only=True)
55
+ is_expired = serializers.BooleanField(read_only=True)
56
+ is_valid = serializers.BooleanField(read_only=True)
57
+ days_until_expiry = serializers.IntegerField(read_only=True)
58
58
 
59
59
  class Meta:
60
60
  model = APIKey
@@ -361,6 +361,20 @@ class APIKeyValidationSerializer(serializers.Serializer):
361
361
  }
362
362
 
363
363
 
364
+ class APIKeyValidationResponseSerializer(serializers.Serializer):
365
+ """
366
+ API key validation response serializer.
367
+
368
+ Defines the structure of API key validation response for OpenAPI schema.
369
+ """
370
+ success = serializers.BooleanField(help_text="Whether the validation was successful")
371
+ valid = serializers.BooleanField(help_text="Whether the API key is valid")
372
+ api_key = APIKeySerializer(allow_null=True, help_text="API key details if valid")
373
+ message = serializers.CharField(help_text="Validation message")
374
+ error = serializers.CharField(required=False, help_text="Error message if validation failed")
375
+ error_code = serializers.CharField(required=False, help_text="Error code if validation failed")
376
+
377
+
364
378
  class APIKeyStatsSerializer(serializers.Serializer):
365
379
  """
366
380
  API key statistics serializer.
@@ -25,9 +25,6 @@ class UserBalanceSerializer(serializers.ModelSerializer):
25
25
  """
26
26
 
27
27
  user = serializers.StringRelatedField(read_only=True)
28
- balance_display = serializers.CharField(source='balance_display', read_only=True)
29
- is_empty = serializers.BooleanField(source='is_empty', read_only=True)
30
- has_transactions = serializers.BooleanField(source='has_transactions', read_only=True)
31
28
 
32
29
  class Meta:
33
30
  model = UserBalance
@@ -51,17 +48,17 @@ class TransactionSerializer(serializers.ModelSerializer):
51
48
  """
52
49
 
53
50
  user = serializers.StringRelatedField(read_only=True)
54
- amount_display = serializers.CharField(source='amount_display', read_only=True)
55
- type_color = serializers.CharField(source='type_color', read_only=True)
56
- is_credit = serializers.BooleanField(source='is_credit', read_only=True)
57
- is_debit = serializers.BooleanField(source='is_debit', read_only=True)
51
+ amount_display = serializers.CharField(read_only=True)
52
+ type_color = serializers.CharField(read_only=True)
53
+ is_credit = serializers.BooleanField(read_only=True)
54
+ is_debit = serializers.BooleanField(read_only=True)
58
55
 
59
56
  class Meta:
60
57
  model = Transaction
61
58
  fields = [
62
59
  'id',
63
60
  'user',
64
- 'amount',
61
+ 'amount_usd',
65
62
  'amount_display',
66
63
  'transaction_type',
67
64
  'type_color',
@@ -22,9 +22,6 @@ class CurrencySerializer(serializers.ModelSerializer):
22
22
  """
23
23
 
24
24
  type_display = serializers.CharField(source='get_currency_type_display', read_only=True)
25
- is_crypto = serializers.BooleanField(source='is_crypto', read_only=True)
26
- is_fiat = serializers.BooleanField(source='is_fiat', read_only=True)
27
- is_stable = serializers.BooleanField(source='is_stable', read_only=True)
28
25
 
29
26
  class Meta:
30
27
  model = Currency
@@ -35,11 +32,10 @@ class CurrencySerializer(serializers.ModelSerializer):
35
32
  'symbol',
36
33
  'currency_type',
37
34
  'type_display',
38
- 'decimals',
35
+ 'decimal_places',
39
36
  'is_active',
40
37
  'is_crypto',
41
38
  'is_fiat',
42
- 'is_stable',
43
39
  'created_at',
44
40
  'updated_at',
45
41
  ]
@@ -113,7 +109,7 @@ class ProviderCurrencySerializer(serializers.ModelSerializer):
113
109
  'min_amount',
114
110
  'max_amount',
115
111
  'fee_percentage',
116
- 'is_active',
112
+ 'is_enabled',
117
113
  'created_at',
118
114
  'updated_at',
119
115
  ]
@@ -25,21 +25,22 @@ class PaymentListSerializer(serializers.ModelSerializer):
25
25
  """
26
26
 
27
27
  status_display = serializers.CharField(source='get_status_display', read_only=True)
28
- amount_display = serializers.CharField(source='amount_display', read_only=True)
29
- crypto_amount_display = serializers.CharField(source='crypto_amount_display', read_only=True)
28
+ amount_display = serializers.SerializerMethodField(read_only=True)
29
+
30
+ def get_amount_display(self, obj):
31
+ """Get formatted amount display."""
32
+ return f"${obj.amount_usd:.2f}"
30
33
 
31
34
  class Meta:
32
35
  model = UniversalPayment
33
36
  fields = [
34
37
  'id',
35
38
  'amount_usd',
36
- 'crypto_amount',
37
- 'currency_code',
39
+ 'currency',
38
40
  'provider',
39
41
  'status',
40
42
  'status_display',
41
43
  'amount_display',
42
- 'crypto_amount_display',
43
44
  'created_at',
44
45
  'expires_at',
45
46
  ]
@@ -55,17 +56,33 @@ class PaymentSerializer(serializers.ModelSerializer):
55
56
 
56
57
  user = serializers.StringRelatedField(read_only=True)
57
58
  status_display = serializers.CharField(source='get_status_display', read_only=True)
58
- status_color = serializers.CharField(source='status_color', read_only=True)
59
- amount_display = serializers.CharField(source='amount_display', read_only=True)
60
- crypto_amount_display = serializers.CharField(source='crypto_amount_display', read_only=True)
59
+ amount_display = serializers.SerializerMethodField(read_only=True)
61
60
 
62
61
  # Status check methods
63
- is_pending = serializers.BooleanField(source='is_pending', read_only=True)
64
- is_completed = serializers.BooleanField(source='is_completed', read_only=True)
65
- is_failed = serializers.BooleanField(source='is_failed', read_only=True)
66
- is_expired = serializers.BooleanField(source='is_expired', read_only=True)
67
- can_be_cancelled = serializers.BooleanField(source='can_be_cancelled', read_only=True)
68
- can_be_refunded = serializers.BooleanField(source='can_be_refunded', read_only=True)
62
+ is_pending = serializers.SerializerMethodField(read_only=True)
63
+ is_completed = serializers.SerializerMethodField(read_only=True)
64
+ is_failed = serializers.SerializerMethodField(read_only=True)
65
+ is_expired = serializers.SerializerMethodField(read_only=True)
66
+
67
+ def get_amount_display(self, obj):
68
+ """Get formatted amount display."""
69
+ return f"${obj.amount_usd:.2f}"
70
+
71
+ def get_is_pending(self, obj):
72
+ """Check if payment is pending."""
73
+ return obj.status == obj.PaymentStatus.PENDING
74
+
75
+ def get_is_completed(self, obj):
76
+ """Check if payment is completed."""
77
+ return obj.status == obj.PaymentStatus.COMPLETED
78
+
79
+ def get_is_failed(self, obj):
80
+ """Check if payment is failed."""
81
+ return obj.status == obj.PaymentStatus.FAILED
82
+
83
+ def get_is_expired(self, obj):
84
+ """Check if payment is expired."""
85
+ return obj.status == obj.PaymentStatus.EXPIRED
69
86
 
70
87
  class Meta:
71
88
  model = UniversalPayment
@@ -73,24 +90,20 @@ class PaymentSerializer(serializers.ModelSerializer):
73
90
  'id',
74
91
  'user',
75
92
  'amount_usd',
76
- 'crypto_amount',
77
- 'currency_code',
93
+ 'currency',
94
+ 'network',
78
95
  'provider',
79
96
  'status',
80
97
  'status_display',
81
- 'status_color',
82
98
  'amount_display',
83
- 'crypto_amount_display',
84
99
  'provider_payment_id',
85
100
  'payment_url',
86
- 'qr_code_url',
87
- 'wallet_address',
101
+ 'pay_address',
88
102
  'callback_url',
89
103
  'cancel_url',
90
104
  'description',
91
- 'metadata',
92
105
  'transaction_hash',
93
- 'confirmation_blocks',
106
+ 'confirmations_count',
94
107
  'created_at',
95
108
  'updated_at',
96
109
  'expires_at',
@@ -100,32 +113,24 @@ class PaymentSerializer(serializers.ModelSerializer):
100
113
  'is_completed',
101
114
  'is_failed',
102
115
  'is_expired',
103
- 'can_be_cancelled',
104
- 'can_be_refunded',
105
116
  ]
106
117
  read_only_fields = [
107
118
  'id',
108
119
  'user',
109
- 'crypto_amount',
110
120
  'provider_payment_id',
111
121
  'payment_url',
112
- 'qr_code_url',
113
- 'wallet_address',
122
+ 'pay_address',
114
123
  'transaction_hash',
115
- 'confirmation_blocks',
124
+ 'confirmations_count',
116
125
  'created_at',
117
126
  'updated_at',
118
127
  'completed_at',
119
128
  'status_display',
120
- 'status_color',
121
129
  'amount_display',
122
- 'crypto_amount_display',
123
130
  'is_pending',
124
131
  'is_completed',
125
132
  'is_failed',
126
133
  'is_expired',
127
- 'can_be_cancelled',
128
- 'can_be_refunded',
129
134
  ]
130
135
 
131
136
 
@@ -29,7 +29,7 @@ class EndpointGroupSerializer(serializers.ModelSerializer):
29
29
  'id',
30
30
  'name',
31
31
  'description',
32
- 'is_active',
32
+ 'is_enabled',
33
33
  'created_at',
34
34
  'updated_at',
35
35
  ]
@@ -52,9 +52,9 @@ class TariffSerializer(serializers.ModelSerializer):
52
52
  'id',
53
53
  'name',
54
54
  'description',
55
- 'monthly_price',
55
+ 'monthly_price_usd',
56
56
  'requests_per_month',
57
- 'requests_per_minute',
57
+ 'requests_per_hour',
58
58
  'is_active',
59
59
  'endpoint_groups',
60
60
  'endpoint_groups_count',
@@ -74,8 +74,6 @@ class SubscriptionListSerializer(serializers.ModelSerializer):
74
74
  user = serializers.StringRelatedField(read_only=True)
75
75
  tariff_name = serializers.CharField(source='tariff.name', read_only=True)
76
76
  status_display = serializers.CharField(source='get_status_display', read_only=True)
77
- is_active = serializers.BooleanField(source='is_active', read_only=True)
78
- is_expired = serializers.BooleanField(source='is_expired', read_only=True)
79
77
 
80
78
  class Meta:
81
79
  model = Subscription
@@ -104,18 +102,8 @@ class SubscriptionSerializer(serializers.ModelSerializer):
104
102
  tariff = TariffSerializer(read_only=True)
105
103
  endpoint_group = EndpointGroupSerializer(read_only=True)
106
104
  status_display = serializers.CharField(source='get_status_display', read_only=True)
107
- status_color = serializers.CharField(source='status_color', read_only=True)
108
-
109
- # Status check methods
110
- is_active = serializers.BooleanField(source='is_active', read_only=True)
111
- is_expired = serializers.BooleanField(source='is_expired', read_only=True)
112
- is_trial = serializers.BooleanField(source='is_trial', read_only=True)
113
- can_be_renewed = serializers.BooleanField(source='can_be_renewed', read_only=True)
114
- can_be_cancelled = serializers.BooleanField(source='can_be_cancelled', read_only=True)
115
105
 
116
- # Usage statistics
117
- usage_percentage = serializers.FloatField(source='usage_percentage', read_only=True)
118
- requests_remaining = serializers.IntegerField(source='requests_remaining', read_only=True)
106
+ # Only keep fields that actually exist or are needed
119
107
 
120
108
  class Meta:
121
109
  model = Subscription
@@ -129,16 +117,11 @@ class SubscriptionSerializer(serializers.ModelSerializer):
129
117
  'status_color',
130
118
  'tier',
131
119
  'total_requests',
132
- 'requests_used',
133
- 'requests_remaining',
134
120
  'usage_percentage',
135
121
  'last_request_at',
136
122
  'expires_at',
137
123
  'is_active',
138
124
  'is_expired',
139
- 'is_trial',
140
- 'can_be_renewed',
141
- 'can_be_cancelled',
142
125
  'created_at',
143
126
  'updated_at',
144
127
  ]
@@ -146,8 +129,6 @@ class SubscriptionSerializer(serializers.ModelSerializer):
146
129
  'id',
147
130
  'user',
148
131
  'total_requests',
149
- 'requests_used',
150
- 'requests_remaining',
151
132
  'usage_percentage',
152
133
  'last_request_at',
153
134
  'created_at',
@@ -156,9 +137,6 @@ class SubscriptionSerializer(serializers.ModelSerializer):
156
137
  'status_color',
157
138
  'is_active',
158
139
  'is_expired',
159
- 'is_trial',
160
- 'can_be_renewed',
161
- 'can_be_cancelled',
162
140
  ]
163
141
 
164
142
 
django_cfg/apps/urls.py CHANGED
@@ -52,7 +52,8 @@ def get_django_cfg_urlpatterns() -> List[URLPattern]:
52
52
  # if base_module.is_maintenance_enabled():
53
53
  # patterns.append(path('admin/django_cfg_maintenance/', include('django_cfg.apps.maintenance.urls_admin')))
54
54
 
55
- if base_module.is_payments_enabled():
55
+ # if base_module.is_payments_enabled():
56
+ if True:
56
57
  patterns.append(path('admin/django_cfg_payments/admin/', include('django_cfg.apps.payments.urls_admin')))
57
58
 
58
59
  except Exception:
django_cfg/core/config.py CHANGED
@@ -336,7 +336,6 @@ class DjangoConfig(BaseModel):
336
336
  )
337
337
 
338
338
  # === Internal State ===
339
- _environment: Optional[str] = PrivateAttr(default=None)
340
339
  _base_dir: Optional[Path] = PrivateAttr(default=None)
341
340
  _django_settings: Optional[Dict[str, Any]] = PrivateAttr(default=None)
342
341
 
@@ -353,7 +352,7 @@ class DjangoConfig(BaseModel):
353
352
  super().__init__(**data)
354
353
 
355
354
  # Initialize internal state
356
- self._detect_environment()
355
+ self._auto_detect_env_mode()
357
356
  self._resolve_paths()
358
357
  self._apply_smart_defaults()
359
358
  self._load_environment_config()
@@ -448,14 +447,22 @@ class DjangoConfig(BaseModel):
448
447
  """Check if running in test mode."""
449
448
  return self.env_mode == EnvironmentMode.TEST
450
449
 
451
- def _detect_environment(self) -> None:
452
- """Detect current environment from various sources."""
453
- from django_cfg.core.environment import EnvironmentDetector
454
-
455
- try:
456
- self._environment = EnvironmentDetector.detect_environment()
457
- except Exception as e:
458
- raise EnvironmentError(f"Failed to detect environment: {e}", suggestions=["Set DJANGO_ENV environment variable explicitly"]) from e
450
+ def _auto_detect_env_mode(self) -> None:
451
+ """Auto-detect environment mode from various sources."""
452
+ import os
453
+
454
+ # Check environment variables first
455
+ env_var = os.getenv("DJANGO_ENV", "").lower()
456
+ if env_var in ["development", "dev"]:
457
+ self.env_mode = EnvironmentMode.DEVELOPMENT
458
+ elif env_var in ["production", "prod"]:
459
+ self.env_mode = EnvironmentMode.PRODUCTION
460
+ elif env_var in ["test", "testing"]:
461
+ self.env_mode = EnvironmentMode.TEST
462
+ elif hasattr(self, 'debug') and self.debug:
463
+ # Auto-detect from debug flag if no env var set
464
+ self.env_mode = EnvironmentMode.DEVELOPMENT
465
+ # Otherwise keep the default (PRODUCTION)
459
466
 
460
467
  @property
461
468
  def base_dir(self) -> Path:
@@ -509,17 +516,17 @@ class DjangoConfig(BaseModel):
509
516
  try:
510
517
  # Apply cache defaults
511
518
  if self.cache_default:
512
- self.cache_default = SmartDefaults.configure_cache_backend(self.cache_default, self._environment, self.debug)
519
+ self.cache_default = SmartDefaults.configure_cache_backend(self.cache_default, self.env_mode, self.debug)
513
520
 
514
521
  if self.cache_sessions:
515
- self.cache_sessions = SmartDefaults.configure_cache_backend(self.cache_sessions, self._environment, self.debug)
522
+ self.cache_sessions = SmartDefaults.configure_cache_backend(self.cache_sessions, self.env_mode, self.debug)
516
523
 
517
524
  # Apply email defaults
518
525
  if self.email:
519
- self.email = SmartDefaults.configure_email_backend(self.email, self._environment, self.debug)
526
+ self.email = SmartDefaults.configure_email_backend(self.email, self.env_mode, self.debug)
520
527
 
521
528
  except Exception as e:
522
- raise ConfigurationError(f"Failed to apply smart defaults: {e}", context={"environment": self._environment, "debug": self.debug}) from e
529
+ raise ConfigurationError(f"Failed to apply smart defaults: {e}", context={"environment": self.env_mode, "debug": self.debug}) from e
523
530
 
524
531
  def _load_environment_config(self) -> None:
525
532
  """Load environment-specific configuration from YAML files."""
@@ -775,6 +782,9 @@ class DjangoConfig(BaseModel):
775
782
  if self.enable_accounts:
776
783
  middleware.append("django_cfg.middleware.UserActivityMiddleware")
777
784
 
785
+ # Add static no-cache middleware (auto-detects development mode)
786
+ middleware.append("django_cfg.middleware.StaticNoCacheMiddleware")
787
+
778
788
  # Add payments middleware if enabled
779
789
  if self.payments and self.payments.enabled:
780
790
  middleware.extend(self.payments.get_middleware_classes())
@@ -854,7 +864,7 @@ class DjangoConfig(BaseModel):
854
864
  Returns:
855
865
  Model data with internal fields excluded
856
866
  """
857
- return self.model_dump(exclude={"_environment", "_base_dir", "_django_settings"}, exclude_none=True)
867
+ return self.model_dump(exclude={"_base_dir", "_django_settings"}, exclude_none=True)
858
868
 
859
869
 
860
870
  # Global config instance for access from other modules