Webhooks

Webhooks are events that notify you about the payment status. All Payout integrations should establish a webhook to listen to payout events, like status changes on payments. Webhooks are HTTP callbacks that receive notification messages for events.

Webhooks help you receive automatic updates and are significant for completing the integration with Cashfree. Webhooks are triggered with a request from your side, for example, when a transfer fails or when a daily report is available. Requests get processed both synchronously and asynchronously. In rare scenarios, for some payment methods, the outcome may take a longer duration to confirm. Cashfree notifies you once we get updates from the bank about the payment status.

Do not go live without signature verification if you are using webhooks.

Webhook Events

Cashfree payouts webhooks enable you to receive updates about all event-driven activities originating from your account. Below is the list of payouts webhooks:

Events

Notifications

TRANSFER_SUCCESS​

Transfer successful at the bank and account debited

TRANSFER_FAILED​

Transfer failed

TRANSFER_REVERSED​

Transfer reversed by the beneficiary bank

CREDIT_CONFIRMATION​

Confirmation of balance credit

​TRANSFER_ACKNOWLEDGED​

After the beneficiary bank has deposited the money it confirms the transfer.

​LOW_BALANCE_ALERT​

Payouts recharge account low balance alert

TRANSFER_SUCCESS​

Sent whenever a transfer attempted successfully at the bank and account debited.

Parameters

Description

event

It contains the name of the event which just occurred. Value = TRANSFER_SUCCESS

transferId

Id of the transfer passed by the Merchant

referenceId

Id of the transfer generated by Cashfree

eventTime

Transfer initiation time

utr

Unique transaction reference number provided by the bank

signature

A unique string that helps distinguish that the request is genuine and initiated by Cashfree

​TRANSFER_FAILED

Sent whenever a transfer attempt fails.

Parameters

Description

event

It contains the name of the event which just occurred. Value = TRANSFER_FAILED

transferId

Id of the transfer passed by the Merchant

referenceId

Id of the transfer generated by Cashfree

reason

Reason for failure

signature

A unique string that helps distinguish that the request is genuine and initiated by Cashfree.

TRANSFER_REVERSED

Sent whenever the beneficiary bank reverses a transfer.

Parameters

Description

event

It contains the name of the event which just occurred. Value = TRANSFER_REVERSED

transferId

Id of the transfer passed by the Merchant

referenceId

Id of the transfer generated by Cashfree

eventTime

Time at which the transfer was reversed.

reason

Reason for failure

signature

A unique string which helps distinguish that the request is genuine and initiated by Cashfree

CREDIT_CONFIRMATION

Sent whenever the balance credit is confirmed.

Parameters

Description

event

It contains the name of the event which just occurred. Value = CREDIT_CONFIRMATION

ledgerBalance

The overall balance of ledger

amount

Amount deposited

utr

Unique transaction reference number provided by the bank

signature

A unique string which helps distinguish that the request is genuine and initiated by Cashfree

TRANSFER_ACKNOWLEDGED

Sent whenever the bank acknowledges a transfer.

Parameters

Description

event

It contains the name of the event which just occurred. Value = TRANSFER_ACKNOWLEDGED

transferId

Id of the transfer passed by the Merchant

referenceId

Id of the transfer generated by Cashfree

acknowledged

The flag which illustrates if the beneficiary bank acknowledges the transfer.

signature

A unique string which helps distinguish that the request is genuine and initiated by Cashfree

LOW_BALANCE_ALERT

Sent whenever the payout balance is low.

Parameter

Description

event

It contains the name of the event which just occurred. Value = LOW_BALANCE_ALERT

currentBalance

The current balance of the beneficiary account

alertTime

Alert initiation time

signature

A unique string which helps distinguish that the request is genuine and initiated by Cashfree.

Delay in Receiving the Response

Cashfree gets a response from the bank with the status of the transfer instantly. At times the response may get delayed due to various reasons, and the transaction status will be marked as Pending, till Cashfree gets a response.

Cashfree polls the transaction status from the bank for the next 72 hours. In case we do not receive the status of a transaction after 72 hours, the transaction needs to be manually reconciled.

Signature Verification

Cashfree sends a signature alongside every webhook, verifying this signature ( passed along with the POST parameters ) is mandatory before processing any response. It helps authenticate that the webhook is from cashfree.
We strongly recommend whitelisting Cashfree's production IP to help make your communication with cashfree more secure.

Following are the steps to verify Cashfree's signature:

  1. Get all the POST parameters except ‘signature’ and assign it to an array as key-value pair.
  2. Sort the array based on keys.
  3. Concatenate all the values in this array and resultant is the post data (say, postData).
  4. postData needs to be encrypted using SHA-256, and then base64 encoded.
  5. Now verify if both the signature calculated and the signature received match.
  6. Proceed further if it matches, else discards the request.

Cashfree's libraries also support signature verification. To verify the signature returned by Cashfree call the following methods from the library.

Below code snippets can help you verify Cashfree Signature:

<?php
  $data = $_POST;
  $signature = $_POST["signature"];
  unset($data["signature"]); // $data now has all the POST parameters except signature
  ksort($data); // Sort the $data array based on keys
  $postData = "";
  foreach ($data as $key => $value){
    if (strlen($value) > 0) {
      $postData .= $value;
    }
  }
  $hash_hmac = hash_hmac('sha256', $postData, $clientSecret, true) ;
  
  // Use the clientSecret from the oldest active Key Pair.
  $computedSignature = base64_encode($hash_hmac);
  if ($signature == $computedSignature) {
    // Proceed based on $event
  } else {
    // Reject this call
  }
?>
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Mac;
import org.apache.commons.codec.binary.Base64;

public class ComputedSignature {

    public static boolean checkSignature(String clientSecret, String data) {
   // write your code here
        ObjectMapper objectMapper = new ObjectMapper();
        String signatureKey = "signature";
        String hash = "";
        try {
            Map<String, Object> jsonMap = objectMapper.readValue(data, new TypeReference<Map<String, Object>>(){});
            String signature = jsonMap.get(signatureKey).toString();

            // remove the signature from map
            jsonMap.remove(signatureKey);
             Map<String, Object> sortedMap =  jsonMap.entrySet().
                     stream().
                     sorted(Map.Entry.comparingByKey()).
                     collect(Collectors.toMap(
                             Map.Entry::getKey,
                             Map.Entry::getValue,
                             (oldValue, newValue) -> oldValue, LinkedHashMap::new
                     ));

             // sort the map based on keys
             String postDataString = sortedMap.keySet().stream().map(key -> sortedMap.get(key).toString()).collect(Collectors.joining());
             
             // encode the post data on sha256
             Mac sha256HMAC = Mac.getInstance("HmacSHA256");
             SecretKeySpec secretKey  = new SecretKeySpec(clientSecret.getBytes(), "HmacSHA256");
             sha256HMAC.init(secretKey);

             hash = Base64.encodeBase64String(sha256HMAC.doFinal(postDataString.getBytes()));
             
             // check the hash generated with signature passed
             return hash.equals(signature);

        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}
const Cashfree = require("cashfree-sdk");
//convert post response recived in the notification end point to json
//and pass that to the SDK.
Cashfree.Payouts.VerifySignature(webhookPostDataJson);
from cashfree_sdk import verification

#webhook_data = response.content
webhook_data = '{"cashgramId": "5b8283182e0711eaa4c531df6a4f439b-28", "event": "CASHGRAM_EXPIRED", "eventTime": "2020-01-03 15:01:06", "reason": "OTP_ATTEMPTS_EXCEEDED", "signature": "TBpM+4nr1DsWsov7QiHSTfRJP4Z9BD8XrDgEhBlf9ss="}'
verification.verify_webhook(webhook_data, 'JSON')

Webhook Retries

Cashfree webhooks service does its best to deliver events to your webhook endpoint. It is best practice for your application to respond to the callback. Our webhook service may send many payloads to a single endpoint in quick succession. You will need to build an application and configure your server to receive the response we send when events get triggered during the payout process.

Your server should return a 200 HTTP status code to acknowledge that you received the webhook without any issues. Any other information you return in the request headers or request body gets ignored. Any response code outside the 200 range, including 3xx codes, indicates that you did not receive the webhook.

When Cashfree does not get the acknowledgement due to any reason, we retry to establish the communication at regular intervals. If we do not receive the response after few attempts, we gradually decrease the rate of retries. Based on this count, the service is disabled if it fails more than five times.

If you are not receiving notifications from Cashfree as expected, please reach out to our support team at [email protected]