Webhooks are server callbacks to your server from Cashfree Payments. Webhooks are event-based and are sent when specific events related to that transaction happen.
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
- REFUND_STATUS
SUBSCRIPTION_STATUS_CHANGE
The parameters "cf_subscriptionId" will be available from 31st July 2023 onwards.
Parameter | Type | Description |
---|---|---|
cf_event | String | This event is triggered whenever the subscription status changes. The value for this event is SUBSCRIPTION_STATUS_CHANGE. |
cf_subReferenceId | Integer | A unique Id which was generated when the subscription was created. |
cf_status | String | The new status of the subscription. Click here for information on subscription statuses. |
cf_lastStatus | String | The old status of the subscription. Click here for information on subscription statuses. |
cf_eventTime | String [format - yyyy-MM-dd HH:mm:ss] | The time when the event was dispatched. |
cf_subscriptionId | String | The subscription Id passed by the merchant while creating the subscription |
signature | String | A 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.
Parameter | Type | Description |
---|---|---|
cf_event | String | This event is triggered whenever there is a successful new payment for an authorized subscription. The value for this event is SUBSCRIPTION_NEW_PAYMENT. |
cf_subReferenceId | Integer | A unique Id which was generated when the subscription was created. |
cf_eventTime | String [format - yyyy-MM-dd HH:mm:ss] | The time when the event was dispatched. |
cf_orderId | String | The unique order ID against which the payment is made. |
cf_paymentId | Integer | The unique paymentId for the payment. |
cf_amount | Float | The amount charged for payment. |
cf_subscriptionId | String | The subscription Id passed by the merchant while creating the subscription |
cf_merchantTxnId | String | An id unique to the transaction that merchant passed while raising a transaction for a subscription. |
cf_referenceId | Integer | The unique txnId for the payment. |
cf_retryAttempts | Integer | The number of payment retries. This is applicable for failed payments. |
signature | String | A 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.
Parameter | Type | Description |
---|---|---|
cf_event | String | This webhook is triggered when the charge is cancelled. The value for this event is PAYMENT_CANCELLED_WEBHOOK. |
cf_subReferenceId | Integer | A unique ID which was generated when the subscription was created. |
cf_eventTime | String [format - yyyy-MM-dd HH:mm:ss] | The time when the event was dispatched. |
orderId | String | The unique order ID against which the payment is made. |
paymentId | Integer | The unique paymentId for the payment. |
amount | Float | The amount charged for payment. |
subscriptionId | String | The subscription Id passed by the merchant while creating the subscription |
merchantTxnId | String | An id unique to the transaction that the merchant passed while raising a transaction for a subscription. |
referenceId | Integer | The unique txnId for the payment. |
retryAttempts | Integer | The number of payment retries. This is applicable for failed payments. |
reasons | String | Failure reason |
signature | String | A 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.
Parameter | Type | Description |
---|---|---|
cf_event | String | This event is triggered whenever a payment fails for an authorized subscription. The value for this event is SUBSCRIPTION_PAYMENT_DECLINED. |
cf_subReferenceId | Integer | A unique Id which was generated when the subscription was created. |
cf_eventTime | String [format - yyyy-MM-dd HH:mm:ss] | The time when the event was dispatched. |
cf_paymentId | Integer | The unique paymentId for the payment. |
cf_amount | Float | The amount charged for payment. |
cf_subscriptionId | String | The subscription Id passed by the merchant while creating the subscription |
cf_merchantTxnId | String | An id unique to the transaction that merchant passed while raising a transaction for a subscription. |
cf_referenceId | Integer | The unique txnId for the payment. |
cf_retryAttempts | Integer | The number of payment retries. This is applicable for failed payments. |
cf_reasons | String | A possible reason for failure. |
signature | String | A 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.
Parameter | Type | Description |
---|---|---|
cf_event | String | The event is triggered when the checkout fails The value for this event is SUBSCRIPTION_AUTH_STATUS. |
cf_subReferenceId | Integer | A unique Id which was generated when the subscription was created. |
cf_eventTime | String [format - yyyy-MM-dd HH:mm:ss] | The time when the event was dispatched. |
cf_subscriptionStatus | String | The status of the subscription. Click here for information on subscription statuses. |
cf_authStatus | String | Checkout status. Allowed value: FAILED |
cf_subscriptionId | String | The subscription Id passed by the merchant while creating the subscription |
cf_merchantTxnId | String | An id unique to the transaction that merchant passed while raising a transaction for a subscription. |
cf_authTimestamp | String [format - yyyy-MM-dd HH:mm:ss] | Checkout initiated timestamp. |
cf_authFailureReason | String | Possible checkout failure. It will be empty for successFul authStatus change. |
signature | String | A unique string which helps distinguish that the request is genuine and initiated by Cashfree. Click here for more information. |
REFUND_STATUS
Parameter | Type | Description |
---|---|---|
cf_event | String | This event is triggered whenever a refundStatus of a transaction will be success or failed or cancelled. The value for this event is REFUND_STATUS_WEBHOOK. |
cf_subReferenceId | Integer | A unique Id that was generated when the subscription was created. |
cf_eventTime | String [format - yyyy-MM-dd HH:mm:ss] | The time when the event was dispatched. |
cf_sub_refund_id | String | A unique ID that is generated for every refund at the initialisation of refund state. |
cf_payment_id | Integer | The unique payment ID for the payment. |
cf_refund_amount | BigDecimal | The amount that is to be refunded |
cf_refund_id | Integer | The refund ID is a unique ID generated when processing a refund. |
cf_merchant_refund_id | String | A unique ID for the transaction that merchant passed while raising a refund. |
cf_refund_status | String | Refund status of the transaction. |
signature | String | A 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:
- Please sort the webhook payload in alphabetical order based on fields which are starting with field cf_.
- Append the key-value pair w/o using any delimiter and create a string.
- Hash the string using the secret key which will be provided to you.
- Encode the hashed string using base64 and generate the signature.
Example :
-
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=" }
-
Sorted web hook payload -
cf_amount1cf_eventSUBSCRIPTION_NEW_PAYMENTcf_eventTime2022-01-10 10:51:02cf_paymentId1cf_referenceId2cf_retryAttempts0cf_subReferenceId3
-
Signature generated using encoding with secret key -
tT9pXZkT2LuDzXacYDUaur7EX3dJgNKcITHQng44tns=
-
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
Code | Reason |
---|---|
AP01 | Account Blocked |
AP02 | Account Closed |
AP03 | Account Frozen |
AP04 | Account Inoperative |
AP05 | No Such Account |
AP06 | Not a SBS accoountnumber or old account number represent with CBS number |
AP07 | Refer to the branch KYC not completed |
AP11 | Authentication Failed |
AP14 | Invalid user credentials |
AP15 | Mandate not registered_ not maintaining required balance |
AP16 | Mandate not registered_ minor account |
AP17 | Mandate not registerd_ NRE Account |
AP18 | Mandate registration not allowed for CC account |
AP19 | Mandate registration not allowed for PF account |
AP20 | Mandate registraction not allowed for PPF account |
AP23 | Transaction rejected or canceleed by customer |
AP24 | Account not in regular Status |
AP25 | Withdrawal stopped due to insolvency of account |
AP28 | Mandate registration failed. Please contact your home branch |
AP29 | Technical errors or connectivity issues as bank |
AP30 | Browser closed by customer in mid-transaction |
AP31 | Mandate registration not allowed for joint account |
AP32 | Mandate registration not allowed for wallet account |
AP33 | User rejected the transaction on pre-login page |
AP34 | Account number not registered with net-banking facility |
AP35 | Debit card validation failed due to_ Invalid card number |
AP36 | Debit card validation failed due to_ Invalid expiry date |
AP37 | Debit card validation failed due to_ Invalid PIN |
AP38 | Debit card validation failed due to_ Invalid CVV |
AP39 | OTP invalid |
AP40 | Maximum retries exceeded for OTP |
AP41 | Time expired for OTP |
AP42 | Debit card not activated |
AP43 | Debit card blocked |
AP44 | Debit card hot listed |
AP45 | Debit card expired |
AP46 | No response received from customer while preforming transaction |
AP47 | Account number registered for only view rights in net banking facility. |
We send the webhook response in a form data format.