django-cfg 1.3.9__py3-none-any.whl → 1.3.11__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 (187) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin/networks_admin.py +12 -1
  3. django_cfg/apps/payments/admin/payments_admin.py +13 -0
  4. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +62 -14
  5. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  6. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  7. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  8. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  9. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  10. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  11. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  12. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +33 -3
  13. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  14. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +96 -45
  15. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  16. django_cfg/apps/payments/config/__init__.py +14 -15
  17. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  18. django_cfg/apps/payments/config/helpers.py +8 -13
  19. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  20. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  21. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  22. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  23. django_cfg/apps/payments/models/payments.py +94 -0
  24. django_cfg/apps/payments/services/core/base.py +4 -4
  25. django_cfg/apps/payments/services/core/payment_service.py +265 -38
  26. django_cfg/apps/payments/services/providers/base.py +209 -3
  27. django_cfg/apps/payments/services/providers/models/__init__.py +2 -0
  28. django_cfg/apps/payments/services/providers/models/base.py +25 -2
  29. django_cfg/apps/payments/services/providers/nowpayments/models.py +2 -2
  30. django_cfg/apps/payments/services/providers/nowpayments/provider.py +57 -9
  31. django_cfg/apps/payments/services/providers/registry.py +5 -5
  32. django_cfg/apps/payments/services/types/requests.py +19 -7
  33. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  34. django_cfg/apps/payments/static/payments/js/api-client.js +6 -1
  35. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  36. django_cfg/apps/payments/static/payments/js/payment-form.js +35 -26
  37. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  38. django_cfg/apps/payments/urls.py +3 -2
  39. django_cfg/apps/payments/views/api/currencies.py +3 -0
  40. django_cfg/apps/payments/views/serializers/currencies.py +18 -5
  41. django_cfg/apps/tasks/admin/tasks_admin.py +2 -2
  42. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  43. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  44. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  45. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  46. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  47. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  48. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  49. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  50. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  51. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  52. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  53. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  54. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  55. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  56. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  57. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  58. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  59. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  60. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  61. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  62. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  63. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  64. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  65. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  66. django_cfg/apps/tasks/urls.py +2 -2
  67. django_cfg/apps/tasks/urls_admin.py +2 -2
  68. django_cfg/apps/tasks/utils/__init__.py +1 -0
  69. django_cfg/apps/tasks/utils/simulator.py +356 -0
  70. django_cfg/apps/tasks/views/__init__.py +16 -0
  71. django_cfg/apps/tasks/views/api.py +569 -0
  72. django_cfg/apps/tasks/views/dashboard.py +58 -0
  73. django_cfg/core/integration/__init__.py +21 -0
  74. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  75. django_cfg/models/constance.py +0 -11
  76. django_cfg/models/payments.py +137 -3
  77. django_cfg/modules/django_tasks.py +54 -21
  78. django_cfg/registry/core.py +4 -9
  79. django_cfg/template_archive/django_sample.zip +0 -0
  80. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -2
  81. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/RECORD +84 -152
  82. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  83. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  84. django_cfg/apps/payments/config/constance/fields.py +0 -69
  85. django_cfg/apps/payments/config/constance/settings.py +0 -160
  86. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +0 -26
  87. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +0 -28
  88. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +0 -30
  89. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  90. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  91. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  92. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  93. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  94. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  95. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  96. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  97. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  98. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  99. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  100. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  101. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  102. django_cfg/apps/tasks/views.py +0 -461
  103. django_cfg/management/commands/app_agent_diagnose.py +0 -470
  104. django_cfg/management/commands/app_agent_generate.py +0 -342
  105. django_cfg/management/commands/app_agent_info.py +0 -308
  106. django_cfg/management/commands/auto_generate.py +0 -486
  107. django_cfg/modules/django_app_agent/__init__.py +0 -87
  108. django_cfg/modules/django_app_agent/agents/__init__.py +0 -40
  109. django_cfg/modules/django_app_agent/agents/base/__init__.py +0 -24
  110. django_cfg/modules/django_app_agent/agents/base/agent.py +0 -354
  111. django_cfg/modules/django_app_agent/agents/base/context.py +0 -236
  112. django_cfg/modules/django_app_agent/agents/base/executor.py +0 -430
  113. django_cfg/modules/django_app_agent/agents/generation/__init__.py +0 -12
  114. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +0 -15
  115. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +0 -147
  116. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +0 -99
  117. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +0 -32
  118. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +0 -290
  119. django_cfg/modules/django_app_agent/agents/interfaces.py +0 -376
  120. django_cfg/modules/django_app_agent/core/__init__.py +0 -33
  121. django_cfg/modules/django_app_agent/core/config.py +0 -300
  122. django_cfg/modules/django_app_agent/core/exceptions.py +0 -359
  123. django_cfg/modules/django_app_agent/models/__init__.py +0 -71
  124. django_cfg/modules/django_app_agent/models/base.py +0 -283
  125. django_cfg/modules/django_app_agent/models/context.py +0 -496
  126. django_cfg/modules/django_app_agent/models/enums.py +0 -481
  127. django_cfg/modules/django_app_agent/models/requests.py +0 -500
  128. django_cfg/modules/django_app_agent/models/responses.py +0 -585
  129. django_cfg/modules/django_app_agent/pytest.ini +0 -6
  130. django_cfg/modules/django_app_agent/services/__init__.py +0 -42
  131. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +0 -30
  132. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +0 -133
  133. django_cfg/modules/django_app_agent/services/app_generator/context.py +0 -40
  134. django_cfg/modules/django_app_agent/services/app_generator/main.py +0 -202
  135. django_cfg/modules/django_app_agent/services/app_generator/structure.py +0 -316
  136. django_cfg/modules/django_app_agent/services/app_generator/validation.py +0 -125
  137. django_cfg/modules/django_app_agent/services/base.py +0 -437
  138. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +0 -34
  139. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +0 -141
  140. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +0 -276
  141. django_cfg/modules/django_app_agent/services/context_builder/main.py +0 -272
  142. django_cfg/modules/django_app_agent/services/context_builder/models.py +0 -40
  143. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +0 -85
  144. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +0 -31
  145. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +0 -311
  146. django_cfg/modules/django_app_agent/services/project_scanner/main.py +0 -221
  147. django_cfg/modules/django_app_agent/services/project_scanner/models.py +0 -59
  148. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +0 -94
  149. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +0 -28
  150. django_cfg/modules/django_app_agent/services/questioning_service/main.py +0 -273
  151. django_cfg/modules/django_app_agent/services/questioning_service/models.py +0 -111
  152. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +0 -251
  153. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +0 -347
  154. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +0 -356
  155. django_cfg/modules/django_app_agent/services/report_service.py +0 -332
  156. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +0 -18
  157. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +0 -236
  158. django_cfg/modules/django_app_agent/services/template_manager/main.py +0 -159
  159. django_cfg/modules/django_app_agent/services/template_manager/models.py +0 -36
  160. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +0 -100
  161. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +0 -105
  162. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +0 -31
  163. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +0 -44
  164. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +0 -81
  165. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +0 -107
  166. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +0 -139
  167. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +0 -91
  168. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +0 -195
  169. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +0 -35
  170. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +0 -211
  171. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +0 -200
  172. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +0 -25
  173. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +0 -333
  174. django_cfg/modules/django_app_agent/services/validation_service/main.py +0 -242
  175. django_cfg/modules/django_app_agent/services/validation_service/models.py +0 -66
  176. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +0 -352
  177. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +0 -272
  178. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +0 -203
  179. django_cfg/modules/django_app_agent/ui/__init__.py +0 -25
  180. django_cfg/modules/django_app_agent/ui/cli.py +0 -419
  181. django_cfg/modules/django_app_agent/ui/rich_components.py +0 -622
  182. django_cfg/modules/django_app_agent/utils/__init__.py +0 -38
  183. django_cfg/modules/django_app_agent/utils/logging.py +0 -360
  184. django_cfg/modules/django_app_agent/utils/validation.py +0 -417
  185. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  186. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  187. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -23,9 +23,14 @@ from django_cfg.apps.payments.admin_interface.serializers import (
23
23
  WebhookActionSerializer,
24
24
  WebhookActionResultSerializer,
25
25
  )
26
- from django_cfg.apps.payments.services.core.webhook_service import WebhookService
27
26
  from django_cfg.apps.payments.models import UniversalPayment
28
27
  from django_cfg.modules.django_logger import get_logger
28
+ from django_cfg.apps.payments.services.integrations.ngrok_service import (
29
+ get_all_webhook_urls,
30
+ get_api_base_url,
31
+ is_ngrok_available
32
+ )
33
+ from django_cfg.apps.payments.services.core.webhook_service import WebhookService
29
34
 
30
35
  logger = get_logger("admin_webhook_api")
31
36
 
@@ -41,31 +46,57 @@ class AdminWebhookViewSet(AdminReadOnlyViewSet):
41
46
  # No model - this is for webhook configuration data
42
47
  serializer_class = WebhookStatsSerializer
43
48
 
49
+ def __init__(self, **kwargs):
50
+ """Initialize with ngrok service."""
51
+ super().__init__(**kwargs)
52
+
53
+ self.get_webhook_urls = get_all_webhook_urls
54
+ self.get_base_url = get_api_base_url
55
+ self.is_ngrok_active = is_ngrok_available
56
+
44
57
  def list(self, request):
45
- """List webhook providers and configurations."""
46
- # Mock webhook provider data - replace with real configuration
47
- providers_data = [
48
- {
49
- 'name': 'nowpayments',
50
- 'display_name': 'NowPayments',
51
- 'enabled': True,
52
- 'webhook_url': 'https://api.nowpayments.io/v1/webhooks',
58
+ """List webhook providers and configurations with real ngrok URLs."""
59
+ # Get real webhook URLs
60
+ webhook_urls = self.get_webhook_urls()
61
+ base_url = self.get_base_url()
62
+ ngrok_active = self.is_ngrok_active()
63
+
64
+ # Get real provider data based on actual payments
65
+ active_providers = UniversalPayment.objects.values('provider').distinct()
66
+
67
+ providers_data = []
68
+ for provider_data in active_providers:
69
+ provider_name = provider_data['provider']
70
+ provider_payments = UniversalPayment.objects.filter(provider=provider_name)
71
+
72
+ # Calculate real statistics
73
+ total_payments = provider_payments.count()
74
+ last_payment = provider_payments.order_by('-created_at').first()
75
+
76
+ provider_info = {
77
+ 'name': provider_name,
78
+ 'display_name': provider_name.title(),
79
+ 'enabled': total_payments > 0,
80
+ 'webhook_url': webhook_urls.get(provider_name, f"{base_url}/api/webhooks/{provider_name}/"),
53
81
  'supported_events': ['payment.created', 'payment.completed', 'payment.failed'],
54
- 'last_ping': timezone.now() - timedelta(minutes=5),
55
- 'status': 'active'
56
- },
57
- {
58
- 'name': 'stripe',
59
- 'display_name': 'Stripe',
60
- 'enabled': False,
61
- 'webhook_url': 'https://api.stripe.com/v1/webhooks',
62
- 'supported_events': ['payment_intent.succeeded', 'payment_intent.payment_failed'],
63
- 'last_ping': None,
64
- 'status': 'inactive'
82
+ 'last_ping': last_payment.created_at if last_payment else None,
83
+ 'status': 'active' if total_payments > 0 else 'inactive',
84
+ 'ngrok_active': ngrok_active,
85
+ 'base_url': base_url
65
86
  }
66
- ]
87
+ providers_data.append(provider_info)
67
88
 
68
- serializer = self.get_serializer(providers_data, many=True)
89
+ # Add ngrok status to response
90
+ response_data = {
91
+ 'providers': providers_data,
92
+ 'ngrok_status': {
93
+ 'active': ngrok_active,
94
+ 'base_url': base_url,
95
+ 'webhook_urls': webhook_urls
96
+ }
97
+ }
98
+
99
+ serializer = self.get_serializer(response_data)
69
100
  return Response(serializer.data)
70
101
 
71
102
  @action(detail=False, methods=['get'])
@@ -126,8 +157,17 @@ class AdminWebhookEventViewSet(AdminReadOnlyViewSet):
126
157
  filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
127
158
  filterset_fields = ['event_type', 'status', 'provider']
128
159
  search_fields = ['event_type', 'webhook_url']
129
- ordering_fields = ['created_at', 'event_type', 'status']
130
- ordering = ['-created_at']
160
+ ordering_fields = ['timestamp', 'event_type', 'status']
161
+ ordering = ['-timestamp']
162
+
163
+ def __init__(self, **kwargs):
164
+ """Initialize with webhook and ngrok services."""
165
+ super().__init__(**kwargs)
166
+
167
+ self.webhook_service = WebhookService()
168
+ self.get_webhook_urls = get_all_webhook_urls
169
+ self.get_base_url = get_api_base_url
170
+ self.is_ngrok_active = is_ngrok_available
131
171
 
132
172
  def get_queryset(self):
133
173
  """Get webhook events queryset."""
@@ -138,9 +178,9 @@ class AdminWebhookEventViewSet(AdminReadOnlyViewSet):
138
178
  def list(self, request, webhook_pk=None):
139
179
  """List webhook events with filtering and pagination."""
140
180
  # Get filter parameters
141
- event_type = request.query_params.get('event_type')
142
- status_filter = request.query_params.get('status')
143
- provider = request.query_params.get('provider')
181
+ event_type = request.GET.get('event_type')
182
+ status_filter = request.GET.get('status')
183
+ provider = request.GET.get('provider')
144
184
 
145
185
  # Get real payment data to generate realistic mock events
146
186
  payments = UniversalPayment.objects.all()[:50] # Limit for performance
@@ -152,23 +192,28 @@ class AdminWebhookEventViewSet(AdminReadOnlyViewSet):
152
192
  event_types = ['payment.created', 'payment.completed'] if payment.status == 'completed' else ['payment.created']
153
193
 
154
194
  for event_type_name in event_types:
195
+ # Create payload for the event
196
+ payload = {
197
+ 'payment_id': str(payment.id),
198
+ 'amount': str(payment.amount_usd),
199
+ 'currency': payment.currency.code if payment.currency else payment.currency_code,
200
+ 'status': payment.status,
201
+ 'timestamp': payment.created_at.isoformat()
202
+ }
203
+
155
204
  event = {
156
- 'id': f"evt_{payment.id}_{event_type_name.split('.')[1]}",
205
+ 'id': int(str(hash(f"{payment.id}_{event_type_name}_{i}"))[:8], 16),
206
+ 'provider': payment.provider,
157
207
  'event_type': event_type_name,
158
- 'webhook_url': f'https://example.com/webhook/{payment.id}',
159
208
  'status': 'success' if i % 5 != 0 else 'failed',
160
- 'provider': 'nowpayments' if i % 2 == 0 else 'stripe',
161
- 'created_at': payment.created_at,
162
- 'response_code': 200 if i % 5 != 0 else 500,
163
- 'response_time': f"{50 + (i % 200)}ms",
164
- 'attempts': 1 if i % 5 != 0 else 3,
165
- 'payload': {
166
- 'payment_id': str(payment.id),
167
- 'amount': str(payment.amount),
168
- 'currency': payment.currency,
169
- 'status': payment.status,
170
- 'timestamp': payment.created_at.isoformat()
171
- }
209
+ 'timestamp': payment.created_at,
210
+ 'payload_size': len(str(payload)),
211
+ 'response_time': 50 + (i % 200),
212
+ 'retry_count': 0 if i % 5 != 0 else 2,
213
+ 'error_message': '' if i % 5 != 0 else 'Connection timeout',
214
+ 'payload_preview': str(payload)[:200],
215
+ 'response_status_code': 200 if i % 5 != 0 else 500,
216
+ 'webhook_url': self.get_webhook_urls().get(payment.provider, f"{self.get_base_url()}/api/webhooks/{payment.provider}/"),
172
217
  }
173
218
 
174
219
  # Apply filters
@@ -181,12 +226,13 @@ class AdminWebhookEventViewSet(AdminReadOnlyViewSet):
181
226
 
182
227
  events.append(event)
183
228
 
184
- # Sort by created_at descending
185
- events.sort(key=lambda x: x['created_at'], reverse=True)
229
+ # Sort by timestamp descending (only if events exist)
230
+ if events:
231
+ events.sort(key=lambda x: x.get('timestamp', timezone.now()), reverse=True)
186
232
 
187
233
  # Pagination
188
234
  page_size = 20
189
- page = int(request.query_params.get('page', 1))
235
+ page = int(request.GET.get('page', 1))
190
236
  start = (page - 1) * page_size
191
237
  end = start + page_size
192
238
  paginated_events = events[start:end]
@@ -197,7 +243,12 @@ class AdminWebhookEventViewSet(AdminReadOnlyViewSet):
197
243
  'page': page,
198
244
  'per_page': page_size,
199
245
  'has_next': end < len(events),
200
- 'has_previous': page > 1
246
+ 'has_previous': page > 1,
247
+ 'ngrok_status': {
248
+ 'active': self.is_ngrok_active(),
249
+ 'base_url': self.get_base_url(),
250
+ 'webhook_urls': self.get_webhook_urls()
251
+ }
201
252
  }
202
253
 
203
254
  serializer = self.get_serializer(response_data)
@@ -51,7 +51,7 @@ class PaymentDetailView(AdminTemplateViewMixin, LoginRequiredMixin, DetailView):
51
51
 
52
52
  def get_queryset(self):
53
53
  """Optimized queryset with related objects."""
54
- return UniversalPayment.objects.select_related('user')
54
+ return UniversalPayment.objects.select_related('user', 'currency', 'network')
55
55
 
56
56
  def get_context_data(self, **kwargs):
57
57
  """Add detail context data."""
@@ -59,7 +59,11 @@ class PaymentDetailView(AdminTemplateViewMixin, LoginRequiredMixin, DetailView):
59
59
 
60
60
  payment = self.get_object()
61
61
 
62
+ # Force refresh from database to get latest data
63
+ payment.refresh_from_db()
64
+
62
65
  context.update({
66
+ 'payment': payment,
63
67
  'page_title': f'Payment {payment.internal_payment_id or payment.id}',
64
68
  'page_subtitle': f'Payment details and transaction history',
65
69
  'show_actions': True,
@@ -1,42 +1,41 @@
1
1
  """
2
2
  Configuration module for the Universal Payment System v2.0.
3
3
 
4
- Provides clean separation between:
5
- - django-cfg integration (static config)
6
- - Constance integration (dynamic config)
4
+ Provides unified configuration through BaseCfgAutoModule:
5
+ - django-cfg integration (all configuration)
7
6
  - Configuration utilities and helpers
8
7
  """
9
8
 
10
- # Django-cfg integration
9
+ # Django-cfg integration (BaseCfgAutoModule)
11
10
  from .django_cfg_integration import (
11
+ PaymentsConfigManager,
12
12
  PaymentsConfigMixin,
13
13
  get_payments_config,
14
14
  is_payments_enabled,
15
- )
16
-
17
- # Constance integration (safe - no Django models)
18
- from .constance import (
19
- get_django_cfg_payments_constance_fields,
20
- PaymentConstanceSettings,
15
+ is_payments_configured,
16
+ get_config_summary,
17
+ reset_payments_config_cache,
21
18
  )
22
19
 
23
20
  # Configuration helpers
24
21
  from .helpers import (
25
22
  MiddlewareConfigHelper,
26
23
  CacheConfigHelper,
24
+ RedisConfigHelper,
27
25
  )
28
26
 
29
27
  __all__ = [
30
- # Django-cfg integration
28
+ # Django-cfg integration (BaseCfgAutoModule)
29
+ 'PaymentsConfigManager',
31
30
  'PaymentsConfigMixin',
32
31
  'get_payments_config',
33
32
  'is_payments_enabled',
34
-
35
- # Constance integration
36
- 'get_django_cfg_payments_constance_fields',
37
- 'PaymentConstanceSettings',
33
+ 'is_payments_configured',
34
+ 'get_config_summary',
35
+ 'reset_payments_config_cache',
38
36
 
39
37
  # Configuration helpers
40
38
  'MiddlewareConfigHelper',
41
39
  'CacheConfigHelper',
40
+ 'RedisConfigHelper',
42
41
  ]
@@ -136,7 +136,7 @@ class PaymentsConfigManager:
136
136
  'rate_limiting_enabled': config.rate_limiting_enabled,
137
137
  'usage_tracking_enabled': config.usage_tracking_enabled,
138
138
  'cache_timeouts': config.cache_timeouts,
139
- 'api_prefixes': config.api_prefixes,
139
+ 'enabled_providers': config.get_enabled_providers(),
140
140
  }
141
141
  except Exception as e:
142
142
  return {
@@ -144,6 +144,64 @@ class PaymentsConfigManager:
144
144
  'error': str(e),
145
145
  'enabled': False,
146
146
  }
147
+
148
+ @classmethod
149
+ def get_provider_api_config(cls, provider: str) -> dict:
150
+ """
151
+ Get provider-specific API configuration from BaseCfgAutoModule.
152
+
153
+ Args:
154
+ provider: Provider name (e.g., 'nowpayments')
155
+
156
+ Returns:
157
+ Dictionary with provider API configuration
158
+ """
159
+ try:
160
+ config = cls.get_payments_config()
161
+ return config.get_provider_api_config(provider)
162
+ except Exception as e:
163
+ logger.error(f"Failed to get provider config for {provider}: {e}")
164
+ return {'enabled': False}
165
+
166
+ @classmethod
167
+ def get_all_provider_configs(cls) -> dict:
168
+ """
169
+ Get all provider configurations for registry initialization.
170
+
171
+ Returns:
172
+ Dictionary with all provider configurations
173
+ """
174
+ try:
175
+ config = cls.get_payments_config()
176
+ providers = {}
177
+
178
+ # Get all enabled providers
179
+ for provider_name in config.get_enabled_providers():
180
+ provider_config = config.get_provider_api_config(provider_name)
181
+ if provider_config.get('enabled', False):
182
+ providers[provider_name] = provider_config
183
+
184
+ return providers
185
+ except Exception as e:
186
+ logger.error(f"Failed to get all provider configs: {e}")
187
+ return {}
188
+
189
+ @classmethod
190
+ def is_provider_enabled(cls, provider: str) -> bool:
191
+ """
192
+ Check if a specific provider is enabled.
193
+
194
+ Args:
195
+ provider: Provider name
196
+
197
+ Returns:
198
+ True if provider is enabled
199
+ """
200
+ try:
201
+ config = cls.get_payments_config()
202
+ return config.is_provider_enabled(provider)
203
+ except Exception:
204
+ return False
147
205
 
148
206
 
149
207
  # Legacy compatibility - keep old interface
@@ -18,20 +18,11 @@ class MiddlewareConfigHelper(PaymentsConfigMixin):
18
18
 
19
19
  @classmethod
20
20
  def get_middleware_config(cls) -> Dict[str, Any]:
21
- """Get middleware configuration combining django-cfg and Constance."""
21
+ """Get middleware configuration from BaseCfgAutoModule."""
22
22
  config = cls.get_payments_config()
23
23
 
24
- # Get Constance settings for dynamic config
25
- try:
26
- from .constance import get_payment_config_service
27
- config_service = get_payment_config_service()
28
- constance_settings = config_service.get_constance_settings()
29
- except Exception as e:
30
- logger.warning(f"Failed to load Constance settings: {e}")
31
- constance_settings = None
32
-
33
24
  return {
34
- # Static settings from django-cfg
25
+ # All settings from BaseCfgAutoModule (django-cfg)
35
26
  'enabled': config.enabled and config.middleware_enabled,
36
27
  'protected_paths': config.protected_paths,
37
28
  'protected_patterns': config.protected_patterns,
@@ -41,8 +32,12 @@ class MiddlewareConfigHelper(PaymentsConfigMixin):
41
32
  'track_anonymous_usage': config.track_anonymous_usage,
42
33
  'cache_timeouts': config.cache_timeouts,
43
34
 
44
- # Dynamic settings from Constance (if available)
45
- 'constance_settings': constance_settings,
35
+ # Provider API configurations
36
+ 'enabled_providers': config.get_enabled_providers(),
37
+ 'provider_configs': {
38
+ provider: config.get_provider_api_config(provider)
39
+ for provider in config.get_enabled_providers()
40
+ },
46
41
  }
47
42
 
48
43
 
@@ -1,4 +1,4 @@
1
- # Generated by Django 5.2.6 on 2025-09-27 10:37
1
+ # Generated by Django 5.2.6 on 2025-09-29 06:18
2
2
 
3
3
  import django.core.validators
4
4
  import django.db.models.deletion
@@ -148,6 +148,18 @@ class Migration(migrations.Migration):
148
148
  max_length=50,
149
149
  ),
150
150
  ),
151
+ (
152
+ "usd_rate",
153
+ models.FloatField(
154
+ default=1.0, help_text="Current USD exchange rate (1 unit = X USD)"
155
+ ),
156
+ ),
157
+ (
158
+ "usd_rate_updated_at",
159
+ models.DateTimeField(
160
+ blank=True, help_text="When USD rate was last updated", null=True
161
+ ),
162
+ ),
151
163
  ],
152
164
  options={
153
165
  "verbose_name": "Currency",
@@ -272,50 +284,12 @@ class Migration(migrations.Migration):
272
284
  help_text="Currency code as used by the provider", max_length=20
273
285
  ),
274
286
  ),
275
- (
276
- "min_amount",
277
- models.DecimalField(
278
- blank=True,
279
- decimal_places=8,
280
- help_text="Minimum payment amount for this currency",
281
- max_digits=20,
282
- null=True,
283
- ),
284
- ),
285
- (
286
- "max_amount",
287
- models.DecimalField(
288
- blank=True,
289
- decimal_places=8,
290
- help_text="Maximum payment amount for this currency",
291
- max_digits=20,
292
- null=True,
293
- ),
294
- ),
295
287
  (
296
288
  "is_enabled",
297
289
  models.BooleanField(
298
290
  default=True, help_text="Whether this currency is enabled for this provider"
299
291
  ),
300
292
  ),
301
- (
302
- "fee_percentage",
303
- models.DecimalField(
304
- decimal_places=4,
305
- default=0,
306
- help_text="Fee percentage (0.0250 = 2.5%)",
307
- max_digits=5,
308
- ),
309
- ),
310
- (
311
- "fixed_fee",
312
- models.DecimalField(
313
- decimal_places=8,
314
- default=0,
315
- help_text="Fixed fee amount in this currency",
316
- max_digits=20,
317
- ),
318
- ),
319
293
  (
320
294
  "currency",
321
295
  models.ForeignKey(
@@ -981,7 +955,6 @@ class Migration(migrations.Migration):
981
955
  options={
982
956
  "verbose_name": "Universal Payment",
983
957
  "verbose_name_plural": "Universal Payments",
984
- "db_table": "payments_universal",
985
958
  "ordering": ["-created_at"],
986
959
  },
987
960
  ),
@@ -1002,6 +975,14 @@ class Migration(migrations.Migration):
1002
975
  validators=[django.core.validators.MinValueValidator(0.0)],
1003
976
  ),
1004
977
  ),
978
+ (
979
+ "reserved_usd",
980
+ models.FloatField(
981
+ default=0.0,
982
+ help_text="Reserved amount in USD (pending transactions)",
983
+ validators=[django.core.validators.MinValueValidator(0.0)],
984
+ ),
985
+ ),
1005
986
  (
1006
987
  "total_deposited",
1007
988
  models.FloatField(
@@ -1209,33 +1190,33 @@ class Migration(migrations.Migration):
1209
1190
  ),
1210
1191
  migrations.AddIndex(
1211
1192
  model_name="universalpayment",
1212
- index=models.Index(fields=["user", "status"], name="payments_un_user_id_8ce187_idx"),
1193
+ index=models.Index(fields=["user", "status"], name="payments_un_user_id_7f6e79_idx"),
1213
1194
  ),
1214
1195
  migrations.AddIndex(
1215
1196
  model_name="universalpayment",
1216
1197
  index=models.Index(
1217
- fields=["provider", "status"], name="payments_un_provide_904e1a_idx"
1198
+ fields=["provider", "status"], name="payments_un_provide_982d48_idx"
1218
1199
  ),
1219
1200
  ),
1220
1201
  migrations.AddIndex(
1221
1202
  model_name="universalpayment",
1222
1203
  index=models.Index(
1223
- fields=["status", "created_at"], name="payments_un_status_fd808c_idx"
1204
+ fields=["status", "created_at"], name="payments_un_status_eba1d1_idx"
1224
1205
  ),
1225
1206
  ),
1226
1207
  migrations.AddIndex(
1227
1208
  model_name="universalpayment",
1228
1209
  index=models.Index(
1229
- fields=["provider_payment_id"], name="payments_un_provide_553809_idx"
1210
+ fields=["provider_payment_id"], name="payments_un_provide_8ed72f_idx"
1230
1211
  ),
1231
1212
  ),
1232
1213
  migrations.AddIndex(
1233
1214
  model_name="universalpayment",
1234
- index=models.Index(fields=["transaction_hash"], name="payments_un_transac_5a0fe3_idx"),
1215
+ index=models.Index(fields=["transaction_hash"], name="payments_un_transac_6095d4_idx"),
1235
1216
  ),
1236
1217
  migrations.AddIndex(
1237
1218
  model_name="universalpayment",
1238
- index=models.Index(fields=["expires_at"], name="payments_un_expires_3b92ad_idx"),
1219
+ index=models.Index(fields=["expires_at"], name="payments_un_expires_6a9f9d_idx"),
1239
1220
  ),
1240
1221
  migrations.AddConstraint(
1241
1222
  model_name="universalpayment",
@@ -1265,4 +1246,10 @@ class Migration(migrations.Migration):
1265
1246
  condition=models.Q(("balance_usd__gte", 0.0)), name="balance_non_negative_check"
1266
1247
  ),
1267
1248
  ),
1249
+ migrations.AddConstraint(
1250
+ model_name="userbalance",
1251
+ constraint=models.CheckConstraint(
1252
+ condition=models.Q(("reserved_usd__gte", 0.0)), name="reserved_non_negative_check"
1253
+ ),
1254
+ ),
1268
1255
  ]
@@ -0,0 +1,46 @@
1
+ # Generated by Django 5.2.6 on 2025-09-29 06:23
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("payments", "0001_initial"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.RenameIndex(
13
+ model_name="universalpayment",
14
+ new_name="payments_un_user_id_8ce187_idx",
15
+ old_name="payments_un_user_id_7f6e79_idx",
16
+ ),
17
+ migrations.RenameIndex(
18
+ model_name="universalpayment",
19
+ new_name="payments_un_provide_904e1a_idx",
20
+ old_name="payments_un_provide_982d48_idx",
21
+ ),
22
+ migrations.RenameIndex(
23
+ model_name="universalpayment",
24
+ new_name="payments_un_status_fd808c_idx",
25
+ old_name="payments_un_status_eba1d1_idx",
26
+ ),
27
+ migrations.RenameIndex(
28
+ model_name="universalpayment",
29
+ new_name="payments_un_provide_553809_idx",
30
+ old_name="payments_un_provide_8ed72f_idx",
31
+ ),
32
+ migrations.RenameIndex(
33
+ model_name="universalpayment",
34
+ new_name="payments_un_transac_5a0fe3_idx",
35
+ old_name="payments_un_transac_6095d4_idx",
36
+ ),
37
+ migrations.RenameIndex(
38
+ model_name="universalpayment",
39
+ new_name="payments_un_expires_3b92ad_idx",
40
+ old_name="payments_un_expires_6a9f9d_idx",
41
+ ),
42
+ migrations.AlterModelTable(
43
+ name="universalpayment",
44
+ table="payments_universal",
45
+ ),
46
+ ]
@@ -0,0 +1,25 @@
1
+ # Generated by Django 5.2.6 on 2025-09-30 07:52
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ (
9
+ "payments",
10
+ "0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more",
11
+ ),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AddField(
16
+ model_name="universalpayment",
17
+ name="status_changed_at",
18
+ field=models.DateTimeField(
19
+ blank=True,
20
+ db_index=True,
21
+ help_text="When the payment status was last changed",
22
+ null=True,
23
+ ),
24
+ ),
25
+ ]