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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/payments/admin_interface/old/payments/base.html +175 -0
- django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +125 -0
- django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +113 -0
- django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +35 -0
- django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +309 -0
- django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +303 -0
- django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +382 -0
- django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +518 -0
- django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/components.css +248 -9
- django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +163 -0
- django_cfg/apps/payments/admin_interface/serializers/__init__.py +39 -0
- django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +149 -0
- django_cfg/apps/payments/admin_interface/serializers/webhook_serializers.py +114 -0
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +55 -90
- django_cfg/apps/payments/admin_interface/templates/payments/components/dialog.html +81 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_help_dialog.html +112 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/ngrok_status.html +175 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +21 -17
- django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +123 -250
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +170 -269
- django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +152 -355
- django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +202 -551
- django_cfg/apps/payments/admin_interface/views/__init__.py +25 -14
- django_cfg/apps/payments/admin_interface/views/api/__init__.py +20 -0
- django_cfg/apps/payments/admin_interface/views/api/payments.py +191 -0
- django_cfg/apps/payments/admin_interface/views/api/stats.py +206 -0
- django_cfg/apps/payments/admin_interface/views/api/users.py +60 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +257 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +70 -0
- django_cfg/apps/payments/admin_interface/views/base.py +114 -0
- django_cfg/apps/payments/admin_interface/views/dashboard.py +60 -0
- django_cfg/apps/payments/admin_interface/views/forms.py +94 -0
- django_cfg/apps/payments/config/helpers.py +2 -2
- django_cfg/apps/payments/management/commands/cleanup_expired_data.py +429 -0
- django_cfg/apps/payments/management/commands/currency_stats.py +443 -0
- django_cfg/apps/payments/management/commands/manage_currencies.py +9 -20
- django_cfg/apps/payments/management/commands/manage_providers.py +5 -5
- django_cfg/apps/payments/management/commands/process_pending_payments.py +357 -0
- django_cfg/apps/payments/management/commands/test_providers.py +434 -0
- django_cfg/apps/payments/middleware/api_access.py +35 -34
- django_cfg/apps/payments/migrations/0001_initial.py +1 -1
- django_cfg/apps/payments/models/balance.py +5 -2
- django_cfg/apps/payments/models/managers/api_key_managers.py +6 -2
- django_cfg/apps/payments/models/managers/balance_managers.py +3 -3
- django_cfg/apps/payments/models/managers/payment_managers.py +5 -0
- django_cfg/apps/payments/models/managers/subscription_managers.py +3 -3
- django_cfg/apps/payments/models/subscriptions.py +0 -24
- django_cfg/apps/payments/services/cache/__init__.py +1 -1
- django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
- django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
- django_cfg/apps/payments/services/cache_service/interfaces.py +32 -0
- django_cfg/apps/payments/services/cache_service/keys.py +49 -0
- django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
- django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
- django_cfg/apps/payments/services/core/balance_service.py +13 -2
- django_cfg/apps/payments/services/core/payment_service.py +49 -22
- django_cfg/apps/payments/services/integrations/ngrok_service.py +3 -3
- django_cfg/apps/payments/services/providers/registry.py +20 -0
- django_cfg/apps/payments/signals/api_key_signals.py +2 -2
- django_cfg/apps/payments/signals/balance_signals.py +8 -5
- django_cfg/apps/payments/static/payments/js/api-client.js +385 -0
- django_cfg/apps/payments/static/payments/js/ngrok-status.js +58 -0
- django_cfg/apps/payments/static/payments/js/payment-dashboard.js +50 -0
- django_cfg/apps/payments/static/payments/js/payment-form.js +175 -0
- django_cfg/apps/payments/static/payments/js/payment-list.js +95 -0
- django_cfg/apps/payments/static/payments/js/webhook-dashboard.js +154 -0
- django_cfg/apps/payments/urls.py +4 -0
- django_cfg/apps/payments/urls_admin.py +37 -18
- django_cfg/apps/payments/views/api/api_keys.py +14 -0
- django_cfg/apps/payments/views/api/base.py +1 -0
- django_cfg/apps/payments/views/api/currencies.py +2 -2
- django_cfg/apps/payments/views/api/payments.py +11 -5
- django_cfg/apps/payments/views/api/subscriptions.py +36 -31
- django_cfg/apps/payments/views/overview/__init__.py +40 -0
- django_cfg/apps/payments/views/overview/serializers.py +205 -0
- django_cfg/apps/payments/views/overview/services.py +439 -0
- django_cfg/apps/payments/views/overview/urls.py +27 -0
- django_cfg/apps/payments/views/overview/views.py +231 -0
- django_cfg/apps/payments/views/serializers/api_keys.py +20 -6
- django_cfg/apps/payments/views/serializers/balances.py +5 -8
- django_cfg/apps/payments/views/serializers/currencies.py +2 -6
- django_cfg/apps/payments/views/serializers/payments.py +37 -32
- django_cfg/apps/payments/views/serializers/subscriptions.py +4 -26
- django_cfg/apps/urls.py +2 -1
- django_cfg/core/config.py +25 -15
- django_cfg/core/generation.py +12 -12
- django_cfg/core/integration/display/startup.py +1 -1
- django_cfg/core/validation.py +4 -4
- django_cfg/management/commands/show_config.py +2 -2
- django_cfg/management/commands/tree.py +1 -3
- django_cfg/middleware/__init__.py +2 -0
- django_cfg/middleware/static_nocache.py +55 -0
- django_cfg/models/payments.py +13 -15
- django_cfg/models/security.py +15 -0
- django_cfg/modules/django_ngrok.py +6 -0
- django_cfg/modules/django_unfold/dashboard.py +1 -3
- django_cfg/utils/smart_defaults.py +51 -5
- {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/METADATA +1 -1
- {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/RECORD +111 -69
- django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +0 -38
- django_cfg/apps/payments/admin_interface/views/payment_views.py +0 -259
- django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +0 -37
- django_cfg/apps/payments/services/cache/cache_service.py +0 -235
- /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/loading_spinner.html +0 -0
- /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/notification.html +0 -0
- /django_cfg/apps/payments/admin_interface/{templates → old}/payments/components/provider_card.html +0 -0
- /django_cfg/apps/payments/admin_interface/{templates → old}/payments/currency_converter.html +0 -0
- /django_cfg/apps/payments/admin_interface/{templates → old}/payments/payment_status.html +0 -0
- /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/css/dashboard.css +0 -0
- /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/components.js +0 -0
- /django_cfg/apps/payments/{static → admin_interface/old/static}/payments/js/utils.js +0 -0
- {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.1.dist-info → django_cfg-1.3.5.dist-info}/entry_points.txt +0 -0
- {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
|
-
'
|
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
|
-
'
|
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
|
-
|
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
|
-
|
48
|
-
|
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.
|
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
|
+
}
|