---
title: Verifi RDR Webhook Event
slug: verifi-rdr-webhook-event
description: Learn how to handle Verifi RDR webhook events with Butter Payments. Access header specs, JSON payload details, field descriptions, and code snippets for secure signature verification and streamlined dispute resolution.
docTags: 
createdAt: 2024-08-31T05:22:16.485Z
---

## Webhook Headers

- `X-Butter-Webhook-Type`
  - **Value**: verifi.rdr
  - **Description**: The type of webhook event.
- `X-Butter-Webhook-Signature`
  - **Description**: A signed hash of the webhook body used to validate the authenticity. Examples of calculating signature hashes can be found below in the *Webhook Signature Code Snippets* section.
- `X-Butter-Webhook-Deduplication-ID`
  - **Description**: An Idempotent ID to verify if you have received the same webhook before.
- `X-Butter-Webhook-Created`
  - **Description**: Time webhook was created, additionally used for calculating the signature hash.



## Webhook Body

```json
{
  "id": "event_ac7d19a7863bec06d7b1ae56",
  "object": "verifi.rdr",
  "data": {
    "case": {
      "date": "2024-01-01",
      "amount": 19.99,
      "currency": "USD",
    },
    "authorization_code": "12345D",
    "acquirer_reference_number": "78709066262500100248962",
    "refunded": true,
    "original_transaction": {
      "date": "2023-11-01",
      "amount": 19.99,
      "currency": "USD"
    },
    "card": {
      "bin": 424242,
      "last4": 4242
    },
    "network": {
      "merchant_id": 123456789,
      "scheme": "visa",
      "transaction_id": "103431687468834",
      "reason_code": "13.2",
      "merchant_order_id": "string",
      "merchant_category_code": 5411
    },
    "psp_transaction": {
      "type": "charge",
      "id": "ch_3MmlLrLkdIwHu7ix0snN0B15"
    }
  },
  "created_at": 1735689600
}
```

### Field Descriptors

- `case.date`
  - **Type**: string
  - **Format**: YYYY-MM-DD
  - **Description**: The date when the Verifi case was opened.&#x20;
  - **Nullable**: false
- `case.amount`
  - **Type**: float
  - **Description**: The disputed amount as reported by the card network.
  - **Nullable**: false
- `case.currency`
  - **Type**: string
  - **Format**: ISO-4217
  - **Description**: The currency of the original transaction as reported by the card network.
  - **Nullable**: false
- `authorization_code`
  - **Type**: string
  - **Description**: The authorization code of the original transaction as reported by the card network.
  - **Nullable**: false
- `acquirer_reference_number`
  - **Type**: string
  - **Description**: The Acquirer Reference Number (ARN) of the original transaction as reported by the card network.
  - **Nullable**: false
- `refunded`
  - **Type**: boolean
  - **Description**: Indicates whether the transaction was refunded by an applied RDR rule.
  - **Nullable**: false
- `original_transaction.date`
  - **Type**: string
  - **Format**: YYYY-MM-DD
  - **Description**: The date of the original transaction as reported by the card network.&#x20;
  - **Nullable**: false
- `original_transaction.amount`
  - **Type**: float
  - **Description**: The amount of the original transaction as reported by the card network.
  - **Nullable**: false
- `original_transaction.currency`
  - **Type**: string
  - **Format**: ISO-4217
  - **Description**: The currency of the original transaction as reported by the card network.
  - **Nullable**: false
- `card.bin`
  - **Type**: integer
  - **Description**: The first six to eight digits of the card used to make the transaction as reported by the card network.
  - **Nullable**: false
- `card.last4`
  - **Type**: integer
  - **Description**: The last four digits of the card used to make the transaction as reported by the card network.
  - **Nullable**: false
- `network.merchant_id`
  - **Type**: integer
  - **Description**: The merchant ID (MID) used during processing of the transaction as reported by the card network.
  - **Nullable**: false
- `network.scheme`
  - **Type**: string
  - **Description**: The card network where the transaction was made.
  - **Nullable**: false
  - **Enums**:
    - visa
- `network.reason_code`
  - **Type**: string
  - **Description**: The reason the transaction was disputed as reported by the card network.
  - **Enums**:
    - 10.4 (Other Fraud - Card Absent Environment)
    - 10.5 (Visa Fraud Monitoring Program)
    - 11.1 (Card Recovery Bulletin)
    - 11.2 (Declined Authorization)
    - 11.3 (No Authorization)
    - 12.1 (Late Presentment)
    - 12.2 (Incorrect Transaction Code)
    - 12.3 (Incorrect Currency)
    - 12.4 (Incorrect Account Number)
    - 12.5 (Incorrect Amount)
    - 12.6.1 (Duplicate processing)
    - 12.6.2 (Paid by other means)
    - 13.1 (Merchandise/Services Not Received)
    - 13.8 (Original Credit Transaction Not Accepted)
    - 13.9 (Non-Receipt of Cash or Load Transaction Value)
    - 13.2 (Cancelled Recurring)
    - 13.3 (Not as Described or Defective Merchandise/Services)
    - 13.4 (Counterfeit Merchandise)
    - 13.5 (Misrepresentation)
    - 13.6 (Credit not processed)
    - 13.7 (Cancelled Merchandise/Services)
  - **Nullable**: false
- `network.merchant_order_id`
  - **Type**: string
  - **Description**: The order ID reported to the card network by the processor.
  - **Nullable**: true
- `network.merchant_category_code`
  - **Type:** integer
  - **Description**: The Merchant Category Code (MCC) as reported by the card network.
  - **Nullable**: false
- `psp_transaction`
  - **Type**: object
  - **Description**: Contains information about the linked transaction in the respective PSP.
  - **Nullable**: true
- `psp_transaction.type`
  - **Type**: string
  - **Description**: Type of ID used for linking the disputed transaction to the transaction in the respective PSP.
  - **Enums**
    - charge
  - **Nullable**: false
- `psp_transaction.id`
  - **Type**: string
  - **Description**: The ID linking the disputed transaction to the transaction in the respective PSP.
- `created_at`
  - **Type**: integer
  - **Description**: A unix timestamp of when the webhook was created.



## Webhook Signature Code Snippets

```python
import json
import hashlib
import hmac
from time import time

def verify_webhook_signature(body: dict, headers: dict, signing_key: str) -> bool:
	payload = json.dumps(body, separators=(",", ":"))
	provided_signature = headers.get("X-Butter-Webhook-Signature")
	created_at = headers.get("X-Butter-Webhook-Created")
	message = f"{payload}+{created_at}".encode("utf-8")
	cacluated_signature = hmac.new(signing_key.encode("utf-8"), message, hashlib.sha256).hexdigest()
	return provided_signature == cacluated_signature
```



```javascript
const crypto = require('crypto');

function verifyWebhookSignature(body, headers, signingKey) {
	const payload = JSON.stringify(body);
	const createdAt = headers["X-Butter-Webhook-Created"];
	const providedSignature = headers["X-Butter-Webhook-Signature"];
	const hmac = crypto.createHmac('SHA256', signingKey);
	const data = hmac.update(Buffer.from(`${JSON.stringify(body)}+${createdAt}`));
	const calculatedSignature = data.digest('hex');
	return (calculatedSignature == providedSignature) ? true : false;
}
```



```go
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "strconv"
    "time"
)

func verifyWebhookSignature(headers map[string]string, body string, signingKey string) bool {
    createdAtStr := headers["X-Butter-Webhook-Expires"]
    createdAt, err := strconv.ParseInt(createdAtStr, 10, 64)
    if err != nil {
        fmt.Println("Error parsing createdAt timestamp:", err)
        return false
    }
    providedSignature := headers["X-Butter-Webhook-Signature"]

    hmacSha256 := hmac.New(sha256.New, []byte(signingKey))
    _, err = hmacSha256.Write([]byte(body + "+" + createdAtStr))
    if err != nil {
        fmt.Println("Error computing HMAC:", err)
        return false
    }
    calculatedSignature := hex.EncodeToString(hmacSha256.Sum(nil))

    if calculatedSignature != providedSignature {
        return false
    }

    return true
}
```



```java
import java.nio.charset.StandardCharsets;
import java.util.Map;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.fasterxml.jackson.databind.ObjectMapper;

public class WebhookVerifier {
    public static boolean verifyWebhookSignature(Map<String, String> headers, Object body, String signingKey) {
        String createdAtStr = headers.get("X-Butter-Webhook-Created");
        long createdAt = Long.parseLong(createdAtStr);
        String providedSignature = headers.get("X-Butter-Webhook-Signature");

        try {
            ObjectMapper objectMapper = new ObjectMapper();
            String payload = objectMapper.writeValueAsString(body);

            Mac hmac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(signingKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            hmac.init(secretKeySpec);

            byte[] payloadBytes = (payload + "+" + createdAtStr).getBytes(StandardCharsets.UTF_8);
            byte[] signatureBytes = hmac.doFinal(payloadBytes);
            String calculatedSignature = bytesToHex(signatureBytes);

            if (!calculatedSignature.equals(providedSignature)) {
                return false;
            }

            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xFF & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
}
```























