# Cashfree Webhooks

[Cashfree Payments](https://www.cashfree.com) is an Indian payments company. One signing scheme covers four of its webhook products: **Payment Gateway** (payments, refunds, settlements, subscriptions, links, token vault, disputes), **Payouts V2**, **Secure ID** (Video KYC, bank account verification, reverse penny drop) and **PPI** wallets. Each delivery carries `x-webhook-signature` and `x-webhook-timestamp` headers.

Hookdeck setup note (source type **Cashfree**): Paste your Cashfree client secret (the Partner API key for partner webhooks). Legacy Payouts V1 and Cashgram webhooks use another scheme and fail verification.

## When to Use This Skill

* How do I receive Cashfree webhooks?
* How do I verify a Cashfree webhook signature (`x-webhook-signature`)?
* What is the signed content: timestamp and body, with or without a separator?
* How do I handle `PAYMENT_SUCCESS_WEBHOOK`, `REFUND_STATUS_WEBHOOK` or `TRANSFER_SUCCESS`?
* Why does Cashfree keep retrying my webhook? (Anything other than HTTP 200 is a failure.)
* Why does my Cashfree webhook signature verification fail?

## Verification (core)

| Item | Value |
| --- | --- |
| Signature header | `x-webhook-signature`: bare standard base64 (padded, 44 chars), no prefix |
| Timestamp header | `x-webhook-timestamp` |
| Algorithm | HMAC-SHA256 |
| Signed content | the `x-webhook-timestamp` value, then the raw body, **no separator** |
| Key | Cashfree client secret as UTF-8 (Partner API key for partner webhooks), not decoded |

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

// rawBody: Buffer of the exact request bytes (express.raw({ type: '*/*' }))
function verifyCashfree(rawBody, headers, secret) {
  const sig = headers['x-webhook-signature'];
  const ts = headers['x-webhook-timestamp'];
  if (!sig || !ts || !/^[A-Za-z0-9+/]{43}=$/.test(sig)) return false; // validate before comparing
  const expected = crypto.createHmac('sha256', Buffer.from(secret, 'utf8'))
    .update(Buffer.concat([Buffer.from(ts, 'latin1'), rawBody])) // timestamp FIRST, body second
    .digest('base64');
  const a = Buffer.from(expected, 'latin1');
  const b = Buffer.from(sig, 'latin1');
  return a.length === b.length && crypto.timingSafeEqual(a, b); // whole header, case-sensitive
}

```

Python: `hmac.new(secret.encode(), timestamp.encode("latin-1") + raw_body, hashlib.sha256).digest()`, base64-encode it, then `hmac.compare_digest` on **bytes** against the header. Respond `401` for a missing, malformed or mismatching signature.

Cashfree's SDK has `PGVerifyWebhookSignature` (`cashfree-pg`), but the examples verify by hand so every framework treats the body as raw bytes. Never `JSON.parse` / `JSON.stringify` before verifying. Details: [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cashfree-webhooks/references/verification.md).

**Timestamp window: off by default.** Hookdeck retries and replays resend the original headers and timestamp, and Hookdeck applies no tolerance, so a fixed window would reject legitimate retries. The examples offer an opt-in `CASHFREE_WEBHOOK_TOLERANCE_SECONDS` (unset or `0` = off). Cashfree's docs suggest 5 minutes, but the unit of `x-webhook-timestamp` is not stated, so check a real delivery first.

Legacy **Payouts V1 and Cashgram** webhooks (form-encoded, different scheme) are not covered and fail this verification.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/cashfree-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/cashfree-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/cashfree-webhooks/examples/fastapi/).

## Common Event Types

Payment Gateway and Payouts V2 bodies carry the event in `type`; Secure ID and PPI bodies carry it in `event_type`. Read `type`, fall back to `event_type`.

| Event | Product | Fired when |
| --- | --- | --- |
| `PAYMENT_SUCCESS_WEBHOOK` | Payment Gateway | A payment succeeds |
| `PAYMENT_FAILED_WEBHOOK` | Payment Gateway | A payment fails |
| `PAYMENT_USER_DROPPED_WEBHOOK` | Payment Gateway | The customer abandons the payment |
| `REFUND_STATUS_WEBHOOK` | Payment Gateway | A refund changes status |
| `SETTLEMENT_STATUS_WEBHOOK` | Payment Gateway | A settlement changes status |
| `TRANSFER_SUCCESS` / `TRANSFER_FAILED` / `TRANSFER_REVERSED` | Payouts V2 | A transfer succeeds, fails or is reversed |
| `BANK_ACCOUNT_VERIFICATION_*` (e.g. `_SUCCESS`), `VKYC_*` (e.g. `VKYC_AUDITOR_REVIEW_COMPLETED`) | Secure ID | Verification and Video KYC progress |
| `PPI_CREDIT_SUCCESS` | PPI | A wallet credit succeeds |

The overview page's samples abbreviate some Payment Gateway names (e.g. `PAYMENT_SUCCESS`), so handle both spellings. More: [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cashfree-webhooks/references/overview.md).

## Delivery Semantics

* Respond **HTTP 200**. Anything else, 3xx included, fails and is retried.
* Payment Gateway: per-endpoint policy (default 3 retries at 2, 10 and 30 minutes, or Fixed/Exponential/Custom up to 10) and Batch Resend in the dashboard. Payouts V2: fixed schedule, endpoint disabled after 5 consecutive failures.
* At-least-once, unordered: dedupe on business IDs (`cf_payment_id`, `transfer_id`, ...) and process asynchronously.
* Hookdeck's Cashfree source manages the method as `POST`.

## Environment Variables

```bash
CASHFREE_WEBHOOK_SECRET=your_cashfree_client_secret   # handlers fail closed (500) without it
# CASHFREE_WEBHOOK_TOLERANCE_SECONDS=0                # optional replay window; 0/unset = off

```

## Local Development

```bash
# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 cashfree --path /webhooks/cashfree

```

Register the printed URL as a webhook endpoint in the Cashfree Merchant Dashboard. See [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cashfree-webhooks/references/setup.md).

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cashfree-webhooks/references/overview.md) - Products, event catalog, payload shapes, retries
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cashfree-webhooks/references/setup.md) - Hookdeck source setup, client secret, endpoint registration
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cashfree-webhooks/references/verification.md) - Algorithm, manual verification, gotchas, debugging