paymongo-cli 1.4.4 → 1.4.7
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/.github/copilot-instructions.md +95 -95
- package/CHANGELOG.md +85 -1
- package/LICENSE +20 -20
- package/dist/.tsbuildinfo +1 -1
- package/dist/commands/config.js +30 -15
- package/dist/commands/dev/logs.js +3 -3
- package/dist/commands/dev/status.js +2 -2
- package/dist/commands/dev/stop.js +3 -3
- package/dist/commands/dev.js +9 -8
- package/dist/commands/env.js +6 -6
- package/dist/commands/generate/templates/checkout-page/index.js +520 -520
- package/dist/commands/generate/templates/payment-intent/javascript.js +68 -68
- package/dist/commands/generate/templates/payment-intent/typescript.js +92 -92
- package/dist/commands/generate/templates/webhook-handler/javascript.js +192 -147
- package/dist/commands/generate/templates/webhook-handler/typescript.js +147 -117
- package/dist/commands/generate.js +43 -37
- package/dist/commands/init.js +25 -8
- package/dist/commands/login.js +56 -19
- package/dist/commands/payments.js +9 -8
- package/dist/commands/team/index.js +11 -9
- package/dist/commands/trigger.js +58 -18
- package/dist/commands/webhooks.js +8 -7
- package/dist/index.js +9 -2
- package/dist/services/analytics/service.js +24 -19
- package/dist/services/api/client.js +16 -16
- package/dist/services/config/manager.js +6 -8
- package/dist/services/dev/process-manager.js +30 -32
- package/dist/services/dev/server.js +45 -39
- package/dist/services/team/service.js +4 -1
- package/dist/types/schemas.js +38 -9
- package/dist/utils/bulk.js +36 -4
- package/dist/utils/constants.js +11 -1
- package/dist/utils/errors.js +6 -0
- package/dist/utils/validator.js +10 -9
- package/dist/utils/webhook-store.js +18 -15
- package/eslint.config.ts +70 -70
- package/package.json +2 -2
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -281
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -281
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov.info +0 -5053
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/dist/commands/config.d.ts +0 -21
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/dev.d.ts +0 -16
- package/dist/commands/dev.d.ts.map +0 -1
- package/dist/commands/dev.js.map +0 -1
- package/dist/commands/env.d.ts +0 -4
- package/dist/commands/env.d.ts.map +0 -1
- package/dist/commands/env.js.map +0 -1
- package/dist/commands/init.d.ts +0 -15
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/login.d.ts +0 -20
- package/dist/commands/login.d.ts.map +0 -1
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/payments.d.ts +0 -41
- package/dist/commands/payments.d.ts.map +0 -1
- package/dist/commands/payments.js.map +0 -1
- package/dist/commands/team/index.d.ts +0 -4
- package/dist/commands/team/index.d.ts.map +0 -1
- package/dist/commands/team/index.js.map +0 -1
- package/dist/commands/trigger.d.ts +0 -4
- package/dist/commands/trigger.d.ts.map +0 -1
- package/dist/commands/trigger.js.map +0 -1
- package/dist/commands/webhooks.d.ts +0 -23
- package/dist/commands/webhooks.d.ts.map +0 -1
- package/dist/commands/webhooks.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/services/analytics/service.d.ts +0 -35
- package/dist/services/analytics/service.d.ts.map +0 -1
- package/dist/services/analytics/service.js.map +0 -1
- package/dist/services/api/client.d.ts +0 -26
- package/dist/services/api/client.d.ts.map +0 -1
- package/dist/services/api/client.js.map +0 -1
- package/dist/services/api/rate-limiter.d.ts +0 -64
- package/dist/services/api/rate-limiter.d.ts.map +0 -1
- package/dist/services/api/rate-limiter.js.map +0 -1
- package/dist/services/api/undici-client.d.ts +0 -39
- package/dist/services/api/undici-client.d.ts.map +0 -1
- package/dist/services/api/undici-client.js +0 -288
- package/dist/services/api/undici-client.js.map +0 -1
- package/dist/services/config/manager.d.ts +0 -16
- package/dist/services/config/manager.d.ts.map +0 -1
- package/dist/services/config/manager.js.map +0 -1
- package/dist/services/dev/process-manager.d.ts +0 -50
- package/dist/services/dev/process-manager.d.ts.map +0 -1
- package/dist/services/dev/process-manager.js.map +0 -1
- package/dist/services/github/auth.d.ts +0 -15
- package/dist/services/github/auth.d.ts.map +0 -1
- package/dist/services/github/auth.js +0 -79
- package/dist/services/github/auth.js.map +0 -1
- package/dist/services/github/client.d.ts +0 -95
- package/dist/services/github/client.d.ts.map +0 -1
- package/dist/services/github/client.js +0 -130
- package/dist/services/github/client.js.map +0 -1
- package/dist/services/github/sync.d.ts +0 -26
- package/dist/services/github/sync.d.ts.map +0 -1
- package/dist/services/github/sync.js +0 -203
- package/dist/services/github/sync.js.map +0 -1
- package/dist/services/payments/simulator.d.ts +0 -28
- package/dist/services/payments/simulator.d.ts.map +0 -1
- package/dist/services/payments/simulator.js.map +0 -1
- package/dist/services/team/service.d.ts +0 -44
- package/dist/services/team/service.d.ts.map +0 -1
- package/dist/services/team/service.js.map +0 -1
- package/dist/services/web/server.d.ts +0 -31
- package/dist/services/web/server.d.ts.map +0 -1
- package/dist/services/web/server.js +0 -206
- package/dist/services/web/server.js.map +0 -1
- package/dist/types/paymongo.d.ts +0 -204
- package/dist/types/paymongo.d.ts.map +0 -1
- package/dist/types/paymongo.js.map +0 -1
- package/dist/types/schemas.d.ts +0 -80
- package/dist/types/schemas.d.ts.map +0 -1
- package/dist/types/schemas.js.map +0 -1
- package/dist/utils/bulk.d.ts +0 -62
- package/dist/utils/bulk.d.ts.map +0 -1
- package/dist/utils/bulk.js.map +0 -1
- package/dist/utils/cache.d.ts +0 -22
- package/dist/utils/cache.d.ts.map +0 -1
- package/dist/utils/cache.js.map +0 -1
- package/dist/utils/constants.d.ts +0 -32
- package/dist/utils/constants.d.ts.map +0 -1
- package/dist/utils/constants.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -34
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -20
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/spinner.d.ts +0 -17
- package/dist/utils/spinner.d.ts.map +0 -1
- package/dist/utils/spinner.js.map +0 -1
- package/dist/utils/validator.d.ts +0 -10
- package/dist/utils/validator.d.ts.map +0 -1
- package/dist/utils/validator.js.map +0 -1
- package/dist/utils/webhook-store.d.ts +0 -22
- package/dist/utils/webhook-store.d.ts.map +0 -1
- package/dist/utils/webhook-store.js.map +0 -1
|
@@ -1,165 +1,210 @@
|
|
|
1
1
|
function generateEventHandlers(events) {
|
|
2
2
|
return events
|
|
3
|
-
.map((event) => `
|
|
4
|
-
case '${event}':
|
|
5
|
-
console.log('Processing ${event} event:', data);
|
|
6
|
-
// Add your ${event} handling logic here
|
|
3
|
+
.map((event) => `
|
|
4
|
+
case '${event}':
|
|
5
|
+
console.log('Processing ${event} event:', data);
|
|
6
|
+
// Add your ${event} handling logic here
|
|
7
7
|
break;`)
|
|
8
8
|
.join('');
|
|
9
9
|
}
|
|
10
10
|
export function expressTemplate(events) {
|
|
11
11
|
const eventHandlers = generateEventHandlers(events);
|
|
12
|
-
return `const express = require('express');
|
|
13
|
-
const crypto = require('crypto');
|
|
14
|
-
|
|
15
|
-
const app = express();
|
|
16
|
-
app.use(express.json());
|
|
17
|
-
|
|
18
|
-
// Webhook secret from PayMongo dashboard
|
|
19
|
-
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
20
|
-
|
|
21
|
-
function verifySignature(payload,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
12
|
+
return `const express = require('express');
|
|
13
|
+
const crypto = require('crypto');
|
|
14
|
+
|
|
15
|
+
const app = express();
|
|
16
|
+
app.use(express.json());
|
|
17
|
+
|
|
18
|
+
// Webhook secret from PayMongo dashboard
|
|
19
|
+
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
20
|
+
|
|
21
|
+
function verifySignature(payload, signatureHeader, secret) {
|
|
22
|
+
if (!signatureHeader) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const parts = signatureHeader.split(',');
|
|
27
|
+
const timestamp = parts.find((part) => part.startsWith('t='))?.split('=')[1];
|
|
28
|
+
const signature = parts.find((part) => part.startsWith('te='))?.split('=')[1];
|
|
29
|
+
|
|
30
|
+
if (!timestamp || !signature) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const expectedSignature = crypto
|
|
35
|
+
.createHmac('sha256', secret)
|
|
36
|
+
.update(timestamp + '.' + payload, 'utf8')
|
|
37
|
+
.digest('hex');
|
|
38
|
+
|
|
39
|
+
return crypto.timingSafeEqual(
|
|
40
|
+
Buffer.from(signature, 'hex'),
|
|
41
|
+
Buffer.from(expectedSignature, 'hex')
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
app.post('/webhooks/paymongo', (req, res) => {
|
|
46
|
+
try {
|
|
47
|
+
const signature = req.headers['paymongo-signature'];
|
|
48
|
+
const payload = JSON.stringify(req.body);
|
|
49
|
+
|
|
50
|
+
// Verify webhook signature (optional but recommended)
|
|
51
|
+
if (WEBHOOK_SECRET && !verifySignature(payload, signature, WEBHOOK_SECRET)) {
|
|
52
|
+
console.log('Invalid signature');
|
|
53
|
+
return res.status(400).json({ error: 'Invalid signature' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { data } = req.body;
|
|
57
|
+
const eventType = data.attributes.type;
|
|
58
|
+
|
|
59
|
+
switch (eventType) {${eventHandlers}
|
|
60
|
+
default:
|
|
61
|
+
console.log('Unhandled event type:', eventType);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
res.json({ received: true });
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('Webhook processing error:', error);
|
|
67
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const PORT = process.env.PORT || 3000;
|
|
72
|
+
app.listen(PORT, () => {
|
|
73
|
+
console.log(\`Webhook server running on port \${PORT}\`);
|
|
59
74
|
});`;
|
|
60
75
|
}
|
|
61
76
|
export function fastifyTemplate(events) {
|
|
62
77
|
const eventHandlers = generateEventHandlers(events);
|
|
63
|
-
return `const fastify = require('fastify')({ logger: true });
|
|
64
|
-
const crypto = require('crypto');
|
|
65
|
-
|
|
66
|
-
// Webhook secret from PayMongo dashboard
|
|
67
|
-
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
68
|
-
|
|
69
|
-
function verifySignature(payload,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
78
|
+
return `const fastify = require('fastify')({ logger: true });
|
|
79
|
+
const crypto = require('crypto');
|
|
80
|
+
|
|
81
|
+
// Webhook secret from PayMongo dashboard
|
|
82
|
+
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
83
|
+
|
|
84
|
+
function verifySignature(payload, signatureHeader, secret) {
|
|
85
|
+
if (!signatureHeader) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const parts = signatureHeader.split(',');
|
|
90
|
+
const timestamp = parts.find((part) => part.startsWith('t='))?.split('=')[1];
|
|
91
|
+
const signature = parts.find((part) => part.startsWith('te='))?.split('=')[1];
|
|
92
|
+
|
|
93
|
+
if (!timestamp || !signature) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const expectedSignature = crypto
|
|
98
|
+
.createHmac('sha256', secret)
|
|
99
|
+
.update(timestamp + '.' + payload, 'utf8')
|
|
100
|
+
.digest('hex');
|
|
101
|
+
|
|
102
|
+
return crypto.timingSafeEqual(
|
|
103
|
+
Buffer.from(signature, 'hex'),
|
|
104
|
+
Buffer.from(expectedSignature, 'hex')
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
fastify.post('/webhooks/paymongo', async (request, reply) => {
|
|
109
|
+
try {
|
|
110
|
+
const signature = request.headers['paymongo-signature'];
|
|
111
|
+
const payload = JSON.stringify(request.body);
|
|
112
|
+
|
|
113
|
+
// Verify webhook signature (optional but recommended)
|
|
114
|
+
if (WEBHOOK_SECRET && !verifySignature(payload, signature, WEBHOOK_SECRET)) {
|
|
115
|
+
console.log('Invalid signature');
|
|
116
|
+
return reply.code(400).send({ error: 'Invalid signature' });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const { data } = request.body;
|
|
120
|
+
const eventType = data.attributes.type;
|
|
121
|
+
|
|
122
|
+
switch (eventType) {${eventHandlers}
|
|
123
|
+
default:
|
|
124
|
+
console.log('Unhandled event type:', eventType);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { received: true };
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error('Webhook processing error:', error);
|
|
130
|
+
return reply.code(500).send({ error: 'Internal server error' });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const start = async () => {
|
|
135
|
+
try {
|
|
136
|
+
await fastify.listen({ port: process.env.PORT || 3000 });
|
|
137
|
+
} catch (err) {
|
|
138
|
+
fastify.log.error(err);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
113
143
|
start();`;
|
|
114
144
|
}
|
|
115
145
|
export function genericTemplate(events) {
|
|
116
146
|
const eventHandlers = generateEventHandlers(events);
|
|
117
|
-
return `// Simple webhook handler for ${events.join(', ')}
|
|
118
|
-
|
|
119
|
-
const crypto = require('crypto');
|
|
120
|
-
|
|
121
|
-
// Webhook secret from PayMongo dashboard
|
|
122
|
-
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
123
|
-
|
|
124
|
-
function verifySignature(payload,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
147
|
+
return `// Simple webhook handler for ${events.join(', ')}
|
|
148
|
+
|
|
149
|
+
const crypto = require('crypto');
|
|
150
|
+
|
|
151
|
+
// Webhook secret from PayMongo dashboard
|
|
152
|
+
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
153
|
+
|
|
154
|
+
function verifySignature(payload, signatureHeader, secret) {
|
|
155
|
+
if (!signatureHeader) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const parts = signatureHeader.split(',');
|
|
160
|
+
const timestamp = parts.find((part) => part.startsWith('t='))?.split('=')[1];
|
|
161
|
+
const signature = parts.find((part) => part.startsWith('te='))?.split('=')[1];
|
|
162
|
+
|
|
163
|
+
if (!timestamp || !signature) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const expectedSignature = crypto
|
|
168
|
+
.createHmac('sha256', secret)
|
|
169
|
+
.update(timestamp + '.' + payload, 'utf8')
|
|
170
|
+
.digest('hex');
|
|
171
|
+
|
|
172
|
+
return crypto.timingSafeEqual(
|
|
173
|
+
Buffer.from(signature, 'hex'),
|
|
174
|
+
Buffer.from(expectedSignature, 'hex')
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function handleWebhook(request, response) {
|
|
179
|
+
try {
|
|
180
|
+
const signature = request.headers['paymongo-signature'];
|
|
181
|
+
const payload = JSON.stringify(request.body);
|
|
182
|
+
|
|
183
|
+
// Verify webhook signature (optional but recommended)
|
|
184
|
+
if (WEBHOOK_SECRET && !verifySignature(payload, signature, WEBHOOK_SECRET)) {
|
|
185
|
+
console.log('Invalid signature');
|
|
186
|
+
response.writeHead(400, { 'Content-Type': 'application/json' });
|
|
187
|
+
response.end(JSON.stringify({ error: 'Invalid signature' }));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const { data } = request.body;
|
|
192
|
+
const eventType = data.attributes.type;
|
|
193
|
+
|
|
194
|
+
switch (eventType) {${eventHandlers}
|
|
195
|
+
default:
|
|
196
|
+
console.log('Unhandled event type:', eventType);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
200
|
+
response.end(JSON.stringify({ received: true }));
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error('Webhook processing error:', error);
|
|
203
|
+
response.writeHead(500, { 'Content-Type': 'application/json' });
|
|
204
|
+
response.end(JSON.stringify({ error: 'Internal server error' }));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
163
208
|
module.exports = { handleWebhook };`;
|
|
164
209
|
}
|
|
165
210
|
export function getWebhookHandlerTemplate(events, framework) {
|
|
@@ -1,131 +1,161 @@
|
|
|
1
1
|
function generateEventHandlers(events) {
|
|
2
2
|
return events
|
|
3
|
-
.map((event) => `
|
|
4
|
-
case '${event}':
|
|
5
|
-
console.log('Processing ${event} event:', data);
|
|
6
|
-
// Add your ${event} handling logic here
|
|
3
|
+
.map((event) => `
|
|
4
|
+
case '${event}':
|
|
5
|
+
console.log('Processing ${event} event:', data);
|
|
6
|
+
// Add your ${event} handling logic here
|
|
7
7
|
break;`)
|
|
8
8
|
.join('');
|
|
9
9
|
}
|
|
10
10
|
export function expressTemplate(events) {
|
|
11
11
|
const eventHandlers = generateEventHandlers(events);
|
|
12
|
-
return `import express, { Request, Response } from 'express';
|
|
13
|
-
import crypto from 'crypto';
|
|
14
|
-
|
|
15
|
-
const app = express();
|
|
16
|
-
app.use(express.json());
|
|
17
|
-
|
|
18
|
-
// Webhook secret from PayMongo dashboard
|
|
19
|
-
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
20
|
-
|
|
21
|
-
interface PayMongoWebhookPayload {
|
|
22
|
-
data: {
|
|
23
|
-
id: string;
|
|
24
|
-
type: string;
|
|
25
|
-
attributes: {
|
|
26
|
-
type: string;
|
|
27
|
-
livemode: boolean;
|
|
28
|
-
created_at: number;
|
|
29
|
-
updated_at: number;
|
|
30
|
-
data: any;
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function verifySignature(payload: string,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
12
|
+
return `import express, { Request, Response } from 'express';
|
|
13
|
+
import crypto from 'crypto';
|
|
14
|
+
|
|
15
|
+
const app = express();
|
|
16
|
+
app.use(express.json());
|
|
17
|
+
|
|
18
|
+
// Webhook secret from PayMongo dashboard
|
|
19
|
+
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
20
|
+
|
|
21
|
+
interface PayMongoWebhookPayload {
|
|
22
|
+
data: {
|
|
23
|
+
id: string;
|
|
24
|
+
type: string;
|
|
25
|
+
attributes: {
|
|
26
|
+
type: string;
|
|
27
|
+
livemode: boolean;
|
|
28
|
+
created_at: number;
|
|
29
|
+
updated_at: number;
|
|
30
|
+
data: any;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function verifySignature(payload: string, signatureHeader: string, secret: string): boolean {
|
|
36
|
+
if (!signatureHeader) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const parts = signatureHeader.split(',');
|
|
41
|
+
const timestamp = parts.find((part) => part.startsWith('t='))?.split('=')[1];
|
|
42
|
+
const signature = parts.find((part) => part.startsWith('te='))?.split('=')[1];
|
|
43
|
+
|
|
44
|
+
if (!timestamp || !signature) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const expectedSignature = crypto
|
|
49
|
+
.createHmac('sha256', secret)
|
|
50
|
+
.update(timestamp + '.' + payload, 'utf8')
|
|
51
|
+
.digest('hex');
|
|
52
|
+
|
|
53
|
+
return crypto.timingSafeEqual(
|
|
54
|
+
Buffer.from(signature, 'hex'),
|
|
55
|
+
Buffer.from(expectedSignature, 'hex')
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
app.post('/webhooks/paymongo', (req: Request, res: Response) => {
|
|
60
|
+
try {
|
|
61
|
+
const signature = req.headers['paymongo-signature'] as string;
|
|
62
|
+
const payload = JSON.stringify(req.body);
|
|
63
|
+
|
|
64
|
+
// Verify webhook signature (optional but recommended)
|
|
65
|
+
if (WEBHOOK_SECRET && !verifySignature(payload, signature, WEBHOOK_SECRET)) {
|
|
66
|
+
console.log('Invalid signature');
|
|
67
|
+
return res.status(400).json({ error: 'Invalid signature' });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const { data }: PayMongoWebhookPayload = req.body;
|
|
71
|
+
const eventType = data.attributes.type;
|
|
72
|
+
|
|
73
|
+
switch (eventType) {${eventHandlers}
|
|
74
|
+
default:
|
|
75
|
+
console.log('Unhandled event type:', eventType);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
res.json({ received: true });
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('Webhook processing error:', error);
|
|
81
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const PORT = process.env.PORT || 3000;
|
|
86
|
+
app.listen(PORT, () => {
|
|
87
|
+
console.log(\`Webhook server running on port \${PORT}\`);
|
|
73
88
|
});`;
|
|
74
89
|
}
|
|
75
90
|
export function genericTemplate(events) {
|
|
76
91
|
const eventHandlers = generateEventHandlers(events);
|
|
77
|
-
return `// TypeScript webhook handler for ${events.join(', ')}
|
|
78
|
-
|
|
79
|
-
import crypto from 'crypto';
|
|
80
|
-
|
|
81
|
-
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
82
|
-
|
|
83
|
-
interface PayMongoWebhookPayload {
|
|
84
|
-
data: {
|
|
85
|
-
id: string;
|
|
86
|
-
type: string;
|
|
87
|
-
attributes: {
|
|
88
|
-
type: string;
|
|
89
|
-
livemode: boolean;
|
|
90
|
-
created_at: number;
|
|
91
|
-
updated_at: number;
|
|
92
|
-
data: any;
|
|
93
|
-
};
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function verifySignature(payload: string,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
92
|
+
return `// TypeScript webhook handler for ${events.join(', ')}
|
|
93
|
+
|
|
94
|
+
import crypto from 'crypto';
|
|
95
|
+
|
|
96
|
+
const WEBHOOK_SECRET = process.env.PAYMONGO_WEBHOOK_SECRET;
|
|
97
|
+
|
|
98
|
+
interface PayMongoWebhookPayload {
|
|
99
|
+
data: {
|
|
100
|
+
id: string;
|
|
101
|
+
type: string;
|
|
102
|
+
attributes: {
|
|
103
|
+
type: string;
|
|
104
|
+
livemode: boolean;
|
|
105
|
+
created_at: number;
|
|
106
|
+
updated_at: number;
|
|
107
|
+
data: any;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function verifySignature(payload: string, signatureHeader: string, secret: string): boolean {
|
|
113
|
+
if (!signatureHeader) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const parts = signatureHeader.split(',');
|
|
118
|
+
const timestamp = parts.find((part) => part.startsWith('t='))?.split('=')[1];
|
|
119
|
+
const signature = parts.find((part) => part.startsWith('te='))?.split('=')[1];
|
|
120
|
+
|
|
121
|
+
if (!timestamp || !signature) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const expectedSignature = crypto
|
|
126
|
+
.createHmac('sha256', secret)
|
|
127
|
+
.update(timestamp + '.' + payload, 'utf8')
|
|
128
|
+
.digest('hex');
|
|
129
|
+
|
|
130
|
+
return crypto.timingSafeEqual(
|
|
131
|
+
Buffer.from(signature, 'hex'),
|
|
132
|
+
Buffer.from(expectedSignature, 'hex')
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function handleWebhook(body: PayMongoWebhookPayload, signature?: string): { received: boolean } {
|
|
137
|
+
try {
|
|
138
|
+
const payload = JSON.stringify(body);
|
|
139
|
+
|
|
140
|
+
// Verify webhook signature (optional but recommended)
|
|
141
|
+
if (WEBHOOK_SECRET && signature && !verifySignature(payload, signature, WEBHOOK_SECRET)) {
|
|
142
|
+
console.log('Invalid signature');
|
|
143
|
+
throw new Error('Invalid signature');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const { data } = body;
|
|
147
|
+
const eventType = data.attributes.type;
|
|
148
|
+
|
|
149
|
+
switch (eventType) {${eventHandlers}
|
|
150
|
+
default:
|
|
151
|
+
console.log('Unhandled event type:', eventType);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return { received: true };
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error('Webhook processing error:', error);
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
129
159
|
}`;
|
|
130
160
|
}
|
|
131
161
|
export function getWebhookHandlerTemplate(events, framework) {
|