Verify the exact raw request body before processing an event
VelarumPay signs timestamp + "." + rawBody. The signature is carried in x-velarumpay-signature as t=<unix-seconds>,v1=<hex-hmac>.
Node.js verification
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyWebhook(publicOrSecretKey, rawBody, signatureHeader, nowSeconds) {
const parts = Object.fromEntries(signatureHeader.split(",").map((part) => {
const [name, ...value] = part.trim().split("=");
return [name, value.join("=")];
}));
if (!/^\d{10,16}$/.test(parts.t || "") || !/^[0-9a-f]{64}$/i.test(parts.v1 || "")) return false;
if (Math.abs(nowSeconds - Number(parts.t)) > 300) return false;
const expected = createHmac("sha256", publicOrSecretKey)
.update(Buffer.from(`${parts.t}.`, "utf8"))
.update(rawBody)
.digest();
const supplied = Buffer.from(parts.v1, "hex");
return expected.length === supplied.length && timingSafeEqual(expected, supplied);
}
Pass the original bytes from the HTTP framework. Parsing and re-serializing JSON changes the byte sequence and invalidates the signature. Reject stale timestamps before business processing.
Headers and replay handling
| Header | Use |
|---|---|
x-velarumpay-signature | Timestamped HMAC described above. |
x-velarumpay-event-id | Stable event identity; deduplicate business processing on this value. |
x-velarumpay-delivery-id | Unique attempt identity for delivery diagnostics. |
Return a 2xx response only after durable acceptance. Retries and manual replay may produce new delivery IDs for the same event ID. Always reconcile final Payment Request state through the authenticated REST API.
Fixed HMAC vector
Download the public deterministic HMAC vector. Its exact raw body, timestamp, signed payload, and expected header are checked during the docs build. The test key is public and must never be used for an endpoint.
Endpoint lifecycle
Use createWebhookEndpoint, verifyWebhookEndpoint, rotateWebhookSecret, and revokeWebhookEndpoint through the merchant session boundary. Rotation and revocation require step-up authentication; overlap is bounded and old secrets expire.