paymongo-cli 1.4.3 → 1.4.5
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/CHANGELOG.md +32 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/commands/dev/logs.js +46 -0
- package/dist/commands/dev/status.js +41 -0
- package/dist/commands/dev/stop.js +41 -0
- package/dist/commands/dev.js +7 -282
- package/dist/commands/generate/templates/checkout-page/index.js +539 -0
- package/dist/commands/generate/templates/index.js +5 -0
- package/dist/commands/generate/templates/payment-intent/javascript.js +71 -0
- package/dist/commands/generate/templates/payment-intent/typescript.js +95 -0
- package/dist/commands/generate/templates/webhook-handler/javascript.js +174 -0
- package/dist/commands/generate/templates/webhook-handler/typescript.js +138 -0
- package/dist/commands/generate.js +7 -999
- package/dist/commands/init.js +22 -5
- package/dist/commands/login.js +52 -15
- package/dist/services/api/client.js +1 -1
- package/dist/services/dev/server.js +167 -0
- package/package.json +2 -2
- package/dist/services/api/undici-client.js +0 -288
|
@@ -4,6 +4,7 @@ import chalk from 'chalk';
|
|
|
4
4
|
import fs from 'fs/promises';
|
|
5
5
|
import ConfigManager from '../services/config/manager.js';
|
|
6
6
|
import Spinner from '../utils/spinner.js';
|
|
7
|
+
import { getJavaScriptWebhookHandler, getTypeScriptWebhookHandler, getJavaScriptPaymentIntent, getTypeScriptPaymentIntent, getCheckoutPageTemplate, } from './generate/templates/index.js';
|
|
7
8
|
const command = new Command('generate');
|
|
8
9
|
command
|
|
9
10
|
.description('Generate boilerplate code for PayMongo integrations')
|
|
@@ -104,10 +105,10 @@ async function generateWebhookHandler(options) {
|
|
|
104
105
|
}
|
|
105
106
|
let code;
|
|
106
107
|
if (options.language === 'typescript') {
|
|
107
|
-
code =
|
|
108
|
+
code = getTypeScriptWebhookHandler(events, options.framework);
|
|
108
109
|
}
|
|
109
110
|
else {
|
|
110
|
-
code =
|
|
111
|
+
code = getJavaScriptWebhookHandler(events, options.framework);
|
|
111
112
|
}
|
|
112
113
|
let outputFile = options.output;
|
|
113
114
|
if (!outputFile) {
|
|
@@ -140,10 +141,10 @@ async function generatePaymentIntent(options) {
|
|
|
140
141
|
}
|
|
141
142
|
let code;
|
|
142
143
|
if (options.language === 'typescript') {
|
|
143
|
-
code =
|
|
144
|
+
code = getTypeScriptPaymentIntent(methods);
|
|
144
145
|
}
|
|
145
146
|
else {
|
|
146
|
-
code =
|
|
147
|
+
code = getJavaScriptPaymentIntent(methods);
|
|
147
148
|
}
|
|
148
149
|
let outputFile = options.output;
|
|
149
150
|
if (!outputFile) {
|
|
@@ -168,24 +169,10 @@ async function generatePaymentIntent(options) {
|
|
|
168
169
|
async function generateCheckoutPage(options) {
|
|
169
170
|
const spinner = new Spinner();
|
|
170
171
|
try {
|
|
171
|
-
|
|
172
|
-
let fileExtension;
|
|
173
|
-
switch (options.language) {
|
|
174
|
-
case 'react':
|
|
175
|
-
code = generateReactCheckoutPage();
|
|
176
|
-
fileExtension = 'jsx';
|
|
177
|
-
break;
|
|
178
|
-
case 'vue':
|
|
179
|
-
code = generateVueCheckoutPage();
|
|
180
|
-
fileExtension = 'vue';
|
|
181
|
-
break;
|
|
182
|
-
default:
|
|
183
|
-
code = generateHTMLCheckoutPage();
|
|
184
|
-
fileExtension = 'html';
|
|
185
|
-
}
|
|
172
|
+
const { code, extension } = getCheckoutPageTemplate(options.language);
|
|
186
173
|
let outputFile = options.output;
|
|
187
174
|
if (!outputFile) {
|
|
188
|
-
const defaultName = `checkout.${
|
|
175
|
+
const defaultName = `checkout.${extension}`;
|
|
189
176
|
outputFile = await input({
|
|
190
177
|
message: 'Output file path:',
|
|
191
178
|
default: defaultName,
|
|
@@ -202,983 +189,4 @@ async function generateCheckoutPage(options) {
|
|
|
202
189
|
console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
|
|
203
190
|
}
|
|
204
191
|
}
|
|
205
|
-
function generateJavaScriptWebhookHandler(events, framework) {
|
|
206
|
-
const eventHandlers = events.map(event => `
|
|
207
|
-
case '${event}':
|
|
208
|
-
console.log('Processing ${event} event:', data);
|
|
209
|
-
// Add your ${event} handling logic here
|
|
210
|
-
break;`).join('');
|
|
211
|
-
switch (framework) {
|
|
212
|
-
case 'express':
|
|
213
|
-
return `const express = require('express');
|
|
214
|
-
const crypto = require('crypto');
|
|
215
|
-
|
|
216
|
-
const app = express();
|
|
217
|
-
app.use(express.json());
|
|
218
|
-
|
|
219
|
-
// Webhook secret from PayMongo dashboard
|
|
220
|
-
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
221
|
-
|
|
222
|
-
function verifySignature(payload, signature, secret) {
|
|
223
|
-
const expectedSignature = crypto
|
|
224
|
-
.createHmac('sha256', secret)
|
|
225
|
-
.update(payload, 'utf8')
|
|
226
|
-
.digest('hex');
|
|
227
|
-
|
|
228
|
-
return signature === \`sha256=\${expectedSignature}\`;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
app.post('/webhooks/paymongo', (req, res) => {
|
|
232
|
-
try {
|
|
233
|
-
const signature = req.headers['paymongo-signature'];
|
|
234
|
-
const payload = JSON.stringify(req.body);
|
|
235
|
-
|
|
236
|
-
// Verify webhook signature (optional but recommended)
|
|
237
|
-
if (WEBHOOK_SECRET && !verifySignature(payload, signature, WEBHOOK_SECRET)) {
|
|
238
|
-
console.log('Invalid signature');
|
|
239
|
-
return res.status(400).json({ error: 'Invalid signature' });
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const { data } = req.body;
|
|
243
|
-
const eventType = data.attributes.type;
|
|
244
|
-
|
|
245
|
-
switch (eventType) {${eventHandlers}
|
|
246
|
-
default:
|
|
247
|
-
console.log('Unhandled event type:', eventType);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
res.json({ received: true });
|
|
251
|
-
} catch (error) {
|
|
252
|
-
console.error('Webhook processing error:', error);
|
|
253
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
const PORT = process.env.PORT || 3000;
|
|
258
|
-
app.listen(PORT, () => {
|
|
259
|
-
console.log(\`Webhook server running on port \${PORT}\`);
|
|
260
|
-
});`;
|
|
261
|
-
case 'fastify':
|
|
262
|
-
return `const fastify = require('fastify')({ logger: true });
|
|
263
|
-
const crypto = require('crypto');
|
|
264
|
-
|
|
265
|
-
// Webhook secret from PayMongo dashboard
|
|
266
|
-
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
267
|
-
|
|
268
|
-
function verifySignature(payload, signature, secret) {
|
|
269
|
-
const expectedSignature = crypto
|
|
270
|
-
.createHmac('sha256', secret)
|
|
271
|
-
.update(payload, 'utf8')
|
|
272
|
-
.digest('hex');
|
|
273
|
-
|
|
274
|
-
return signature === \`sha256=\${expectedSignature}\`;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
fastify.post('/webhooks/paymongo', async (request, reply) => {
|
|
278
|
-
try {
|
|
279
|
-
const signature = request.headers['paymongo-signature'];
|
|
280
|
-
const payload = JSON.stringify(request.body);
|
|
281
|
-
|
|
282
|
-
// Verify webhook signature (optional but recommended)
|
|
283
|
-
if (WEBHOOK_SECRET && !verifySignature(payload, signature, WEBHOOK_SECRET)) {
|
|
284
|
-
console.log('Invalid signature');
|
|
285
|
-
return reply.code(400).send({ error: 'Invalid signature' });
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const { data } = request.body;
|
|
289
|
-
const eventType = data.attributes.type;
|
|
290
|
-
|
|
291
|
-
switch (eventType) {${eventHandlers}
|
|
292
|
-
default:
|
|
293
|
-
console.log('Unhandled event type:', eventType);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return { received: true };
|
|
297
|
-
} catch (error) {
|
|
298
|
-
console.error('Webhook processing error:', error);
|
|
299
|
-
return reply.code(500).send({ error: 'Internal server error' });
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
const start = async () => {
|
|
304
|
-
try {
|
|
305
|
-
await fastify.listen({ port: process.env.PORT || 3000 });
|
|
306
|
-
} catch (err) {
|
|
307
|
-
fastify.log.error(err);
|
|
308
|
-
process.exit(1);
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
start();`;
|
|
313
|
-
default:
|
|
314
|
-
return `// Simple webhook handler for ${events.join(', ')}
|
|
315
|
-
|
|
316
|
-
const crypto = require('crypto');
|
|
317
|
-
|
|
318
|
-
// Webhook secret from PayMongo dashboard
|
|
319
|
-
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
320
|
-
|
|
321
|
-
function verifySignature(payload, signature, secret) {
|
|
322
|
-
const expectedSignature = crypto
|
|
323
|
-
.createHmac('sha256', secret)
|
|
324
|
-
.update(payload, 'utf8')
|
|
325
|
-
.digest('hex');
|
|
326
|
-
|
|
327
|
-
return signature === \`sha256=\${expectedSignature}\`;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function handleWebhook(request, response) {
|
|
331
|
-
try {
|
|
332
|
-
const signature = request.headers['paymongo-signature'];
|
|
333
|
-
const payload = JSON.stringify(request.body);
|
|
334
|
-
|
|
335
|
-
// Verify webhook signature (optional but recommended)
|
|
336
|
-
if (WEBHOOK_SECRET && !verifySignature(payload, signature, WEBHOOK_SECRET)) {
|
|
337
|
-
console.log('Invalid signature');
|
|
338
|
-
response.writeHead(400, { 'Content-Type': 'application/json' });
|
|
339
|
-
response.end(JSON.stringify({ error: 'Invalid signature' }));
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
const { data } = request.body;
|
|
344
|
-
const eventType = data.attributes.type;
|
|
345
|
-
|
|
346
|
-
switch (eventType) {${eventHandlers}
|
|
347
|
-
default:
|
|
348
|
-
console.log('Unhandled event type:', eventType);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
352
|
-
response.end(JSON.stringify({ received: true }));
|
|
353
|
-
} catch (error) {
|
|
354
|
-
console.error('Webhook processing error:', error);
|
|
355
|
-
response.writeHead(500, { 'Content-Type': 'application/json' });
|
|
356
|
-
response.end(JSON.stringify({ error: 'Internal server error' }));
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
module.exports = { handleWebhook };`;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
function generateTypeScriptWebhookHandler(events, framework) {
|
|
364
|
-
const eventHandlers = events.map(event => `
|
|
365
|
-
case '${event}':
|
|
366
|
-
console.log('Processing ${event} event:', data);
|
|
367
|
-
// Add your ${event} handling logic here
|
|
368
|
-
break;`).join('');
|
|
369
|
-
switch (framework) {
|
|
370
|
-
case 'express':
|
|
371
|
-
return `import express, { Request, Response } from 'express';
|
|
372
|
-
import crypto from 'crypto';
|
|
373
|
-
|
|
374
|
-
const app = express();
|
|
375
|
-
app.use(express.json());
|
|
376
|
-
|
|
377
|
-
// Webhook secret from PayMongo dashboard
|
|
378
|
-
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
379
|
-
|
|
380
|
-
interface PayMongoWebhookPayload {
|
|
381
|
-
data: {
|
|
382
|
-
id: string;
|
|
383
|
-
type: string;
|
|
384
|
-
attributes: {
|
|
385
|
-
type: string;
|
|
386
|
-
livemode: boolean;
|
|
387
|
-
created_at: number;
|
|
388
|
-
updated_at: number;
|
|
389
|
-
data: any;
|
|
390
|
-
};
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function verifySignature(payload: string, signature: string, secret: string): boolean {
|
|
395
|
-
const expectedSignature = crypto
|
|
396
|
-
.createHmac('sha256', secret)
|
|
397
|
-
.update(payload, 'utf8')
|
|
398
|
-
.digest('hex');
|
|
399
|
-
|
|
400
|
-
return signature === \`sha256=\${expectedSignature}\`;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
app.post('/webhooks/paymongo', (req: Request, res: Response) => {
|
|
404
|
-
try {
|
|
405
|
-
const signature = req.headers['paymongo-signature'] as string;
|
|
406
|
-
const payload = JSON.stringify(req.body);
|
|
407
|
-
|
|
408
|
-
// Verify webhook signature (optional but recommended)
|
|
409
|
-
if (WEBHOOK_SECRET && !verifySignature(payload, signature, WEBHOOK_SECRET)) {
|
|
410
|
-
console.log('Invalid signature');
|
|
411
|
-
return res.status(400).json({ error: 'Invalid signature' });
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const { data }: PayMongoWebhookPayload = req.body;
|
|
415
|
-
const eventType = data.attributes.type;
|
|
416
|
-
|
|
417
|
-
switch (eventType) {${eventHandlers}
|
|
418
|
-
default:
|
|
419
|
-
console.log('Unhandled event type:', eventType);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
res.json({ received: true });
|
|
423
|
-
} catch (error) {
|
|
424
|
-
console.error('Webhook processing error:', error);
|
|
425
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
const PORT = process.env.PORT || 3000;
|
|
430
|
-
app.listen(PORT, () => {
|
|
431
|
-
console.log(\`Webhook server running on port \${PORT}\`);
|
|
432
|
-
});`;
|
|
433
|
-
default:
|
|
434
|
-
return `// TypeScript webhook handler for ${events.join(', ')}
|
|
435
|
-
|
|
436
|
-
import crypto from 'crypto';
|
|
437
|
-
|
|
438
|
-
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
439
|
-
|
|
440
|
-
interface PayMongoWebhookPayload {
|
|
441
|
-
data: {
|
|
442
|
-
id: string;
|
|
443
|
-
type: string;
|
|
444
|
-
attributes: {
|
|
445
|
-
type: string;
|
|
446
|
-
livemode: boolean;
|
|
447
|
-
created_at: number;
|
|
448
|
-
updated_at: number;
|
|
449
|
-
data: any;
|
|
450
|
-
};
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function verifySignature(payload: string, signature: string, secret: string): boolean {
|
|
455
|
-
const expectedSignature = crypto
|
|
456
|
-
.createHmac('sha256', secret)
|
|
457
|
-
.update(payload, 'utf8')
|
|
458
|
-
.digest('hex');
|
|
459
|
-
|
|
460
|
-
return signature === \`sha256=\${expectedSignature}\`;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
export function handleWebhook(body: PayMongoWebhookPayload, signature?: string): { received: boolean } {
|
|
464
|
-
try {
|
|
465
|
-
const payload = JSON.stringify(body);
|
|
466
|
-
|
|
467
|
-
// Verify webhook signature (optional but recommended)
|
|
468
|
-
if (WEBHOOK_SECRET && signature && !verifySignature(payload, signature, WEBHOOK_SECRET)) {
|
|
469
|
-
console.log('Invalid signature');
|
|
470
|
-
throw new Error('Invalid signature');
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const { data } = body;
|
|
474
|
-
const eventType = data.attributes.type;
|
|
475
|
-
|
|
476
|
-
switch (eventType) {${eventHandlers}
|
|
477
|
-
default:
|
|
478
|
-
console.log('Unhandled event type:', eventType);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
return { received: true };
|
|
482
|
-
} catch (error) {
|
|
483
|
-
console.error('Webhook processing error:', error);
|
|
484
|
-
throw error;
|
|
485
|
-
}
|
|
486
|
-
}`;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
function generateJavaScriptPaymentIntent(methods) {
|
|
490
|
-
return `const axios = require('axios');
|
|
491
|
-
|
|
492
|
-
// PayMongo API credentials
|
|
493
|
-
const PAYMONGO_SECRET_KEY = process.env.PAYMONGO_SECRET_KEY;
|
|
494
|
-
const PAYMONGO_PUBLIC_KEY = process.env.PAYMONGO_PUBLIC_KEY;
|
|
495
|
-
|
|
496
|
-
async function createPaymentIntent(amount, currency = 'PHP', description = '') {
|
|
497
|
-
try {
|
|
498
|
-
const response = await axios.post(
|
|
499
|
-
'https://api.paymongo.com/v1/payment_intents',
|
|
500
|
-
{
|
|
501
|
-
data: {
|
|
502
|
-
attributes: {
|
|
503
|
-
amount: amount, // Amount in centavos (e.g., 10000 = ₱100.00)
|
|
504
|
-
currency: currency,
|
|
505
|
-
description: description,
|
|
506
|
-
payment_method_allowed: ${JSON.stringify(methods)},
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
},
|
|
510
|
-
{
|
|
511
|
-
headers: {
|
|
512
|
-
'Authorization': \`Basic \${Buffer.from(PAYMONGO_SECRET_KEY + ':').toString('base64')}\`,
|
|
513
|
-
'Content-Type': 'application/json',
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
);
|
|
517
|
-
|
|
518
|
-
const paymentIntent = response.data.data;
|
|
519
|
-
|
|
520
|
-
console.log('Payment Intent created:', paymentIntent.id);
|
|
521
|
-
console.log('Client Key:', paymentIntent.attributes.client_key);
|
|
522
|
-
console.log('Amount:', (paymentIntent.attributes.amount / 100).toFixed(2), paymentIntent.attributes.currency);
|
|
523
|
-
|
|
524
|
-
return {
|
|
525
|
-
id: paymentIntent.id,
|
|
526
|
-
clientKey: paymentIntent.attributes.client_key,
|
|
527
|
-
amount: paymentIntent.attributes.amount,
|
|
528
|
-
currency: paymentIntent.attributes.currency,
|
|
529
|
-
status: paymentIntent.attributes.status
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
} catch (error) {
|
|
533
|
-
console.error('Error creating payment intent:', error.response?.data || error.message);
|
|
534
|
-
throw error;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// Example usage
|
|
539
|
-
async function example() {
|
|
540
|
-
try {
|
|
541
|
-
const paymentIntent = await createPaymentIntent(
|
|
542
|
-
10000, // ₱100.00
|
|
543
|
-
'PHP',
|
|
544
|
-
'Sample payment'
|
|
545
|
-
);
|
|
546
|
-
|
|
547
|
-
console.log('Use this client key in your frontend:', paymentIntent.clientKey);
|
|
548
|
-
|
|
549
|
-
} catch (error) {
|
|
550
|
-
console.error('Failed to create payment intent');
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
module.exports = { createPaymentIntent };
|
|
555
|
-
|
|
556
|
-
if (require.main === module) {
|
|
557
|
-
example();
|
|
558
|
-
}`;
|
|
559
|
-
}
|
|
560
|
-
function generateTypeScriptPaymentIntent(methods) {
|
|
561
|
-
return `import axios from 'axios';
|
|
562
|
-
|
|
563
|
-
interface PaymentIntent {
|
|
564
|
-
id: string;
|
|
565
|
-
clientKey: string;
|
|
566
|
-
amount: number;
|
|
567
|
-
currency: string;
|
|
568
|
-
status: string;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
interface PayMongoPaymentIntentResponse {
|
|
572
|
-
data: {
|
|
573
|
-
id: string;
|
|
574
|
-
attributes: {
|
|
575
|
-
amount: number;
|
|
576
|
-
currency: string;
|
|
577
|
-
description: string;
|
|
578
|
-
status: string;
|
|
579
|
-
client_key: string;
|
|
580
|
-
payment_method_allowed: string[];
|
|
581
|
-
};
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// PayMongo API credentials
|
|
586
|
-
const PAYMONGO_SECRET_KEY = process.env.PAYMONGO_SECRET_KEY!;
|
|
587
|
-
const PAYMONGO_PUBLIC_KEY = process.env.PAYMONGO_PUBLIC_KEY!;
|
|
588
|
-
|
|
589
|
-
export async function createPaymentIntent(
|
|
590
|
-
amount: number,
|
|
591
|
-
currency: string = 'PHP',
|
|
592
|
-
description: string = ''
|
|
593
|
-
): Promise<PaymentIntent> {
|
|
594
|
-
try {
|
|
595
|
-
const response = await axios.post<PayMongoPaymentIntentResponse>(
|
|
596
|
-
'https://api.paymongo.com/v1/payment_intents',
|
|
597
|
-
{
|
|
598
|
-
data: {
|
|
599
|
-
attributes: {
|
|
600
|
-
amount: amount, // Amount in centavos (e.g., 10000 = ₱100.00)
|
|
601
|
-
currency: currency,
|
|
602
|
-
description: description,
|
|
603
|
-
payment_method_allowed: ${JSON.stringify(methods)},
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
},
|
|
607
|
-
{
|
|
608
|
-
headers: {
|
|
609
|
-
'Authorization': \`Basic \${Buffer.from(PAYMONGO_SECRET_KEY + ':').toString('base64')}\`,
|
|
610
|
-
'Content-Type': 'application/json',
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
);
|
|
614
|
-
|
|
615
|
-
const paymentIntent = response.data.data;
|
|
616
|
-
|
|
617
|
-
console.log('Payment Intent created:', paymentIntent.id);
|
|
618
|
-
console.log('Client Key:', paymentIntent.attributes.client_key);
|
|
619
|
-
console.log('Amount:', (paymentIntent.attributes.amount / 100).toFixed(2), paymentIntent.attributes.currency);
|
|
620
|
-
|
|
621
|
-
return {
|
|
622
|
-
id: paymentIntent.id,
|
|
623
|
-
clientKey: paymentIntent.attributes.client_key,
|
|
624
|
-
amount: paymentIntent.attributes.amount,
|
|
625
|
-
currency: paymentIntent.attributes.currency,
|
|
626
|
-
status: paymentIntent.attributes.status
|
|
627
|
-
};
|
|
628
|
-
|
|
629
|
-
} catch (error: any) {
|
|
630
|
-
console.error('Error creating payment intent:', error.response?.data || error.message);
|
|
631
|
-
throw error;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
// Example usage
|
|
636
|
-
async function example(): Promise<void> {
|
|
637
|
-
try {
|
|
638
|
-
const paymentIntent = await createPaymentIntent(
|
|
639
|
-
10000, // ₱100.00
|
|
640
|
-
'PHP',
|
|
641
|
-
'Sample payment'
|
|
642
|
-
);
|
|
643
|
-
|
|
644
|
-
console.log('Use this client key in your frontend:', paymentIntent.clientKey);
|
|
645
|
-
|
|
646
|
-
} catch (error) {
|
|
647
|
-
console.error('Failed to create payment intent');
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
if (require.main === module) {
|
|
652
|
-
example();
|
|
653
|
-
}`;
|
|
654
|
-
}
|
|
655
|
-
function generateHTMLCheckoutPage() {
|
|
656
|
-
return `<!DOCTYPE html>
|
|
657
|
-
<html lang="en">
|
|
658
|
-
<head>
|
|
659
|
-
<meta charset="UTF-8">
|
|
660
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
661
|
-
<title>PayMongo Checkout</title>
|
|
662
|
-
<script src="https://js.paymongo.com/v1/paymongo.js"></script>
|
|
663
|
-
<style>
|
|
664
|
-
body {
|
|
665
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
666
|
-
max-width: 400px;
|
|
667
|
-
margin: 50px auto;
|
|
668
|
-
padding: 20px;
|
|
669
|
-
}
|
|
670
|
-
.checkout-form {
|
|
671
|
-
background: #f9f9f9;
|
|
672
|
-
padding: 20px;
|
|
673
|
-
border-radius: 8px;
|
|
674
|
-
}
|
|
675
|
-
.form-group {
|
|
676
|
-
margin-bottom: 15px;
|
|
677
|
-
}
|
|
678
|
-
label {
|
|
679
|
-
display: block;
|
|
680
|
-
margin-bottom: 5px;
|
|
681
|
-
font-weight: 500;
|
|
682
|
-
}
|
|
683
|
-
input, select {
|
|
684
|
-
width: 100%;
|
|
685
|
-
padding: 10px;
|
|
686
|
-
border: 1px solid #ddd;
|
|
687
|
-
border-radius: 4px;
|
|
688
|
-
font-size: 16px;
|
|
689
|
-
}
|
|
690
|
-
button {
|
|
691
|
-
width: 100%;
|
|
692
|
-
padding: 12px;
|
|
693
|
-
background: #007bff;
|
|
694
|
-
color: white;
|
|
695
|
-
border: none;
|
|
696
|
-
border-radius: 4px;
|
|
697
|
-
font-size: 16px;
|
|
698
|
-
cursor: pointer;
|
|
699
|
-
}
|
|
700
|
-
button:hover {
|
|
701
|
-
background: #0056b3;
|
|
702
|
-
}
|
|
703
|
-
button:disabled {
|
|
704
|
-
background: #ccc;
|
|
705
|
-
cursor: not-allowed;
|
|
706
|
-
}
|
|
707
|
-
</style>
|
|
708
|
-
</head>
|
|
709
|
-
<body>
|
|
710
|
-
<div class="checkout-form">
|
|
711
|
-
<h2>Complete Your Payment</h2>
|
|
712
|
-
<form id="payment-form">
|
|
713
|
-
<div class="form-group">
|
|
714
|
-
<label for="email">Email</label>
|
|
715
|
-
<input type="email" id="email" required>
|
|
716
|
-
</div>
|
|
717
|
-
|
|
718
|
-
<div class="form-group">
|
|
719
|
-
<label for="card-number">Card Number</label>
|
|
720
|
-
<input type="text" id="card-number" placeholder="1234 5678 9012 3456" required>
|
|
721
|
-
</div>
|
|
722
|
-
|
|
723
|
-
<div class="form-group">
|
|
724
|
-
<label for="expiry">Expiry Date</label>
|
|
725
|
-
<input type="text" id="expiry" placeholder="MM/YY" required>
|
|
726
|
-
</div>
|
|
727
|
-
|
|
728
|
-
<div class="form-group">
|
|
729
|
-
<label for="cvc">CVC</label>
|
|
730
|
-
<input type="text" id="cvc" placeholder="123" required>
|
|
731
|
-
</div>
|
|
732
|
-
|
|
733
|
-
<button type="submit" id="pay-button">Pay ₱100.00</button>
|
|
734
|
-
</form>
|
|
735
|
-
</div>
|
|
736
|
-
|
|
737
|
-
<script>
|
|
738
|
-
// Replace with your actual client key from the payment intent
|
|
739
|
-
const clientKey = 'YOUR_CLIENT_KEY_HERE';
|
|
740
|
-
|
|
741
|
-
const paymongo = new Paymongo(clientKey);
|
|
742
|
-
|
|
743
|
-
document.getElementById('payment-form').addEventListener('submit', async (e) => {
|
|
744
|
-
e.preventDefault();
|
|
745
|
-
|
|
746
|
-
const payButton = document.getElementById('pay-button');
|
|
747
|
-
payButton.disabled = true;
|
|
748
|
-
payButton.textContent = 'Processing...';
|
|
749
|
-
|
|
750
|
-
try {
|
|
751
|
-
// Create payment method
|
|
752
|
-
const paymentMethod = await paymongo.createPaymentMethod({
|
|
753
|
-
type: 'card',
|
|
754
|
-
details: {
|
|
755
|
-
card_number: document.getElementById('card-number').value.replace(/\\s/g, ''),
|
|
756
|
-
exp_month: document.getElementById('expiry').value.split('/')[0],
|
|
757
|
-
exp_year: '20' + document.getElementById('expiry').value.split('/')[1],
|
|
758
|
-
cvc: document.getElementById('cvc').value,
|
|
759
|
-
},
|
|
760
|
-
billing: {
|
|
761
|
-
email: document.getElementById('email').value,
|
|
762
|
-
},
|
|
763
|
-
});
|
|
764
|
-
|
|
765
|
-
// Attach payment method to payment intent
|
|
766
|
-
const result = await paymongo.attachPaymentIntent('YOUR_PAYMENT_INTENT_ID', {
|
|
767
|
-
payment_method: paymentMethod.id,
|
|
768
|
-
return_url: window.location.origin + '/success',
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
if (result.next_action) {
|
|
772
|
-
// Handle 3D Secure or other next actions
|
|
773
|
-
window.location.href = result.next_action.redirect.url;
|
|
774
|
-
} else {
|
|
775
|
-
// Payment succeeded
|
|
776
|
-
window.location.href = '/success';
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
} catch (error) {
|
|
780
|
-
console.error('Payment failed:', error);
|
|
781
|
-
alert('Payment failed. Please try again.');
|
|
782
|
-
payButton.disabled = false;
|
|
783
|
-
payButton.textContent = 'Pay ₱100.00';
|
|
784
|
-
}
|
|
785
|
-
});
|
|
786
|
-
</script>
|
|
787
|
-
</body>
|
|
788
|
-
</html>`;
|
|
789
|
-
}
|
|
790
|
-
function generateReactCheckoutPage() {
|
|
791
|
-
return `import React, { useState } from 'react';
|
|
792
|
-
|
|
793
|
-
interface CheckoutFormProps {
|
|
794
|
-
clientKey: string;
|
|
795
|
-
paymentIntentId: string;
|
|
796
|
-
amount: number;
|
|
797
|
-
onSuccess: (result: any) => void;
|
|
798
|
-
onError: (error: any) => void;
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
const CheckoutForm: React.FC<CheckoutFormProps> = ({
|
|
802
|
-
clientKey,
|
|
803
|
-
paymentIntentId,
|
|
804
|
-
amount,
|
|
805
|
-
onSuccess,
|
|
806
|
-
onError
|
|
807
|
-
}) => {
|
|
808
|
-
const [loading, setLoading] = useState(false);
|
|
809
|
-
const [formData, setFormData] = useState({
|
|
810
|
-
email: '',
|
|
811
|
-
cardNumber: '',
|
|
812
|
-
expiry: '',
|
|
813
|
-
cvc: ''
|
|
814
|
-
});
|
|
815
|
-
|
|
816
|
-
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
817
|
-
const { name, value } = e.target;
|
|
818
|
-
setFormData(prev => ({
|
|
819
|
-
...prev,
|
|
820
|
-
[name]: value
|
|
821
|
-
}));
|
|
822
|
-
};
|
|
823
|
-
|
|
824
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
825
|
-
e.preventDefault();
|
|
826
|
-
setLoading(true);
|
|
827
|
-
|
|
828
|
-
try {
|
|
829
|
-
// Load PayMongo script dynamically if not already loaded
|
|
830
|
-
if (!window.Paymongo) {
|
|
831
|
-
await new Promise((resolve, reject) => {
|
|
832
|
-
const script = document.createElement('script');
|
|
833
|
-
script.src = 'https://js.paymongo.com/v1/paymongo.js';
|
|
834
|
-
script.onload = resolve;
|
|
835
|
-
script.onerror = reject;
|
|
836
|
-
document.head.appendChild(script);
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
const paymongo = new (window as any).Paymongo(clientKey);
|
|
841
|
-
|
|
842
|
-
// Create payment method
|
|
843
|
-
const paymentMethod = await paymongo.createPaymentMethod({
|
|
844
|
-
type: 'card',
|
|
845
|
-
details: {
|
|
846
|
-
card_number: formData.cardNumber.replace(/\\s/g, ''),
|
|
847
|
-
exp_month: parseInt(formData.expiry.split('/')[0]),
|
|
848
|
-
exp_year: 2000 + parseInt(formData.expiry.split('/')[1]),
|
|
849
|
-
cvc: formData.cvc,
|
|
850
|
-
},
|
|
851
|
-
billing: {
|
|
852
|
-
email: formData.email,
|
|
853
|
-
},
|
|
854
|
-
});
|
|
855
|
-
|
|
856
|
-
// Attach payment method to payment intent
|
|
857
|
-
const result = await paymongo.attachPaymentIntent(paymentIntentId, {
|
|
858
|
-
payment_method: paymentMethod.id,
|
|
859
|
-
return_url: window.location.origin + '/success',
|
|
860
|
-
});
|
|
861
|
-
|
|
862
|
-
onSuccess(result);
|
|
863
|
-
|
|
864
|
-
} catch (error) {
|
|
865
|
-
onError(error);
|
|
866
|
-
} finally {
|
|
867
|
-
setLoading(false);
|
|
868
|
-
}
|
|
869
|
-
};
|
|
870
|
-
|
|
871
|
-
return (
|
|
872
|
-
<div style={{ maxWidth: '400px', margin: '50px auto', padding: '20px' }}>
|
|
873
|
-
<div style={{
|
|
874
|
-
background: '#f9f9f9',
|
|
875
|
-
padding: '20px',
|
|
876
|
-
borderRadius: '8px'
|
|
877
|
-
}}>
|
|
878
|
-
<h2>Complete Your Payment</h2>
|
|
879
|
-
<form onSubmit={handleSubmit}>
|
|
880
|
-
<div style={{ marginBottom: '15px' }}>
|
|
881
|
-
<label style={{ display: 'block', marginBottom: '5px', fontWeight: '500' }}>
|
|
882
|
-
Email
|
|
883
|
-
</label>
|
|
884
|
-
<input
|
|
885
|
-
type="email"
|
|
886
|
-
name="email"
|
|
887
|
-
value={formData.email}
|
|
888
|
-
onChange={handleInputChange}
|
|
889
|
-
required
|
|
890
|
-
style={{
|
|
891
|
-
width: '100%',
|
|
892
|
-
padding: '10px',
|
|
893
|
-
border: '1px solid #ddd',
|
|
894
|
-
borderRadius: '4px',
|
|
895
|
-
fontSize: '16px'
|
|
896
|
-
}}
|
|
897
|
-
/>
|
|
898
|
-
</div>
|
|
899
|
-
|
|
900
|
-
<div style={{ marginBottom: '15px' }}>
|
|
901
|
-
<label style={{ display: 'block', marginBottom: '5px', fontWeight: '500' }}>
|
|
902
|
-
Card Number
|
|
903
|
-
</label>
|
|
904
|
-
<input
|
|
905
|
-
type="text"
|
|
906
|
-
name="cardNumber"
|
|
907
|
-
value={formData.cardNumber}
|
|
908
|
-
onChange={handleInputChange}
|
|
909
|
-
placeholder="1234 5678 9012 3456"
|
|
910
|
-
required
|
|
911
|
-
style={{
|
|
912
|
-
width: '100%',
|
|
913
|
-
padding: '10px',
|
|
914
|
-
border: '1px solid #ddd',
|
|
915
|
-
borderRadius: '4px',
|
|
916
|
-
fontSize: '16px'
|
|
917
|
-
}}
|
|
918
|
-
/>
|
|
919
|
-
</div>
|
|
920
|
-
|
|
921
|
-
<div style={{ marginBottom: '15px' }}>
|
|
922
|
-
<label style={{ display: 'block', marginBottom: '5px', fontWeight: '500' }}>
|
|
923
|
-
Expiry Date
|
|
924
|
-
</label>
|
|
925
|
-
<input
|
|
926
|
-
type="text"
|
|
927
|
-
name="expiry"
|
|
928
|
-
value={formData.expiry}
|
|
929
|
-
onChange={handleInputChange}
|
|
930
|
-
placeholder="MM/YY"
|
|
931
|
-
required
|
|
932
|
-
style={{
|
|
933
|
-
width: '100%',
|
|
934
|
-
padding: '10px',
|
|
935
|
-
border: '1px solid #ddd',
|
|
936
|
-
borderRadius: '4px',
|
|
937
|
-
fontSize: '16px'
|
|
938
|
-
}}
|
|
939
|
-
/>
|
|
940
|
-
</div>
|
|
941
|
-
|
|
942
|
-
<div style={{ marginBottom: '15px' }}>
|
|
943
|
-
<label style={{ display: 'block', marginBottom: '5px', fontWeight: '500' }}>
|
|
944
|
-
CVC
|
|
945
|
-
</label>
|
|
946
|
-
<input
|
|
947
|
-
type="text"
|
|
948
|
-
name="cvc"
|
|
949
|
-
value={formData.cvc}
|
|
950
|
-
onChange={handleInputChange}
|
|
951
|
-
placeholder="123"
|
|
952
|
-
required
|
|
953
|
-
style={{
|
|
954
|
-
width: '100%',
|
|
955
|
-
padding: '10px',
|
|
956
|
-
border: '1px solid #ddd',
|
|
957
|
-
borderRadius: '4px',
|
|
958
|
-
fontSize: '16px'
|
|
959
|
-
}}
|
|
960
|
-
/>
|
|
961
|
-
</div>
|
|
962
|
-
|
|
963
|
-
<button
|
|
964
|
-
type="submit"
|
|
965
|
-
disabled={loading}
|
|
966
|
-
style={{
|
|
967
|
-
width: '100%',
|
|
968
|
-
padding: '12px',
|
|
969
|
-
background: loading ? '#ccc' : '#007bff',
|
|
970
|
-
color: 'white',
|
|
971
|
-
border: 'none',
|
|
972
|
-
borderRadius: '4px',
|
|
973
|
-
fontSize: '16px',
|
|
974
|
-
cursor: loading ? 'not-allowed' : 'pointer'
|
|
975
|
-
}}
|
|
976
|
-
>
|
|
977
|
-
{loading ? 'Processing...' : \`Pay ₱\${(amount / 100).toFixed(2)}\`}
|
|
978
|
-
</button>
|
|
979
|
-
</form>
|
|
980
|
-
</div>
|
|
981
|
-
</div>
|
|
982
|
-
);
|
|
983
|
-
};
|
|
984
|
-
|
|
985
|
-
export default CheckoutForm;`;
|
|
986
|
-
}
|
|
987
|
-
function generateVueCheckoutPage() {
|
|
988
|
-
return `<template>
|
|
989
|
-
<div class="checkout-container">
|
|
990
|
-
<div class="checkout-form">
|
|
991
|
-
<h2>Complete Your Payment</h2>
|
|
992
|
-
<form @submit.prevent="handleSubmit">
|
|
993
|
-
<div class="form-group">
|
|
994
|
-
<label for="email">Email</label>
|
|
995
|
-
<input
|
|
996
|
-
v-model="formData.email"
|
|
997
|
-
type="email"
|
|
998
|
-
id="email"
|
|
999
|
-
required
|
|
1000
|
-
>
|
|
1001
|
-
</div>
|
|
1002
|
-
|
|
1003
|
-
<div class="form-group">
|
|
1004
|
-
<label for="cardNumber">Card Number</label>
|
|
1005
|
-
<input
|
|
1006
|
-
v-model="formData.cardNumber"
|
|
1007
|
-
type="text"
|
|
1008
|
-
id="cardNumber"
|
|
1009
|
-
placeholder="1234 5678 9012 3456"
|
|
1010
|
-
required
|
|
1011
|
-
>
|
|
1012
|
-
</div>
|
|
1013
|
-
|
|
1014
|
-
<div class="form-group">
|
|
1015
|
-
<label for="expiry">Expiry Date</label>
|
|
1016
|
-
<input
|
|
1017
|
-
v-model="formData.expiry"
|
|
1018
|
-
type="text"
|
|
1019
|
-
id="expiry"
|
|
1020
|
-
placeholder="MM/YY"
|
|
1021
|
-
required
|
|
1022
|
-
>
|
|
1023
|
-
</div>
|
|
1024
|
-
|
|
1025
|
-
<div class="form-group">
|
|
1026
|
-
<label for="cvc">CVC</label>
|
|
1027
|
-
<input
|
|
1028
|
-
v-model="formData.cvc"
|
|
1029
|
-
type="text"
|
|
1030
|
-
id="cvc"
|
|
1031
|
-
placeholder="123"
|
|
1032
|
-
required
|
|
1033
|
-
>
|
|
1034
|
-
</div>
|
|
1035
|
-
|
|
1036
|
-
<button
|
|
1037
|
-
type="submit"
|
|
1038
|
-
:disabled="loading"
|
|
1039
|
-
class="pay-button"
|
|
1040
|
-
>
|
|
1041
|
-
{{ loading ? 'Processing...' : \`Pay ₱\${(amount / 100).toFixed(2)}\` }}
|
|
1042
|
-
</button>
|
|
1043
|
-
</form>
|
|
1044
|
-
</div>
|
|
1045
|
-
</div>
|
|
1046
|
-
</template>
|
|
1047
|
-
|
|
1048
|
-
<script>
|
|
1049
|
-
export default {
|
|
1050
|
-
name: 'CheckoutForm',
|
|
1051
|
-
props: {
|
|
1052
|
-
clientKey: {
|
|
1053
|
-
type: String,
|
|
1054
|
-
required: true
|
|
1055
|
-
},
|
|
1056
|
-
paymentIntentId: {
|
|
1057
|
-
type: String,
|
|
1058
|
-
required: true
|
|
1059
|
-
},
|
|
1060
|
-
amount: {
|
|
1061
|
-
type: Number,
|
|
1062
|
-
required: true
|
|
1063
|
-
}
|
|
1064
|
-
},
|
|
1065
|
-
data() {
|
|
1066
|
-
return {
|
|
1067
|
-
loading: false,
|
|
1068
|
-
formData: {
|
|
1069
|
-
email: '',
|
|
1070
|
-
cardNumber: '',
|
|
1071
|
-
expiry: '',
|
|
1072
|
-
cvc: ''
|
|
1073
|
-
}
|
|
1074
|
-
};
|
|
1075
|
-
},
|
|
1076
|
-
methods: {
|
|
1077
|
-
async handleSubmit() {
|
|
1078
|
-
this.loading = true;
|
|
1079
|
-
|
|
1080
|
-
try {
|
|
1081
|
-
// Load PayMongo script if not loaded
|
|
1082
|
-
if (!window.Paymongo) {
|
|
1083
|
-
await this.loadPayMongoScript();
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
const paymongo = new window.Paymongo(this.clientKey);
|
|
1087
|
-
|
|
1088
|
-
// Create payment method
|
|
1089
|
-
const paymentMethod = await paymongo.createPaymentMethod({
|
|
1090
|
-
type: 'card',
|
|
1091
|
-
details: {
|
|
1092
|
-
card_number: this.formData.cardNumber.replace(/\\s/g, ''),
|
|
1093
|
-
exp_month: parseInt(this.formData.expiry.split('/')[0]),
|
|
1094
|
-
exp_year: 2000 + parseInt(this.formData.expiry.split('/')[1]),
|
|
1095
|
-
cvc: this.formData.cvc,
|
|
1096
|
-
},
|
|
1097
|
-
billing: {
|
|
1098
|
-
email: this.formData.email,
|
|
1099
|
-
},
|
|
1100
|
-
});
|
|
1101
|
-
|
|
1102
|
-
// Attach payment method to payment intent
|
|
1103
|
-
const result = await paymongo.attachPaymentIntent(this.paymentIntentId, {
|
|
1104
|
-
payment_method: paymentMethod.id,
|
|
1105
|
-
return_url: window.location.origin + '/success',
|
|
1106
|
-
});
|
|
1107
|
-
|
|
1108
|
-
this.$emit('success', result);
|
|
1109
|
-
|
|
1110
|
-
} catch (error) {
|
|
1111
|
-
console.error('Payment failed:', error);
|
|
1112
|
-
this.$emit('error', error);
|
|
1113
|
-
} finally {
|
|
1114
|
-
this.loading = false;
|
|
1115
|
-
}
|
|
1116
|
-
},
|
|
1117
|
-
|
|
1118
|
-
loadPayMongoScript() {
|
|
1119
|
-
return new Promise((resolve, reject) => {
|
|
1120
|
-
const script = document.createElement('script');
|
|
1121
|
-
script.src = 'https://js.paymongo.com/v1/paymongo.js';
|
|
1122
|
-
script.onload = resolve;
|
|
1123
|
-
script.onerror = reject;
|
|
1124
|
-
document.head.appendChild(script);
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
};
|
|
1129
|
-
</script>
|
|
1130
|
-
|
|
1131
|
-
<style scoped>
|
|
1132
|
-
.checkout-container {
|
|
1133
|
-
max-width: 400px;
|
|
1134
|
-
margin: 50px auto;
|
|
1135
|
-
padding: 20px;
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
.checkout-form {
|
|
1139
|
-
background: #f9f9f9;
|
|
1140
|
-
padding: 20px;
|
|
1141
|
-
border-radius: 8px;
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
.form-group {
|
|
1145
|
-
margin-bottom: 15px;
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
label {
|
|
1149
|
-
display: block;
|
|
1150
|
-
margin-bottom: 5px;
|
|
1151
|
-
font-weight: 500;
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
input {
|
|
1155
|
-
width: 100%;
|
|
1156
|
-
padding: 10px;
|
|
1157
|
-
border: 1px solid #ddd;
|
|
1158
|
-
border-radius: 4px;
|
|
1159
|
-
font-size: 16px;
|
|
1160
|
-
box-sizing: border-box;
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
.pay-button {
|
|
1164
|
-
width: 100%;
|
|
1165
|
-
padding: 12px;
|
|
1166
|
-
background: #007bff;
|
|
1167
|
-
color: white;
|
|
1168
|
-
border: none;
|
|
1169
|
-
border-radius: 4px;
|
|
1170
|
-
font-size: 16px;
|
|
1171
|
-
cursor: pointer;
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
.pay-button:hover:not(:disabled) {
|
|
1175
|
-
background: #0056b3;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
.pay-button:disabled {
|
|
1179
|
-
background: #ccc;
|
|
1180
|
-
cursor: not-allowed;
|
|
1181
|
-
}
|
|
1182
|
-
</style>`;
|
|
1183
|
-
}
|
|
1184
192
|
export default command;
|