krsyer-server-monitor-pro 1.0.8 → 1.0.10
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.
- package/lib/controllers/cloudflareController.js +1 -1
- package/lib/controllers/dockerController.js +1 -1
- package/lib/controllers/networkController.js +1 -1
- package/lib/controllers/serverController.js +1 -1
- package/lib/ecosystem.config.js +1 -1
- package/lib/middleware/saasAuth.js +1 -1
- package/lib/public/script.js +82 -2
- package/lib/routes/cloudflareRoutes.js +1 -1
- package/lib/routes/dockerRoutes.js +1 -1
- package/lib/routes/networkRoutes.js +1 -1
- package/lib/routes/serverRoutes.js +1 -1
- package/lib/server.js +1 -1
- package/lib/services/cashfreeService.js +1 -1
- package/lib/views/index.html +9 -0
- package/license-portal/.env.example +6 -0
- package/license-portal/package.json +2 -1
- package/license-portal/server.js +89 -43
- package/license-portal/views/pricing.html +211 -57
- package/package.json +1 -1
package/license-portal/server.js
CHANGED
|
@@ -12,24 +12,63 @@ app.use(express.urlencoded({ extended: true }));
|
|
|
12
12
|
app.use(cors());
|
|
13
13
|
|
|
14
14
|
// --- Database (Simple JSON File Persistence) ---
|
|
15
|
-
const
|
|
15
|
+
const mysql = require('mysql2/promise');
|
|
16
|
+
|
|
17
|
+
// --- Database (MySQL) ---
|
|
18
|
+
const dbConfig = {
|
|
19
|
+
host: process.env.DB_HOST || 'localhost',
|
|
20
|
+
user: process.env.DB_USER || 'root',
|
|
21
|
+
password: process.env.DB_PASSWORD || '',
|
|
22
|
+
waitForConnections: true,
|
|
23
|
+
connectionLimit: 10,
|
|
24
|
+
queueLimit: 0
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const DB_NAME = process.env.DB_NAME || 'license_portal';
|
|
28
|
+
|
|
29
|
+
let pool;
|
|
16
30
|
|
|
17
|
-
//
|
|
18
|
-
|
|
31
|
+
// Init DB
|
|
32
|
+
(async () => {
|
|
19
33
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
34
|
+
// 1. Create DB if not exists
|
|
35
|
+
const output = await mysql.createConnection({
|
|
36
|
+
host: dbConfig.host,
|
|
37
|
+
user: dbConfig.user,
|
|
38
|
+
password: dbConfig.password
|
|
39
|
+
});
|
|
40
|
+
await output.query(`CREATE DATABASE IF NOT EXISTS ${DB_NAME}`);
|
|
41
|
+
await output.end();
|
|
42
|
+
|
|
43
|
+
// 2. Init Pool
|
|
44
|
+
pool = mysql.createPool({
|
|
45
|
+
...dbConfig,
|
|
46
|
+
database: DB_NAME
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// 3. Create Table
|
|
50
|
+
const conn = await pool.getConnection();
|
|
51
|
+
await conn.query(`
|
|
52
|
+
CREATE TABLE IF NOT EXISTS licenses (
|
|
53
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
54
|
+
license_key VARCHAR(255) NOT NULL UNIQUE,
|
|
55
|
+
order_id VARCHAR(255),
|
|
56
|
+
customer_email VARCHAR(255),
|
|
57
|
+
customer_phone VARCHAR(50),
|
|
58
|
+
plan VARCHAR(50) DEFAULT 'Pro',
|
|
59
|
+
active BOOLEAN DEFAULT TRUE,
|
|
60
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
61
|
+
expiry_date DATETIME,
|
|
62
|
+
INDEX idx_key (license_key)
|
|
63
|
+
)
|
|
64
|
+
`);
|
|
65
|
+
console.log('MySQL Database initialized');
|
|
66
|
+
conn.release();
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.error('MySQL Init Error:', err);
|
|
69
|
+
}
|
|
70
|
+
})();
|
|
26
71
|
|
|
27
|
-
// Helper to save licenses
|
|
28
|
-
const saveLicense = (key, data) => {
|
|
29
|
-
const licenses = getLicenses();
|
|
30
|
-
licenses[key] = data;
|
|
31
|
-
fs.writeFileSync(DB_FILE, JSON.stringify(licenses, null, 2));
|
|
32
|
-
};
|
|
33
72
|
|
|
34
73
|
// --- Cashfree Init Helper ---
|
|
35
74
|
const getCashfree = () => {
|
|
@@ -37,8 +76,6 @@ const getCashfree = () => {
|
|
|
37
76
|
cf.XClientId = process.env.CASHFREE_APP_ID;
|
|
38
77
|
cf.XClientSecret = process.env.CASHFREE_SECRET_KEY;
|
|
39
78
|
cf.XEnvironment = CFEnvironment.PRODUCTION;
|
|
40
|
-
// VERY IMPORTANT: Set version config if needed, but instance method PGCreateOrder logic
|
|
41
|
-
// internally uses this.XApiVersion if available.
|
|
42
79
|
return cf;
|
|
43
80
|
};
|
|
44
81
|
|
|
@@ -55,6 +92,9 @@ app.post('/api/buy', async (req, res) => {
|
|
|
55
92
|
const { email, phone } = req.body;
|
|
56
93
|
const orderId = `order_${Date.now()}`;
|
|
57
94
|
|
|
95
|
+
// Store temp order info? Not strictly needed if we trust Cashfree return
|
|
96
|
+
// But helpful for reconciliation. For now, rely on Return URL params.
|
|
97
|
+
|
|
58
98
|
const request = {
|
|
59
99
|
order_amount: 1.00,
|
|
60
100
|
order_currency: "INR",
|
|
@@ -70,13 +110,10 @@ app.post('/api/buy', async (req, res) => {
|
|
|
70
110
|
};
|
|
71
111
|
|
|
72
112
|
const cf = getCashfree();
|
|
73
|
-
// FIXED: For SDK v5+, remove the API Version string argument.
|
|
74
|
-
// It should be: await cf.PGCreateOrder(request)
|
|
75
113
|
const response = await cf.PGCreateOrder(request);
|
|
76
114
|
res.json(response.data);
|
|
77
115
|
|
|
78
116
|
} catch (error) {
|
|
79
|
-
// Detailed error logging
|
|
80
117
|
console.error("Cashfree Order Create Error:", error.response?.data || error.message);
|
|
81
118
|
res.status(500).json({ error: "Failed to create order" });
|
|
82
119
|
}
|
|
@@ -87,13 +124,17 @@ app.get('/verify', async (req, res) => {
|
|
|
87
124
|
const { order_id } = req.query;
|
|
88
125
|
try {
|
|
89
126
|
const cf = getCashfree();
|
|
90
|
-
// FIXED: Remove version string argument here too
|
|
91
127
|
const response = await cf.PGOrderFetchPayments(order_id);
|
|
92
128
|
const payments = response.data;
|
|
93
|
-
|
|
94
129
|
const isPaid = payments.some(p => p.payment_status === 'SUCCESS');
|
|
95
130
|
|
|
96
131
|
if (isPaid) {
|
|
132
|
+
// Get Customer Details from Order Fetch?
|
|
133
|
+
// To simplify, we get email from stored order or just query Cashfree Order details again
|
|
134
|
+
const orderDetails = await cf.PGOrderFetch(order_id);
|
|
135
|
+
const email = orderDetails.data.customer_details.customer_email;
|
|
136
|
+
const phone = orderDetails.data.customer_details.customer_phone;
|
|
137
|
+
|
|
97
138
|
// Generate License
|
|
98
139
|
const licenseKey = 'PRO-' + Math.random().toString(36).substring(2, 10).toUpperCase() + '-' + Date.now().toString(36).toUpperCase();
|
|
99
140
|
|
|
@@ -101,13 +142,11 @@ app.get('/verify', async (req, res) => {
|
|
|
101
142
|
const expiryDate = new Date();
|
|
102
143
|
expiryDate.setFullYear(expiryDate.getFullYear() + 1);
|
|
103
144
|
|
|
104
|
-
// Save to
|
|
105
|
-
|
|
106
|
-
active
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
expiry: expiryDate.toISOString()
|
|
110
|
-
});
|
|
145
|
+
// Save to MySQL
|
|
146
|
+
await pool.query(
|
|
147
|
+
'INSERT INTO licenses (license_key, order_id, customer_email, customer_phone, expiry_date, active) VALUES (?, ?, ?, ?, ?, ?)',
|
|
148
|
+
[licenseKey, order_id, email, phone, expiryDate, true]
|
|
149
|
+
);
|
|
111
150
|
|
|
112
151
|
// Show Success Page
|
|
113
152
|
res.send(`
|
|
@@ -141,34 +180,41 @@ app.get('/verify', async (req, res) => {
|
|
|
141
180
|
});
|
|
142
181
|
|
|
143
182
|
// 4. API Validation Endpoint
|
|
144
|
-
app.get('/api/validate', (req, res) => {
|
|
183
|
+
app.get('/api/validate', async (req, res) => {
|
|
145
184
|
let { key } = req.query;
|
|
146
185
|
if (!key) return res.json({ valid: false });
|
|
147
186
|
|
|
148
187
|
key = key.trim();
|
|
149
188
|
|
|
150
|
-
const licenses = getLicenses();
|
|
151
|
-
const license = licenses[key];
|
|
152
|
-
|
|
153
189
|
// Special Demo Key
|
|
154
190
|
if (key === 'DEMO-123') {
|
|
155
191
|
return res.json({ valid: true, plan: 'Pro (Demo)' });
|
|
156
192
|
}
|
|
157
193
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const
|
|
161
|
-
const expiry = new Date(license.expiry);
|
|
194
|
+
try {
|
|
195
|
+
const [rows] = await pool.query('SELECT * FROM licenses WHERE license_key = ?', [key]);
|
|
196
|
+
const license = rows[0];
|
|
162
197
|
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
|
|
198
|
+
if (license && license.active) {
|
|
199
|
+
// Check Expiry
|
|
200
|
+
const now = new Date();
|
|
201
|
+
const expiry = new Date(license.expiry_date);
|
|
166
202
|
|
|
167
|
-
|
|
203
|
+
if (expiry && now > expiry) {
|
|
204
|
+
return res.json({ valid: false, message: 'Subscription Expired' });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return res.json({
|
|
208
|
+
valid: true,
|
|
209
|
+
plan: license.plan,
|
|
210
|
+
expiry: license.expiry_date
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
} catch (e) {
|
|
214
|
+
console.error("Validation Error", e);
|
|
168
215
|
}
|
|
169
216
|
|
|
170
|
-
|
|
171
|
-
res.json({ valid: false, message: 'Invalid Key' });
|
|
217
|
+
return res.json({ valid: false, message: 'Invalid Key' });
|
|
172
218
|
});
|
|
173
219
|
|
|
174
220
|
const PORT = process.env.PORT || 3011;
|
|
@@ -4,109 +4,262 @@
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="UTF-8">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title>
|
|
7
|
+
<title>Server Monitor Pro - Professional Server Management</title>
|
|
8
8
|
<script src="https://sdk.cashfree.com/js/v3/cashfree.js"></script>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap"
|
|
10
|
+
rel="stylesheet">
|
|
9
11
|
<style>
|
|
12
|
+
:root {
|
|
13
|
+
--primary: #8b5cf6;
|
|
14
|
+
--primary-hover: #7c3aed;
|
|
15
|
+
--secondary: #06b6d4;
|
|
16
|
+
--bg-dark: #0f172a;
|
|
17
|
+
--card-bg: #1e293b;
|
|
18
|
+
--text-main: #f8fafc;
|
|
19
|
+
--text-muted: #94a3b8;
|
|
20
|
+
--accent: #f43f5e;
|
|
21
|
+
--success: #10b981;
|
|
22
|
+
}
|
|
23
|
+
|
|
10
24
|
body {
|
|
11
|
-
font-family: '
|
|
12
|
-
background:
|
|
13
|
-
color:
|
|
14
|
-
display: flex;
|
|
15
|
-
justify-content: center;
|
|
16
|
-
align-items: center;
|
|
17
|
-
height: 100vh;
|
|
25
|
+
font-family: 'Space Grotesk', sans-serif;
|
|
26
|
+
background-color: var(--bg-dark);
|
|
27
|
+
color: var(--text-main);
|
|
18
28
|
margin: 0;
|
|
29
|
+
padding: 0;
|
|
30
|
+
line-height: 1.6;
|
|
19
31
|
}
|
|
20
32
|
|
|
21
|
-
.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
.container {
|
|
34
|
+
max-width: 1200px;
|
|
35
|
+
margin: 0 auto;
|
|
36
|
+
padding: 0 20px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Hero Section */
|
|
40
|
+
.hero {
|
|
41
|
+
padding: 80px 0 40px;
|
|
27
42
|
text-align: center;
|
|
28
|
-
|
|
43
|
+
background: radial-gradient(circle at center, rgba(139, 92, 246, 0.15) 0%, transparent 70%);
|
|
29
44
|
}
|
|
30
45
|
|
|
31
46
|
h1 {
|
|
32
|
-
font-size:
|
|
33
|
-
margin-bottom:
|
|
47
|
+
font-size: 3.5rem;
|
|
48
|
+
margin-bottom: 1rem;
|
|
49
|
+
background: linear-gradient(135deg, #fff 0%, #cbd5e1 100%);
|
|
50
|
+
-webkit-background-clip: text;
|
|
51
|
+
-webkit-text-fill-color: transparent;
|
|
52
|
+
font-weight: 700;
|
|
34
53
|
}
|
|
35
54
|
|
|
36
|
-
|
|
37
|
-
|
|
55
|
+
.tagline {
|
|
56
|
+
font-size: 1.25rem;
|
|
57
|
+
color: var(--secondary);
|
|
38
58
|
margin-bottom: 2rem;
|
|
59
|
+
font-weight: 500;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Installation Section */
|
|
63
|
+
.install-section {
|
|
64
|
+
background: var(--card-bg);
|
|
65
|
+
border-radius: 16px;
|
|
66
|
+
padding: 40px;
|
|
67
|
+
margin: 40px auto;
|
|
68
|
+
max-width: 800px;
|
|
69
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
70
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.section-title {
|
|
74
|
+
text-align: center;
|
|
75
|
+
font-size: 1.8rem;
|
|
76
|
+
margin-bottom: 30px;
|
|
77
|
+
color: #fff;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.code-block {
|
|
81
|
+
background: #000;
|
|
82
|
+
padding: 20px;
|
|
83
|
+
border-radius: 8px;
|
|
84
|
+
font-family: 'Courier New', monospace;
|
|
85
|
+
color: #4ade80;
|
|
86
|
+
margin-bottom: 15px;
|
|
87
|
+
position: relative;
|
|
88
|
+
border-left: 4px solid var(--primary);
|
|
89
|
+
overflow-x: auto;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.code-comment {
|
|
93
|
+
color: #64748b;
|
|
94
|
+
display: block;
|
|
95
|
+
margin-bottom: 5px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Checkout Card */
|
|
99
|
+
.card {
|
|
100
|
+
background: white;
|
|
101
|
+
padding: 2.5rem;
|
|
102
|
+
border-radius: 24px;
|
|
103
|
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
|
104
|
+
text-align: center;
|
|
105
|
+
width: 100%;
|
|
106
|
+
max-width: 420px;
|
|
107
|
+
margin: 0 auto 60px;
|
|
108
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
109
|
+
position: relative;
|
|
110
|
+
background: linear-gradient(145deg, #1e293b, #0f172a);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.price-tag {
|
|
114
|
+
font-size: 3rem;
|
|
115
|
+
font-weight: 700;
|
|
116
|
+
color: var(--text-main);
|
|
117
|
+
margin: 1.5rem 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.price-sub {
|
|
121
|
+
font-size: 1rem;
|
|
122
|
+
color: var(--text-muted);
|
|
123
|
+
font-weight: 400;
|
|
39
124
|
}
|
|
40
125
|
|
|
41
126
|
input {
|
|
42
127
|
width: 100%;
|
|
43
|
-
padding:
|
|
44
|
-
margin
|
|
45
|
-
background: #0f172a;
|
|
128
|
+
padding: 14px;
|
|
129
|
+
margin: 10px 0;
|
|
46
130
|
border: 1px solid #334155;
|
|
47
|
-
|
|
48
|
-
border-radius: 8px;
|
|
131
|
+
border-radius: 12px;
|
|
49
132
|
box-sizing: border-box;
|
|
133
|
+
background: #0f172a;
|
|
134
|
+
color: white;
|
|
135
|
+
font-family: inherit;
|
|
136
|
+
transition: all 0.3s;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
input:focus {
|
|
140
|
+
outline: none;
|
|
141
|
+
border-color: var(--primary);
|
|
142
|
+
box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.2);
|
|
50
143
|
}
|
|
51
144
|
|
|
52
145
|
button {
|
|
53
146
|
width: 100%;
|
|
54
|
-
padding:
|
|
55
|
-
background:
|
|
147
|
+
padding: 16px;
|
|
148
|
+
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-hover) 100%);
|
|
56
149
|
color: white;
|
|
57
150
|
border: none;
|
|
58
|
-
border-radius:
|
|
59
|
-
font-
|
|
151
|
+
border-radius: 12px;
|
|
152
|
+
font-size: 1.1rem;
|
|
153
|
+
font-weight: 600;
|
|
60
154
|
cursor: pointer;
|
|
61
|
-
transition: 0.
|
|
155
|
+
transition: transform 0.1s, box-shadow 0.3s;
|
|
156
|
+
margin-top: 10px;
|
|
62
157
|
}
|
|
63
158
|
|
|
64
159
|
button:hover {
|
|
65
|
-
|
|
160
|
+
transform: translateY(-2px);
|
|
161
|
+
box-shadow: 0 10px 20px rgba(139, 92, 246, 0.3);
|
|
66
162
|
}
|
|
67
163
|
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
164
|
+
.secure-badge {
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
justify-content: center;
|
|
168
|
+
gap: 8px;
|
|
169
|
+
margin-top: 20px;
|
|
170
|
+
color: var(--text-muted);
|
|
171
|
+
font-size: 0.9rem;
|
|
73
172
|
}
|
|
74
173
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
174
|
+
/* Responsive */
|
|
175
|
+
@media (max-width: 768px) {
|
|
176
|
+
h1 {
|
|
177
|
+
font-size: 2.5rem;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.install-section {
|
|
181
|
+
padding: 20px;
|
|
182
|
+
}
|
|
79
183
|
}
|
|
80
184
|
</style>
|
|
81
185
|
</head>
|
|
82
186
|
|
|
83
187
|
<body>
|
|
84
188
|
|
|
85
|
-
<div class="
|
|
86
|
-
<
|
|
87
|
-
|
|
189
|
+
<div class="hero">
|
|
190
|
+
<div class="container">
|
|
191
|
+
<h1>Server Monitor Pro</h1>
|
|
192
|
+
<p class="tagline">The ultimate realtime monitoring solution for your infrastructure.</p>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div class="container">
|
|
197
|
+
<!-- Installation Guide -->
|
|
198
|
+
<div class="install-section">
|
|
199
|
+
<h2 class="section-title">🚀 Installation Guide</h2>
|
|
200
|
+
|
|
201
|
+
<div class="code-block">
|
|
202
|
+
<span class="code-comment"># 1. Install the package globally</span>
|
|
203
|
+
sudo npm install -g krsyer-server-monitor-pro@latest
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<div class="code-block">
|
|
207
|
+
<span class="code-comment"># 2. Setup the system service</span>
|
|
208
|
+
sudo npx server-monitor setup
|
|
209
|
+
</div>
|
|
88
210
|
|
|
89
|
-
|
|
211
|
+
<div class="code-block">
|
|
212
|
+
<span class="code-comment"># 3. Start the monitor</span>
|
|
213
|
+
sudo systemctl restart krsyer-monitor
|
|
214
|
+
</div>
|
|
90
215
|
|
|
91
|
-
|
|
92
|
-
|
|
216
|
+
<p style="text-align: center; color: var(--text-muted); margin-top: 20px;">
|
|
217
|
+
Once installed, visit your server IP on port 3000 to activate.
|
|
218
|
+
</p>
|
|
219
|
+
</div>
|
|
93
220
|
|
|
94
|
-
|
|
221
|
+
<!-- Checkout / License Section -->
|
|
222
|
+
<div class="card">
|
|
223
|
+
<h2 style="margin-top: 0; margin-bottom: 10px;">Get Your License</h2>
|
|
224
|
+
<p style="color: var(--text-muted); margin-bottom: 20px;">Unlimited servers, 1 year updates</p>
|
|
225
|
+
|
|
226
|
+
<div class="price-tag">₹1 <span class="price-sub">/ year</span></div>
|
|
227
|
+
|
|
228
|
+
<input type="email" id="email" placeholder="Email Address" required>
|
|
229
|
+
<input type="tel" id="phone" placeholder="Phone Number" required>
|
|
230
|
+
|
|
231
|
+
<button id="buyBtn">Buy License Now</button>
|
|
232
|
+
|
|
233
|
+
<div class="secure-badge">
|
|
234
|
+
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
235
|
+
<path
|
|
236
|
+
d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z" />
|
|
237
|
+
</svg>
|
|
238
|
+
Secured by Cashfree Payments
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
95
241
|
</div>
|
|
96
242
|
|
|
97
243
|
<script>
|
|
98
|
-
const cashfree = Cashfree({
|
|
244
|
+
const cashfree = Cashfree({
|
|
245
|
+
mode: "production"
|
|
246
|
+
});
|
|
99
247
|
|
|
100
|
-
async
|
|
248
|
+
document.getElementById('buyBtn').addEventListener('click', async () => {
|
|
101
249
|
const email = document.getElementById('email').value;
|
|
102
250
|
const phone = document.getElementById('phone').value;
|
|
103
|
-
|
|
251
|
+
const btn = document.getElementById('buyBtn');
|
|
104
252
|
|
|
105
|
-
|
|
106
|
-
|
|
253
|
+
if (!email || !phone) {
|
|
254
|
+
alert('Please fill in all details');
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
btn.innerText = 'Processing...';
|
|
107
259
|
btn.disabled = true;
|
|
108
260
|
|
|
109
261
|
try {
|
|
262
|
+
// Create Order
|
|
110
263
|
const res = await fetch('/api/buy', {
|
|
111
264
|
method: 'POST',
|
|
112
265
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -115,24 +268,25 @@
|
|
|
115
268
|
const data = await res.json();
|
|
116
269
|
|
|
117
270
|
if (data.payment_session_id) {
|
|
271
|
+
// Redirect to Payment
|
|
118
272
|
cashfree.checkout({
|
|
119
273
|
paymentSessionId: data.payment_session_id,
|
|
120
274
|
redirectTarget: "_self"
|
|
275
|
+
// returnUrl: `${window.location.origin}/verify?order_id=${data.order_id}` // Handled by Cashfree self redirect if desired, but better to use returnUrl
|
|
121
276
|
});
|
|
122
277
|
} else {
|
|
123
|
-
alert(
|
|
278
|
+
alert('Error creating order');
|
|
279
|
+
btn.innerText = 'Buy License Now';
|
|
124
280
|
btn.disabled = false;
|
|
125
|
-
btn.innerText = "Buy Now";
|
|
126
281
|
}
|
|
127
282
|
} catch (e) {
|
|
128
283
|
console.error(e);
|
|
129
|
-
alert(
|
|
284
|
+
alert('Connection Error');
|
|
285
|
+
btn.innerText = 'Buy License Now';
|
|
130
286
|
btn.disabled = false;
|
|
131
|
-
btn.innerText = "Buy Now";
|
|
132
287
|
}
|
|
133
|
-
}
|
|
288
|
+
});
|
|
134
289
|
</script>
|
|
135
|
-
|
|
136
290
|
</body>
|
|
137
291
|
|
|
138
292
|
</html>
|