Webhook'lar

Verifying signatures

Every delivery is signed with your endpoint secret. Check it before you act on the payload.

Concatenate the timestamp, a dot and the raw request body, compute HMAC-SHA256 with the endpoint secret, and compare it to v1 using a constant-time comparison. Reject timestamps older than five minutes and use X-VeliraPay-Delivery to ignore duplicates.

// PHP
[$t, $v1] = array_map(fn ($part) => explode('=', $part, 2)[1], explode(',', $_SERVER['HTTP_X_VELIRAPAY_SIGNATURE']));
$body = file_get_contents('php://input');
$expected = hash_hmac('sha256', "{$t}.{$body}", $secret);

if (! hash_equals($expected, $v1) || abs(time() - (int) $t) > 300) {
    http_response_code(400);
    exit;
}

// Node.js
const [t, v1] = req.headers['x-velirapay-signature'].split(',').map((p) => p.split('=')[1]);
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) && Math.abs(Date.now() / 1000 - t) < 300;

Sign the raw body exactly as it arrived: parsing and re-encoding the JSON changes the bytes and the signature will not match.