Webhooks

Receive real-time notifications when payments complete or fail.

Verifying webhook signatures

Every request includes an X-Vexutopia-Signature header. Verify it using your signing secret to ensure the request is from Vexutopia.

const crypto = require('crypto');

// Header format: "t=<unix_timestamp>,v1=<hmac_hex>"
const header = req.headers['x-vexutopia-signature'];
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
const { t, v1 } = parts;

// Signing string = timestamp + "." + raw body string (before JSON.parse)
const signingString = `${t}.${rawBody}`;
const expected = crypto
  .createHmac('sha256', VEXUTOPIA_WEBHOOK_SECRET)
  .update(signingString)
  .digest('hex');

if (!crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) {
  return res.status(401).end(); // reject
}

// safe to process
return res.status(200).end();