const crypto = require('crypto');
const express = require('express');
const app = express();
// Your Grid public key provided during integration
const GRID_WEBHOOK_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----`;
app.post('/webhooks/uma', (req, res) => {
const signatureHeader = req.header('X-Grid-Signature');
if (!signatureHeader) {
return res.status(401).json({ error: 'Signature missing' });
}
try {
let signature: Buffer;
try {
// Parse the signature as JSON. It's in the format {"v": "1", "s": "base64_signature"}
const signatureObj = JSON.parse(signatureHeader);
if (signatureObj.v && signatureObj.s) {
// The signature is in the 's' field
signature = Buffer.from(signatureObj.s, "base64");
} else {
throw new Error("Invalid JSON signature format");
}
} catch {
// If JSON parsing fails, treat as direct base64
signature = Buffer.from(signatureHeader, "base64");
}
// Create verifier with the public key and correct algorithm
const verifier = crypto.createVerify("SHA256");
const payload = await request.text();
verifier.update(payload);
verifier.end();
// Verify the signature using the webhook public key
const isValid = verifier.verify(
{
key: GRID_WEBHOOK_PUBLIC_KEY,
format: "pem",
type: "spki",
},
signature,
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Webhook is verified, process it based on type
const webhookData = req.body;
if (webhookData.type === 'INCOMING_PAYMENT') {
// Process incoming payment webhook
// ...
} else if (webhookData.type === 'OUTGOING_PAYMENT') {
// Process outgoing payment webhook
// ...
}
// Acknowledge receipt of the webhook
return res.status(200).json({ received: true });
} catch (error) {
console.error('Signature verification error:', error);
return res.status(401).json({ error: 'Signature verification failed' });
}
});
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});