hooksig / Cashfree
HMAC-SHA256 · base64 · timestamped
Cashfree
India's other big gateway, with a timestamp folded straight in front of the body before hashing.
Try it, live in your browser
Request: edit anything
Verify this in code
import { verify } from "hooksig";
const result = await verify("cashfree", {
payload: rawBody, // the RAW body, not parsed JSON
headers: request.headers,
secret: env.WEBHOOK_SECRET,
});
if (!result.verified) {
return new Response(result.reason, { status: 400 });
}no match: signature invalid
Signed string
HMAC-SHA256( secret, signed string ) = expected
Expected vs provided
expected
provided
How it's built
The scheme
- Header
x-webhook-signature - AlgorithmHMAC-SHA256
- Encodingbase64
- Timestampyes, so it has replay protection
- SignsHMAC-SHA256 over `{timestamp}{rawBody}`, base64-encoded, keyed by the client secret. The timestamp arrives in x-webhook-timestamp.
Gotchas
- The signed string is the timestamp concatenated directly in front of the raw body, no separator: `{x-webhook-timestamp}{body}`.
- Sign the RAW body. Parsing to JSON can turn 170.00 into 170 and break the signature.
- The key is your client secret (x-client-secret), and the digest is base64, not hex.
- Reject if x-webhook-timestamp is outside your tolerance to stop replays.