> ## Documentation Index
> Fetch the complete documentation index at: https://www.cashfree.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Signature Verification

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.

<Warning>
  * 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.
</Warning>

### Sample Code

<CodeGroup>
  ```javascript Node (Express) theme={"dark"}
  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");
  }
  ```

  ```go Go (Echo) theme={"dark"}
  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)
  ```

  ```php PHP theme={"dark"}
  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
  }
  ```

  ```java Java (Servlet) theme={"dark"}
  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()));
  }
  ```

  ```python Python (Flask/Django) theme={"dark"}
  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
  ```
</CodeGroup>

## 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) |
