Webhooks

In this article, you will learn about the various webhooks involved in Cashfree Subscriptions.

Configure Webhook

  • You can now configure webhooks from the merchant dashboard. Click here to know how to add a new webhook.
  • Ensure you do not process duplicate events.

Webhooks will be sent to your configured endpoint as a POST request with the body containing the various parameters specifying the details of each event. Each request contains an event parameter that identifies its type.

Below are the various events that can be sent to your webhook endpoint.

  • SUBSCRIPTION_STATUS_CHANGE
  • SUBSCRIPTION_NEW_PAYMENT
  • SUBSCRIPTION_PAYMENT_CANCELLED
  • SUBSCRIPTION_PAYMENT_DECLINED
  • SUBSCRIPTION_AUTH_STATUS

SUBSCRIPTION_STATUS_CHANGE

πŸ“˜

The parameters "cf_subscriptionId" will be available from 31st July 2023 onwards.

ParameterTypeDescription
cf_eventStringThis event is triggered whenever the subscription status changes. The value for this event is SUBSCRIPTION_STATUS_CHANGE.
cf_subReferenceIdIntegerA unique Id which was generated when the subscription was created.
cf_statusStringThe new status of the subscription. Click here for information on subscription statuses.
cf_lastStatusStringThe old status of the subscription. Click here for information on subscription statuses.
cf_eventTimeString [format - yyyy-MM-dd HH:mm:ss]The time when the event was dispatched.
cf_subscriptionIdStringThe subscription Id passed by the merchant while creating the subscription
signatureStringA unique string which helps distinguish that the request is genuine and initiated by Cashfree. Click here for more information.

SUBSCRIPTION_NEW_PAYMENT

πŸ“˜

The parameters "cf_subscriptionId" and "cf_merchantTxnId" will be available from 31st July 2023 onwards.

ParameterTypeDescription
cf_eventStringThis event is triggered whenever there is a successful new payment for an authorized subscription. The value for this event is SUBSCRIPTION_NEW_PAYMENT.
cf_subReferenceIdIntegerA unique Id which was generated when the subscription was created.
cf_eventTimeString [format - yyyy-MM-dd HH:mm:ss]The time when the event was dispatched.
cf_orderIdStringThe unique order ID against which the payment is made.
cf_paymentIdIntegerThe unique paymentId for the payment.
cf_amountFloatThe amount charged for payment.
cf_subscriptionIdStringThe subscription Id passed by the merchant while creating the subscription
cf_merchantTxnIdStringAn id unique to the transaction that merchant passed while raising a transaction for a subscription.
cf_referenceIdIntegerThe unique txnId for the payment.
cf_retryAttemptsIntegerThe number of payment retries. This is applicable for failed payments.
signatureStringA unique string which helps distinguish that the request is genuine and initiated by Cashfree. Click here for more information.

SUBSCRIPTION_PAYMENT_CANCELLED

πŸ“˜

The parameters "cf_subscriptionId" and "cf_merchantTxnId" will be available from 31st July 2023 onwards.

ParameterTypeDescription
cf_eventStringThis event is triggered whenever a charge is in initialized state and the subscription is in cancelled state,we fail the charge and it is moved to cancelled state. The value for this event is PAYMENT_CANCELLED_WEBHOOK
cf_subReferenceIdIntegerA unique ID which was generated when the subscription was created.
cf_eventTimeString [format - yyyy-MM-dd HH:mm:ss]The time when the event was dispatched.
orderIdStringThe unique order ID against which the payment is made.
paymentIdIntegerThe unique paymentId for the payment.
amountFloatThe amount charged for payment.
subscriptionIdStringThe subscription Id passed by the merchant while creating the subscription
merchantTxnIdStringAn id unique to the transaction that the merchant passed while raising a transaction for a subscription.
referenceIdIntegerThe unique txnId for the payment.
retryAttemptsIntegerThe number of payment retries. This is applicable for failed payments.
reasonsStringFailure reason
signatureStringA unique string which helps distinguish that the request is genuine and initiated by Cashfree. Click here for more information.

SUBSCRIPTION_PAYMENT_DECLINED

πŸ“˜

The parameters "cf_subscriptionId" and "cf_merchantTxnId" will be available from 31st July 2023 onwards.

ParameterTypeDescription
cf_eventStringThis event is triggered whenever a payment fails for an authorized subscription. The value for this event is SUBSCRIPTION_PAYMENT_DECLINED.
cf_subReferenceIdIntegerA unique Id which was generated when the subscription was created.
cf_eventTimeString [format - yyyy-MM-dd HH:mm:ss]The time when the event was dispatched.
cf_paymentIdIntegerThe unique paymentId for the payment.
cf_amountFloatThe amount charged for payment.
cf_subscriptionIdStringThe subscription Id passed by the merchant while creating the subscription
cf_merchantTxnIdStringAn id unique to the transaction that merchant passed while raising a transaction for a subscription.
cf_referenceIdIntegerThe unique txnId for the payment.
cf_retryAttemptsIntegerThe number of payment retries. This is applicable for failed payments.
cf_reasonsStringA possible reason for failure.
signatureStringA unique string which helps distinguish that the request is genuine and initiated by Cashfree. Click here for more information.

SUBSCRIPTION_AUTH_STATUS

πŸ“˜

The parameters "cf_subscriptionId" and "cf_merchantTxnId" will be available from 31st July 2023 onwards.

The event is triggered when the checkout is completed by the customer for success and failed cases.

ParameterTypeDescription
cf_eventStringThe event is triggered when the checkout fails The value for this event is SUBSCRIPTION_AUTH_STATUS.
cf_subReferenceIdIntegerA unique Id which was generated when the subscription was created.
cf_eventTimeString [format - yyyy-MM-dd HH:mm:ss]The time when the event was dispatched.
cf_subscriptionStatusStringThe status of the subscription. Click here for information on subscription statuses.
cf_authStatusStringCheckout status. Allowed value: FAILED
cf_subscriptionIdStringThe subscription Id passed by the merchant while creating the subscription
cf_merchantTxnIdStringAn id unique to the transaction that merchant passed while raising a transaction for a subscription.
cf_authTimestampString [format - yyyy-MM-dd HH:mm:ss]Checkout initiated timestamp.
cf_authFailureReasonStringPossible checkout failure. It will be empty for successFul authStatus change.
signatureStringA unique string which helps distinguish that the request is genuine and initiated by Cashfree. Click here for more information.

Verify Signature

Verifying the signature (passed along with the POST parameters) is mandatory before you process any response. We also recommend whitelisting only our IP address on your webhook endpoint.

Follow the steps below to calculate & verify the signature on the webhook payload:

  1. Please sort the webhook payload in alphabetical order based on fields which are starting with field cf_.
  2. Append the key-value pair w/o using any delimiter and create a string.
  3. Hash the string using the secret key which will be provided to you.
  4. Encode the hashed string using base64 and generate the signature.

Example :

  1. Sample web hook payload -

    
    {
       "cf_retryAttempts":0,
       "cf_amount":1,
       "cf_event":"SUBSCRIPTION_NEW_PAYMENT",
       "cf_eventTime":"2022-01-10 10:03:50",
       "cf_paymentId":1,
       "cf_referenceId":2,
       "cf_subReferenceId":3,
       "signature":"tT9pXZkT2LuDzXacYDUaur7EX3dJgNKcITHQng44tns="
    }
    
  2. Sorted web hook payload -

    cf_amount1cf_eventSUBSCRIPTION_NEW_PAYMENTcf_eventTime2022-01-10 10:51:02cf_paymentId1cf_referenceId2cf_retryAttempts0cf_subReferenceId3

  3. Signature generated using encoding with secret key -

    tT9pXZkT2LuDzXacYDUaur7EX3dJgNKcITHQng44tns=

  4. Generated signature should match the signature present in the webhook payload.

Following code snippets show you how to generate and verify the signature. This should not be assumed production-ready, kindly consider adding necessary validation before processing.

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class WebhookHandler {

  private static final String MAC_SHA256_ALGO = "HmacSHA256";
  
  public boolean verifySignature(Map <String, Object> payload) {
    final String signature = payload.get("signature");
    final String computedSignature = generateSignature(payload);
    return signature.equals(computedSignature);
  }

  public String generateSignature(Map <String, Object> payload) {
    try {
      String secretKey = "ac7960e7995536f0177fd210f3b3937fc2d974a4"; // merchant will have this.
      SortedSet<String> keys = new TreeSet<String>(payload.keySet());
      for (String key : keys) {
        if ((key.length() > 3) && (key.substring(0, 3).equals("cf_"))) {
          data = data + key + ((String[])postData.get(key))[0];
        }
      }
      Mac mac = Mac.getInstance(MAC_SHA256_ALGO);
      SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), MAC_SHA256_ALGO);
      mac.init(secretKeySpec);
      final byte[] encrypted = mac.doFinal(data.getBytes());
      return Base64.getEncoder().encodeToString(encrypted);
    }
    catch (Exception ex) {
      // handle
    }
  }
}
<?php

class WebhookHandler {

  public static function triggerHook ($data) {
    $signature = $data["signature"];
    $secretKey = "ac7960e7995536f0177fd210f3b3937fc2d974a4"; // merchant will have this.
    $computedSignature = static::generateSignature($data, $secretKey);
    return $signature == $computedSignature;
  }

  private static function generateSignature ($data, $clientSecret) {
  ksort($data); // Sort the $data array based on keys
  $signatureData = "";
  foreach ($data as $key => $value) {
    if (strlen($key) > 0 && substr($key, 0, 3) == "cf_") {
      $signatureData .= $key . $value;
    }
  }
  $hash_hmac = hash_hmac('sha256', $signatureData, $secretKey, true);
  
  $computedSignature = base64_encode($hash_hmac);
  return $computedSignature;
  } 

?>
import json
data=json.loads("""
{"cf_event":"SUBSCRIPTION_STATUS_CHANGE","cf_eventTime":"2023-01-13 13:57:50","cf_lastStatus":"INITIALIZED","cf_status":"BANK_APPROVAL_PENDING","cf_subReferenceId":108587
,"signature":"eFiNqQ8U/fJNrklKa100muagXZZvMH+a7ysFCfmE2+A="}""")

sign=data["signature"];
del(data["signature"])

cs="TEST307e06bddd583cc3f86edf02f410fa8a69653d7d" #PG
#cs="fc7a78ea906e3c9f93f14cfcc70f5928feb002f2" #Payout

from urllib import parse
import hmac
import hashlib
import base64

data = json.dumps(data, sort_keys=True)
print(data)

data=json.loads(data)

f = ""
for x in data:
    f=f+str(x)+str(data[x])
print(f)

data=f
val_str = str(data)

hash_val = hmac.new(cs.encode(), val_str.encode(), hashlib.sha256).digest()

gen_signature = base64.b64encode(hash_val)

gen_signature=str(gen_signature)
print(gen_signature)

sign="b'"+sign+"'"

print(sign)

if (sign==gen_signature):
    print("MATCHED")
else:
    print("MISMATCH")

Failure Reasons

CodeReason
AP01Account Blocked
AP02Account Closed
AP03Account Frozen
AP04Account Inoperative
AP05No Such Account
AP06Not a SBS accoountnumber or old account number represent with CBS number
AP07Refer to the branch KYC not completed
AP11Authentication Failed
AP14Invalid user credentials
AP15Mandate not registered_ not maintaining required balance
AP16Mandate not registered_ minor account
AP17Mandate not registerd_ NRE Account
AP18Mandate registration not allowed for CC account
AP19Mandate registration not allowed for PF account
AP20Mandate registraction not allowed for PPF account
AP23Transaction rejected or canceleed by customer
AP24Account not in regular Status
AP25Withdrawal stopped due to insolvency of account
AP28Mandate registration failed. Please contact your home branch
AP29Technical errors or connectivity issues as bank
AP30Browser closed by customer in mid-transaction
AP31Mandate registration not allowed for joint account
AP32Mandate registration not allowed for wallet account
AP33User rejected the transaction on pre-login page
AP34Account number not registered with net-banking facility
AP35Debit card validation failed due to_ Invalid card number
AP36Debit card validation failed due to_ Invalid expiry date
AP37Debit card validation failed due to_ Invalid PIN
AP38Debit card validation failed due to_ Invalid CVV
AP39OTP invalid
AP40Maximum retries exceeded for OTP
AP41Time expired for OTP
AP42Debit card not activated
AP43Debit card blocked
AP44Debit card hot listed
AP45Debit card expired
AP46No response received from customer while preforming transaction
AP47Account number registered for only view rights in net banking facility.

πŸ“˜

We send the webhook response in a form data format.