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

# Webhooks

> Enable merchants, payment aggregators, and marketplaces outside India to accept payments from customers in India, covering import webhooks, event types, payloads, and signature verification.

Webhooks are server callbacks to your server from Cashfree Payments. Webhooks are event-based and are sent when specific events related to the transaction happen.

Click [here](/payments/online/webhooks/configure) to know how to configure webhooks.

<Tabs>
  <Tab title="Payment Verification">
    ## Payment Verification details webhook version 1 (2022-09-01)

    The payment verification details update webhook notifies you about the payment details verification, and it give you comprehensive information about verification details update.

    A notification is sent to your backend from cashfree when there is any new update for payment verification. These notification are useful in cases when the internet connection is unstable or slow while the payment is getting processed. This will allow you to reconcile all the payment verification update at your end. Notifications will be sent to **notifyUrl** which is a part of the request parameter specified while creating an order request.

    ### Sample payload

    ```json Json theme={"dark"}
    {
    	"data": {
    		"cf_payment_id": 5114910634577,
    		"payment_status": "SUCCESS",
    		"payment_verification_status": "ACTION_REQUIRED",
    		"payment_verification_expiry": "2024-07-12T15:19:42+05:30",
    		"remarks": null,
    		"required_details": [
    			{
                    "doc_name": "shop_or_brand_name",
                    "doc_type": "VALUE",
                    "doc_status": "ACTION_REQUIRED",
                    "remarks": null
                },
                {
                    "doc_name": "partner_order_number",
                    "doc_type": "VALUE",
                    "doc_status": "ACTION_REQUIRED",
                    "remarks": null
                },
                {
                    "doc_name": "goods_description",
                    "doc_type": "VALUE",
                    "doc_status": "ACTION_REQUIRED",
                    "remarks": null
                },
                {
                    "doc_name": "invoice_number",
                    "doc_type": "VALUE",
                    "doc_status": "IN_REVIEW",
                    "remarks": null
                },
                {
                    "doc_name": "importer_name",
                    "doc_type": "VALUE",
                    "doc_status": "IN_REVIEW",
                    "remarks": null
                },
                {
                    "doc_name": "importer_address",
                    "doc_type": "VALUE",
                    "doc_status": "IN_REVIEW",
                    "remarks": null
                },
                {
                    "doc_name": "country_of_origin",
                    "doc_type": "VALUE",
                    "doc_status": "IN_REVIEW",
                    "remarks": null
                },
                {
                    "doc_name": "invoice_file",
                    "doc_type": "DOCUMENT",
                    "doc_status": "IN_REVIEW",
                    "remarks": null
                },
                {
                    "doc_name": "ecommerce_order_serial_number", //Required for Physical Goods and Digital Goods
                    "doc_type": "VALUE",
                    "doc_status": "ACTION_REQUIRED",
                    "remarks": null
                },
                {
                    "doc_name": "hs_code", //Required for Physical Goods and Digital Goods
                    "doc_type": "VALUE",
                    "doc_status": "IN_REVIEW",
                    "remarks": null
                },
                {
                    "doc_name": "shipment_date", //Required for Physical Goods
                    "doc_type": "VALUE",
                    "doc_status": "ACTION_REQUIRED",
                    "remarks": null
                },
                {
                    "doc_name": "port_of_loading",//Required for Physical Goods
                    "doc_type": "VALUE",
                    "doc_status": "IN_REVIEW",
                    "remarks": null
                },
                    {
                    "doc_name": "awb_number",//Required for Physical Goods
                    "doc_type": "VALUE",
                    "doc_status": "IN_REVIEW",
                    "remarks": null
                }
    		]
    	},
    	"event_time": "2024-07-12T13:39:42+05:30",
    	"type": "PAYMENT_VERIFICATION_UPDATE"
    }
    ```
  </Tab>

  <Tab title="Settlement Details">
    ## ICA Settlement details webhook version 1 (2022-09-01)

    The ICA Settlement Details Update webhook notifies you about the ica settlement.

    A notification is sent to your backend from cashfree when there is any new update for ica settlement. These notification are useful in cases when the internet connection is unstable or slow while the payment is getting processed. This will allow you to reconcile all the ica settlement update at your end. Notifications will be sent to **notifyUrl** which is a part of the request parameter specified while creating an order request.

    ### Sample payload

    <br />

    ```json Json theme={"dark"}
    {
    	"data": {
    		"adjustment_amount_inr": -347641.22,
    		"collection_amount_inr": 604854.0,
    		"initiated_on": null,
    		"payment_from": "2024-09-26T15:43:55",
    		"payment_till": "2024-09-26T16:43:13",
    		"service_charge_inr": null,
    		"service_tax_inr": 2068.59,
    		"settled_on": null,
    		"settlement_amount_inr": 243651.95,
    		"settlement_charges_inr": 0.0,
    		"settlement_foreign_currency_details": {
    			"settlement_amount_fcy": null,
    			"settlement_currency": "USD",
    			"settlement_forex_rate": null
    		},
    		"settlement_id": 12,
    		"settlement_tax_inr": 0.0,
    		"settlement_utr": null,
    		"status": "NOT_INITIATED"
    	},
    	"event_time": "2024-10-03T13:27:36+05:30",
    	"type": "ICA_SETTLEMENT_UPDATE"
    }
    ```
  </Tab>

  <Tab title="Signature Verification">
    ## Signature verification

    The signature must be used to verify if the request has not been tampered with. To verify the signature at your end, you will need your Cashfree PG secret key along with the payload.

    timestamp is present in the header x-webhook-timestamp\
    Actual signature is present in the header x-webhook-signature

    ```
    timestamp := 1617695238078;
    signedPayload := $timestamp.$payload;
    expectedSignature := Base64Encode(HMACSHA256($signedPayload, $merchantSecretKey));
    ```
  </Tab>

  <Tab title="Sample Code">
    ## Sample code

    ### Verify Signature using SDK

    <CodeGroup>
      ```node Node(express) theme={"dark"}
      using cashfree_pg.Client;
      using cashfree_pg.Model;

          	[Route("api/[controller]")]
          [ApiController]
          public class YourController : ControllerBase
          {
              [HttpPost]
              public async Task<IActionResult> Post()
              {
                  // Read the raw body of the POST request
                  using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
                  {
                      string requestBody = await reader.ReadToEndAsync();
                      var headers = Request.Headers;
                      var signature = headers["x-webhook-signature"];
                      var timestamp = headers["x-webhook-timestamp"];

                      Cashfree.XClientId = "<x-client-id>";
                      Cashfree.XClientSecret = "<x-client-secret>";
                      Cashfree.XEnvironment = Cashfree.SANDBOX;
          							var cashfree = new Cashfree();

                      try {
                      var response = cashfree.PGVerifyWebhookSignature(signature, requestBody, timestamp);
                      } catch(Exception e) {
                      // Error if signature mis matches
                      }
                  }
              }
          }

      ```

      ```go Go theme={"dark"}
      import (
        cashfree "github.com/cashfree/cashfree-pg/v4"
      )

      // Route
      e.POST("/webhook", _this.Webhook)

      // Controller
      func (_this *WebhookRoute) Webhook(c echo.Context) error {
        	clientId := "<x-client-id>"
      		clientSecret := "<x-client-secret>"
      		cashfree.XClientId = &clientId
      		cashfree.XClientSecret = &clientSecret
      		cashfree.XEnvironment = cashfree.SANDBOX

          signature := c.Request().Header.Get("x-webhook-signature")
          timestamp := c.Request().Header.Get("x-webhook-timestamp")

          body, _ := ioutil.ReadAll(c.Request().Body)
          rawBody := string(body)
          webhookEvent, err := cashfree.PGVerifyWebhookSignature(signature, rawBody, timestamp)
          if err != nil {
      		fmt.Println(err.Error())
      	} else {
      		fmt.Println(webhookEvent.Object)
      	}
      }
      ```

      ```php PHP theme={"dark"}
      <?php

      $inputJSON = file_get_contents('php://input');

      $expectedSig = getallheaders()['x-webhook-signature'];
      $ts = getallheaders()['x-webhook-timestamp'];

      if(!isset($expectedSig) || !isset($ts)){
          echo "Bad Request";
          die();
      }

      \Cashfree\Cashfree::$XClientId = "<x-client-id>";
      \Cashfree\Cashfree::$XClientSecret = "<x-client-secret>";
      $cashfree = new \Cashfree\Cashfree();

      try {
       $response =  cashfree->PGVerifyWebhookSignature($expectedSig, $inputJSON, $ts);
      } catch(Exception $e) {
        // Error if signature verification fails
      }
      ?>
      ```

      ```python Python theme={"dark"}
      from cashfree_pg.api_client import Cashfree

      @app.route('/webhook', methods = ['POST'])
      def disp():
      		# Get the raw body from the request
          raw_body = request.data

          # Decode the raw body bytes into a string
          decoded_body = raw_body.decode('utf-8')

          #verify_signature
          timestamp = request.headers['x-webhook-timestamp']
          signature = request.headers['x-webhook-signature'

      		cashfree = Cashfree()
      		cashfree.XClientId = "<app_id>"
      		cashfree.XClientSecret = "<secret_key>"
      		try:
      			cashfreeWebhookResponse = cashfree.PGVerifyWebhookSignature(signature, decoded_body, timestamp)
      		except:
      			# If Signature mis-match
      ```

      ```java Java theme={"dark"}
      import com.cashfree.*;

      @PostMapping("/my-endpoint")
      public String handlePost(HttpServletRequest request) throws IOException {
          Cashfree.XClientId = "<x-client-id>";
      		Cashfree.XClientSecret = "<x-client-secret>";
      		Cashfree.XEnvironment = Cashfree.SANDBOX;

        	StringBuilder stringBuilder = new StringBuilder();
          BufferedReader bufferedReader = null;

          try {
            bufferedReader = request.getReader();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line).append('\n');
            }


            String rawBody = stringBuilder.toString();
            String signature = request.getHeader("x-webhook-signature");
            String timestamp = request.getHeader("x-webhook-timestamp");

            Cashfree cashfree = new Cashfree();
            PGWebhookEvent webhook = cashfree.PGVerifyWebhookSignature(signature, rawBody, timestamp);

          } catch (Exception e) {
                  // Error if verification fails
          } finally {
               if (bufferedReader != null) {
                  bufferedReader.close();
      		}
      	}

      }
      ```

      ```c C# theme={"dark"}
      using cashfree_pg.Client;
      using cashfree_pg.Model;


      		[Route("api/[controller]")]
          [ApiController]
          public class YourController : ControllerBase
          {
              [HttpPost]
              public async Task<IActionResult> Post()
              {
                  // Read the raw body of the POST request
                  using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
                  {
                      string requestBody = await reader.ReadToEndAsync();
                      var headers = Request.Headers;
                      var signature = headers["x-webhook-signature"];
                      var timestamp = headers["x-webhook-timestamp"];

                      Cashfree.XClientId = "<x-client-id>";
                      Cashfree.XClientSecret = "<x-client-secret>";
                      Cashfree.XEnvironment = Cashfree.SANDBOX;
      								var cashfree = new Cashfree();

                      try {
                      var response = cashfree.PGVerifyWebhookSignature(signature, requestBody, timestamp);
                      } catch(Exception e) {
                      // Error if signature mis matches
                      }
                  }
              }
          }
      ```
    </CodeGroup>

    ### Compute Signature and Verify manually

    <CodeGroup>
      ```node Node(express) theme={"dark"}
      function verify(ts, rawBody){
          const body = req.headers["x-webhook-timestamp"] + req.rawBody;
          const secretKey = "<your secret key>";
          let genSignature = crypto.createHmac('sha256',secretKey).update(body).digest("base64");
          return genSignature
      }

      ```

      ```go Go theme={"dark"}
      func VerifySignature(expectedSig string, ts string, body string) (string, error) {
      	t := time.Now()
      	currentTS := t.Unix()
      	if currentTS-ts > 1000*300 {
      		return "", errors.New("webhook delivered too late")
      	}
      	signStr := strconv.FormatInt(ts, 10) + body
      	fmt.Println("signing String: ", signStr)
      	key := ""
      	h := hmac.New(sha256.New, []byte(key))
      	h.Write([]byte(signStr))
      	b := h.Sum(nil)
      	return base64.StdEncoding.EncodeToString(b), nil
      }

      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($ts, $rawBody){
          $rawBody = file_get_contents('php://input');
      		$ts = getallheaders()['x-webhook-timestamp'];

          $signStr = $ts . $rawBody;
          $key = "";
          $computeSig = base64_encode(hash_hmac('sha256', $signStr, $key, true));
          return $computeSig;
      }
      ```

      ```java Java theme={"dark"}
      public String generateSignature() {

      	bufferedReader = request.getReader();
        String line;
        while ((line = bufferedReader.readLine()) != null) {
           stringBuilder.append(line).append('\n');
        }
        String payload = stringBuilder.toString();
        String timestamp = request.getHeader("x-webhook-timestamp");

        String data = timestamp+payload;

        String secretKey = "SECRET-KEY"; // Get secret key from Cashfree Merchant Dashboard;
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key_spec = new SecretKeySpec(secretKey.getBytes(),"HmacSHA256");
        sha256_HMAC.init(secret_key_spec);
        String computed_signature = Base64.getEncoder().encodeToString(sha256_HMAC.doFinal(data.getBytes()));
        return computed_signature; // compare with "x-webhook-signature"
      }
      ```

      ```python Python theme={"dark"}
      import base64
      import hashlib
      import hmac

      def generateSignature():
          # Get the raw body from the request
          raw_body = request.data

          # Decode the raw body bytes into a string
          payload = raw_body.decode('utf-8')

          #verify_signature
          timestamp = request.headers['x-webhook-timestamp']

          signatureData = timestamp+payload
          message = bytes(signatureData, 'utf-8')
          secretkey=bytes("Secret_Key",'utf-8') #Get Secret_Key from Cashfree Merchant Dashboard.
          signature = base64.b64encode(hmac.new(secretkey, message, digestmod=hashlib.sha256).digest())
          computed_signature = str(signature, encoding='utf8')
          return computed_signature #compare with "x-webhook-signature"
      ```
    </CodeGroup>
  </Tab>
</Tabs>
