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.
@@ -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 = generateTypeScriptWebhookHandler(events, options.framework);
108
+ code = getTypeScriptWebhookHandler(events, options.framework);
108
109
  }
109
110
  else {
110
- code = generateJavaScriptWebhookHandler(events, options.framework);
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 = generateTypeScriptPaymentIntent(methods);
144
+ code = getTypeScriptPaymentIntent(methods);
144
145
  }
145
146
  else {
146
- code = generateJavaScriptPaymentIntent(methods);
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
- let code;
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.${fileExtension}`;
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;