Dispute Webhooks

Learn in detail about dispute webhooks.

Dispute webhooks can be configured to receive automated notifications when disputes are created, updated and closed.

The webhook notification will be sent on all the URLs added and enabled under the dispute webhook. Merchants can add new URLs and enable or disable existing URLs for refund webhook at any point in time and it will be reflected instantaneously.

WebhookDescription
DISPUTE_CREATEDDispute_created webhook will be triggered when a new dispute is created.
DISPUTE_UPDATEDDispute_updated webhook will be triggered when a dispute is updated, for example when a comment is added by the Cashfree team, when the dispute moves into further stages like Pre-arbitration, Arbitration, or when the status of the dispute changes.
DISPUTE_CLOSEDThis webhook will be triggered when a dispute is closed.

Click here to know how to configure webhooks.

Dispute Created

Sample Payload


"{
  "data": {
    "dispute": {
      "dispute_id": "433475258",
      "dispute_type": "DISPUTE",
      "reason_code": "1402",
      "reason_description": "Duplicate Processing",
      "dispute_amount": 3,
      "created_at": "2023-06-15T21:49:48+05:30",
      "updated_at": "2023-06-15T21:49:48+05:30",
      "respond_by": "2023-06-18T23:59:59+05:30",
      "dispute_status": ""DISPUTE_CREATED"",
      "cf_dispute_remarks": "Dispute is created, please take action",
      "dispute_action_on": "MERCHANT"
    },
    "order_details": {
      "order_id": "order_1944392DR1kMTFYdIf8bI2awAcC3i9FTa",
      "order_amount": 3,
      "order_currency": "INR",
      "cf_payment_id": 885473311,
      "payment_amount": 3,
      "payment_currency": "INR"
    },
    "customer_details": {
      "customer_name": "Dileep Kumar s",
      "customer_phone": "8000000000",
      "customer_email": "[email protected]"
    }
  },
  "event_time": ""2023-06-15T21:50:04+05:30",
  "type": "DISPUTE_CREATED"
}"

Dispute Updated

Sample Payload


"{
  "data": {
    "dispute": {
      "dispute_id": "433475257",
      "dispute_type": "PRE_ARBITRATION",
      "reason_code": "13.1",
      "reason_description": "Merchandise / Services Not Received",
      "dispute_amount": 40000,
      "created_at": "2023-06-15T21:16:03+05:30",
      "updated_at": "2023-06-15T21:19:15+05:30",
      "respond_by": "2023-06-19T23:59:59+05:30",
      "dispute_status": "PRE_ARBITRATION_CREATED",
      "cf_dispute_remarks": "Pre Arbitration request has been raised for this case.\nTarget Date :: 2023-06-18T00:00 -> 2023-06-19T23:59:59.",
      "dispute_update": "TYPE_UPDATE",
      "dispute_action_on": "MERCHANT"
    },
    "order_details": {
      "order_id": "order_1944392D4jHtCeVPPdTXkaUwg5cfnujQe",
      "order_amount": 40000,
      "order_currency": "INR",
      "cf_payment_id": 885457437,
      "payment_amount": 40000,
      "payment_currency": "INR"
    },
    "customer_details": {
      "customer_name": "Dileep Kumar s",
      "customer_phone": "8000000000",
      "customer_email": "[email protected]"
    }
  },
  "event_time": "2023-06-15T21:20:24+05:30",
  "type": "DISPUTE_UPDATED"
}"

Dispute Closed

Sample Payload


"{
  "data": {
    "dispute": {
      "dispute_id": "433475257",
      "dispute_type": "CHARGEBACK",
      "reason_code": "4855",
      "reason_description": "Goods or Services Not Provided",
      "dispute_amount": 4500,
      "created_at": "2023-06-15T21:16:03+05:30",
      "updated_at": "2023-06-15T21:16:51+05:30",
      "respond_by": "2023-06-18T00:00:00+05:30",
      "resolved_at": "2023-06-15T21:16:51.682836678+05:30",
      "dispute_status": "CHARGEBACK_MERCHANT_WON",
      "cf_dispute_remarks": "Chargeback won by merchant"
    },
    "order_details": {
      "order_id": "order_1944392D4jHtCeVPPdTXkaUwg5cfnujQe",
      "order_amount": 4500,
      "order_currency": "INR",
      "cf_payment_id": 885457437,
      "payment_amount": 4500,
      "payment_currency": "INR"
    },
    "customer_details": {
      "customer_name": "Dileep Kumar s",
      "customer_phone": "8000000000",
      "customer_email": "[email protected]"
    }
  },
  "event_time": "2023-06-15T21:17:14+05:30",
  "type": "DISPUTE_CLOSED"
}"

Payload Field Description

FieldDescriptionExampleType
dispute_idCashfree’s unique ID to identify a dispute.433475257Long
dispute_typeType of dispute created. Possible values:
- DISPUTE
- RETRIEVAL
- CHARGEBACK
- PRE_ARBITRATION
- ARBITRATION
ChargebackString
reason_codeCondition for which the customer is filing the case. Click here to know more about dispute reason codes.13.6String
reason_descriptionDescription of the reason code. Codes for Chargeback cases are specified by Card networks/NPCI.Credit not processedString
dispute_amountThe amount for which the dispute has been created.4500BigDecimal
created_atTime on which the dispute was registered on Cashfree’s system.2023-06-15T21:16:03+05:30LocalDateTime
updated_atTime on which the dispute was updated.2023-06-15T21:16:51+05:30LocalDateTime
respond_byTime by which the merchant is expected to respond to the dispute.2023-06-18T00:00:00+05:30LocalDateTime
dispute_statusStatus of Dispute. All possible values are listed in the table below.CHARGEBACK_CREATEDString
cf_dispute_remarksAny remarks specified by Cashfree on the dispute.Please submit documents.String
dispute_updateSpecifies what has been updated on the dispute. Possible Values:
- STATUS_UPDATE
- TYPE_UPDATE
- COMMENT_UPDATE
TYPE_UPDATEString
dispute_action_onSpecifies whether the action is on Cashfree or Merchant at a time.
Possible Values:
- MERCHANT
- CASHFREE
MERCHANTString
resolved_atTime on which the dispute was resolved/closed.2023-06-15T21:16:51.682836678+05:30LocalDateTime
event_timeTime at which dispute webhook was initiated.2023-06-15T21:16:51+05:30LocalDateTime
typeType of webhook. Possible Values:
- DISPUTE_CREATED
- DISPUTE_UPDATED
- DISPUTE_CLOSED
DISPUTE_CREATEDString

List of Dispute Status
DISPUTE_CREATED
DISPUTE_DOCS_RECEIVED
DISPUTE_UNDER_REVIEW
DISPUTE_MERCHANT_WON
DISPUTE_MERCHANT_LOST
DISPUTE_MERCHANT_ACCEPTED
DISPUTE_INSUFFICIENT_EVIDENCE
RETRIEVAL_CREATED
RETRIEVAL_DOCS_RECEIVED
RETRIEVAL_UNDER_REVIEW
RETRIEVAL_MERCHANT_WON
RETRIEVAL_MERCHANT_LOST
RETRIEVAL_MERCHANT_ACCEPTED
RETRIEVAL_INSUFFICIENT_EVIDENCE
CHARGEBACK_CREATED
CHARGEBACK_DOCS_RECEIVED
CHARGEBACK_UNDER_REVIEW
CHARGEBACK_MERCHANT_WON
CHARGEBACK_MERCHANT_LOST
CHARGEBACK_MERCHANT_ACCEPTED
CHARGEBACK_INSUFFICIENT_EVIDENCE
PRE_ARBITRATION_CREATED
PRE_ARBITRATION_DOCS_RECEIVED
PRE_ARBITRATION_UNDER_REVIEW
PRE_ARBITRATION_MERCHANT_WON
PRE_ARBITRATION_MERCHANT_LOST
PRE_ARBITRATION_MERCHANT_ACCEPTED
PRE_ARBITRATION_INSUFFICIENT_EVIDENCE
ARBITRATION_CREATED
ARBITRATION_DOCS_RECEIVED
ARBITRATION_UNDER_REVIEW
ARBITRATION_MERCHANT_WON
ARBITRATION_MERCHANT_LOST
ARBITRATION_MERCHANT_ACCEPTED
ARBITRATION_INSUFFICIENT_EVIDENCE

Compute Signature and Verify

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

timestamp := 1617695238078;  
signedPayload := $timestamp.$payload;
expectedSignature := Base64Encode(HMACSHA256($signedPayload, $merchantSecretKey));

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"