Auto-Refund Webhooks
Auto-refund webhooks can be configured to receive automated notifications when auto-refunds are initiated, processed and delayed. Auto-refunds are refunds triggered automatically for unsuccessful payments, disputes, duplicate payments; or instances where payments are received directly to your VPA, regardless of whether an order has been generated.
Webhook notifications are sent to every URL added and enabled under Auto-refund Webhooks. Merchants can add new URLs and enable or disable existing ones for auto-refund webhooks at any point in time. The changes are reflected instantaneously.
You can learn more on configuring webhooks.
Sample Payload
{
"data": {
"auto_refund": {
"event": "AUTO-REFUND",
"cf_refund_id": 1243460973,
"cf_payment_id": "2148333968",
"bank_reference": "234928698581",
"order_id": "order_1944392Tpba8y2fHcHVx0SwREojp51Jgr",
"refund_amount": 39,
"refund_currency": "INR",
"refund_type": "PAYMENT_AUTO_REFUND",
"refund_arn": "205907014017",
"refund_status": "SUCCESS",
"status_description": "Auto-Refund processed successfully",
"refund_reason": "Multiple payments were performed against same order.",
"created_at": "2023-08-11T14:08:28+05:30",
"processed_at": null,
"refund_charge": 0,
"refund_splits": null,
"metadata": null
}
},
"event_time": "2023-08-11T14:10:21+05:30",
"type": "AUTO_REFUND_STATUS_WEBHOOK"
}
Find the description for each field of the payload below:
Field | Description | Example |
---|---|---|
cf_refund_id | It represents the ID created by Cashfree Payments to identify the refund. | 1243460973 |
cf_payment_id | It represents the ID created by Cashfree Payments to identify the payment for the initiated refund. | 2148333968 |
order_id | It represents the ID created by you to identify the order for which the refund is initiated. | sampleorder0413 |
bank_reference | It represents the bank reference number for the transaction. | 234928698581 |
refund_currency | It represents the currency of the refund amount. | INR |
refund_amount | It represents the refunded amount. | 2 |
refund_type | It represents the refund type. The value will always be PAYMENT_AUTO_REFUND. | PAYMENT_AUTO_REFUND |
refund_arn | It represents the bank reference number for the refund. | 205907014017 |
refund_status | It represents the status of the refund (either SUCCESS or INITIATED) | SUCCESS |
status_description | It represents the description of the refund status (Two possible values: Auto-Refund initiated successfully or Auto-Refund processed successfully). | Auto-Refund initiated successfully |
created_at | It represents the time of auto-refund creation. | 2022-02-28T12:54:25+05:30 |
processed_at | It represents when the auto-refund was processed successfully. | 2022-02-28T13:04:27+05:30 |
refund_reason | It represents the reason of initiation of Auto-refund (Possible values: Credit was received for an incomplete payment. Refunded to customer for Dispute. Payment made directly to your VPA without order creation. Multiple payments were performed against same order. Payment details (amount, bank, etc.) do not match with created order or the payment was made after TTL.) | Multiple payments were performed against same order |
refund_charge | It represents the charges for processing refund in INR. | 0 |
refund_splits | It represents the refund split details. | [] |
metadata | It represents the additional metadata. | null |
event_time | It represents when the refund webhook was initiated. | 2022-02-28T13:04:28+05:30 |
type | It represents the webhook type. The value will always be AUTO_REFUND_STATUS_WEBHOOK. | AUTO_REFUND_STATUS_WEBHOOK |
Compute and Verify Signature
Sample Code
Verify Signature using SDK
var express = require('express')
import { Cashfree } from "cashfree-pg";
var app = express()
Cashfree.XClientId = "<x-client-id>";
Cashfree.XClientSecret = "<x-client-secret>";
Cashfree.XEnvironment = Cashfree.Environment.SANDBOX;
app.post('/webhook', function (req, res) {
try {
Cashfree.PGVerifyWebhookSignature(req.headers["x-webhook-signature"], req.rawBody, req.headers["x-webhook-timestamp"]))
} catch (err) {
console.log(err.message)
}
})
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
$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
}
?>
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
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();
}
}
}
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
}
}
}
}
Compute Signature and Verify manually
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
}
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)
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;
}
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"
}
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"
Subscribe to Developer Updates
Updated 6 months ago