django-cfg 1.3.1__py3-none-any.whl → 1.3.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin_interface/old/payments/base.html +175 -0
  3. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +125 -0
  4. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +113 -0
  5. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +35 -0
  6. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +309 -0
  7. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +303 -0
  8. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +382 -0
  9. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +518 -0
  10. django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/components.css +248 -9
  11. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +163 -0
  12. django_cfg/apps/payments/admin_interface/serializers/__init__.py +39 -0
  13. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +149 -0
  14. django_cfg/apps/payments/admin_interface/serializers/webhook_serializers.py +114 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/base.html +55 -90
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/dialog.html +81 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_help_dialog.html +112 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_status.html +175 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +21 -17
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +123 -250
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +170 -269
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +152 -355
  23. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +202 -551
  24. django_cfg/apps/payments/admin_interface/views/__init__.py +25 -14
  25. django_cfg/apps/payments/admin_interface/views/api/__init__.py +20 -0
  26. django_cfg/apps/payments/admin_interface/views/api/payments.py +191 -0
  27. django_cfg/apps/payments/admin_interface/views/api/stats.py +206 -0
  28. django_cfg/apps/payments/admin_interface/views/api/users.py +60 -0
  29. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +257 -0
  30. django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +70 -0
  31. django_cfg/apps/payments/admin_interface/views/base.py +114 -0
  32. django_cfg/apps/payments/admin_interface/views/dashboard.py +60 -0
  33. django_cfg/apps/payments/admin_interface/views/forms.py +94 -0
  34. django_cfg/apps/payments/config/helpers.py +2 -2
  35. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +429 -0
  36. django_cfg/apps/payments/management/commands/currency_stats.py +443 -0
  37. django_cfg/apps/payments/management/commands/manage_currencies.py +9 -20
  38. django_cfg/apps/payments/management/commands/manage_providers.py +5 -5
  39. django_cfg/apps/payments/management/commands/process_pending_payments.py +357 -0
  40. django_cfg/apps/payments/management/commands/test_providers.py +434 -0
  41. django_cfg/apps/payments/middleware/api_access.py +35 -34
  42. django_cfg/apps/payments/migrations/0001_initial.py +1 -1
  43. django_cfg/apps/payments/models/balance.py +5 -2
  44. django_cfg/apps/payments/models/managers/api_key_managers.py +6 -2
  45. django_cfg/apps/payments/models/managers/balance_managers.py +3 -3
  46. django_cfg/apps/payments/models/managers/payment_managers.py +5 -0
  47. django_cfg/apps/payments/models/managers/subscription_managers.py +3 -3
  48. django_cfg/apps/payments/models/subscriptions.py +0 -24
  49. django_cfg/apps/payments/services/cache/__init__.py +1 -1
  50. django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
  51. django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
  52. django_cfg/apps/payments/services/cache_service/interfaces.py +32 -0
  53. django_cfg/apps/payments/services/cache_service/keys.py +49 -0
  54. django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
  55. django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
  56. django_cfg/apps/payments/services/core/balance_service.py +13 -2
  57. django_cfg/apps/payments/services/core/payment_service.py +49 -22
  58. django_cfg/apps/payments/services/integrations/ngrok_service.py +3 -3
  59. django_cfg/apps/payments/services/providers/registry.py +20 -0
  60. django_cfg/apps/payments/signals/api_key_signals.py +2 -2
  61. django_cfg/apps/payments/signals/balance_signals.py +8 -5
  62. django_cfg/apps/payments/static/payments/js/api-client.js +385 -0
  63. django_cfg/apps/payments/static/payments/js/ngrok-status.js +58 -0
  64. django_cfg/apps/payments/static/payments/js/payment-dashboard.js +50 -0
  65. django_cfg/apps/payments/static/payments/js/payment-form.js +175 -0
  66. django_cfg/apps/payments/static/payments/js/payment-list.js +95 -0
  67. django_cfg/apps/payments/static/payments/js/webhook-dashboard.js +154 -0
  68. django_cfg/apps/payments/urls.py +4 -0
  69. django_cfg/apps/payments/urls_admin.py +37 -18
  70. django_cfg/apps/payments/views/api/api_keys.py +14 -0
  71. django_cfg/apps/payments/views/api/base.py +1 -0
  72. django_cfg/apps/payments/views/api/currencies.py +2 -2
  73. django_cfg/apps/payments/views/api/payments.py +11 -5
  74. django_cfg/apps/payments/views/api/subscriptions.py +36 -31
  75. django_cfg/apps/payments/views/overview/__init__.py +40 -0
  76. django_cfg/apps/payments/views/overview/serializers.py +205 -0
  77. django_cfg/apps/payments/views/overview/services.py +439 -0
  78. django_cfg/apps/payments/views/overview/urls.py +27 -0
  79. django_cfg/apps/payments/views/overview/views.py +231 -0
  80. django_cfg/apps/payments/views/serializers/api_keys.py +20 -6
  81. django_cfg/apps/payments/views/serializers/balances.py +5 -8
  82. django_cfg/apps/payments/views/serializers/currencies.py +2 -6
  83. django_cfg/apps/payments/views/serializers/payments.py +37 -32
  84. django_cfg/apps/payments/views/serializers/subscriptions.py +4 -26
  85. django_cfg/apps/urls.py +2 -1
  86. django_cfg/core/config.py +25 -15
  87. django_cfg/core/generation.py +12 -12
  88. django_cfg/core/integration/display/startup.py +1 -1
  89. django_cfg/core/validation.py +4 -4
  90. django_cfg/management/commands/show_config.py +2 -2
  91. django_cfg/management/commands/tree.py +1 -3
  92. django_cfg/middleware/__init__.py +2 -0
  93. django_cfg/middleware/static_nocache.py +55 -0
  94. django_cfg/models/payments.py +13 -15
  95. django_cfg/models/security.py +15 -0
  96. django_cfg/modules/django_ngrok.py +6 -0
  97. django_cfg/modules/django_unfold/dashboard.py +1 -3
  98. django_cfg/utils/smart_defaults.py +51 -5
  99. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/METADATA +1 -1
  100. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/RECORD +111 -69
  101. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +0 -38
  102. django_cfg/apps/payments/admin_interface/views/payment_views.py +0 -259
  103. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +0 -37
  104. django_cfg/apps/payments/services/cache/cache_service.py +0 -235
  105. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/loading_spinner.html +0 -0
  106. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/notification.html +0 -0
  107. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/provider_card.html +0 -0
  108. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/currency_converter.html +0 -0
  109. /django_cfg/apps/payments/admin_interface/{templates → old}/payments/payment_status.html +0 -0
  110. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/dashboard.css +0 -0
  111. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/components.js +0 -0
  112. /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/utils.js +0 -0
  113. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/WHEEL +0 -0
  114. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/entry_points.txt +0 -0
  115. {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/licenses/LICENSE +0 -0
@@ -411,6 +411,26 @@ class ProviderRegistry:
411
411
  self._provider_configs[provider_name] = config_class
412
412
  logger.info(f"Registered provider class: {provider_name}")
413
413
 
414
+ def register_provider(self, provider_name: str, provider_instance: BaseProvider):
415
+ """
416
+ Register provider instance directly (for testing).
417
+
418
+ Args:
419
+ provider_name: Provider name
420
+ provider_instance: Provider instance
421
+ """
422
+ self._providers[provider_name] = provider_instance
423
+ logger.info(f"Registered provider instance: {provider_name}")
424
+
425
+ @property
426
+ def providers(self) -> Dict[str, BaseProvider]:
427
+ """Get dictionary of registered providers."""
428
+ return self._providers.copy()
429
+
430
+ def list_providers(self) -> List[str]:
431
+ """Get list of registered provider names."""
432
+ return list(self._providers.keys())
433
+
414
434
  def __len__(self) -> int:
415
435
  """Get number of initialized providers."""
416
436
  return len(self._providers)
@@ -43,7 +43,7 @@ def handle_api_key_changes(sender, instance: APIKey, created: bool, **kwargs):
43
43
  logger.info(f"New API key created", extra={
44
44
  'api_key_id': str(instance.id),
45
45
  'user_id': instance.user.id,
46
- 'name': instance.name,
46
+ 'key_name': instance.name,
47
47
  'expires_at': instance.expires_at.isoformat() if instance.expires_at else None
48
48
  })
49
49
 
@@ -52,7 +52,7 @@ def handle_api_key_changes(sender, instance: APIKey, created: bool, **kwargs):
52
52
  f"api_key_created:{instance.user.id}:{instance.id}",
53
53
  {
54
54
  'api_key_id': str(instance.id),
55
- 'name': instance.name,
55
+ 'key_name': instance.name,
56
56
  'timestamp': timezone.now().isoformat()
57
57
  },
58
58
  timeout=86400 # 24 hours
@@ -9,6 +9,7 @@ from django.db.models.signals import post_save, post_delete, pre_save
9
9
  from django.dispatch import receiver
10
10
  from django.core.cache import cache
11
11
  from django.utils import timezone
12
+ from decimal import Decimal
12
13
 
13
14
  from ..models import UserBalance, Transaction
14
15
  from django_cfg.modules.django_logger import get_logger
@@ -22,7 +23,8 @@ def store_original_balance(sender, instance: UserBalance, **kwargs):
22
23
  if instance.pk:
23
24
  try:
24
25
  original = UserBalance.objects.get(pk=instance.pk)
25
- instance._original_balance = original.balance_usd
26
+ # Ensure _original_balance is always Decimal
27
+ instance._original_balance = Decimal(str(original.balance_usd)) if original.balance_usd is not None else None
26
28
  except UserBalance.DoesNotExist:
27
29
  instance._original_balance = None
28
30
  else:
@@ -44,8 +46,9 @@ def handle_balance_change(sender, instance: UserBalance, created: bool, **kwargs
44
46
  else:
45
47
  # Check if balance changed
46
48
  if hasattr(instance, '_original_balance'):
47
- old_balance = instance._original_balance or 0.0
48
- new_balance = instance.balance_usd
49
+ # Ensure both values are Decimal for consistent arithmetic
50
+ old_balance = Decimal(str(instance._original_balance or 0.0))
51
+ new_balance = Decimal(str(instance.balance_usd))
49
52
 
50
53
  if old_balance != new_balance:
51
54
  balance_change = new_balance - old_balance
@@ -81,7 +84,7 @@ def handle_transaction_creation(sender, instance: Transaction, created: bool, **
81
84
  'transaction_id': str(instance.id),
82
85
  'user_id': instance.user.id,
83
86
  'transaction_type': instance.transaction_type,
84
- 'amount': instance.amount,
87
+ 'amount': instance.amount_usd,
85
88
  'payment_id': str(instance.payment_id) if instance.payment_id else None
86
89
  })
87
90
 
@@ -143,7 +146,7 @@ def _handle_zero_balance(balance: UserBalance):
143
146
  f"zero_balance:{balance.user.id}",
144
147
  {
145
148
  'timestamp': timezone.now().isoformat(),
146
- 'previous_balance': getattr(balance, '_original_balance', 0.0)
149
+ 'previous_balance': getattr(balance, '_original_balance', Decimal('0.0'))
147
150
  },
148
151
  timeout=86400 * 7 # 7 days
149
152
  )
@@ -0,0 +1,385 @@
1
+ /**
2
+ * Payment API Client
3
+ * Provides convenient methods for API calls with automatic JSON handling
4
+ */
5
+ class PaymentAPIClient {
6
+ constructor() {
7
+ this.baseURL = '/api/payments';
8
+ this.adminURL = '/cfg/admin/django_cfg_payments/admin';
9
+ }
10
+
11
+ // Helper method to get CSRF token
12
+ getCSRFToken() {
13
+ const token = document.querySelector('[name=csrfmiddlewaretoken]');
14
+ return token ? token.value : '';
15
+ }
16
+
17
+ // Generic request method
18
+ async request(url, options = {}) {
19
+ const config = {
20
+ headers: {
21
+ 'Content-Type': 'application/json',
22
+ 'X-CSRFToken': this.getCSRFToken(),
23
+ ...options.headers
24
+ },
25
+ ...options
26
+ };
27
+
28
+ try {
29
+ const response = await fetch(url, config);
30
+
31
+ if (!response.ok) {
32
+ const error = await response.json().catch(() => ({ message: 'Request failed' }));
33
+ throw new Error(error.message || `HTTP ${response.status}`);
34
+ }
35
+
36
+ return await response.json();
37
+ } catch (error) {
38
+ console.error('API Request failed:', error);
39
+ throw error;
40
+ }
41
+ }
42
+
43
+ // GET request
44
+ async get(url, params = {}) {
45
+ const urlWithParams = new URL(url, window.location.origin);
46
+ Object.entries(params).forEach(([key, value]) => {
47
+ if (value !== null && value !== undefined && value !== '') {
48
+ urlWithParams.searchParams.append(key, value);
49
+ }
50
+ });
51
+
52
+ return this.request(urlWithParams.toString());
53
+ }
54
+
55
+ // POST request
56
+ async post(url, data = {}) {
57
+ return this.request(url, {
58
+ method: 'POST',
59
+ body: JSON.stringify(data)
60
+ });
61
+ }
62
+
63
+ // PUT request
64
+ async put(url, data = {}) {
65
+ return this.request(url, {
66
+ method: 'PUT',
67
+ body: JSON.stringify(data)
68
+ });
69
+ }
70
+
71
+ // DELETE request
72
+ async delete(url) {
73
+ return this.request(url, {
74
+ method: 'DELETE'
75
+ });
76
+ }
77
+
78
+ // PAYMENTS API
79
+ payments = {
80
+ // Get all payments
81
+ list: (params = {}) => this.get(`${this.baseURL}/payments/`, params),
82
+
83
+ // Get payment by ID
84
+ get: (id) => this.get(`${this.baseURL}/payments/${id}/`),
85
+
86
+ // Create payment
87
+ create: (data) => this.post(`${this.baseURL}/payments/create/`, data),
88
+
89
+ // Get payment status
90
+ status: (id) => this.get(`${this.baseURL}/payments/status/${id}/`),
91
+
92
+ // Check payment status
93
+ checkStatus: (id) => this.post(`${this.baseURL}/payments/${id}/check_status/`),
94
+
95
+ // Cancel payment
96
+ cancel: (id) => this.post(`${this.baseURL}/payments/${id}/cancel/`),
97
+
98
+ // Get payment stats
99
+ stats: () => this.get(`${this.baseURL}/payments/stats/`),
100
+
101
+ // Get payment analytics
102
+ analytics: (params = {}) => this.get(`${this.baseURL}/payments/analytics/`, params),
103
+
104
+ // Get payments by provider
105
+ byProvider: (provider) => this.get(`${this.baseURL}/payments/by_provider/`, { provider })
106
+ };
107
+
108
+ // WEBHOOKS API
109
+ webhooks = {
110
+ // Get webhook stats
111
+ stats: () => this.get(`${this.baseURL}/webhooks/stats/`),
112
+
113
+ // Get supported providers
114
+ providers: () => this.get(`${this.baseURL}/webhooks/providers/`),
115
+
116
+ // Health check
117
+ health: () => this.get(`${this.baseURL}/webhooks/health/`)
118
+ };
119
+
120
+ // CURRENCIES API
121
+ currencies = {
122
+ // Get all currencies
123
+ list: () => this.get(`${this.baseURL}/currencies/`),
124
+
125
+ // Get currency by ID
126
+ get: (id) => this.get(`${this.baseURL}/currencies/${id}/`),
127
+
128
+ // Get supported currencies
129
+ supported: (params = {}) => this.get(`${this.baseURL}/currencies/supported/`, params),
130
+
131
+ // Get currencies by provider
132
+ byProvider: (provider) => this.get(`${this.baseURL}/currencies/supported/`, { provider }),
133
+
134
+ // Get exchange rates
135
+ rates: (params = {}) => this.get(`${this.baseURL}/currencies/rates/`, params),
136
+
137
+ // Convert currency
138
+ convert: (from, to, amount) => this.post(`${this.baseURL}/currencies/convert/`, { from, to, amount }),
139
+
140
+ // Get provider-specific currency configurations
141
+ providerConfigs: (provider) => this.get(`${this.baseURL}/provider-currencies/`, { provider }),
142
+
143
+ // Get currencies grouped by provider
144
+ byProviderGrouped: () => this.get(`${this.baseURL}/provider-currencies/by_provider/`)
145
+ };
146
+
147
+ // BALANCES API
148
+ balances = {
149
+ // Get all balances
150
+ list: (params = {}) => this.get(`${this.baseURL}/balances/`, params),
151
+
152
+ // Get balance by ID
153
+ get: (id) => this.get(`${this.baseURL}/balances/${id}/`),
154
+
155
+ // Top up balance
156
+ topup: (id, amount, currency) => this.post(`${this.baseURL}/balances/${id}/topup/`, { amount, currency }),
157
+
158
+ // Withdraw from balance
159
+ withdraw: (id, amount, currency) => this.post(`${this.baseURL}/balances/${id}/withdraw/`, { amount, currency })
160
+ };
161
+
162
+ // TRANSACTIONS API
163
+ transactions = {
164
+ // Get all transactions
165
+ list: (params = {}) => this.get(`${this.baseURL}/transactions/`, params),
166
+
167
+ // Get transaction by ID
168
+ get: (id) => this.get(`${this.baseURL}/transactions/${id}/`),
169
+
170
+ // Get recent transactions
171
+ recent: (limit = 10) => this.get(`${this.baseURL}/transactions/recent/`, { limit }),
172
+
173
+ // Get transactions by type
174
+ byType: (type) => this.get(`${this.baseURL}/transactions/by_type/`, { type }),
175
+
176
+ // Get transaction stats
177
+ stats: () => this.get(`${this.baseURL}/transactions/stats/`)
178
+ };
179
+
180
+ // API KEYS API
181
+ apiKeys = {
182
+ // Get all API keys
183
+ list: () => this.get(`${this.baseURL}/api-keys/`),
184
+
185
+ // Get API key by ID
186
+ get: (id) => this.get(`${this.baseURL}/api-keys/${id}/`),
187
+
188
+ // Generate new API key
189
+ generate: (data) => this.post(`${this.baseURL}/api-keys/generate/`, data),
190
+
191
+ // Revoke API key
192
+ revoke: (id) => this.post(`${this.baseURL}/api-keys/${id}/revoke/`),
193
+
194
+ // Get API key stats
195
+ stats: () => this.get(`${this.baseURL}/api-keys/stats/`)
196
+ };
197
+
198
+ // DASHBOARD API
199
+ dashboard = {
200
+ // Get dashboard overview
201
+ overview: () => this.get(`${this.baseURL}/overview/dashboard/overview/`),
202
+
203
+ // Get dashboard metrics
204
+ metrics: () => this.get(`${this.baseURL}/overview/dashboard/metrics/`),
205
+
206
+ // Get chart data
207
+ chartData: (period = '7d') => this.get(`${this.baseURL}/overview/dashboard/chart_data/`, { period }),
208
+
209
+ // Get recent payments
210
+ recentPayments: (limit = 5) => this.get(`${this.baseURL}/overview/dashboard/recent_payments/`, { limit }),
211
+
212
+ // Get recent transactions
213
+ recentTransactions: (limit = 5) => this.get(`${this.baseURL}/overview/dashboard/recent_transactions/`, { limit }),
214
+
215
+ // Get payment analytics
216
+ paymentAnalytics: () => this.get(`${this.baseURL}/overview/dashboard/payment_analytics/`),
217
+
218
+ // Get balance overview
219
+ balanceOverview: () => this.get(`${this.baseURL}/overview/dashboard/balance_overview/`),
220
+
221
+ // Get subscription overview
222
+ subscriptionOverview: () => this.get(`${this.baseURL}/overview/dashboard/subscription_overview/`),
223
+
224
+ // Get API keys overview
225
+ apiKeysOverview: () => this.get(`${this.baseURL}/overview/dashboard/api_keys_overview/`)
226
+ };
227
+
228
+ // NGROK API (custom endpoints)
229
+ ngrok = {
230
+ // Get ngrok status from webhook health endpoint
231
+ status: async () => {
232
+ try {
233
+ const response = await this.get('/api/payments/webhooks/health/');
234
+ const ngrokActive = response.details?.ngrok_available || false;
235
+ const apiUrl = response.details?.api_base_url || '';
236
+
237
+ if (ngrokActive) {
238
+ return {
239
+ active: true,
240
+ public_url: apiUrl,
241
+ webhook_url: apiUrl + '/api/payments/webhooks/',
242
+ region: 'auto',
243
+ proto: apiUrl.startsWith('https') ? 'https' : 'http',
244
+ error: null
245
+ };
246
+ } else {
247
+ return {
248
+ active: false,
249
+ public_url: '',
250
+ webhook_url: '',
251
+ region: 'us',
252
+ proto: 'https',
253
+ error: 'Ngrok tunnel not active'
254
+ };
255
+ }
256
+ } catch (error) {
257
+ return {
258
+ active: false,
259
+ public_url: '',
260
+ webhook_url: '',
261
+ region: 'us',
262
+ proto: 'https',
263
+ error: error.message || 'Health check API not accessible'
264
+ };
265
+ }
266
+ },
267
+
268
+ // Start ngrok tunnel (placeholder - not implemented)
269
+ start: () => this.post(`${this.adminURL}/ngrok/start/`),
270
+
271
+ // Stop ngrok tunnel (placeholder - not implemented)
272
+ stop: () => this.post(`${this.adminURL}/ngrok/stop/`)
273
+ };
274
+
275
+ // ADMIN API ENDPOINTS (DRF nested router structure)
276
+ admin = {
277
+ // Payments API
278
+ payments: {
279
+ list: (params = {}) => this.get(`${this.adminURL}/api/payments/`, params),
280
+ get: (id) => this.get(`${this.adminURL}/api/payments/${id}/`),
281
+ create: (data) => this.post(`${this.adminURL}/api/payments/`, data),
282
+ update: (id, data) => this.patch(`${this.adminURL}/api/payments/${id}/`, data),
283
+ delete: (id) => this.delete(`${this.adminURL}/api/payments/${id}/`),
284
+ cancel: (id) => this.post(`${this.adminURL}/api/payments/${id}/cancel/`),
285
+ refund: (id) => this.post(`${this.adminURL}/api/payments/${id}/refund/`),
286
+ stats: () => this.get(`${this.adminURL}/api/payments/stats/`)
287
+ },
288
+
289
+ // Webhooks API
290
+ webhooks: {
291
+ list: () => this.get(`${this.adminURL}/api/webhooks/`),
292
+ stats: () => this.get(`${this.adminURL}/api/webhooks/stats/`),
293
+
294
+ // Nested webhook events
295
+ events: {
296
+ list: (webhookId = 1, params = {}) => this.get(`${this.adminURL}/api/webhooks/${webhookId}/events/`, params),
297
+ retry: (webhookId, eventId) => this.post(`${this.adminURL}/api/webhooks/${webhookId}/events/${eventId}/retry/`),
298
+ clearAll: (webhookId) => this.post(`${this.adminURL}/api/webhooks/${webhookId}/events/clear_all/`),
299
+ retryFailed: (webhookId) => this.post(`${this.adminURL}/api/webhooks/${webhookId}/events/retry_failed/`)
300
+ }
301
+ },
302
+
303
+ // Webhook test method
304
+ webhookTest: {
305
+ send: (url, eventType) => this.post(`${this.adminURL}/api/webhooks/test/`, {
306
+ webhook_url: url,
307
+ event_type: eventType
308
+ })
309
+ },
310
+
311
+ // Stats API
312
+ stats: {
313
+ overview: () => this.get(`${this.adminURL}/api/stats/`),
314
+ payments: () => this.get(`${this.adminURL}/api/stats/payments/`),
315
+ webhooks: () => this.get(`${this.adminURL}/api/stats/webhooks/`),
316
+ system: () => this.get(`${this.adminURL}/api/stats/system/`)
317
+ },
318
+
319
+ // Users API
320
+ users: {
321
+ list: (params = {}) => this.get(`${this.adminURL}/api/users/`, params),
322
+ get: (id) => this.get(`${this.adminURL}/api/users/${id}/`),
323
+ search: (query) => this.get(`${this.adminURL}/api/users/`, { q: query })
324
+ }
325
+ };
326
+
327
+
328
+ // Utility methods
329
+ utils = {
330
+ // Format currency
331
+ formatCurrency: (amount, currency = 'USD') => {
332
+ return new Intl.NumberFormat('en-US', {
333
+ style: 'currency',
334
+ currency: currency
335
+ }).format(amount);
336
+ },
337
+
338
+ // Format date
339
+ formatDate: (dateString) => {
340
+ return new Date(dateString).toLocaleDateString('en-US', {
341
+ year: 'numeric',
342
+ month: 'short',
343
+ day: 'numeric',
344
+ hour: '2-digit',
345
+ minute: '2-digit'
346
+ });
347
+ },
348
+
349
+ // Show notification (simple implementation)
350
+ showNotification: (message, type = 'info') => {
351
+ const notification = document.createElement('div');
352
+ notification.className = `fixed top-4 right-4 px-4 py-2 rounded-md shadow-lg z-50 ${
353
+ type === 'success' ? 'bg-green-500 text-white' :
354
+ type === 'error' ? 'bg-red-500 text-white' :
355
+ type === 'warning' ? 'bg-yellow-500 text-white' :
356
+ 'bg-blue-500 text-white'
357
+ }`;
358
+ notification.textContent = message;
359
+ document.body.appendChild(notification);
360
+
361
+ setTimeout(() => {
362
+ notification.remove();
363
+ }, 3000);
364
+ },
365
+
366
+ // Copy to clipboard
367
+ copyToClipboard: async (text) => {
368
+ try {
369
+ await navigator.clipboard.writeText(text);
370
+ PaymentAPI.utils.showNotification('Copied to clipboard!', 'success');
371
+ } catch (error) {
372
+ console.error('Failed to copy:', error);
373
+ PaymentAPI.utils.showNotification('Failed to copy', 'error');
374
+ }
375
+ }
376
+ };
377
+ }
378
+
379
+ // Create global instance
380
+ window.PaymentAPI = new PaymentAPIClient();
381
+
382
+ // Export for modules
383
+ if (typeof module !== 'undefined' && module.exports) {
384
+ module.exports = PaymentAPIClient;
385
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Ngrok Status Component
3
+ * Handles ngrok tunnel status monitoring and control
4
+ */
5
+ function ngrokStatus() {
6
+ return {
7
+ loading: false,
8
+ status: {
9
+ active: false,
10
+ public_url: '',
11
+ webhook_url: '',
12
+ error: null,
13
+ region: 'us',
14
+ proto: 'https'
15
+ },
16
+
17
+ async init() {
18
+ await this.refreshStatus();
19
+ // Auto-refresh every 10 seconds
20
+ setInterval(() => this.refreshStatus(), 10000);
21
+ },
22
+
23
+ async refreshStatus() {
24
+ this.loading = true;
25
+ try {
26
+ const data = await PaymentAPI.ngrok.status();
27
+ this.status = { ...this.status, ...data };
28
+ } catch (error) {
29
+ console.error('Failed to fetch ngrok status:', error);
30
+ this.status.active = false;
31
+ this.status.error = error.message || 'Connection failed';
32
+ } finally {
33
+ this.loading = false;
34
+ }
35
+ },
36
+
37
+
38
+
39
+ copyUrl() {
40
+ if (this.status.public_url) {
41
+ PaymentAPI.utils.copyToClipboard(this.status.public_url);
42
+ }
43
+ },
44
+
45
+ copyWebhookUrl() {
46
+ if (this.status.webhook_url) {
47
+ PaymentAPI.utils.copyToClipboard(this.status.webhook_url);
48
+ }
49
+ },
50
+
51
+ openTunnel() {
52
+ if (this.status.public_url) {
53
+ window.open(this.status.public_url, '_blank');
54
+ }
55
+ },
56
+
57
+ };
58
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Payment Dashboard Component
3
+ * Handles dashboard data loading and display
4
+ */
5
+ function paymentDashboard() {
6
+ return {
7
+ loading: false,
8
+ stats: {
9
+ total: 0,
10
+ successful: 0,
11
+ pending: 0,
12
+ failed: 0
13
+ },
14
+ recentPayments: [],
15
+ recentActivity: [
16
+ { id: 1, message: 'Payment created', time: '2 minutes ago' },
17
+ { id: 2, message: 'Webhook received', time: '5 minutes ago' },
18
+ { id: 3, message: 'Payment completed', time: '10 minutes ago' }
19
+ ],
20
+
21
+ async init() {
22
+ await this.loadDashboardData();
23
+ // Auto-refresh every 60 seconds
24
+ setInterval(() => this.loadDashboardData(), 60000);
25
+ },
26
+
27
+ async loadDashboardData() {
28
+ this.loading = true;
29
+ try {
30
+ // Load dashboard data using API client
31
+ const [overview, recentPayments] = await Promise.all([
32
+ PaymentAPI.dashboard.overview(),
33
+ PaymentAPI.dashboard.recentPayments(5)
34
+ ]);
35
+
36
+ this.stats = overview.stats || this.stats;
37
+ this.recentPayments = recentPayments || [];
38
+ } catch (error) {
39
+ console.error('Failed to load dashboard data:', error);
40
+ PaymentAPI.utils.showNotification('Failed to load dashboard data', 'error');
41
+ } finally {
42
+ this.loading = false;
43
+ }
44
+ },
45
+
46
+ async refreshDashboard() {
47
+ await this.loadDashboardData();
48
+ }
49
+ };
50
+ }