| 1 | const express = require('express'); |
| 2 | const crypto = require('crypto'); |
| 3 | const app = express(); |
| 4 | |
| 5 | app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; } })); |
| 6 | |
| 7 | // Your API credentials |
| 8 | const CLIENT_ID = 'CAS-CI_*'; |
| 9 | const API_KEY = 'KEY_*'; // API key used to create the webhook. |
| 10 | const API_SECRET = 'CASH_*'; // API secret generated when webhook was created. |
| 11 | |
| 12 | function verifyWebhookSignature(req) { |
| 13 | |
| 14 | // Step 1: Retrieve signature from Webhook x-signature header. |
| 15 | const signatureHeader = req.headers['x-signature']; |
| 16 | if (!signatureHeader) return false; |
| 17 | |
| 18 | // Extract received signature (removing "V1 " prefix) |
| 19 | const [version, receivedSignature] = signatureHeader.split(' '); |
| 20 | if (version !== 'V1' || !receivedSignature) return false; |
| 21 | |
| 22 | // Step 2: Construct the raw signature in canonical form. |
| 23 | const method = 'POST'; |
| 24 | const path = '/'; |
| 25 | const host = req.headers['host']; |
| 26 | const headers = 'accept:*/*' + '\nauthorization:Client ' + CLIENT_ID + ' ' + API_KEY + '\ncontent-type:application/json; charset=utf-8' + '\nhost:' + host; |
| 27 | const bodyDigest = crypto |
| 28 | .createHash('sha256') |
| 29 | .update(req.rawBody, 'utf8') |
| 30 | .digest('hex'); |
| 31 | const rawSignature = `${method}\n${path}\n${headers}\n${bodyDigest}`; |
| 32 | |
| 33 | // Step 3: Create HMAC SHA-256 value. |
| 34 | const expectedSignature = crypto |
| 35 | .createHmac('sha256', API_SECRET) |
| 36 | .update(rawSignature) |
| 37 | .digest('hex'); |
| 38 | |
| 39 | // Step 4: Assert recieved signature with expected signature. |
| 40 | const receivedBuffer = Buffer.from(receivedSignature, 'hex'); |
| 41 | const expectedBuffer = Buffer.from(expectedSignature, 'hex'); |
| 42 | return crypto.timingSafeEqual(receivedBuffer, expectedBuffer); |
| 43 | } |
| 44 | |
| 45 | // Webhook endpoint |
| 46 | app.post('*', (req, res) => { |
| 47 | if (!verifyWebhookSignature(req)) { |
| 48 | return res.status(403).send("Invalid signature"); |
| 49 | } |
| 50 | |
| 51 | console.log("✅ Webhook verified:", req.body); |
| 52 | res.status(200).send("Webhook received"); |
| 53 | }); |
| 54 | |
| 55 | app.listen(80, () => { |
| 56 | console.log('Webhook server listening on port 80'); |
| 57 | }); |