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.