Developers · Webhooks
Webhooks
JS callbacks are UX-only. The signed webhook is the only source of truth for whether a payment succeeded.
Events
payment.successPayment verified successfully. Fulfil the order.payment.failedPayment failed or was rejected.payment.expiredOrder expired without a verified payment.payment.manual_reviewPayment needs extended verification. Do not fulfil yet.settlement.processedA settlement batch was processed.Signature verification
Vexoli signs each delivery as HMAC-SHA256 over timestamp + "." + raw body, using your webhook secret. Headers: X-Vexoli-Timestamp and X-Vexoli-Signature. Always use the raw request body and a constant-time comparison.
// Node.js (Express, raw body required)
const crypto = require("crypto");
function verify(req, secret) {
const ts = req.headers["x-vexoli-timestamp"];
const sig = req.headers["x-vexoli-signature"]; // hex HMAC-SHA256
const raw = req.rawBody; // exact bytes received — do NOT re-serialise JSON
const expected = crypto.createHmac("sha256", secret)
.update(ts + "." + raw).digest("hex");
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
// Reject events whose timestamp is more than 5 minutes old.Retries
Return any 2xx quickly; do heavy work async. If delivery fails (non-2xx or timeout), Vexoli retries on this schedule, then marks the delivery as failed in the dashboard where you can replay it:
1 minute5 minutes15 minutes1 hour6 hours12 hours24 hours
Make your handler idempotent: the same event may be delivered more than once. Deduplicate by event / order ID.