django-cfg 1.3.3__py3-none-any.whl → 1.3.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +16 -6
- django_cfg/apps/payments/management/commands/currency_stats.py +72 -5
- 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/middleware/api_access.py +35 -34
- django_cfg/apps/payments/migrations/0001_initial.py +1 -1
- django_cfg/apps/payments/models/managers/api_key_managers.py +4 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +5 -0
- django_cfg/apps/payments/models/subscriptions.py +0 -24
- django_cfg/apps/payments/services/cache/__init__.py +1 -1
- django_cfg/apps/payments/services/core/balance_service.py +13 -2
- 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/balance_signals.py +7 -4
- 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 +41 -1
- {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/METADATA +1 -1
- {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/RECORD +98 -65
- 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/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.3.dist-info → django_cfg-1.3.5.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.3.dist-info → django_cfg-1.3.5.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
"""
|
2
|
+
Webhook Serializers for Admin Interface.
|
3
|
+
|
4
|
+
DRF serializers for webhook management in admin dashboard.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from rest_framework import serializers
|
8
|
+
from datetime import datetime
|
9
|
+
from typing import Dict, Any
|
10
|
+
|
11
|
+
|
12
|
+
class WebhookEventSerializer(serializers.Serializer):
|
13
|
+
"""
|
14
|
+
Serializer for individual webhook event.
|
15
|
+
"""
|
16
|
+
id = serializers.IntegerField(read_only=True)
|
17
|
+
provider = serializers.CharField(max_length=50)
|
18
|
+
event_type = serializers.CharField(max_length=100)
|
19
|
+
status = serializers.ChoiceField(choices=[
|
20
|
+
('success', 'Success'),
|
21
|
+
('failed', 'Failed'),
|
22
|
+
('pending', 'Pending'),
|
23
|
+
('retry', 'Retry'),
|
24
|
+
])
|
25
|
+
timestamp = serializers.DateTimeField()
|
26
|
+
payload_size = serializers.IntegerField(help_text="Size in bytes")
|
27
|
+
response_time = serializers.IntegerField(help_text="Response time in ms")
|
28
|
+
retry_count = serializers.IntegerField(default=0)
|
29
|
+
error_message = serializers.CharField(max_length=500, required=False, allow_blank=True)
|
30
|
+
payload_preview = serializers.CharField(max_length=200, required=False, allow_blank=True)
|
31
|
+
response_status_code = serializers.IntegerField(required=False)
|
32
|
+
webhook_url = serializers.URLField(required=False)
|
33
|
+
|
34
|
+
|
35
|
+
class WebhookEventListSerializer(serializers.Serializer):
|
36
|
+
"""
|
37
|
+
Serializer for paginated webhook events list.
|
38
|
+
"""
|
39
|
+
events = WebhookEventSerializer(many=True)
|
40
|
+
total = serializers.IntegerField()
|
41
|
+
page = serializers.IntegerField()
|
42
|
+
per_page = serializers.IntegerField()
|
43
|
+
has_next = serializers.BooleanField()
|
44
|
+
has_previous = serializers.BooleanField()
|
45
|
+
|
46
|
+
|
47
|
+
class WebhookProviderStatsSerializer(serializers.Serializer):
|
48
|
+
"""
|
49
|
+
Serializer for provider-specific webhook statistics.
|
50
|
+
"""
|
51
|
+
total = serializers.IntegerField()
|
52
|
+
successful = serializers.IntegerField()
|
53
|
+
failed = serializers.IntegerField()
|
54
|
+
pending = serializers.IntegerField(default=0)
|
55
|
+
success_rate = serializers.FloatField()
|
56
|
+
|
57
|
+
|
58
|
+
class WebhookStatsSerializer(serializers.Serializer):
|
59
|
+
"""
|
60
|
+
Serializer for comprehensive webhook statistics.
|
61
|
+
"""
|
62
|
+
total = serializers.IntegerField()
|
63
|
+
successful = serializers.IntegerField()
|
64
|
+
failed = serializers.IntegerField()
|
65
|
+
pending = serializers.IntegerField()
|
66
|
+
success_rate = serializers.FloatField()
|
67
|
+
|
68
|
+
# Provider breakdown
|
69
|
+
providers = serializers.DictField(
|
70
|
+
child=WebhookProviderStatsSerializer(),
|
71
|
+
help_text="Statistics by provider"
|
72
|
+
)
|
73
|
+
|
74
|
+
# Time-based stats
|
75
|
+
last_24h = serializers.DictField(
|
76
|
+
child=serializers.IntegerField(),
|
77
|
+
help_text="Events in last 24 hours"
|
78
|
+
)
|
79
|
+
|
80
|
+
# Performance metrics
|
81
|
+
avg_response_time = serializers.FloatField()
|
82
|
+
max_response_time = serializers.IntegerField()
|
83
|
+
|
84
|
+
|
85
|
+
class WebhookActionSerializer(serializers.Serializer):
|
86
|
+
"""
|
87
|
+
Serializer for webhook actions (retry, clear, etc.).
|
88
|
+
"""
|
89
|
+
action = serializers.ChoiceField(choices=[
|
90
|
+
('retry', 'Retry'),
|
91
|
+
('clear', 'Clear'),
|
92
|
+
('retry_failed', 'Retry Failed'),
|
93
|
+
])
|
94
|
+
event_ids = serializers.ListField(
|
95
|
+
child=serializers.IntegerField(),
|
96
|
+
required=False,
|
97
|
+
help_text="List of event IDs to process"
|
98
|
+
)
|
99
|
+
|
100
|
+
|
101
|
+
class WebhookActionResultSerializer(serializers.Serializer):
|
102
|
+
"""
|
103
|
+
Serializer for webhook action results.
|
104
|
+
"""
|
105
|
+
success = serializers.BooleanField()
|
106
|
+
message = serializers.CharField(max_length=200)
|
107
|
+
event_id = serializers.IntegerField(required=False)
|
108
|
+
processed_count = serializers.IntegerField(required=False)
|
109
|
+
failed_count = serializers.IntegerField(required=False)
|
110
|
+
errors = serializers.ListField(
|
111
|
+
child=serializers.CharField(),
|
112
|
+
required=False,
|
113
|
+
help_text="List of error messages if any"
|
114
|
+
)
|
@@ -4,113 +4,80 @@
|
|
4
4
|
<head>
|
5
5
|
<meta charset="UTF-8">
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
|
-
<title>{% block title %}
|
7
|
+
<title>{% block title %}Payment Admin - Django CFG{% endblock %}</title>
|
8
8
|
|
9
9
|
<!-- Material Icons -->
|
10
|
-
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
11
10
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">
|
12
|
-
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
|
13
11
|
|
14
12
|
<!-- Tailwind CSS -->
|
15
13
|
<script src="https://cdn.tailwindcss.com"></script>
|
16
14
|
|
17
|
-
<!-- Alpine.js
|
15
|
+
<!-- Alpine.js -->
|
18
16
|
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
19
17
|
|
20
|
-
<!-- Chart.js for statistics -->
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
22
|
-
|
23
|
-
<!-- Custom CSS -->
|
24
|
-
<link rel="stylesheet" href="{% static 'payments/css/dashboard.css' %}">
|
25
|
-
<link rel="stylesheet" href="{% static 'payments/css/components.css' %}">
|
26
|
-
|
27
|
-
<!-- Custom styles -->
|
28
18
|
<style>
|
29
19
|
[x-cloak] { display: none !important; }
|
30
|
-
|
31
|
-
/* Custom scrollbar */
|
32
|
-
.custom-scrollbar::-webkit-scrollbar {
|
33
|
-
width: 6px;
|
34
|
-
}
|
35
|
-
.custom-scrollbar::-webkit-scrollbar-track {
|
36
|
-
background: #f1f5f9;
|
37
|
-
}
|
38
|
-
.custom-scrollbar::-webkit-scrollbar-thumb {
|
39
|
-
background: #cbd5e1;
|
40
|
-
border-radius: 3px;
|
41
|
-
}
|
42
|
-
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
43
|
-
background: #94a3b8;
|
44
|
-
}
|
45
|
-
|
46
|
-
/* Dark mode scrollbar */
|
47
|
-
.dark .custom-scrollbar::-webkit-scrollbar-track {
|
48
|
-
background: #1e293b;
|
49
|
-
}
|
50
|
-
.dark .custom-scrollbar::-webkit-scrollbar-thumb {
|
51
|
-
background: #475569;
|
52
|
-
}
|
53
|
-
.dark .custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
54
|
-
background: #64748b;
|
55
|
-
}
|
56
20
|
</style>
|
57
21
|
|
22
|
+
<!-- Dark mode initialization script to prevent flash -->
|
23
|
+
<script>
|
24
|
+
// Initialize dark mode before Alpine.js loads
|
25
|
+
if (localStorage.getItem('darkMode') === 'true' ||
|
26
|
+
(!localStorage.getItem('darkMode') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
27
|
+
document.documentElement.classList.add('dark');
|
28
|
+
}
|
29
|
+
</script>
|
30
|
+
|
58
31
|
{% block extra_head %}{% endblock %}
|
59
32
|
</head>
|
60
|
-
<body class="h-full bg-gray-50 dark:bg-gray-900" x-data="{
|
61
|
-
|
62
|
-
|
33
|
+
<body class="h-full bg-gray-50 dark:bg-gray-900" x-data="{
|
34
|
+
darkMode: localStorage.getItem('darkMode') === 'true' ||
|
35
|
+
(!localStorage.getItem('darkMode') && window.matchMedia('(prefers-color-scheme: dark)').matches),
|
36
|
+
showNgrokHelp: false
|
37
|
+
}" x-init="$watch('darkMode', val => {
|
38
|
+
localStorage.setItem('darkMode', val);
|
39
|
+
document.documentElement.classList.toggle('dark', val);
|
40
|
+
})">
|
63
41
|
|
64
42
|
<!-- Navigation -->
|
65
|
-
<nav class="bg-white dark:bg-gray-800 shadow
|
43
|
+
<nav class="bg-white dark:bg-gray-800 shadow border-b border-gray-200 dark:border-gray-700">
|
66
44
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
67
|
-
<div class="flex justify-between
|
68
|
-
<!-- Logo
|
45
|
+
<div class="flex justify-between h-16">
|
46
|
+
<!-- Logo -->
|
69
47
|
<div class="flex items-center">
|
70
|
-
<
|
71
|
-
|
72
|
-
<h1 class="text-xl font-bold text-gray-900 dark:text-white">
|
73
|
-
Universal Payment System v2.0
|
74
|
-
</h1>
|
75
|
-
</div>
|
48
|
+
<span class="material-icons-outlined text-blue-600 mr-2">payment</span>
|
49
|
+
<h1 class="text-xl font-semibold text-gray-900 dark:text-white">Payment Admin</h1>
|
76
50
|
</div>
|
77
51
|
|
78
|
-
<!-- Navigation
|
79
|
-
<div class="hidden md:block">
|
80
|
-
<div class="ml-10 flex items-baseline space-x-4">
|
81
|
-
<a href="{% url 'cfg_payments_admin:webhook-dashboard' %}"
|
82
|
-
class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white px-3 py-2 rounded-md text-sm font-medium flex items-center">
|
83
|
-
<span class="material-icons-outlined mr-1 text-sm">webhook</span>
|
84
|
-
Webhooks
|
85
|
-
</a>
|
86
|
-
<a href="{% url 'cfg_payments_admin:payment-list' %}"
|
87
|
-
class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white px-3 py-2 rounded-md text-sm font-medium flex items-center">
|
88
|
-
<span class="material-icons-outlined mr-1 text-sm">payments</span>
|
89
|
-
Payments
|
90
|
-
</a>
|
91
|
-
<a href="{% url 'admin:payments_universalpayment_changelist' %}"
|
92
|
-
class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white px-3 py-2 rounded-md text-sm font-medium flex items-center">
|
93
|
-
<span class="material-icons-outlined mr-1 text-sm">admin_panel_settings</span>
|
94
|
-
Admin
|
95
|
-
</a>
|
96
|
-
</div>
|
97
|
-
</div>
|
98
|
-
|
99
|
-
<!-- Dark mode toggle -->
|
52
|
+
<!-- Navigation Links -->
|
100
53
|
<div class="flex items-center space-x-4">
|
54
|
+
<a href="{% url 'cfg_payments_admin:dashboard' %}"
|
55
|
+
class="text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white px-3 py-2 rounded-md text-sm font-medium">
|
56
|
+
Dashboard
|
57
|
+
</a>
|
58
|
+
<a href="{% url 'cfg_payments_admin:payment-list' %}"
|
59
|
+
class="text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white px-3 py-2 rounded-md text-sm font-medium">
|
60
|
+
Payments
|
61
|
+
</a>
|
62
|
+
<a href="{% url 'cfg_payments_admin:webhook-dashboard' %}"
|
63
|
+
class="text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white px-3 py-2 rounded-md text-sm font-medium">
|
64
|
+
Webhooks
|
65
|
+
</a>
|
66
|
+
|
67
|
+
<!-- Dark Mode Toggle -->
|
101
68
|
<button @click="darkMode = !darkMode"
|
102
|
-
class="p-2
|
103
|
-
<span x-show="!darkMode" class="material-icons-outlined">dark_mode</span>
|
104
|
-
<span x-show="darkMode" class="material-icons-outlined">light_mode</span>
|
69
|
+
class="p-2 text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white transition-colors">
|
70
|
+
<span x-show="!darkMode" x-cloak class="material-icons-outlined">dark_mode</span>
|
71
|
+
<span x-show="darkMode" x-cloak class="material-icons-outlined">light_mode</span>
|
105
72
|
</button>
|
106
73
|
</div>
|
107
74
|
</div>
|
108
75
|
</div>
|
109
76
|
</nav>
|
110
77
|
|
111
|
-
<!-- Main
|
78
|
+
<!-- Main Content -->
|
112
79
|
<main class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
113
|
-
<!-- Page
|
80
|
+
<!-- Page Header -->
|
114
81
|
{% block header %}
|
115
82
|
<div class="mb-8">
|
116
83
|
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">
|
@@ -118,7 +85,7 @@
|
|
118
85
|
</h2>
|
119
86
|
{% block page_subtitle %}
|
120
87
|
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
121
|
-
|
88
|
+
Manage payments and webhooks
|
122
89
|
</p>
|
123
90
|
{% endblock %}
|
124
91
|
</div>
|
@@ -128,34 +95,32 @@
|
|
128
95
|
{% if messages %}
|
129
96
|
<div class="mb-6 space-y-2">
|
130
97
|
{% for message in messages %}
|
131
|
-
<div class="rounded-md p-4 {% if message.tags == 'error' %}bg-red-50 text-red-800 border border-red-200{% elif message.tags == 'warning' %}bg-yellow-50 text-yellow-800 border border-yellow-200{% elif message.tags == 'success' %}bg-green-50 text-green-800 border border-green-200{% else %}bg-blue-50 text-blue-800 border border-blue-200{% endif %}">
|
98
|
+
<div class="rounded-md p-4 {% if message.tags == 'error' %}bg-red-50 text-red-800 border border-red-200 dark:bg-red-900/20 dark:text-red-400 dark:border-red-800{% elif message.tags == 'warning' %}bg-yellow-50 text-yellow-800 border border-yellow-200 dark:bg-yellow-900/20 dark:text-yellow-400 dark:border-yellow-800{% elif message.tags == 'success' %}bg-green-50 text-green-800 border border-green-200 dark:bg-green-900/20 dark:text-green-400 dark:border-green-800{% else %}bg-blue-50 text-blue-800 border border-blue-200 dark:bg-blue-900/20 dark:text-blue-400 dark:border-blue-800{% endif %}">
|
132
99
|
{{ message }}
|
133
100
|
</div>
|
134
101
|
{% endfor %}
|
135
102
|
</div>
|
136
103
|
{% endif %}
|
137
104
|
|
138
|
-
<!-- Page
|
105
|
+
<!-- Page Content -->
|
139
106
|
{% block content %}{% endblock %}
|
140
107
|
</main>
|
141
108
|
|
142
109
|
<!-- Footer -->
|
143
110
|
<footer class="bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 mt-12">
|
144
111
|
<div class="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8">
|
145
|
-
<div class="
|
146
|
-
|
147
|
-
Universal Payment System v2.0 - Built with Django-CFG
|
148
|
-
</div>
|
149
|
-
<div>
|
150
|
-
{% now "Y-m-d H:i:s" %} UTC
|
151
|
-
</div>
|
112
|
+
<div class="text-center text-sm text-gray-600 dark:text-gray-400">
|
113
|
+
Payment Admin - Django CFG
|
152
114
|
</div>
|
153
115
|
</div>
|
154
116
|
</footer>
|
155
117
|
|
156
|
-
<!--
|
157
|
-
|
158
|
-
|
118
|
+
<!-- Global Dialogs -->
|
119
|
+
{% include 'payments/components/ngrok_help_dialog.html' %}
|
120
|
+
|
121
|
+
<!-- Payment System JS Components -->
|
122
|
+
<script src="{% static 'payments/js/api-client.js' %}"></script>
|
123
|
+
<script src="{% static 'payments/js/ngrok-status.js' %}"></script>
|
159
124
|
|
160
125
|
{% block extra_js %}{% endblock %}
|
161
126
|
</body>
|
@@ -0,0 +1,81 @@
|
|
1
|
+
{% comment %}
|
2
|
+
Dialog Component
|
3
|
+
|
4
|
+
Usage:
|
5
|
+
{% include 'payments/components/dialog.html' with dialog_id="myDialog" title="Dialog Title" %}
|
6
|
+
|
7
|
+
Then in your Alpine.js component:
|
8
|
+
x-data="{ showMyDialog: false }"
|
9
|
+
@click="showMyDialog = true"
|
10
|
+
|
11
|
+
And in the dialog content:
|
12
|
+
<div x-show="showMyDialog" x-cloak>
|
13
|
+
{% include 'payments/components/dialog.html' with dialog_id="myDialog" title="My Title" %}
|
14
|
+
</div>
|
15
|
+
{% endcomment %}
|
16
|
+
|
17
|
+
<!-- Dialog Backdrop -->
|
18
|
+
<div x-show="show{{ dialog_id|title }}"
|
19
|
+
x-cloak
|
20
|
+
x-transition:enter="transition ease-out duration-300"
|
21
|
+
x-transition:enter-start="opacity-0"
|
22
|
+
x-transition:enter-end="opacity-100"
|
23
|
+
x-transition:leave="transition ease-in duration-200"
|
24
|
+
x-transition:leave-start="opacity-100"
|
25
|
+
x-transition:leave-end="opacity-0"
|
26
|
+
class="fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50"
|
27
|
+
@click.away="show{{ dialog_id|title }} = false">
|
28
|
+
|
29
|
+
<!-- Dialog Panel -->
|
30
|
+
<div x-show="show{{ dialog_id|title }}"
|
31
|
+
x-transition:enter="transition ease-out duration-300"
|
32
|
+
x-transition:enter-start="opacity-0 transform scale-95"
|
33
|
+
x-transition:enter-end="opacity-100 transform scale-100"
|
34
|
+
x-transition:leave="transition ease-in duration-200"
|
35
|
+
x-transition:leave-start="opacity-100 transform scale-100"
|
36
|
+
x-transition:leave-end="opacity-0 transform scale-95"
|
37
|
+
class="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full mx-4 max-h-96 overflow-y-auto"
|
38
|
+
@click.stop>
|
39
|
+
|
40
|
+
<!-- Dialog Header -->
|
41
|
+
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
42
|
+
<div class="flex items-center justify-between">
|
43
|
+
<h3 class="text-lg font-medium text-gray-900 dark:text-white">
|
44
|
+
{% if icon %}
|
45
|
+
<span class="material-icons-outlined mr-2 text-{{ icon_color|default:'blue' }}-600 dark:text-{{ icon_color|default:'blue' }}-400">{{ icon }}</span>
|
46
|
+
{% endif %}
|
47
|
+
{{ title }}
|
48
|
+
</h3>
|
49
|
+
<button @click="show{{ dialog_id|title }} = false"
|
50
|
+
class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200">
|
51
|
+
<span class="material-icons-outlined">close</span>
|
52
|
+
</button>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
|
56
|
+
<!-- Dialog Content -->
|
57
|
+
<div class="px-6 py-4">
|
58
|
+
{% block dialog_content %}
|
59
|
+
{{ content|default:"Dialog content goes here" }}
|
60
|
+
{% endblock %}
|
61
|
+
</div>
|
62
|
+
|
63
|
+
<!-- Dialog Footer -->
|
64
|
+
{% if show_footer|default:True %}
|
65
|
+
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex justify-end space-x-3">
|
66
|
+
{% block dialog_footer %}
|
67
|
+
<button @click="show{{ dialog_id|title }} = false"
|
68
|
+
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600">
|
69
|
+
{{ cancel_text|default:"Close" }}
|
70
|
+
</button>
|
71
|
+
{% if confirm_action %}
|
72
|
+
<button @click="{{ confirm_action }}; show{{ dialog_id|title }} = false"
|
73
|
+
class="px-4 py-2 text-sm font-medium text-white bg-{{ confirm_color|default:'blue' }}-600 border border-transparent rounded-md hover:bg-{{ confirm_color|default:'blue' }}-700">
|
74
|
+
{{ confirm_text|default:"Confirm" }}
|
75
|
+
</button>
|
76
|
+
{% endif %}
|
77
|
+
{% endblock %}
|
78
|
+
</div>
|
79
|
+
{% endif %}
|
80
|
+
</div>
|
81
|
+
</div>
|
@@ -0,0 +1,112 @@
|
|
1
|
+
{% comment %}
|
2
|
+
Ngrok Help Dialog Component
|
3
|
+
Shows instructions for starting ngrok tunnel
|
4
|
+
{% endcomment %}
|
5
|
+
|
6
|
+
<!-- Ngrok Help Dialog -->
|
7
|
+
<div x-show="showNgrokHelp"
|
8
|
+
x-cloak
|
9
|
+
x-transition:enter="transition ease-out duration-300"
|
10
|
+
x-transition:enter-start="opacity-0"
|
11
|
+
x-transition:enter-end="opacity-100"
|
12
|
+
x-transition:leave="transition ease-in duration-200"
|
13
|
+
x-transition:leave-start="opacity-100"
|
14
|
+
x-transition:leave-end="opacity-0"
|
15
|
+
class="fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50"
|
16
|
+
@click.away="showNgrokHelp = false">
|
17
|
+
|
18
|
+
<!-- Dialog Panel -->
|
19
|
+
<div x-show="showNgrokHelp"
|
20
|
+
x-transition:enter="transition ease-out duration-300"
|
21
|
+
x-transition:enter-start="opacity-0 transform scale-95"
|
22
|
+
x-transition:enter-end="opacity-100 transform scale-100"
|
23
|
+
x-transition:leave="transition ease-in duration-200"
|
24
|
+
x-transition:leave-start="opacity-100 transform scale-100"
|
25
|
+
x-transition:leave-end="opacity-0 transform scale-95"
|
26
|
+
class="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-lg w-full mx-4"
|
27
|
+
@click.stop>
|
28
|
+
|
29
|
+
<!-- Dialog Header -->
|
30
|
+
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
31
|
+
<div class="flex items-center justify-between">
|
32
|
+
<h3 class="text-lg font-medium text-gray-900 dark:text-white flex items-center">
|
33
|
+
<span class="material-icons-outlined mr-2 text-blue-600 dark:text-blue-400">help</span>
|
34
|
+
How to Start Ngrok Tunnel
|
35
|
+
</h3>
|
36
|
+
<button @click="showNgrokHelp = false"
|
37
|
+
class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200">
|
38
|
+
<span class="material-icons-outlined">close</span>
|
39
|
+
</button>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<!-- Dialog Content -->
|
44
|
+
<div class="px-6 py-4 space-y-4">
|
45
|
+
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
|
46
|
+
<div class="flex items-start">
|
47
|
+
<span class="material-icons-outlined text-blue-600 dark:text-blue-400 mr-2 mt-0.5">info</span>
|
48
|
+
<div>
|
49
|
+
<h4 class="font-medium text-blue-900 dark:text-blue-100 mb-1">Ngrok is not active</h4>
|
50
|
+
<p class="text-sm text-blue-800 dark:text-blue-200">
|
51
|
+
To enable ngrok tunnel for webhook testing, restart your development server with the ngrok command.
|
52
|
+
</p>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<div>
|
58
|
+
<h4 class="font-medium text-gray-900 dark:text-white mb-2 flex items-center">
|
59
|
+
<span class="material-icons-outlined mr-2 text-green-600 dark:text-green-400">terminal</span>
|
60
|
+
Start Server with Ngrok
|
61
|
+
</h4>
|
62
|
+
<div class="bg-gray-900 dark:bg-gray-800 rounded-lg p-4 font-mono text-sm">
|
63
|
+
<div class="text-green-400 mb-2"># Stop current server (Ctrl+C) and run:</div>
|
64
|
+
<div class="text-white">python manage.py runserver_ngrok</div>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
|
68
|
+
<div>
|
69
|
+
<h4 class="font-medium text-gray-900 dark:text-white mb-2 flex items-center">
|
70
|
+
<span class="material-icons-outlined mr-2 text-purple-600 dark:text-purple-400">settings</span>
|
71
|
+
Advanced Options
|
72
|
+
</h4>
|
73
|
+
<div class="space-y-2 text-sm">
|
74
|
+
<div class="bg-gray-50 dark:bg-gray-700 rounded p-3">
|
75
|
+
<div class="font-mono text-gray-800 dark:text-gray-200 mb-1">python manage.py runserver_ngrok --domain myapp</div>
|
76
|
+
<div class="text-gray-600 dark:text-gray-400">Use custom subdomain</div>
|
77
|
+
</div>
|
78
|
+
<div class="bg-gray-50 dark:bg-gray-700 rounded p-3">
|
79
|
+
<div class="font-mono text-gray-800 dark:text-gray-200 mb-1">python manage.py runserver_ngrok 0.0.0.0:8080</div>
|
80
|
+
<div class="text-gray-600 dark:text-gray-400">Use custom port</div>
|
81
|
+
</div>
|
82
|
+
</div>
|
83
|
+
</div>
|
84
|
+
|
85
|
+
<div class="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
|
86
|
+
<div class="flex items-start">
|
87
|
+
<span class="material-icons-outlined text-yellow-600 dark:text-yellow-400 mr-2 mt-0.5">lightbulb</span>
|
88
|
+
<div>
|
89
|
+
<h4 class="font-medium text-yellow-900 dark:text-yellow-100 mb-1">Pro Tip</h4>
|
90
|
+
<p class="text-sm text-yellow-800 dark:text-yellow-200">
|
91
|
+
Once ngrok is active, you'll see the public URL here and can copy it for webhook configuration.
|
92
|
+
</p>
|
93
|
+
</div>
|
94
|
+
</div>
|
95
|
+
</div>
|
96
|
+
</div>
|
97
|
+
|
98
|
+
<!-- Dialog Footer -->
|
99
|
+
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex justify-between">
|
100
|
+
<a href="https://ngrok.com/docs"
|
101
|
+
target="_blank"
|
102
|
+
class="text-sm text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 flex items-center">
|
103
|
+
<span class="material-icons-outlined mr-1 text-sm">open_in_new</span>
|
104
|
+
Ngrok Documentation
|
105
|
+
</a>
|
106
|
+
<button @click="showNgrokHelp = false"
|
107
|
+
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700">
|
108
|
+
Got it!
|
109
|
+
</button>
|
110
|
+
</div>
|
111
|
+
</div>
|
112
|
+
</div>
|