Skip to main content
Verifying the signature is mandatory before processing any response. It helps authenticate that the webhook is from Cashfree Payments. Every webhook contains a signature (x-webhook-signature) in the header. The verification process is as follows:
  1. Extract the x-webhook-timestamp from the headers.
  2. Concatenate the timestamp and the raw request body (exact payload, unmodified).
  3. Generate an HMAC-SHA256 hash of this string using your client secret.
  4. Base64-encode the hash.
  5. Compare it with the x-webhook-signature header value. If they match, the webhook is valid.
  • Always use the raw request body and not a parsed JSON object. This prevents signature mismatch.
  • Reject the webhook if the signature does not match.

Sample Code

const crypto = require("crypto");

function verify(req) {
  const ts = req.headers["x-webhook-timestamp"];
  const rawBody = req.rawBody; // middleware must store raw body
  const secretKey = "<client-secret>";
  const signStr = ts + rawBody;
  return crypto.createHmac("sha256", secretKey).update(signStr).digest("base64");
}
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "io/ioutil"
)

func VerifySignature(expectedSig, ts, body string) (string, error) {
    signStr := ts + body
    key := "<client-secret>"
    h := hmac.New(sha256.New, []byte(key))
    h.Write([]byte(signStr))
    b := h.Sum(nil)
    return base64.StdEncoding.EncodeToString(b), nil
}

// usage
timestamp := c.Request().Header.Get("x-webhook-timestamp")
body, _ := ioutil.ReadAll(c.Request().Body)
rawBody := string(body)
signature := c.Request().Header.Get("x-webhook-signature")

VerifySignature(signature, timestamp, rawBody)
function computeSignature() {
    $rawBody = file_get_contents('php://input');
    $ts = getallheaders()['x-webhook-timestamp'];

    $signStr = $ts . $rawBody;
    $key = "<client-secret>";
    $computeSig = base64_encode(hash_hmac('sha256', $signStr, $key, true));
    return $computeSig; // compare with x-webhook-signature
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public String generateSignature(HttpServletRequest request) throws Exception {
    BufferedReader bufferedReader = request.getReader();
    StringBuilder stringBuilder = new StringBuilder();
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        stringBuilder.append(line);
    }
    String payload = stringBuilder.toString();
    String timestamp = request.getHeader("x-webhook-timestamp");

    String data = timestamp + payload;
    String secretKey = "<client-secret>";

    Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
    SecretKeySpec secret_key_spec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
    sha256_HMAC.init(secret_key_spec);

    return Base64.getEncoder().encodeToString(sha256_HMAC.doFinal(data.getBytes()));
}
import base64
import hashlib
import hmac

def generateSignature(request):
    raw_body = request.data
    payload = raw_body.decode('utf-8')
    timestamp = request.headers['x-webhook-timestamp']

    signatureData = timestamp + payload
    message = bytes(signatureData, 'utf-8')
    secretkey = bytes("<client-secret>", 'utf-8')
    signature = base64.b64encode(hmac.new(secretkey, message, digestmod=hashlib.sha256).digest())
    return signature.decode("utf-8")  # compare with x-webhook-signature

IPs to whitelist

When you decide to consume the webhooks, first, you need to verify if your systems need an IP whitelisting to be done at your end or not. Accordingly you can whitelist the below IPs of Cashfree:
Sandbox
52.66.25.127
15.206.45.168
Prod
52.66.101.190
3.109.102.144
18.60.134.245
18.60.183.142
Port
443 (secured)