docs: add Cashfree payment gateway documentation and integration guides
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 41s

This commit is contained in:
vickytechkey 2026-10-18 21:11:24 +05:30
parent 30576a9c6e
commit befd83ad53
24 changed files with 3578 additions and 0 deletions

View file

@ -0,0 +1,134 @@
# Cashfree Payments - Overview & Quickstart Guide
## 1. Introduction
Cashfree Payments is a full-stack payments and API banking platform in India providing payment gateway, automated payouts, identity verification (Secure ID), and settlement infrastructure.
The core service offerings include:
1. **Payment Gateway (PG)**: Multi-channel online payment collection supporting 120+ payment modes (UPI, Cards, Netbanking, Wallets, EMI, Pay Later).
2. **Payouts**: Bulk and instant bank/UPI disbursements (IMPS, NEFT, RTGS, UPI) 24x7x365.
3. **Secure ID**: KYC, Bank account penny-drop verification, UPI VPA verification, PAN, Aadhaar, GSTIN validation.
4. **Auto-Collect / Virtual Accounts**: Dedicated virtual accounts (VBA) and virtual UPI IDs to reconcile B2B/inward payments automatically.
---
## 2. Platform Architecture & Environments
Cashfree operates two distinct environments:
| Environment | Base URL (Payment Gateway) | Base URL (Payouts) | Purpose |
| :--- | :--- | :--- | :--- |
| **Sandbox (Test)** | `https://sandbox.cashfree.com/pg` | `https://payout-api.cashfree.com/payout/v1` (test mode) | Testing workflows, simulated cards & UPI |
| **Production (Live)** | `https://api.cashfree.com/pg` | `https://payout-api.cashfree.com/payout/v1` | Real financial transactions & settlements |
### Dashboard URLs
- **Sandbox Merchant Dashboard**: `https://merchant.cashfree.com/merchants/test`
- **Production Merchant Dashboard**: `https://merchant.cashfree.com`
---
## 3. High-Level Payment Gateway Lifecycle
The complete order-to-settlement lifecycle follows an asynchronous, event-driven pattern:
```mermaid
sequenceDiagram
autonumber
actor Customer as Customer / User
participant Frontend as Tradhox Frontend (Web / App)
participant Backend as Tradhox Payment Service
participant Cashfree as Cashfree Payment Gateway
participant Bank as Issuing Bank / UPI Network
Customer->>Frontend: Click "Pay Now" at Checkout
Frontend->>Backend: Request Order Creation
Backend->>Cashfree: POST /orders (x-client-id, x-client-secret)
Cashfree-->>Backend: Return cf_order_id & payment_session_id
Backend-->>Frontend: Return payment_session_id
Frontend->>Cashfree: Render Cashfree Checkout SDK / Drop-in
Customer->>Cashfree: Selects UPI / Card / Netbanking & Authorizes
Cashfree->>Bank: Process Payment
Bank-->>Cashfree: Success confirmation
Cashfree-->>Frontend: Return payment status & redirect to return_url
Cashfree->>Backend: POST Webhook (PAYMENT_SUCCESS_WEBHOOK with signature)
Backend->>Backend: Verify HMAC-SHA256 signature & Update DB
Backend-->>Cashfree: 200 OK Response
Backend->>Cashfree: GET /orders/{order_id} (Server-to-Server reconciliation)
```
---
## 4. Key Concepts
### Order (`order_id`)
- Every transaction begins by generating an `order_id` in your system.
- An order contains the order amount, customer details, expiry time, return URL, and notification webhook URL.
- Once created, Cashfree returns a `payment_session_id`.
### Payment Session (`payment_session_id`)
- A secure, short-lived session token used by frontend SDKs (Cashfree JS, React, Flutter, Android, iOS) to securely render payment methods without exposing secret keys.
### Idempotency
- Protect critical endpoints like Order creation and Refund creation using `x-idempotency-key` to ensure duplicate network retries do not duplicate charges.
### Webhooks
- Asynchronous notifications sent directly from Cashfree to your server when payment, refund, or settlement events occur. Webhooks are cryptographically signed with HMAC-SHA256.
---
## 5. Quickstart Step-by-Step
### Step 1: Obtain API Keys
1. Log in to the [Cashfree Merchant Dashboard](https://merchant.cashfree.com).
2. Switch to **Test Environment** (Sandbox).
3. Navigate to **Developers** -> **API Keys**.
4. Generate **App ID** (`x-client-id`) and **Secret Key** (`x-client-secret`).
### Step 2: Make Your First API Call
Create an order using cURL:
```bash
curl --request POST \
--url https://sandbox.cashfree.com/pg/orders \
--header 'Content-Type: application/json' \
--header 'x-api-version: 2023-08-01' \
--header 'x-client-id: <YOUR_APP_ID>' \
--header 'x-client-secret: <YOUR_SECRET_KEY>' \
--data '{
"order_id": "order_test_1001",
"order_amount": 100.00,
"order_currency": "INR",
"customer_details": {
"customer_id": "cust_101",
"customer_email": "vignesh@tradhox.com",
"customer_phone": "9999999999",
"customer_name": "Vignesh"
},
"order_meta": {
"return_url": "https://betapayments.tradhox.com/api/payments/return?order_id={order_id}",
"notify_url": "https://betapayments.tradhox.com/api/payments/webhooks/"
}
}'
```
### Step 3: Response Structure
```json
{
"cf_order_id": 14958231,
"order_id": "order_test_1001",
"entity": "order",
"order_currency": "INR",
"order_amount": 100.00,
"order_status": "ACTIVE",
"payment_session_id": "session_test_xyz123...",
"order_expiry_time": "2026-10-18T21:30:00Z",
"created_at": "2026-10-18T21:00:00Z",
"customer_details": {
"customer_id": "cust_101",
"customer_name": "Vignesh",
"customer_email": "vignesh@tradhox.com",
"customer_phone": "9999999999"
}
}
```
The frontend SDK uses this `payment_session_id` to initiate checkout.

View file

@ -0,0 +1,96 @@
# Cashfree Payments - Authentication, Headers & Security
## 1. Authentication Model
Cashfree Payment Gateway uses API key-based authentication passed via HTTP headers for all server-to-server REST calls.
### Required Authentication Headers
| Header | Type | Description | Example |
| :--- | :--- | :--- | :--- |
| `x-client-id` | String | Your unique Merchant App ID obtained from Dashboard | `TEST103848...` or `103848...` |
| `x-client-secret` | String | Your private Merchant Secret Key (keep confidential) | `cfsk_ma_test_abc123...` |
| `x-api-version` | String | Cashfree API version to ensure backward compatibility | `2023-08-01` (Recommended) |
| `Content-Type` | String | Content encoding (JSON) | `application/json` |
> [!CAUTION]
> Never expose `x-client-secret` in frontend applications (browser JavaScript, mobile apps). All calls containing credentials must originate from your secure backend (such as Tradhox Payment Service).
---
## 2. Optional & Operational Headers
| Header | Description | Use Case |
| :--- | :--- | :--- |
| `x-idempotency-key` | Unique string (e.g. UUIDv4) generated by caller | Prevents duplicate processing of payments, orders, or refunds during network retries |
| `x-request-id` | Tracing identifier | Correlation ID for debugging issues with Cashfree customer support |
### Idempotency Flow
When an API request includes an `x-idempotency-key`:
1. If the request succeeds, Cashfree caches the response against the key.
2. If the request times out or disconnects and you retry with the **same** idempotency key, Cashfree returns the cached response rather than executing a second charge or refund.
3. If the payload differs with the same key, Cashfree returns an `idempotency_conflict` HTTP 409 error.
---
## 3. Rate Limits & Throttling
Cashfree enforces rate limits per endpoint category to ensure system stability.
### Throttling Response Headers
Cashfree returns the following headers on API responses:
```http
x-ratelimit-limit: 120
x-ratelimit-remaining: 119
x-ratelimit-retry-after: 60
```
- When the limit is exceeded, Cashfree responds with HTTP `429 Too Many Requests`.
- Standard limits:
- **Orders API**: 120 requests/minute (Sandbox), 1000+ requests/minute (Production).
- **Refunds API**: 60 requests/minute.
- **Verification / Secure ID**: Based on plan quota.
### Handling 429 Errors in Python / Django
Implement exponential backoff when detecting HTTP 429:
```python
import time
import requests
def call_cashfree_with_retry(method, url, headers, json_data=None, max_retries=3):
retries = 0
backoff = 1.0 # seconds
while retries < max_retries:
response = requests.request(method, url, headers=headers, json=json_data, timeout=10)
if response.status_code == 429:
retry_after = int(response.headers.get('x-ratelimit-retry-after', backoff))
time.sleep(retry_after)
retries += 1
backoff *= 2
continue
return response
return response
```
---
## 4. API Versioning
Cashfree uses date-based versioning. Currently supported versions:
- `2023-08-01` (Latest stable - recommended for Tradhox)
- `2022-09-01`
- `2021-05-21` (Legacy)
Specifying `x-api-version: 2023-08-01` ensures responses follow the modern schema including `payment_session_id`, `customer_details`, and enhanced webhook payloads.
---
## 5. Security & Compliance Checklist
1. **IP Whitelisting**:
- In Production, configure your server public IPs (e.g. EC2 NAT Gateway or Elastic IP) in **Cashfree Dashboard -> Developers -> IP Whitelist**.
2. **Webhook Signature Verification**:
- Never trust incoming webhooks without verifying the `x-webhook-signature` using HMAC-SHA256 and checking the timestamp to prevent replay attacks.
3. **Data Protection & RBI Tokenization**:
- Do not store raw customer Credit/Debit card numbers (PAN) or CVVs on your servers. Use Cashfree's RBI-compliant Token Vault (Card-on-File Tokenization).

View file

@ -0,0 +1,191 @@
# Cashfree Payments - Orders API Specification
## 1. Overview
The Orders API is the starting point of every payment workflow in Cashfree. An order represents an intent to collect payment from a customer for a specific amount.
---
## 2. Create Order
### Endpoint
```http
POST /orders
```
- **Sandbox**: `https://sandbox.cashfree.com/pg/orders`
- **Production**: `https://api.cashfree.com/pg/orders`
### Request Headers
```http
Content-Type: application/json
x-api-version: 2023-08-01
x-client-id: <APP_ID>
x-client-secret: <SECRET_KEY>
x-idempotency-key: <UUIDv4> (Recommended)
```
### Request Payload Parameters
| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `order_id` | String | **Yes** | Unique merchant order ID. Alphanumeric, underscores, hyphens (3-50 chars). |
| `order_amount` | Number | **Yes** | Total payable amount up to 2 decimal places (e.g. `249.00`). |
| `order_currency` | String | **Yes** | 3-letter ISO currency code (`INR`, `USD`, `EUR`, etc.). |
| `customer_details` | Object | **Yes** | Customer identification object. |
| ↳ `customer_id` | String | **Yes** | Unique customer identifier in your system (e.g. `user_10294`). |
| ↳ `customer_phone` | String | **Yes** | 10-digit mobile number for Indian transactions. |
| ↳ `customer_email` | String | **Yes** | Customer email address. |
| ↳ `customer_name` | String | No | Full name of the customer. |
| `order_meta` | Object | No | Meta information controlling redirect and notifications. |
| ↳ `return_url` | String | No | URL to redirect customer after payment. Supports `{order_id}` placeholder. |
| ↳ `notify_url` | String | No | Webhook URL for server-to-server notifications. |
| ↳ `payment_methods` | String | No | Restrict allowed payment modes (e.g. `upi,cc,dc,nb`). |
| `order_expiry_time` | String | No | Expiration timestamp in ISO 8601 UTC format. |
| `order_note` | String | No | Brief note describing the transaction. |
| `order_tags` | Object | No | Custom key-value pairs for metadata and analytics. |
### Sample Request
```json
{
"order_id": "tradhox_ord_987654",
"order_amount": 1499.00,
"order_currency": "INR",
"customer_details": {
"customer_id": "tradhox_cust_42",
"customer_name": "Vignesh Tech",
"customer_email": "vignesh@tradhox.com",
"customer_phone": "9876543210"
},
"order_meta": {
"return_url": "https://betapayments.tradhox.com/api/payments/return?order_id={order_id}",
"notify_url": "https://betapayments.tradhox.com/api/payments/webhooks/",
"payment_methods": "upi,cc,dc,nb,wallet"
},
"order_expiry_time": "2026-10-18T22:30:00Z",
"order_note": "Subscription payment for Tradhox Pro Tier"
}
```
### Sample Response (`201 Created`)
```json
{
"cf_order_id": "291847101",
"order_id": "tradhox_ord_987654",
"entity": "order",
"order_currency": "INR",
"order_amount": 1499.00,
"order_status": "ACTIVE",
"payment_session_id": "session_a1b2c3d4e5f6g7h8_sandbox",
"order_expiry_time": "2026-10-18T22:30:00Z",
"created_at": "2026-10-18T21:05:00Z",
"order_meta": {
"return_url": "https://betapayments.tradhox.com/api/payments/return?order_id={order_id}",
"notify_url": "https://betapayments.tradhox.com/api/payments/webhooks/",
"payment_methods": null
},
"customer_details": {
"customer_id": "tradhox_cust_42",
"customer_name": "Vignesh Tech",
"customer_email": "vignesh@tradhox.com",
"customer_phone": "9876543210"
}
}
```
---
## 3. Order Statuses (`order_status`)
| Status | Description | Action Required |
| :--- | :--- | :--- |
| `ACTIVE` | Order is open and waiting for customer payment | Customer can attempt payment using `payment_session_id` |
| `PAID` | Payment successfully captured | Fulfill order / update order status to completed |
| `EXPIRED` | Order passed its `order_expiry_time` without payment | Customer must initiate a new checkout |
| `TERMINATED` | Order was cancelled by merchant using Terminate API | No further payments accepted |
---
## 4. Get Order Details
Use this endpoint to verify an order's status server-side after return or webhook arrival.
### Endpoint
```http
GET /orders/{order_id}
```
### Sample Response (`200 OK`)
```json
{
"cf_order_id": "291847101",
"order_id": "tradhox_ord_987654",
"entity": "order",
"order_currency": "INR",
"order_amount": 1499.00,
"order_status": "PAID",
"payment_session_id": null,
"order_expiry_time": "2026-10-18T22:30:00Z",
"created_at": "2026-10-18T21:05:00Z",
"order_splits": []
}
```
---
## 5. Get Payments for an Order
Retrieves all payment attempts associated with an order, including failed, user-dropped, and successful attempts.
### Endpoint
```http
GET /orders/{order_id}/payments
```
### Sample Response (`200 OK`)
```json
[
{
"cf_payment_id": "1948572019",
"order_id": "tradhox_ord_987654",
"entity": "payment",
"payment_currency": "INR",
"payment_amount": 1499.00,
"payment_time": "2026-10-18T21:06:12Z",
"payment_completion_time": "2026-10-18T21:06:18Z",
"payment_status": "SUCCESS",
"payment_message": "Transaction Successful",
"bank_reference": "629184719283",
"auth_id": null,
"payment_method": {
"upi": {
"channel": "intent",
"upi_id": "user@okaxis"
}
},
"payment_group": "upi"
}
]
```
### Payment Statuses (`payment_status`)
- `SUCCESS`: Payment captured successfully.
- `FAILED`: Payment declined by bank or user entered wrong credentials.
- `PENDING`: Waiting for bank processing (common in Netbanking / UPI collect).
- `USER_DROPPED`: Customer cancelled or closed payment window without completing.
---
## 6. Terminate an Order
Marks an unpaid `ACTIVE` order as `TERMINATED`, preventing any further payment attempts.
### Endpoint
```http
PATCH /orders/{order_id}
```
### Request Payload
```json
{
"order_status": "TERMINATED"
}
```

View file

@ -0,0 +1,232 @@
# Cashfree Payments - Checkout Integrations & Payment Methods
## 1. Checkout Integration Models
Cashfree provides 3 integration approaches depending on control vs. effort:
```mermaid
graph TD
A[Checkout Approach] --> B[1. Hosted Checkout]
A --> C[2. Drop-in JS SDK]
A --> D[3. Seamless / Custom API]
B -->|Fastest| E[Redirect to Cashfree Hosted Page]
C -->|Recommended| F[Embed Cashfree Modal or Inline UI on your site]
D -->|Full UI Control| G[Direct Server/App API calls with custom frontend forms]
```
---
## 2. Approach 1: Hosted Checkout (Redirect Flow)
The fastest way to accept payments. After backend generates `payment_session_id`, redirect the user to Cashfree's hosted page:
### Redirect URL Format
- **Sandbox**: `https://payments-test.cashfree.com/order/#<payment_session_id>`
- **Production**: `https://payments.cashfree.com/order/#<payment_session_id>`
### Example Redirect
```python
# Django view example
from django.shortcuts import redirect
def start_payment_view(request):
session_id = create_cashfree_order_session(...)
checkout_url = f"https://payments-test.cashfree.com/order/#{session_id}"
return redirect(checkout_url)
```
Once payment completes, Cashfree redirects the customer back to your configured `return_url`.
---
## 3. Approach 2: Cashfree Drop-in Web SDK (v3)
The recommended integration for modern web apps (React, Next.js, Vue, or Vanilla JS). Renders an in-page modal or iframe with zero PCI-DSS compliance overhead.
### Step 1: Add Cashfree JS Script
```html
<!-- Production -->
<script src="https://sdk.cashfree.com/js/v3/cashfree.js"></script>
```
### Step 2: Initialize and Trigger Checkout
```javascript
// Initialize SDK with environment mode
const cashfree = Cashfree({
mode: "sandbox" // "sandbox" or "production"
});
// Trigger checkout with session ID
function openCheckout(paymentSessionId) {
const checkoutOptions = {
paymentSessionId: paymentSessionId,
redirectTarget: "_self" // "_self", "_blank", or "_modal"
};
cashfree.checkout(checkoutOptions).then((result) => {
if (result.error) {
console.error("Payment error or dismiss:", result.error);
}
if (result.redirect) {
console.log("Customer redirected");
}
if (result.paymentDetails) {
console.log("Payment completed:", result.paymentDetails);
}
});
}
```
---
## 4. Approach 3: Seamless / Headless Payment API
For custom mobile apps or fully native checkouts where your app collects payment details directly.
### Endpoint
```http
POST /orders/sessions
```
Or Order Pay API:
```http
POST /orders/pay
```
### 1. UPI Payment Modes
#### A. UPI Intent (Mobile Apps - Google Pay, PhonePe, Paytm chooser)
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"upi": {
"channel": "intent"
}
}
}
```
*Returns `data.payload.intent` link (`upi://pay?...`) which triggers mobile UPI apps directly.*
#### B. UPI Collect (VPA Prompt)
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"upi": {
"channel": "collect",
"upi_id": "customer@okhdfcbank"
}
}
}
```
*Triggers a push notification on the customer's UPI app.*
#### C. Dynamic UPI QR Code
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"upi": {
"channel": "qrcode"
}
}
}
```
*Returns a dynamic Base64 encoded QR image and UPI URI for in-store or desktop scanning.*
---
### 2. Cards (Credit / Debit)
Supports Visa, Mastercard, RuPay, American Express, Diners, Maestro.
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"card": {
"channel": "link",
"card_number": "4012001037141112",
"card_holder_name": "Vignesh Tech",
"card_expiry_mm": "12",
"card_expiry_yy": "28",
"card_cvv": "123"
}
}
}
```
*Returns 3D-Secure ACS authentication URL (`action: "3ds"`, `url: "https://..."`) to redirect customer for OTP verification.*
#### Saved Card Tokenization (Token Vault / CoFT)
To comply with RBI guidelines, card numbers cannot be stored. Cashfree provides Card-on-File Tokenization:
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"card": {
"channel": "link",
"card_alias": "token_ref_98234",
"card_cvv": "123"
}
}
}
```
---
### 3. Net Banking
Supports 50+ Indian commercial and public sector banks.
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"netbanking": {
"channel": "link",
"netbanking_bank_code": 3003
}
}
}
```
#### Major Bank Codes
| Bank Name | Bank Code |
| :--- | :--- |
| **HDFC Bank** | `3003` |
| **ICICI Bank** | `3021` |
| **State Bank of India (SBI)** | `3044` |
| **Axis Bank** | `3001` |
| **Kotak Mahindra Bank** | `3032` |
| **Punjab National Bank (PNB)** | `3038` |
| **Bank of Baroda** | `3005` |
| **Canara Bank** | `3011` |
---
### 4. Wallets
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"app": {
"channel": "link",
"provider": "paytm",
"phone": "9876543210"
}
}
}
```
#### Supported Wallet Providers:
`paytm`, `phonepe`, `mobikwik`, `freecharge`, `airtelmoney`, `olamoney`, `amazonpay`.
---
### 5. Pay Later & EMI
- **Pay Later Providers**: `simpl`, `lazypay`, `zestmoney`.
- **Cardless EMI**: `zestmoney`, `earlysalary`, `instacred`.
- **Bank EMI**: Available for major credit cards with 3, 6, 9, 12, 18, 24 month tenures.

View file

@ -0,0 +1,238 @@
# Cashfree Payments - Webhooks & Signature Verification
## 1. Webhooks Architecture
Webhooks are automated HTTP POST requests sent from Cashfree to your backend when critical transaction events occur. They serve as the single source of truth for transaction states (such as asynchronous UPI confirmation, Netbanking approval, or 3DS completion).
> [!IMPORTANT]
> Never fulfill orders solely based on frontend redirection or query parameters. Always verify the transaction status via Webhooks or the server-to-server `GET /orders/{order_id}` API.
---
## 2. Webhook Event Types
Cashfree emits events categorized by product:
| Event Type (`type`) | Description |
| :--- | :--- |
| `PAYMENT_SUCCESS_WEBHOOK` | Customer successfully paid; funds captured. |
| `PAYMENT_FAILED_WEBHOOK` | Transaction declined by bank, insufficient funds, or wrong PIN. |
| `PAYMENT_USER_DROPPED_WEBHOOK` | Customer exited checkout without completing payment. |
| `REFUND_STATUS_WEBHOOK` | Refund status changed (`SUCCESS`, `FAILED`, `CANCELLED`). |
| `SETTLEMENT_SUCCESS_WEBHOOK` | Settlement transferred to merchant bank account. |
| `DISPUTE_CREATED_WEBHOOK` | Customer or bank raised a chargeback dispute. |
---
## 3. Webhook Payload Structure
### Sample `PAYMENT_SUCCESS_WEBHOOK`
```json
{
"data": {
"order": {
"order_id": "tradhox_ord_987654",
"order_amount": 1499.00,
"order_currency": "INR",
"order_tags": null
},
"payment": {
"cf_payment_id": "291847101",
"payment_status": "SUCCESS",
"payment_amount": 1499.00,
"payment_currency": "INR",
"payment_message": "00::Transaction Success",
"payment_time": "2026-10-18T21:06:12Z",
"payment_completion_time": "2026-10-18T21:06:18Z",
"bank_reference": "629184719283",
"auth_id": null,
"payment_method": {
"upi": {
"channel": "intent",
"upi_id": "user@okaxis"
}
},
"payment_group": "upi"
},
"customer_details": {
"customer_name": "Vignesh Tech",
"customer_id": "tradhox_cust_42",
"customer_email": "vignesh@tradhox.com",
"customer_phone": "9876543210"
}
},
"event_time": "2026-10-18T21:06:20Z",
"type": "PAYMENT_SUCCESS_WEBHOOK"
}
```
---
## 4. Cryptographic Signature Verification
Every Cashfree webhook includes two security headers:
- `x-webhook-signature`: Base64 encoded HMAC-SHA256 signature string.
- `x-webhook-timestamp`: Epoch timestamp in milliseconds (or ISO format) when webhook was dispatched.
### Verification Algorithm
```text
1. Read raw request body bytes (do NOT parse or re-serialize JSON before hashing!).
2. Concatenate: signed_data = x-webhook-timestamp + raw_body
3. Compute HMAC-SHA256 using your Cashfree Secret Key (client_secret).
4. Base64 encode the resulting hash.
5. Compare the computed signature with x-webhook-signature using timing-safe comparison.
```
```mermaid
flowchart LR
A[x-webhook-timestamp] --> C[Concatenate]
B[Raw Request Body Bytes] --> C
C --> D[HMAC-SHA256 with Secret Key]
D --> E[Base64 Encode]
E --> F{Constant-time Comparison with x-webhook-signature}
F -->|Match| G[Process Webhook]
F -->|Mismatch| H[Reject 400 Bad Request]
```
---
## 5. Production-Ready Python / Django Implementation
Save this verification helper in your service (e.g. [`payments/utils.py`](file:///d:/Tradhox_workspace/paymentservice/payments/utils.py)):
```python
import hmac
import hashlib
import base64
import time
import logging
logger = logging.getLogger(__name__)
def verify_cashfree_webhook_signature(
raw_body: bytes,
signature: str,
timestamp: str,
secret_key: str,
max_drift_seconds: int = 300
) -> bool:
"""
Verifies Cashfree webhook signature using HMAC-SHA256.
Args:
raw_body: Exact raw bytes from request.body
signature: Value from x-webhook-signature header
timestamp: Value from x-webhook-timestamp header
secret_key: Cashfree Merchant Client Secret Key
max_drift_seconds: Allowed time difference in seconds (anti-replay)
Returns:
bool: True if signature is authentic and timestamp is fresh, False otherwise
"""
if not signature or not timestamp or not secret_key:
logger.warning("Missing required fields for webhook signature verification")
return False
# 1. Anti-Replay Check (5 minutes threshold)
try:
ts_val = int(timestamp)
# If timestamp is in milliseconds, convert to seconds
if ts_val > 10000000000:
ts_val = ts_val // 1000
current_time = int(time.time())
if abs(current_time - ts_val) > max_drift_seconds:
logger.warning(f"Webhook timestamp expired: drift={abs(current_time - ts_val)}s")
return False
except ValueError:
logger.warning(f"Invalid timestamp format: {timestamp}")
return False
# 2. Build signed payload: timestamp + raw_body
ts_bytes = str(timestamp).encode('utf-8')
payload = ts_bytes + raw_body
# 3. Compute HMAC-SHA256
computed_hmac = hmac.new(
key=secret_key.encode('utf-8'),
msg=payload,
digestmod=hashlib.sha256
).digest()
computed_signature = base64.b64encode(computed_hmac).decode('utf-8')
# 4. Constant-time comparison
is_valid = hmac.compare_digest(computed_signature, signature)
if not is_valid:
logger.warning("Webhook signature mismatch!")
return is_valid
```
### Complete Django Webhook View Example
```python
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse, HttpResponse
from django.conf import settings
import json
import logging
from .utils import verify_cashfree_webhook_signature
logger = logging.getLogger(__name__)
@csrf_exempt
def cashfree_webhook_view(request):
if request.method != "POST":
return HttpResponse(status=405)
signature = request.headers.get("x-webhook-signature")
timestamp = request.headers.get("x-webhook-timestamp")
secret_key = getattr(settings, "CASHFREE_SECRET_KEY", "")
# Verify signature
if not verify_cashfree_webhook_signature(request.body, signature, timestamp, secret_key):
return JsonResponse({"error": "Invalid signature"}, status=400)
try:
payload = json.loads(request.body.decode("utf-8"))
event_type = payload.get("type")
data = payload.get("data", {})
order_info = data.get("order", {})
payment_info = data.get("payment", {})
order_id = order_info.get("order_id")
logger.info(f"Received Cashfree Webhook: {event_type} for order {order_id}")
if event_type == "PAYMENT_SUCCESS_WEBHOOK":
# Idempotent check: Ensure order is not already marked paid
# fulfill_order(order_id, payment_info)
pass
elif event_type == "PAYMENT_FAILED_WEBHOOK":
# record_payment_failure(order_id, payment_info)
pass
elif event_type == "REFUND_STATUS_WEBHOOK":
# update_refund_status(data)
pass
# Return 200 OK immediately
return JsonResponse({"status": "acknowledged"}, status=200)
except Exception as e:
logger.error(f"Error processing webhook: {e}", exc_info=True)
return JsonResponse({"status": "internal_error"}, status=500)
```
---
## 6. Retries and Delivery Guarantee
- Cashfree requires your server to respond with HTTP `200 OK` within **5 seconds**.
- If your server responds with an error code (5xx, 4xx) or times out, Cashfree retries up to **7 times** with exponential backoff:
- 1st retry: after 1 minute
- 2nd retry: after 5 minutes
- 3rd retry: after 15 minutes
- 4th retry: after 30 minutes
- 5th retry: after 1 hour
- 6th retry: after 2 hours
- 7th retry: after 4 hours
- **Always design webhook handlers to be strictly idempotent** by verifying against the database before executing side-effects.

View file

@ -0,0 +1,155 @@
# Cashfree Payments - Refunds & Disputes API
## 1. Refunds Overview
Cashfree provides programmatic refund capabilities for both full and partial order amounts. Refunds can be initiated directly against any successfully paid order.
### Refund Speeds
1. **Standard Refund (`STANDARD`)**:
- Follows regular banking clearinghouse cycles.
- Takes 5 to 7 business days to reflect in the customer's source account/card.
2. **Instant Refund (`INSTANT`)**:
- Uses instant payment rails (UPI / IMPS).
- Credits customer account within seconds or minutes.
- Requires Instant Refunds to be enabled in Cashfree Dashboard.
---
## 2. Create a Refund
### Endpoint
```http
POST /orders/{order_id}/refunds
```
- **Sandbox**: `https://sandbox.cashfree.com/pg/orders/{order_id}/refunds`
- **Production**: `https://api.cashfree.com/pg/orders/{order_id}/refunds`
### Request Headers
```http
Content-Type: application/json
x-api-version: 2023-08-01
x-client-id: <APP_ID>
x-client-secret: <SECRET_KEY>
x-idempotency-key: <UUIDv4>
```
### Request Payload Parameters
| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `refund_amount` | Number | **Yes** | Amount to be refunded. Cannot exceed remaining order balance. |
| `refund_id` | String | **Yes** | Unique merchant refund ID (alphanumeric, underscores, hyphens). |
| `refund_note` | String | No | Reason or note for the refund (e.g. `Customer requested return`). |
| `refund_speed` | String | No | `"STANDARD"` (default) or `"INSTANT"`. |
| `refund_splits` | Array | No | For split payments (Easy Split marketplace vendors). |
### Sample Request
```json
{
"refund_amount": 499.00,
"refund_id": "tradhox_ref_10029",
"refund_note": "Partial return for damaged item",
"refund_speed": "STANDARD"
}
```
### Sample Response (`201 Created` / `200 OK`)
```json
{
"cf_refund_id": "39104812",
"cf_payment_id": "291847101",
"refund_id": "tradhox_ref_10029",
"order_id": "tradhox_ord_987654",
"entity": "refund",
"refund_currency": "INR",
"refund_amount": 499.00,
"refund_note": "Partial return for damaged item",
"refund_status": "SUCCESS",
"refund_mode": "STANDARD",
"refund_type": "MERCHANT_INITIATED",
"refund_arn": "62918401928374",
"created_at": "2026-10-18T21:15:00Z",
"processed_at": "2026-10-18T21:15:02Z",
"status_description": "Refund processed successfully"
}
```
> [!NOTE]
> For Card refunds, `refund_arn` represents the **Acquirer Reference Number (ARN)**. Provide this number to customers so their issuing bank can trace the credited amount.
---
## 3. Refund Statuses (`refund_status`)
| Status | Description | Action Required |
| :--- | :--- | :--- |
| `SUCCESS` | Refund processed successfully by Cashfree and partner bank. | Update order/refund record as refunded. |
| `PENDING` | Refund is under processing with the customer's bank. | Wait for `REFUND_STATUS_WEBHOOK` or poll status. |
| `CANCELLED` | Refund was cancelled by merchant before processing. | No funds deducted. |
| `FAILED` | Bank declined the refund (e.g. account closed, card expired). | Retry or disburse via Payouts directly. |
---
## 4. Get Refund Details
Fetch details and status of a specific refund.
### Endpoint
```http
GET /orders/{order_id}/refunds/{refund_id}
```
---
## 5. List All Refunds for an Order
Retrieves all refunds (partial or full) issued against an order.
### Endpoint
```http
GET /orders/{order_id}/refunds
```
### Sample Response (`200 OK`)
```json
[
{
"cf_refund_id": "39104812",
"refund_id": "tradhox_ref_10029",
"order_id": "tradhox_ord_987654",
"refund_amount": 499.00,
"refund_status": "SUCCESS",
"refund_arn": "62918401928374"
}
]
```
---
## 6. Disputes & Chargeback Management
When a cardholder or account owner disputes a transaction with their bank:
### Dispute Lifecycle
1. **Retrieval Request**: Bank requests transaction proof before escalating.
2. **Chargeback / Dispute Created**: Funds temporarily debited; merchant notified via `DISPUTE_CREATED_WEBHOOK`.
3. **Contest Phase**: Merchant submits proof (invoice, AWB tracking, delivery signature).
4. **Resolution**: Bank or Card Network (Visa/Mastercard) awards dispute to merchant or customer.
### Dispute Webhook Payload
```json
{
"data": {
"dispute_id": "disp_8291038",
"order_id": "tradhox_ord_987654",
"cf_payment_id": "291847101",
"dispute_amount": 1499.00,
"dispute_status": "DISPUTE_CREATED",
"respond_by": "2026-10-25T23:59:59Z",
"reason_code": "4853",
"reason_description": "Goods or services not received"
},
"event_time": "2026-10-18T21:20:00Z",
"type": "DISPUTE_CREATED_WEBHOOK"
}
```

View file

@ -0,0 +1,88 @@
# Cashfree Payments - Settlements & Reconciliation
## 1. Settlement Overview
Settlement is the process where captured customer payments are transferred from Cashfree to your registered business bank account after deducting MDR (Merchant Discount Rate) fees and GST.
### Standard Settlement Cycle
- **Standard (T+1 or T+2 Days)**: Funds captured on Day T are credited to the merchant bank account on the next 1-2 banking business days.
- **Instant / On-Demand Settlements**: Allows merchants to trigger immediate settlement transfers 24x7 without waiting for standard banking cutoff windows.
---
## 2. Settlement Calculations & Breakdown
For every transaction, the settlement formula is:
$$\text{Settlement Amount} = \text{Order Amount} - (\text{Payment Gateway Fee} + \text{GST on Fee}) \pm \text{Adjustments}$$
### Sample Breakdown
- **Order Amount**: ₹1,000.00
- **MDR (PG Fee @ 1.9%)**: ₹19.00
- **GST on MDR (18%)**: ₹3.42
- **Net Credited**: ₹977.58
---
## 3. Settlement APIs
### 1. Get Settlements by Order ID
Check if and when a specific order was settled.
```http
GET /orders/{order_id}/settlements
```
#### Response (`200 OK`)
```json
{
"cf_order_id": "291847101",
"order_id": "tradhox_ord_987654",
"settlement_currency": "INR",
"settlement_amount": 977.58,
"service_charge": 19.00,
"service_tax": 3.42,
"settlement_id": "8291048",
"transfer_utr": "HDFCN26101892837",
"transfer_time": "2026-10-19T06:30:00Z",
"settlement_status": "SUCCESS"
}
```
---
### 2. Get All Settlements
Fetch consolidated batch settlements across a date range.
```http
GET /settlements?start_date=2026-10-01&end_date=2026-10-18&limit=50
```
#### Response Fields
- `cf_settlement_id`: Unique identifier for the settlement batch.
- `settlement_amount`: Total amount credited to merchant.
- `transfer_utr`: Bank UTR (Unique Transaction Reference) for reconciliation against bank statements.
- `settlement_status`: `SUCCESS`, `PENDING`, `FAILED`.
---
## 4. Settlement Webhooks
Configure `SETTLEMENT_SUCCESS_WEBHOOK` to receive real-time notification when bank transfer completes.
```json
{
"data": {
"settlement_id": "8291048",
"settlement_amount": 145892.40,
"settlement_currency": "INR",
"transfer_utr": "HDFCN26101994827",
"transfer_time": "2026-10-19T06:30:00Z",
"settlement_status": "SUCCESS"
},
"event_time": "2026-10-19T06:30:05Z",
"type": "SETTLEMENT_SUCCESS_WEBHOOK"
}
```

View file

@ -0,0 +1,177 @@
# Cashfree Payouts & Automated Disbursements
## 1. Overview
Cashfree Payouts allows automated disbursements to vendor bank accounts, delivery partners, refunds, affiliate payouts, and seller settlements 24x7x365 across India.
### Supported Rails
- **UPI**: Instant, up to ₹1,00,000 per transaction (or ₹5,00,000 for select categories).
- **IMPS**: Instant, available 24x7, up to ₹5,00,000 per transaction.
- **NEFT**: Batch settlements throughout the day, no upper limit.
- **RTGS**: Real-time gross settlement for high-value transactions (minimum ₹2,00,000).
- **Card Payouts**: Direct credits to Visa/Mastercard debit cards.
---
## 2. Environments & Base URLs
| Environment | Base URL |
| :--- | :--- |
| **Sandbox (Gamma)** | `https://payout-gamma.cashfree.com/payout/v1` |
| **Production** | `https://payout-api.cashfree.com/payout/v1` |
### Authentication
Payouts API supports passing credentials in headers or exchanging them for a short-lived bearer token:
```http
X-Client-Id: <PAYOUT_CLIENT_ID>
X-Client-Secret: <PAYOUT_CLIENT_SECRET>
```
Or via Auth Token:
```http
POST /payout/v1/authorize
Authorization: Bearer <TOKEN>
```
---
## 3. Beneficiary Management
Before transferring funds, register the beneficiary (or use quick-transfer APIs).
### Add Beneficiary
```http
POST /payout/v1/addBeneficiary
```
#### Request Payload
```json
{
"beneId": "vendor_vignesh_01",
"name": "Vignesh Tech",
"email": "vignesh@tradhox.com",
"phone": "9876543210",
"bankAccount": "50100234567890",
"ifsc": "HDFC0001234",
"address1": "Tradhox HQ, Tech Park",
"city": "Bengaluru",
"state": "Karnataka",
"pincode": "560001"
}
```
*For UPI Disbursements, replace `bankAccount` and `ifsc` with `vpa`:*
```json
{
"beneId": "seller_upi_42",
"name": "Seller Partner",
"email": "partner@tradhox.com",
"phone": "9876543210",
"vpa": "partner@okaxis",
"address1": "Seller Hub",
"city": "Mumbai",
"state": "Maharashtra",
"pincode": "400001"
}
```
---
## 4. Request a Transfer (Disbursement)
Initiates a payout from your Cashfree account balance to the beneficiary.
### Endpoint
```http
POST /payout/v1/requestTransfer
```
#### Request Payload
```json
{
"transferId": "tradhox_payout_88291",
"beneId": "vendor_vignesh_01",
"amount": "25000.00",
"transferMode": "IMPS",
"remarks": "Vendor weekly payout settlement"
}
```
#### Transfer Modes (`transferMode`):
- `IMPS`: Instant bank transfer.
- `NEFT`: Standard bank clearing.
- `RTGS`: High value bank transfer (>= ₹2,00,000).
- `UPI`: Instant transfer to UPI VPA.
#### Response (`200 OK`)
```json
{
"status": "SUCCESS",
"subCode": "200",
"message": "Transfer initiated successfully",
"data": {
"referenceId": "1948571029",
"transferId": "tradhox_payout_88291",
"utr": "629184910284",
"acknowledged": 1
}
}
```
---
## 5. Get Transfer Status
### Endpoint
```http
GET /payout/v1/getTransferStatus?transferId=tradhox_payout_88291
```
#### Response (`200 OK`)
```json
{
"status": "SUCCESS",
"subCode": "200",
"message": "Transfer details fetched successfully",
"data": {
"transfer": {
"transferId": "tradhox_payout_88291",
"referenceId": "1948571029",
"status": "SUCCESS",
"statusDescription": "Transfer completed successfully",
"amount": "25000.00",
"utr": "629184910284",
"addedOn": "2026-10-18 21:20:00",
"processedOn": "2026-10-18 21:20:03"
}
}
}
```
### Payout Statuses
- `SUCCESS`: Transfer completed; beneficiary account credited.
- `PENDING`: In progress with clearing network (e.g. NEFT window or slow bank response).
- `FAILED`: Transfer rejected by beneficiary bank (invalid account, IFSC, or limit reached).
- `REVERSED`: Transaction initially cleared but subsequently reversed by receiving bank.
---
## 6. Check Account Balance
Verify available wallet balance before triggering payouts.
### Endpoint
```http
GET /payout/v1/getBalance
```
#### Response (`200 OK`)
```json
{
"status": "SUCCESS",
"subCode": "200",
"message": "Balance fetched successfully",
"data": {
"balance": "485290.50",
"availableBalance": "485290.50"
}
}
```

View file

@ -0,0 +1,157 @@
# Cashfree Secure ID & KYC Verification APIs
## 1. Overview
Cashfree Secure ID provides API-driven identity, bank account, and compliance verification against authoritative Indian databases (NPCI, NSDL, UIDAI, GSTN).
Common use cases for Tradhox:
- Verifying vendor / seller bank accounts before onboarding.
- Verifying PAN & GSTIN for marketplace merchant compliance.
- Verifying customer UPI IDs to prevent payment failures and fraud.
---
## 2. Bank Account Verification (Penny Drop)
Deposits ₹1.00 into the target bank account via IMPS to verify that the account is active and retrieve the exact registered name.
### Endpoint
```http
GET /verification/bank-account/sync?name={name}&phone={phone}&bankAccount={account}&ifsc={ifsc}
```
Or via POST:
```http
POST /verification/bank-account/sync
```
#### Request Payload
```json
{
"bankAccount": "50100234567890",
"ifsc": "HDFC0001234",
"name": "Vignesh Tech",
"phone": "9876543210"
}
```
#### Response (`200 OK`)
```json
{
"status": "SUCCESS",
"subCode": "200",
"message": "Bank account verified successfully",
"accountStatus": "VALID",
"accountStatusCode": "ACCOUNT_IS_VALID",
"data": {
"nameAtBank": "VIGNESH TECH PRIVATE LIMITED",
"refId": "1948571029",
"bankName": "HDFC BANK",
"branch": "INDIRANAGAR",
"city": "BANGALORE",
"micr": "560240012",
"nameMatchScore": 95,
"nameMatchResult": "SUCCESS"
}
}
```
> [!TIP]
> The `nameMatchScore` provides a 0-100 fuzzy matching score comparing the provided name with the official `nameAtBank`.
---
## 3. UPI VPA Verification
Checks whether a UPI ID exists without deducting or transferring money.
### Endpoint
```http
POST /verification/vpa
```
#### Request Payload
```json
{
"vpa": "vignesh@okhdfcbank",
"name": "Vignesh"
}
```
#### Response (`200 OK`)
```json
{
"status": "SUCCESS",
"subCode": "200",
"message": "VPA verified successfully",
"vpa": "vignesh@okhdfcbank",
"accountExists": "YES",
"nameAtBank": "Vignesh Tech",
"nameMatchScore": 100
}
```
---
## 4. PAN Verification
Validates PAN against the Income Tax Department / NSDL database.
### Endpoint
```http
POST /verification/pan
```
#### Request Payload
```json
{
"pan": "ABCDE1234F",
"name": "Vignesh Tech"
}
```
#### Response (`200 OK`)
```json
{
"status": "VALID",
"pan": "ABCDE1234F",
"type": "Individual",
"registered_name": "VIGNESH TECH",
"name_match_score": 100,
"aadhaar_seeding_status": "LINKED"
}
```
---
## 5. GSTIN Verification
Validates Goods and Services Tax Identification Number.
### Endpoint
```http
POST /verification/gstin
```
#### Request Payload
```json
{
"gstin": "29ABCDE1234F1Z5"
}
```
#### Response (`200 OK`)
```json
{
"status": "VALID",
"gstin": "29ABCDE1234F1Z5",
"legal_name": "TRADHOX TECHNOLOGIES PRIVATE LIMITED",
"trade_name": "TRADHOX",
"registration_date": "2024-01-15",
"gstin_status": "Active",
"taxpayer_type": "Regular",
"principal_place_of_business": {
"address": "Tech Park, Outer Ring Road",
"state": "Karnataka",
"pincode": "560103"
}
}
```

View file

@ -0,0 +1,78 @@
# Cashfree Payments - Error Codes & Sandbox Test Data
## 1. Error Response Structure
All Cashfree Payment Gateway APIs return consistent JSON error objects when a request fails:
```json
{
"message": "Order with id 'tradhox_ord_987654' is already paid",
"code": "order_already_paid",
"type": "invalid_request_error"
}
```
### Top-Level Error Types (`type`)
| Error Type | HTTP Status | Meaning |
| :--- | :--- | :--- |
| `invalid_request_error` | `400`, `404`, `409` | Malformed body, missing required parameters, invalid IDs, or invalid state transitions. |
| `authentication_error` | `401` | Invalid or missing `x-client-id`, `x-client-secret`, or expired tokens. |
| `rate_limit_error` | `429` | Request volume exceeded allowed threshold. Implement backoff. |
| `api_error` | `500`, `502`, `503` | Temporary server-side issue at Cashfree or partner bank. |
---
## 2. Common API Error Codes
| Error Code (`code`) | HTTP Status | Description & Recommended Handling |
| :--- | :--- | :--- |
| `order_already_paid` | 400 | The order has already been successfully paid. Do not prompt user again; fetch order status. |
| `order_expired` | 400 | The order has passed its `order_expiry_time`. Generate a new order. |
| `order_terminated` | 400 | The order was marked as terminated. |
| `order_not_found` | 404 | No order exists matching the provided `order_id`. Verify ID spelling. |
| `duplicate_order_id` | 409 | An order with this `order_id` already exists. Use a unique order ID generator. |
| `idempotency_conflict` | 409 | The same `x-idempotency-key` was submitted with a different request payload. |
| `invalid_customer_details` | 400 | Phone number is not 10 digits or email format is invalid. |
| `payment_declined` | 400 | Customer's issuing bank declined the card or UPI payment. |
| `insufficient_balance` | 400 | Insufficient balance in merchant account to process instant refund or payout. |
| `unauthorized_ip` | 401 | Calling server IP is not in your Merchant Dashboard IP Whitelist. |
---
## 3. Sandbox Test Credentials & Simulators
When testing in the **Sandbox** environment (`https://sandbox.cashfree.com/pg`):
### 1. Test Cards
| Card Type | Card Number | Expiry | CVV | Expected Result |
| :--- | :--- | :--- | :--- | :--- |
| **Visa (Success)** | `4012 0010 3714 1112` | Any future date (e.g. `12/28`) | `123` | **SUCCESS** (3DS OTP: `123456`) |
| **Mastercard (Success)** | `5123 4567 8901 2346` | Any future date | `123` | **SUCCESS** |
| **RuPay (Success)** | `6071 2345 6789 0123` | Any future date | `123` | **SUCCESS** |
| **Card Declined** | `4012 0010 3714 1113` | Any future date | `123` | **FAILED** (`payment_declined`) |
| **Insufficient Funds** | `4012 0010 3714 1114` | Any future date | `123` | **FAILED** (`insufficient_funds`) |
| **3DS Timeout** | `4012 0010 3714 1115` | Any future date | `123` | **FAILED** (`user_dropped`) |
#### Test 3D-Secure OTP
On the simulated bank OTP page, enter `123456` or click the **Success** button.
---
### 2. Test UPI Handles
| VPA / UPI ID | Action / Behaviour |
| :--- | :--- |
| `success@upi` | Auto-approves payment; triggers instant `PAYMENT_SUCCESS_WEBHOOK`. |
| `failure@upi` | Auto-declines payment; returns bank refusal. |
| `test@okhdfcbank` | Simulates UPI Intent and Collect prompt. |
---
### 3. Test Netbanking
Select any major bank (e.g. HDFC, ICICI, SBI) in Sandbox Checkout. Cashfree displays a mock banking portal with options:
- **Success**: Simulates customer approving netbanking login and OTP.
- **Fail**: Simulates bank session timeout or incorrect credentials.
- **Pending**: Simulates delayed bank settlement.

View file

@ -0,0 +1,197 @@
# Cashfree Payments - Django Microservice Architecture Blueprint
## 1. Architecture Overview for Tradhox Payment Service
This blueprint outlines the recommended production architecture for integrating Cashfree Payment Gateway into the `paymentservice` Django app.
```mermaid
graph TD
A[Tradhox Customer Website / App] -->|1. Request Order| B[POST /api/payments/orders/create/]
B -->|2. Create Order via SDK| C[Cashfree API Gateway]
C -->|3. Return payment_session_id| B
B -->|4. Return session_id| A
A -->|5. Render Cashfree Checkout| D[Customer Completes Payment]
D -->|6. Direct S2S Webhook| E[POST /api/payments/webhooks/cashfree/]
E -->|7. Verify HMAC-SHA256| F[Update Database & Emit Events]
A -->|8. Redirect on Return| G[GET /api/payments/orders/status/]
```
---
## 2. Django Settings Configuration
Add the following to [`config/settings.py`](file:///d:/Tradhox_workspace/paymentservice/config/settings.py):
```python
# Cashfree Payment Gateway Configuration
CASHFREE_APP_ID = os.environ.get('CASHFREE_APP_ID', 'TEST103848...')
CASHFREE_SECRET_KEY = os.environ.get('CASHFREE_SECRET_KEY', 'cfsk_ma_test_...')
CASHFREE_ENVIRONMENT = os.environ.get('CASHFREE_ENVIRONMENT', 'sandbox') # 'sandbox' or 'production'
CASHFREE_API_VERSION = os.environ.get('CASHFREE_API_VERSION', '2023-08-01')
if CASHFREE_ENVIRONMENT == 'production':
CASHFREE_BASE_URL = 'https://api.cashfree.com/pg'
else:
CASHFREE_BASE_URL = 'https://sandbox.cashfree.com/pg'
```
---
## 3. Core Cashfree Client Class
Implement [`payments/cashfree_client.py`](file:///d:/Tradhox_workspace/paymentservice/payments/cashfree_client.py):
```python
import hmac
import hashlib
import base64
import time
import requests
import logging
from django.conf import settings
logger = logging.getLogger(__name__)
class CashfreeClient:
def __init__(self):
self.app_id = settings.CASHFREE_APP_ID
self.secret_key = settings.CASHFREE_SECRET_KEY
self.base_url = settings.CASHFREE_BASE_URL
self.api_version = settings.CASHFREE_API_VERSION
def _get_headers(self, idempotency_key=None):
headers = {
'Content-Type': 'application/json',
'x-api-version': self.api_version,
'x-client-id': self.app_id,
'x-client-secret': self.secret_key,
}
if idempotency_key:
headers['x-idempotency-key'] = idempotency_key
return headers
def create_order(self, order_id, amount, currency, customer, return_url, notify_url=None, note=None):
url = f"{self.base_url}/orders"
payload = {
"order_id": str(order_id),
"order_amount": float(amount),
"order_currency": currency,
"customer_details": {
"customer_id": str(customer.get("id")),
"customer_name": customer.get("name", "Customer"),
"customer_email": customer.get("email"),
"customer_phone": str(customer.get("phone")),
},
"order_meta": {
"return_url": return_url,
"notify_url": notify_url or return_url
}
}
if note:
payload["order_note"] = note
response = requests.post(url, json=payload, headers=self._get_headers(), timeout=10)
return response.status_code, response.json()
def get_order(self, order_id):
url = f"{self.base_url}/orders/{order_id}"
response = requests.get(url, headers=self._get_headers(), timeout=10)
return response.status_code, response.json()
def create_refund(self, order_id, refund_id, amount, note="Refund", speed="STANDARD"):
url = f"{self.base_url}/orders/{order_id}/refunds"
payload = {
"refund_id": str(refund_id),
"refund_amount": float(amount),
"refund_note": note,
"refund_speed": speed
}
response = requests.post(url, json=payload, headers=self._get_headers(), timeout=10)
return response.status_code, response.json()
def verify_webhook_signature(self, raw_body: bytes, signature: str, timestamp: str) -> bool:
if not signature or not timestamp or not self.secret_key:
return False
try:
ts_val = int(timestamp)
if ts_val > 10000000000:
ts_val = ts_val // 1000
if abs(int(time.time()) - ts_val) > 300:
logger.warning("Webhook timestamp older than 5 minutes")
return False
except ValueError:
return False
payload = str(timestamp).encode('utf-8') + raw_body
computed_hmac = hmac.new(
key=self.secret_key.encode('utf-8'),
msg=payload,
digestmod=hashlib.sha256
).digest()
computed_sig = base64.b64encode(computed_hmac).decode('utf-8')
return hmac.compare_digest(computed_sig, signature)
```
---
## 4. Suggested Database Models
Add to [`payments/models.py`](file:///d:/Tradhox_workspace/paymentservice/payments/models.py):
```python
from django.db import models
class PaymentOrder(models.Model):
STATUS_CHOICES = [
('ACTIVE', 'Active'),
('PAID', 'Paid'),
('EXPIRED', 'Expired'),
('TERMINATED', 'Terminated'),
]
order_id = models.CharField(max_length=64, unique=True, primary_key=True)
cf_order_id = models.CharField(max_length=64, blank=True, null=True)
amount = models.DecimalField(max_digits=12, decimal_places=2)
currency = models.CharField(max_length=3, default='INR')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='ACTIVE')
customer_id = models.CharField(max_length=64)
customer_email = models.EmailField()
customer_phone = models.CharField(max_length=15)
payment_session_id = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class PaymentTransaction(models.Model):
STATUS_CHOICES = [
('SUCCESS', 'Success'),
('FAILED', 'Failed'),
('PENDING', 'Pending'),
('USER_DROPPED', 'User Dropped'),
]
cf_payment_id = models.CharField(max_length=64, unique=True, primary_key=True)
order = models.ForeignKey(PaymentOrder, on_delete=models.CASCADE, related_name='transactions')
amount = models.DecimalField(max_digits=12, decimal_places=2)
status = models.CharField(max_length=20, choices=STATUS_CHOICES)
payment_method = models.CharField(max_length=50, blank=True, null=True)
bank_reference = models.CharField(max_length=100, blank=True, null=True)
payment_time = models.DateTimeField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
class PaymentRefund(models.Model):
STATUS_CHOICES = [
('SUCCESS', 'Success'),
('PENDING', 'Pending'),
('CANCELLED', 'Cancelled'),
('FAILED', 'Failed'),
]
refund_id = models.CharField(max_length=64, unique=True, primary_key=True)
cf_refund_id = models.CharField(max_length=64, blank=True, null=True)
order = models.ForeignKey(PaymentOrder, on_delete=models.CASCADE, related_name='refunds')
amount = models.DecimalField(max_digits=12, decimal_places=2)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING')
refund_arn = models.CharField(max_length=64, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
```

View file

@ -0,0 +1,46 @@
# Cashfree Knowledge Base
Complete end-to-end documentation for integrating and managing Cashfree across the Tradhox Payments Microservice (`paymentservice`).
---
## Documentation Index
| Module | Document | Description |
| :--- | :--- | :--- |
| **01** | [Overview & Quickstart](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/01_overview_and_quickstart.md) | Platform overview, environment endpoints (Sandbox & Production), architecture sequence diagram, and first API call. |
| **02** | [Authentication, Headers & Security](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/02_authentication_and_headers.md) | API key credentials (`x-client-id`, `x-client-secret`), versioning (`2023-08-01`), idempotency, rate limiting, and security compliance. |
| **03** | [Orders API Specification](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/03_orders_api.md) | Create Order (`POST /orders`), order lifecycle (`ACTIVE`, `PAID`, `EXPIRED`, `TERMINATED`), Get Order status, and payments lookup. |
| **04** | [Checkout Integrations & Payment Methods](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/04_payment_methods_and_checkout.md) | Hosted Checkout (redirect), Drop-in JS SDK v3, and Seamless API for UPI (Intent, Collect, QR), Cards (Token Vault), Netbanking, and Wallets. |
| **05** | [Webhooks & Signature Verification](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/05_webhooks_and_signature_verification.md) | Event schemas, HMAC-SHA256 signature verification algorithm, anti-replay timestamp validation, and production Django webhook view. |
| **06** | [Refunds & Disputes API](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/06_refunds_and_disputes.md) | Full/Partial refunds (`POST /orders/{order_id}/refunds`), Standard vs Instant refunds, ARN tracking, and chargeback dispute workflows. |
| **07** | [Settlements & Reconciliation](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/07_settlements_and_reconciliation.md) | T+1/T+2 banking settlement cycles, MDR fee & tax calculations, Order-level settlement API, UTR tracking, and settlement webhooks. |
| **08** | [Payouts & Automated Disbursements](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/08_payouts_and_transfers.md) | 24x7 automated disbursements via UPI, IMPS, NEFT, RTGS, Beneficiary onboarding, transfer status tracking, and balance inquiries. |
| **09** | [Secure ID & KYC Verification](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/09_secureid_and_kyc_verification.md) | Bank Account Penny Drop verification, UPI VPA verification, PAN validation, and GSTIN lookup for merchant & vendor compliance. |
| **10** | [Error Codes & Sandbox Test Data](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/10_error_codes_and_test_data.md) | Standard error formats, common error codes dictionary, test Visa/Mastercard/RuPay card numbers, test UPI handles, and simulated netbanking. |
| **11** | [Django Integration Blueprint](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/11_django_integration_blueprint.md) | Production Python `CashfreeClient`, Django models (`PaymentOrder`, `PaymentTransaction`, `PaymentRefund`), settings configuration, and API views. |
---
## Quick Reference Summary
### Base URLs
- **Sandbox**: `https://sandbox.cashfree.com/pg`
- **Production**: `https://api.cashfree.com/pg`
### Standard Request Headers
```http
Content-Type: application/json
x-api-version: 2023-08-01
x-client-id: <CASHFREE_APP_ID>
x-client-secret: <CASHFREE_SECRET_KEY>
x-idempotency-key: <UUIDv4>
```
### Webhook Verification Formula
```text
payload = x-webhook-timestamp + raw_body
computed_hmac = HMAC_SHA256(secret_key, payload)
computed_signature = Base64Encode(computed_hmac)
valid = (computed_signature == x-webhook-signature)
```

View file

@ -0,0 +1,134 @@
# Cashfree Payments - Overview & Quickstart Guide
## 1. Introduction
Cashfree Payments is a full-stack payments and API banking platform in India providing payment gateway, automated payouts, identity verification (Secure ID), and settlement infrastructure.
The core service offerings include:
1. **Payment Gateway (PG)**: Multi-channel online payment collection supporting 120+ payment modes (UPI, Cards, Netbanking, Wallets, EMI, Pay Later).
2. **Payouts**: Bulk and instant bank/UPI disbursements (IMPS, NEFT, RTGS, UPI) 24x7x365.
3. **Secure ID**: KYC, Bank account penny-drop verification, UPI VPA verification, PAN, Aadhaar, GSTIN validation.
4. **Auto-Collect / Virtual Accounts**: Dedicated virtual accounts (VBA) and virtual UPI IDs to reconcile B2B/inward payments automatically.
---
## 2. Platform Architecture & Environments
Cashfree operates two distinct environments:
| Environment | Base URL (Payment Gateway) | Base URL (Payouts) | Purpose |
| :--- | :--- | :--- | :--- |
| **Sandbox (Test)** | `https://sandbox.cashfree.com/pg` | `https://payout-api.cashfree.com/payout/v1` (test mode) | Testing workflows, simulated cards & UPI |
| **Production (Live)** | `https://api.cashfree.com/pg` | `https://payout-api.cashfree.com/payout/v1` | Real financial transactions & settlements |
### Dashboard URLs
- **Sandbox Merchant Dashboard**: `https://merchant.cashfree.com/merchants/test`
- **Production Merchant Dashboard**: `https://merchant.cashfree.com`
---
## 3. High-Level Payment Gateway Lifecycle
The complete order-to-settlement lifecycle follows an asynchronous, event-driven pattern:
```mermaid
sequenceDiagram
autonumber
actor Customer as Customer / User
participant Frontend as Tradhox Frontend (Web / App)
participant Backend as Tradhox Payment Service
participant Cashfree as Cashfree Payment Gateway
participant Bank as Issuing Bank / UPI Network
Customer->>Frontend: Click "Pay Now" at Checkout
Frontend->>Backend: Request Order Creation
Backend->>Cashfree: POST /orders (x-client-id, x-client-secret)
Cashfree-->>Backend: Return cf_order_id & payment_session_id
Backend-->>Frontend: Return payment_session_id
Frontend->>Cashfree: Render Cashfree Checkout SDK / Drop-in
Customer->>Cashfree: Selects UPI / Card / Netbanking & Authorizes
Cashfree->>Bank: Process Payment
Bank-->>Cashfree: Success confirmation
Cashfree-->>Frontend: Return payment status & redirect to return_url
Cashfree->>Backend: POST Webhook (PAYMENT_SUCCESS_WEBHOOK with signature)
Backend->>Backend: Verify HMAC-SHA256 signature & Update DB
Backend-->>Cashfree: 200 OK Response
Backend->>Cashfree: GET /orders/{order_id} (Server-to-Server reconciliation)
```
---
## 4. Key Concepts
### Order (`order_id`)
- Every transaction begins by generating an `order_id` in your system.
- An order contains the order amount, customer details, expiry time, return URL, and notification webhook URL.
- Once created, Cashfree returns a `payment_session_id`.
### Payment Session (`payment_session_id`)
- A secure, short-lived session token used by frontend SDKs (Cashfree JS, React, Flutter, Android, iOS) to securely render payment methods without exposing secret keys.
### Idempotency
- Protect critical endpoints like Order creation and Refund creation using `x-idempotency-key` to ensure duplicate network retries do not duplicate charges.
### Webhooks
- Asynchronous notifications sent directly from Cashfree to your server when payment, refund, or settlement events occur. Webhooks are cryptographically signed with HMAC-SHA256.
---
## 5. Quickstart Step-by-Step
### Step 1: Obtain API Keys
1. Log in to the [Cashfree Merchant Dashboard](https://merchant.cashfree.com).
2. Switch to **Test Environment** (Sandbox).
3. Navigate to **Developers** -> **API Keys**.
4. Generate **App ID** (`x-client-id`) and **Secret Key** (`x-client-secret`).
### Step 2: Make Your First API Call
Create an order using cURL:
```bash
curl --request POST \
--url https://sandbox.cashfree.com/pg/orders \
--header 'Content-Type: application/json' \
--header 'x-api-version: 2023-08-01' \
--header 'x-client-id: <YOUR_APP_ID>' \
--header 'x-client-secret: <YOUR_SECRET_KEY>' \
--data '{
"order_id": "order_test_1001",
"order_amount": 100.00,
"order_currency": "INR",
"customer_details": {
"customer_id": "cust_101",
"customer_email": "vignesh@tradhox.com",
"customer_phone": "9999999999",
"customer_name": "Vignesh"
},
"order_meta": {
"return_url": "https://betapayments.tradhox.com/api/payments/return?order_id={order_id}",
"notify_url": "https://betapayments.tradhox.com/api/payments/webhooks/"
}
}'
```
### Step 3: Response Structure
```json
{
"cf_order_id": 14958231,
"order_id": "order_test_1001",
"entity": "order",
"order_currency": "INR",
"order_amount": 100.00,
"order_status": "ACTIVE",
"payment_session_id": "session_test_xyz123...",
"order_expiry_time": "2026-10-18T21:30:00Z",
"created_at": "2026-10-18T21:00:00Z",
"customer_details": {
"customer_id": "cust_101",
"customer_name": "Vignesh",
"customer_email": "vignesh@tradhox.com",
"customer_phone": "9999999999"
}
}
```
The frontend SDK uses this `payment_session_id` to initiate checkout.

View file

@ -0,0 +1,96 @@
# Cashfree Payments - Authentication, Headers & Security
## 1. Authentication Model
Cashfree Payment Gateway uses API key-based authentication passed via HTTP headers for all server-to-server REST calls.
### Required Authentication Headers
| Header | Type | Description | Example |
| :--- | :--- | :--- | :--- |
| `x-client-id` | String | Your unique Merchant App ID obtained from Dashboard | `TEST103848...` or `103848...` |
| `x-client-secret` | String | Your private Merchant Secret Key (keep confidential) | `cfsk_ma_test_abc123...` |
| `x-api-version` | String | Cashfree API version to ensure backward compatibility | `2023-08-01` (Recommended) |
| `Content-Type` | String | Content encoding (JSON) | `application/json` |
> [!CAUTION]
> Never expose `x-client-secret` in frontend applications (browser JavaScript, mobile apps). All calls containing credentials must originate from your secure backend (such as Tradhox Payment Service).
---
## 2. Optional & Operational Headers
| Header | Description | Use Case |
| :--- | :--- | :--- |
| `x-idempotency-key` | Unique string (e.g. UUIDv4) generated by caller | Prevents duplicate processing of payments, orders, or refunds during network retries |
| `x-request-id` | Tracing identifier | Correlation ID for debugging issues with Cashfree customer support |
### Idempotency Flow
When an API request includes an `x-idempotency-key`:
1. If the request succeeds, Cashfree caches the response against the key.
2. If the request times out or disconnects and you retry with the **same** idempotency key, Cashfree returns the cached response rather than executing a second charge or refund.
3. If the payload differs with the same key, Cashfree returns an `idempotency_conflict` HTTP 409 error.
---
## 3. Rate Limits & Throttling
Cashfree enforces rate limits per endpoint category to ensure system stability.
### Throttling Response Headers
Cashfree returns the following headers on API responses:
```http
x-ratelimit-limit: 120
x-ratelimit-remaining: 119
x-ratelimit-retry-after: 60
```
- When the limit is exceeded, Cashfree responds with HTTP `429 Too Many Requests`.
- Standard limits:
- **Orders API**: 120 requests/minute (Sandbox), 1000+ requests/minute (Production).
- **Refunds API**: 60 requests/minute.
- **Verification / Secure ID**: Based on plan quota.
### Handling 429 Errors in Python / Django
Implement exponential backoff when detecting HTTP 429:
```python
import time
import requests
def call_cashfree_with_retry(method, url, headers, json_data=None, max_retries=3):
retries = 0
backoff = 1.0 # seconds
while retries < max_retries:
response = requests.request(method, url, headers=headers, json=json_data, timeout=10)
if response.status_code == 429:
retry_after = int(response.headers.get('x-ratelimit-retry-after', backoff))
time.sleep(retry_after)
retries += 1
backoff *= 2
continue
return response
return response
```
---
## 4. API Versioning
Cashfree uses date-based versioning. Currently supported versions:
- `2023-08-01` (Latest stable - recommended for Tradhox)
- `2022-09-01`
- `2021-05-21` (Legacy)
Specifying `x-api-version: 2023-08-01` ensures responses follow the modern schema including `payment_session_id`, `customer_details`, and enhanced webhook payloads.
---
## 5. Security & Compliance Checklist
1. **IP Whitelisting**:
- In Production, configure your server public IPs (e.g. EC2 NAT Gateway or Elastic IP) in **Cashfree Dashboard -> Developers -> IP Whitelist**.
2. **Webhook Signature Verification**:
- Never trust incoming webhooks without verifying the `x-webhook-signature` using HMAC-SHA256 and checking the timestamp to prevent replay attacks.
3. **Data Protection & RBI Tokenization**:
- Do not store raw customer Credit/Debit card numbers (PAN) or CVVs on your servers. Use Cashfree's RBI-compliant Token Vault (Card-on-File Tokenization).

View file

@ -0,0 +1,191 @@
# Cashfree Payments - Orders API Specification
## 1. Overview
The Orders API is the starting point of every payment workflow in Cashfree. An order represents an intent to collect payment from a customer for a specific amount.
---
## 2. Create Order
### Endpoint
```http
POST /orders
```
- **Sandbox**: `https://sandbox.cashfree.com/pg/orders`
- **Production**: `https://api.cashfree.com/pg/orders`
### Request Headers
```http
Content-Type: application/json
x-api-version: 2023-08-01
x-client-id: <APP_ID>
x-client-secret: <SECRET_KEY>
x-idempotency-key: <UUIDv4> (Recommended)
```
### Request Payload Parameters
| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `order_id` | String | **Yes** | Unique merchant order ID. Alphanumeric, underscores, hyphens (3-50 chars). |
| `order_amount` | Number | **Yes** | Total payable amount up to 2 decimal places (e.g. `249.00`). |
| `order_currency` | String | **Yes** | 3-letter ISO currency code (`INR`, `USD`, `EUR`, etc.). |
| `customer_details` | Object | **Yes** | Customer identification object. |
| ↳ `customer_id` | String | **Yes** | Unique customer identifier in your system (e.g. `user_10294`). |
| ↳ `customer_phone` | String | **Yes** | 10-digit mobile number for Indian transactions. |
| ↳ `customer_email` | String | **Yes** | Customer email address. |
| ↳ `customer_name` | String | No | Full name of the customer. |
| `order_meta` | Object | No | Meta information controlling redirect and notifications. |
| ↳ `return_url` | String | No | URL to redirect customer after payment. Supports `{order_id}` placeholder. |
| ↳ `notify_url` | String | No | Webhook URL for server-to-server notifications. |
| ↳ `payment_methods` | String | No | Restrict allowed payment modes (e.g. `upi,cc,dc,nb`). |
| `order_expiry_time` | String | No | Expiration timestamp in ISO 8601 UTC format. |
| `order_note` | String | No | Brief note describing the transaction. |
| `order_tags` | Object | No | Custom key-value pairs for metadata and analytics. |
### Sample Request
```json
{
"order_id": "tradhox_ord_987654",
"order_amount": 1499.00,
"order_currency": "INR",
"customer_details": {
"customer_id": "tradhox_cust_42",
"customer_name": "Vignesh Tech",
"customer_email": "vignesh@tradhox.com",
"customer_phone": "9876543210"
},
"order_meta": {
"return_url": "https://betapayments.tradhox.com/api/payments/return?order_id={order_id}",
"notify_url": "https://betapayments.tradhox.com/api/payments/webhooks/",
"payment_methods": "upi,cc,dc,nb,wallet"
},
"order_expiry_time": "2026-10-18T22:30:00Z",
"order_note": "Subscription payment for Tradhox Pro Tier"
}
```
### Sample Response (`201 Created`)
```json
{
"cf_order_id": "291847101",
"order_id": "tradhox_ord_987654",
"entity": "order",
"order_currency": "INR",
"order_amount": 1499.00,
"order_status": "ACTIVE",
"payment_session_id": "session_a1b2c3d4e5f6g7h8_sandbox",
"order_expiry_time": "2026-10-18T22:30:00Z",
"created_at": "2026-10-18T21:05:00Z",
"order_meta": {
"return_url": "https://betapayments.tradhox.com/api/payments/return?order_id={order_id}",
"notify_url": "https://betapayments.tradhox.com/api/payments/webhooks/",
"payment_methods": null
},
"customer_details": {
"customer_id": "tradhox_cust_42",
"customer_name": "Vignesh Tech",
"customer_email": "vignesh@tradhox.com",
"customer_phone": "9876543210"
}
}
```
---
## 3. Order Statuses (`order_status`)
| Status | Description | Action Required |
| :--- | :--- | :--- |
| `ACTIVE` | Order is open and waiting for customer payment | Customer can attempt payment using `payment_session_id` |
| `PAID` | Payment successfully captured | Fulfill order / update order status to completed |
| `EXPIRED` | Order passed its `order_expiry_time` without payment | Customer must initiate a new checkout |
| `TERMINATED` | Order was cancelled by merchant using Terminate API | No further payments accepted |
---
## 4. Get Order Details
Use this endpoint to verify an order's status server-side after return or webhook arrival.
### Endpoint
```http
GET /orders/{order_id}
```
### Sample Response (`200 OK`)
```json
{
"cf_order_id": "291847101",
"order_id": "tradhox_ord_987654",
"entity": "order",
"order_currency": "INR",
"order_amount": 1499.00,
"order_status": "PAID",
"payment_session_id": null,
"order_expiry_time": "2026-10-18T22:30:00Z",
"created_at": "2026-10-18T21:05:00Z",
"order_splits": []
}
```
---
## 5. Get Payments for an Order
Retrieves all payment attempts associated with an order, including failed, user-dropped, and successful attempts.
### Endpoint
```http
GET /orders/{order_id}/payments
```
### Sample Response (`200 OK`)
```json
[
{
"cf_payment_id": "1948572019",
"order_id": "tradhox_ord_987654",
"entity": "payment",
"payment_currency": "INR",
"payment_amount": 1499.00,
"payment_time": "2026-10-18T21:06:12Z",
"payment_completion_time": "2026-10-18T21:06:18Z",
"payment_status": "SUCCESS",
"payment_message": "Transaction Successful",
"bank_reference": "629184719283",
"auth_id": null,
"payment_method": {
"upi": {
"channel": "intent",
"upi_id": "user@okaxis"
}
},
"payment_group": "upi"
}
]
```
### Payment Statuses (`payment_status`)
- `SUCCESS`: Payment captured successfully.
- `FAILED`: Payment declined by bank or user entered wrong credentials.
- `PENDING`: Waiting for bank processing (common in Netbanking / UPI collect).
- `USER_DROPPED`: Customer cancelled or closed payment window without completing.
---
## 6. Terminate an Order
Marks an unpaid `ACTIVE` order as `TERMINATED`, preventing any further payment attempts.
### Endpoint
```http
PATCH /orders/{order_id}
```
### Request Payload
```json
{
"order_status": "TERMINATED"
}
```

View file

@ -0,0 +1,232 @@
# Cashfree Payments - Checkout Integrations & Payment Methods
## 1. Checkout Integration Models
Cashfree provides 3 integration approaches depending on control vs. effort:
```mermaid
graph TD
A[Checkout Approach] --> B[1. Hosted Checkout]
A --> C[2. Drop-in JS SDK]
A --> D[3. Seamless / Custom API]
B -->|Fastest| E[Redirect to Cashfree Hosted Page]
C -->|Recommended| F[Embed Cashfree Modal or Inline UI on your site]
D -->|Full UI Control| G[Direct Server/App API calls with custom frontend forms]
```
---
## 2. Approach 1: Hosted Checkout (Redirect Flow)
The fastest way to accept payments. After backend generates `payment_session_id`, redirect the user to Cashfree's hosted page:
### Redirect URL Format
- **Sandbox**: `https://payments-test.cashfree.com/order/#<payment_session_id>`
- **Production**: `https://payments.cashfree.com/order/#<payment_session_id>`
### Example Redirect
```python
# Django view example
from django.shortcuts import redirect
def start_payment_view(request):
session_id = create_cashfree_order_session(...)
checkout_url = f"https://payments-test.cashfree.com/order/#{session_id}"
return redirect(checkout_url)
```
Once payment completes, Cashfree redirects the customer back to your configured `return_url`.
---
## 3. Approach 2: Cashfree Drop-in Web SDK (v3)
The recommended integration for modern web apps (React, Next.js, Vue, or Vanilla JS). Renders an in-page modal or iframe with zero PCI-DSS compliance overhead.
### Step 1: Add Cashfree JS Script
```html
<!-- Production -->
<script src="https://sdk.cashfree.com/js/v3/cashfree.js"></script>
```
### Step 2: Initialize and Trigger Checkout
```javascript
// Initialize SDK with environment mode
const cashfree = Cashfree({
mode: "sandbox" // "sandbox" or "production"
});
// Trigger checkout with session ID
function openCheckout(paymentSessionId) {
const checkoutOptions = {
paymentSessionId: paymentSessionId,
redirectTarget: "_self" // "_self", "_blank", or "_modal"
};
cashfree.checkout(checkoutOptions).then((result) => {
if (result.error) {
console.error("Payment error or dismiss:", result.error);
}
if (result.redirect) {
console.log("Customer redirected");
}
if (result.paymentDetails) {
console.log("Payment completed:", result.paymentDetails);
}
});
}
```
---
## 4. Approach 3: Seamless / Headless Payment API
For custom mobile apps or fully native checkouts where your app collects payment details directly.
### Endpoint
```http
POST /orders/sessions
```
Or Order Pay API:
```http
POST /orders/pay
```
### 1. UPI Payment Modes
#### A. UPI Intent (Mobile Apps - Google Pay, PhonePe, Paytm chooser)
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"upi": {
"channel": "intent"
}
}
}
```
*Returns `data.payload.intent` link (`upi://pay?...`) which triggers mobile UPI apps directly.*
#### B. UPI Collect (VPA Prompt)
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"upi": {
"channel": "collect",
"upi_id": "customer@okhdfcbank"
}
}
}
```
*Triggers a push notification on the customer's UPI app.*
#### C. Dynamic UPI QR Code
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"upi": {
"channel": "qrcode"
}
}
}
```
*Returns a dynamic Base64 encoded QR image and UPI URI for in-store or desktop scanning.*
---
### 2. Cards (Credit / Debit)
Supports Visa, Mastercard, RuPay, American Express, Diners, Maestro.
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"card": {
"channel": "link",
"card_number": "4012001037141112",
"card_holder_name": "Vignesh Tech",
"card_expiry_mm": "12",
"card_expiry_yy": "28",
"card_cvv": "123"
}
}
}
```
*Returns 3D-Secure ACS authentication URL (`action: "3ds"`, `url: "https://..."`) to redirect customer for OTP verification.*
#### Saved Card Tokenization (Token Vault / CoFT)
To comply with RBI guidelines, card numbers cannot be stored. Cashfree provides Card-on-File Tokenization:
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"card": {
"channel": "link",
"card_alias": "token_ref_98234",
"card_cvv": "123"
}
}
}
```
---
### 3. Net Banking
Supports 50+ Indian commercial and public sector banks.
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"netbanking": {
"channel": "link",
"netbanking_bank_code": 3003
}
}
}
```
#### Major Bank Codes
| Bank Name | Bank Code |
| :--- | :--- |
| **HDFC Bank** | `3003` |
| **ICICI Bank** | `3021` |
| **State Bank of India (SBI)** | `3044` |
| **Axis Bank** | `3001` |
| **Kotak Mahindra Bank** | `3032` |
| **Punjab National Bank (PNB)** | `3038` |
| **Bank of Baroda** | `3005` |
| **Canara Bank** | `3011` |
---
### 4. Wallets
```json
{
"payment_session_id": "session_xyz...",
"payment_method": {
"app": {
"channel": "link",
"provider": "paytm",
"phone": "9876543210"
}
}
}
```
#### Supported Wallet Providers:
`paytm`, `phonepe`, `mobikwik`, `freecharge`, `airtelmoney`, `olamoney`, `amazonpay`.
---
### 5. Pay Later & EMI
- **Pay Later Providers**: `simpl`, `lazypay`, `zestmoney`.
- **Cardless EMI**: `zestmoney`, `earlysalary`, `instacred`.
- **Bank EMI**: Available for major credit cards with 3, 6, 9, 12, 18, 24 month tenures.

View file

@ -0,0 +1,238 @@
# Cashfree Payments - Webhooks & Signature Verification
## 1. Webhooks Architecture
Webhooks are automated HTTP POST requests sent from Cashfree to your backend when critical transaction events occur. They serve as the single source of truth for transaction states (such as asynchronous UPI confirmation, Netbanking approval, or 3DS completion).
> [!IMPORTANT]
> Never fulfill orders solely based on frontend redirection or query parameters. Always verify the transaction status via Webhooks or the server-to-server `GET /orders/{order_id}` API.
---
## 2. Webhook Event Types
Cashfree emits events categorized by product:
| Event Type (`type`) | Description |
| :--- | :--- |
| `PAYMENT_SUCCESS_WEBHOOK` | Customer successfully paid; funds captured. |
| `PAYMENT_FAILED_WEBHOOK` | Transaction declined by bank, insufficient funds, or wrong PIN. |
| `PAYMENT_USER_DROPPED_WEBHOOK` | Customer exited checkout without completing payment. |
| `REFUND_STATUS_WEBHOOK` | Refund status changed (`SUCCESS`, `FAILED`, `CANCELLED`). |
| `SETTLEMENT_SUCCESS_WEBHOOK` | Settlement transferred to merchant bank account. |
| `DISPUTE_CREATED_WEBHOOK` | Customer or bank raised a chargeback dispute. |
---
## 3. Webhook Payload Structure
### Sample `PAYMENT_SUCCESS_WEBHOOK`
```json
{
"data": {
"order": {
"order_id": "tradhox_ord_987654",
"order_amount": 1499.00,
"order_currency": "INR",
"order_tags": null
},
"payment": {
"cf_payment_id": "291847101",
"payment_status": "SUCCESS",
"payment_amount": 1499.00,
"payment_currency": "INR",
"payment_message": "00::Transaction Success",
"payment_time": "2026-10-18T21:06:12Z",
"payment_completion_time": "2026-10-18T21:06:18Z",
"bank_reference": "629184719283",
"auth_id": null,
"payment_method": {
"upi": {
"channel": "intent",
"upi_id": "user@okaxis"
}
},
"payment_group": "upi"
},
"customer_details": {
"customer_name": "Vignesh Tech",
"customer_id": "tradhox_cust_42",
"customer_email": "vignesh@tradhox.com",
"customer_phone": "9876543210"
}
},
"event_time": "2026-10-18T21:06:20Z",
"type": "PAYMENT_SUCCESS_WEBHOOK"
}
```
---
## 4. Cryptographic Signature Verification
Every Cashfree webhook includes two security headers:
- `x-webhook-signature`: Base64 encoded HMAC-SHA256 signature string.
- `x-webhook-timestamp`: Epoch timestamp in milliseconds (or ISO format) when webhook was dispatched.
### Verification Algorithm
```text
1. Read raw request body bytes (do NOT parse or re-serialize JSON before hashing!).
2. Concatenate: signed_data = x-webhook-timestamp + raw_body
3. Compute HMAC-SHA256 using your Cashfree Secret Key (client_secret).
4. Base64 encode the resulting hash.
5. Compare the computed signature with x-webhook-signature using timing-safe comparison.
```
```mermaid
flowchart LR
A[x-webhook-timestamp] --> C[Concatenate]
B[Raw Request Body Bytes] --> C
C --> D[HMAC-SHA256 with Secret Key]
D --> E[Base64 Encode]
E --> F{Constant-time Comparison with x-webhook-signature}
F -->|Match| G[Process Webhook]
F -->|Mismatch| H[Reject 400 Bad Request]
```
---
## 5. Production-Ready Python / Django Implementation
Save this verification helper in your service (e.g. [`payments/utils.py`](file:///d:/Tradhox_workspace/paymentservice/payments/utils.py)):
```python
import hmac
import hashlib
import base64
import time
import logging
logger = logging.getLogger(__name__)
def verify_cashfree_webhook_signature(
raw_body: bytes,
signature: str,
timestamp: str,
secret_key: str,
max_drift_seconds: int = 300
) -> bool:
"""
Verifies Cashfree webhook signature using HMAC-SHA256.
Args:
raw_body: Exact raw bytes from request.body
signature: Value from x-webhook-signature header
timestamp: Value from x-webhook-timestamp header
secret_key: Cashfree Merchant Client Secret Key
max_drift_seconds: Allowed time difference in seconds (anti-replay)
Returns:
bool: True if signature is authentic and timestamp is fresh, False otherwise
"""
if not signature or not timestamp or not secret_key:
logger.warning("Missing required fields for webhook signature verification")
return False
# 1. Anti-Replay Check (5 minutes threshold)
try:
ts_val = int(timestamp)
# If timestamp is in milliseconds, convert to seconds
if ts_val > 10000000000:
ts_val = ts_val // 1000
current_time = int(time.time())
if abs(current_time - ts_val) > max_drift_seconds:
logger.warning(f"Webhook timestamp expired: drift={abs(current_time - ts_val)}s")
return False
except ValueError:
logger.warning(f"Invalid timestamp format: {timestamp}")
return False
# 2. Build signed payload: timestamp + raw_body
ts_bytes = str(timestamp).encode('utf-8')
payload = ts_bytes + raw_body
# 3. Compute HMAC-SHA256
computed_hmac = hmac.new(
key=secret_key.encode('utf-8'),
msg=payload,
digestmod=hashlib.sha256
).digest()
computed_signature = base64.b64encode(computed_hmac).decode('utf-8')
# 4. Constant-time comparison
is_valid = hmac.compare_digest(computed_signature, signature)
if not is_valid:
logger.warning("Webhook signature mismatch!")
return is_valid
```
### Complete Django Webhook View Example
```python
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse, HttpResponse
from django.conf import settings
import json
import logging
from .utils import verify_cashfree_webhook_signature
logger = logging.getLogger(__name__)
@csrf_exempt
def cashfree_webhook_view(request):
if request.method != "POST":
return HttpResponse(status=405)
signature = request.headers.get("x-webhook-signature")
timestamp = request.headers.get("x-webhook-timestamp")
secret_key = getattr(settings, "CASHFREE_SECRET_KEY", "")
# Verify signature
if not verify_cashfree_webhook_signature(request.body, signature, timestamp, secret_key):
return JsonResponse({"error": "Invalid signature"}, status=400)
try:
payload = json.loads(request.body.decode("utf-8"))
event_type = payload.get("type")
data = payload.get("data", {})
order_info = data.get("order", {})
payment_info = data.get("payment", {})
order_id = order_info.get("order_id")
logger.info(f"Received Cashfree Webhook: {event_type} for order {order_id}")
if event_type == "PAYMENT_SUCCESS_WEBHOOK":
# Idempotent check: Ensure order is not already marked paid
# fulfill_order(order_id, payment_info)
pass
elif event_type == "PAYMENT_FAILED_WEBHOOK":
# record_payment_failure(order_id, payment_info)
pass
elif event_type == "REFUND_STATUS_WEBHOOK":
# update_refund_status(data)
pass
# Return 200 OK immediately
return JsonResponse({"status": "acknowledged"}, status=200)
except Exception as e:
logger.error(f"Error processing webhook: {e}", exc_info=True)
return JsonResponse({"status": "internal_error"}, status=500)
```
---
## 6. Retries and Delivery Guarantee
- Cashfree requires your server to respond with HTTP `200 OK` within **5 seconds**.
- If your server responds with an error code (5xx, 4xx) or times out, Cashfree retries up to **7 times** with exponential backoff:
- 1st retry: after 1 minute
- 2nd retry: after 5 minutes
- 3rd retry: after 15 minutes
- 4th retry: after 30 minutes
- 5th retry: after 1 hour
- 6th retry: after 2 hours
- 7th retry: after 4 hours
- **Always design webhook handlers to be strictly idempotent** by verifying against the database before executing side-effects.

View file

@ -0,0 +1,155 @@
# Cashfree Payments - Refunds & Disputes API
## 1. Refunds Overview
Cashfree provides programmatic refund capabilities for both full and partial order amounts. Refunds can be initiated directly against any successfully paid order.
### Refund Speeds
1. **Standard Refund (`STANDARD`)**:
- Follows regular banking clearinghouse cycles.
- Takes 5 to 7 business days to reflect in the customer's source account/card.
2. **Instant Refund (`INSTANT`)**:
- Uses instant payment rails (UPI / IMPS).
- Credits customer account within seconds or minutes.
- Requires Instant Refunds to be enabled in Cashfree Dashboard.
---
## 2. Create a Refund
### Endpoint
```http
POST /orders/{order_id}/refunds
```
- **Sandbox**: `https://sandbox.cashfree.com/pg/orders/{order_id}/refunds`
- **Production**: `https://api.cashfree.com/pg/orders/{order_id}/refunds`
### Request Headers
```http
Content-Type: application/json
x-api-version: 2023-08-01
x-client-id: <APP_ID>
x-client-secret: <SECRET_KEY>
x-idempotency-key: <UUIDv4>
```
### Request Payload Parameters
| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `refund_amount` | Number | **Yes** | Amount to be refunded. Cannot exceed remaining order balance. |
| `refund_id` | String | **Yes** | Unique merchant refund ID (alphanumeric, underscores, hyphens). |
| `refund_note` | String | No | Reason or note for the refund (e.g. `Customer requested return`). |
| `refund_speed` | String | No | `"STANDARD"` (default) or `"INSTANT"`. |
| `refund_splits` | Array | No | For split payments (Easy Split marketplace vendors). |
### Sample Request
```json
{
"refund_amount": 499.00,
"refund_id": "tradhox_ref_10029",
"refund_note": "Partial return for damaged item",
"refund_speed": "STANDARD"
}
```
### Sample Response (`201 Created` / `200 OK`)
```json
{
"cf_refund_id": "39104812",
"cf_payment_id": "291847101",
"refund_id": "tradhox_ref_10029",
"order_id": "tradhox_ord_987654",
"entity": "refund",
"refund_currency": "INR",
"refund_amount": 499.00,
"refund_note": "Partial return for damaged item",
"refund_status": "SUCCESS",
"refund_mode": "STANDARD",
"refund_type": "MERCHANT_INITIATED",
"refund_arn": "62918401928374",
"created_at": "2026-10-18T21:15:00Z",
"processed_at": "2026-10-18T21:15:02Z",
"status_description": "Refund processed successfully"
}
```
> [!NOTE]
> For Card refunds, `refund_arn` represents the **Acquirer Reference Number (ARN)**. Provide this number to customers so their issuing bank can trace the credited amount.
---
## 3. Refund Statuses (`refund_status`)
| Status | Description | Action Required |
| :--- | :--- | :--- |
| `SUCCESS` | Refund processed successfully by Cashfree and partner bank. | Update order/refund record as refunded. |
| `PENDING` | Refund is under processing with the customer's bank. | Wait for `REFUND_STATUS_WEBHOOK` or poll status. |
| `CANCELLED` | Refund was cancelled by merchant before processing. | No funds deducted. |
| `FAILED` | Bank declined the refund (e.g. account closed, card expired). | Retry or disburse via Payouts directly. |
---
## 4. Get Refund Details
Fetch details and status of a specific refund.
### Endpoint
```http
GET /orders/{order_id}/refunds/{refund_id}
```
---
## 5. List All Refunds for an Order
Retrieves all refunds (partial or full) issued against an order.
### Endpoint
```http
GET /orders/{order_id}/refunds
```
### Sample Response (`200 OK`)
```json
[
{
"cf_refund_id": "39104812",
"refund_id": "tradhox_ref_10029",
"order_id": "tradhox_ord_987654",
"refund_amount": 499.00,
"refund_status": "SUCCESS",
"refund_arn": "62918401928374"
}
]
```
---
## 6. Disputes & Chargeback Management
When a cardholder or account owner disputes a transaction with their bank:
### Dispute Lifecycle
1. **Retrieval Request**: Bank requests transaction proof before escalating.
2. **Chargeback / Dispute Created**: Funds temporarily debited; merchant notified via `DISPUTE_CREATED_WEBHOOK`.
3. **Contest Phase**: Merchant submits proof (invoice, AWB tracking, delivery signature).
4. **Resolution**: Bank or Card Network (Visa/Mastercard) awards dispute to merchant or customer.
### Dispute Webhook Payload
```json
{
"data": {
"dispute_id": "disp_8291038",
"order_id": "tradhox_ord_987654",
"cf_payment_id": "291847101",
"dispute_amount": 1499.00,
"dispute_status": "DISPUTE_CREATED",
"respond_by": "2026-10-25T23:59:59Z",
"reason_code": "4853",
"reason_description": "Goods or services not received"
},
"event_time": "2026-10-18T21:20:00Z",
"type": "DISPUTE_CREATED_WEBHOOK"
}
```

View file

@ -0,0 +1,88 @@
# Cashfree Payments - Settlements & Reconciliation
## 1. Settlement Overview
Settlement is the process where captured customer payments are transferred from Cashfree to your registered business bank account after deducting MDR (Merchant Discount Rate) fees and GST.
### Standard Settlement Cycle
- **Standard (T+1 or T+2 Days)**: Funds captured on Day T are credited to the merchant bank account on the next 1-2 banking business days.
- **Instant / On-Demand Settlements**: Allows merchants to trigger immediate settlement transfers 24x7 without waiting for standard banking cutoff windows.
---
## 2. Settlement Calculations & Breakdown
For every transaction, the settlement formula is:
$$\text{Settlement Amount} = \text{Order Amount} - (\text{Payment Gateway Fee} + \text{GST on Fee}) \pm \text{Adjustments}$$
### Sample Breakdown
- **Order Amount**: ₹1,000.00
- **MDR (PG Fee @ 1.9%)**: ₹19.00
- **GST on MDR (18%)**: ₹3.42
- **Net Credited**: ₹977.58
---
## 3. Settlement APIs
### 1. Get Settlements by Order ID
Check if and when a specific order was settled.
```http
GET /orders/{order_id}/settlements
```
#### Response (`200 OK`)
```json
{
"cf_order_id": "291847101",
"order_id": "tradhox_ord_987654",
"settlement_currency": "INR",
"settlement_amount": 977.58,
"service_charge": 19.00,
"service_tax": 3.42,
"settlement_id": "8291048",
"transfer_utr": "HDFCN26101892837",
"transfer_time": "2026-10-19T06:30:00Z",
"settlement_status": "SUCCESS"
}
```
---
### 2. Get All Settlements
Fetch consolidated batch settlements across a date range.
```http
GET /settlements?start_date=2026-10-01&end_date=2026-10-18&limit=50
```
#### Response Fields
- `cf_settlement_id`: Unique identifier for the settlement batch.
- `settlement_amount`: Total amount credited to merchant.
- `transfer_utr`: Bank UTR (Unique Transaction Reference) for reconciliation against bank statements.
- `settlement_status`: `SUCCESS`, `PENDING`, `FAILED`.
---
## 4. Settlement Webhooks
Configure `SETTLEMENT_SUCCESS_WEBHOOK` to receive real-time notification when bank transfer completes.
```json
{
"data": {
"settlement_id": "8291048",
"settlement_amount": 145892.40,
"settlement_currency": "INR",
"transfer_utr": "HDFCN26101994827",
"transfer_time": "2026-10-19T06:30:00Z",
"settlement_status": "SUCCESS"
},
"event_time": "2026-10-19T06:30:05Z",
"type": "SETTLEMENT_SUCCESS_WEBHOOK"
}
```

View file

@ -0,0 +1,177 @@
# Cashfree Payouts & Automated Disbursements
## 1. Overview
Cashfree Payouts allows automated disbursements to vendor bank accounts, delivery partners, refunds, affiliate payouts, and seller settlements 24x7x365 across India.
### Supported Rails
- **UPI**: Instant, up to ₹1,00,000 per transaction (or ₹5,00,000 for select categories).
- **IMPS**: Instant, available 24x7, up to ₹5,00,000 per transaction.
- **NEFT**: Batch settlements throughout the day, no upper limit.
- **RTGS**: Real-time gross settlement for high-value transactions (minimum ₹2,00,000).
- **Card Payouts**: Direct credits to Visa/Mastercard debit cards.
---
## 2. Environments & Base URLs
| Environment | Base URL |
| :--- | :--- |
| **Sandbox (Gamma)** | `https://payout-gamma.cashfree.com/payout/v1` |
| **Production** | `https://payout-api.cashfree.com/payout/v1` |
### Authentication
Payouts API supports passing credentials in headers or exchanging them for a short-lived bearer token:
```http
X-Client-Id: <PAYOUT_CLIENT_ID>
X-Client-Secret: <PAYOUT_CLIENT_SECRET>
```
Or via Auth Token:
```http
POST /payout/v1/authorize
Authorization: Bearer <TOKEN>
```
---
## 3. Beneficiary Management
Before transferring funds, register the beneficiary (or use quick-transfer APIs).
### Add Beneficiary
```http
POST /payout/v1/addBeneficiary
```
#### Request Payload
```json
{
"beneId": "vendor_vignesh_01",
"name": "Vignesh Tech",
"email": "vignesh@tradhox.com",
"phone": "9876543210",
"bankAccount": "50100234567890",
"ifsc": "HDFC0001234",
"address1": "Tradhox HQ, Tech Park",
"city": "Bengaluru",
"state": "Karnataka",
"pincode": "560001"
}
```
*For UPI Disbursements, replace `bankAccount` and `ifsc` with `vpa`:*
```json
{
"beneId": "seller_upi_42",
"name": "Seller Partner",
"email": "partner@tradhox.com",
"phone": "9876543210",
"vpa": "partner@okaxis",
"address1": "Seller Hub",
"city": "Mumbai",
"state": "Maharashtra",
"pincode": "400001"
}
```
---
## 4. Request a Transfer (Disbursement)
Initiates a payout from your Cashfree account balance to the beneficiary.
### Endpoint
```http
POST /payout/v1/requestTransfer
```
#### Request Payload
```json
{
"transferId": "tradhox_payout_88291",
"beneId": "vendor_vignesh_01",
"amount": "25000.00",
"transferMode": "IMPS",
"remarks": "Vendor weekly payout settlement"
}
```
#### Transfer Modes (`transferMode`):
- `IMPS`: Instant bank transfer.
- `NEFT`: Standard bank clearing.
- `RTGS`: High value bank transfer (>= ₹2,00,000).
- `UPI`: Instant transfer to UPI VPA.
#### Response (`200 OK`)
```json
{
"status": "SUCCESS",
"subCode": "200",
"message": "Transfer initiated successfully",
"data": {
"referenceId": "1948571029",
"transferId": "tradhox_payout_88291",
"utr": "629184910284",
"acknowledged": 1
}
}
```
---
## 5. Get Transfer Status
### Endpoint
```http
GET /payout/v1/getTransferStatus?transferId=tradhox_payout_88291
```
#### Response (`200 OK`)
```json
{
"status": "SUCCESS",
"subCode": "200",
"message": "Transfer details fetched successfully",
"data": {
"transfer": {
"transferId": "tradhox_payout_88291",
"referenceId": "1948571029",
"status": "SUCCESS",
"statusDescription": "Transfer completed successfully",
"amount": "25000.00",
"utr": "629184910284",
"addedOn": "2026-10-18 21:20:00",
"processedOn": "2026-10-18 21:20:03"
}
}
}
```
### Payout Statuses
- `SUCCESS`: Transfer completed; beneficiary account credited.
- `PENDING`: In progress with clearing network (e.g. NEFT window or slow bank response).
- `FAILED`: Transfer rejected by beneficiary bank (invalid account, IFSC, or limit reached).
- `REVERSED`: Transaction initially cleared but subsequently reversed by receiving bank.
---
## 6. Check Account Balance
Verify available wallet balance before triggering payouts.
### Endpoint
```http
GET /payout/v1/getBalance
```
#### Response (`200 OK`)
```json
{
"status": "SUCCESS",
"subCode": "200",
"message": "Balance fetched successfully",
"data": {
"balance": "485290.50",
"availableBalance": "485290.50"
}
}
```

View file

@ -0,0 +1,157 @@
# Cashfree Secure ID & KYC Verification APIs
## 1. Overview
Cashfree Secure ID provides API-driven identity, bank account, and compliance verification against authoritative Indian databases (NPCI, NSDL, UIDAI, GSTN).
Common use cases for Tradhox:
- Verifying vendor / seller bank accounts before onboarding.
- Verifying PAN & GSTIN for marketplace merchant compliance.
- Verifying customer UPI IDs to prevent payment failures and fraud.
---
## 2. Bank Account Verification (Penny Drop)
Deposits ₹1.00 into the target bank account via IMPS to verify that the account is active and retrieve the exact registered name.
### Endpoint
```http
GET /verification/bank-account/sync?name={name}&phone={phone}&bankAccount={account}&ifsc={ifsc}
```
Or via POST:
```http
POST /verification/bank-account/sync
```
#### Request Payload
```json
{
"bankAccount": "50100234567890",
"ifsc": "HDFC0001234",
"name": "Vignesh Tech",
"phone": "9876543210"
}
```
#### Response (`200 OK`)
```json
{
"status": "SUCCESS",
"subCode": "200",
"message": "Bank account verified successfully",
"accountStatus": "VALID",
"accountStatusCode": "ACCOUNT_IS_VALID",
"data": {
"nameAtBank": "VIGNESH TECH PRIVATE LIMITED",
"refId": "1948571029",
"bankName": "HDFC BANK",
"branch": "INDIRANAGAR",
"city": "BANGALORE",
"micr": "560240012",
"nameMatchScore": 95,
"nameMatchResult": "SUCCESS"
}
}
```
> [!TIP]
> The `nameMatchScore` provides a 0-100 fuzzy matching score comparing the provided name with the official `nameAtBank`.
---
## 3. UPI VPA Verification
Checks whether a UPI ID exists without deducting or transferring money.
### Endpoint
```http
POST /verification/vpa
```
#### Request Payload
```json
{
"vpa": "vignesh@okhdfcbank",
"name": "Vignesh"
}
```
#### Response (`200 OK`)
```json
{
"status": "SUCCESS",
"subCode": "200",
"message": "VPA verified successfully",
"vpa": "vignesh@okhdfcbank",
"accountExists": "YES",
"nameAtBank": "Vignesh Tech",
"nameMatchScore": 100
}
```
---
## 4. PAN Verification
Validates PAN against the Income Tax Department / NSDL database.
### Endpoint
```http
POST /verification/pan
```
#### Request Payload
```json
{
"pan": "ABCDE1234F",
"name": "Vignesh Tech"
}
```
#### Response (`200 OK`)
```json
{
"status": "VALID",
"pan": "ABCDE1234F",
"type": "Individual",
"registered_name": "VIGNESH TECH",
"name_match_score": 100,
"aadhaar_seeding_status": "LINKED"
}
```
---
## 5. GSTIN Verification
Validates Goods and Services Tax Identification Number.
### Endpoint
```http
POST /verification/gstin
```
#### Request Payload
```json
{
"gstin": "29ABCDE1234F1Z5"
}
```
#### Response (`200 OK`)
```json
{
"status": "VALID",
"gstin": "29ABCDE1234F1Z5",
"legal_name": "TRADHOX TECHNOLOGIES PRIVATE LIMITED",
"trade_name": "TRADHOX",
"registration_date": "2024-01-15",
"gstin_status": "Active",
"taxpayer_type": "Regular",
"principal_place_of_business": {
"address": "Tech Park, Outer Ring Road",
"state": "Karnataka",
"pincode": "560103"
}
}
```

View file

@ -0,0 +1,78 @@
# Cashfree Payments - Error Codes & Sandbox Test Data
## 1. Error Response Structure
All Cashfree Payment Gateway APIs return consistent JSON error objects when a request fails:
```json
{
"message": "Order with id 'tradhox_ord_987654' is already paid",
"code": "order_already_paid",
"type": "invalid_request_error"
}
```
### Top-Level Error Types (`type`)
| Error Type | HTTP Status | Meaning |
| :--- | :--- | :--- |
| `invalid_request_error` | `400`, `404`, `409` | Malformed body, missing required parameters, invalid IDs, or invalid state transitions. |
| `authentication_error` | `401` | Invalid or missing `x-client-id`, `x-client-secret`, or expired tokens. |
| `rate_limit_error` | `429` | Request volume exceeded allowed threshold. Implement backoff. |
| `api_error` | `500`, `502`, `503` | Temporary server-side issue at Cashfree or partner bank. |
---
## 2. Common API Error Codes
| Error Code (`code`) | HTTP Status | Description & Recommended Handling |
| :--- | :--- | :--- |
| `order_already_paid` | 400 | The order has already been successfully paid. Do not prompt user again; fetch order status. |
| `order_expired` | 400 | The order has passed its `order_expiry_time`. Generate a new order. |
| `order_terminated` | 400 | The order was marked as terminated. |
| `order_not_found` | 404 | No order exists matching the provided `order_id`. Verify ID spelling. |
| `duplicate_order_id` | 409 | An order with this `order_id` already exists. Use a unique order ID generator. |
| `idempotency_conflict` | 409 | The same `x-idempotency-key` was submitted with a different request payload. |
| `invalid_customer_details` | 400 | Phone number is not 10 digits or email format is invalid. |
| `payment_declined` | 400 | Customer's issuing bank declined the card or UPI payment. |
| `insufficient_balance` | 400 | Insufficient balance in merchant account to process instant refund or payout. |
| `unauthorized_ip` | 401 | Calling server IP is not in your Merchant Dashboard IP Whitelist. |
---
## 3. Sandbox Test Credentials & Simulators
When testing in the **Sandbox** environment (`https://sandbox.cashfree.com/pg`):
### 1. Test Cards
| Card Type | Card Number | Expiry | CVV | Expected Result |
| :--- | :--- | :--- | :--- | :--- |
| **Visa (Success)** | `4012 0010 3714 1112` | Any future date (e.g. `12/28`) | `123` | **SUCCESS** (3DS OTP: `123456`) |
| **Mastercard (Success)** | `5123 4567 8901 2346` | Any future date | `123` | **SUCCESS** |
| **RuPay (Success)** | `6071 2345 6789 0123` | Any future date | `123` | **SUCCESS** |
| **Card Declined** | `4012 0010 3714 1113` | Any future date | `123` | **FAILED** (`payment_declined`) |
| **Insufficient Funds** | `4012 0010 3714 1114` | Any future date | `123` | **FAILED** (`insufficient_funds`) |
| **3DS Timeout** | `4012 0010 3714 1115` | Any future date | `123` | **FAILED** (`user_dropped`) |
#### Test 3D-Secure OTP
On the simulated bank OTP page, enter `123456` or click the **Success** button.
---
### 2. Test UPI Handles
| VPA / UPI ID | Action / Behaviour |
| :--- | :--- |
| `success@upi` | Auto-approves payment; triggers instant `PAYMENT_SUCCESS_WEBHOOK`. |
| `failure@upi` | Auto-declines payment; returns bank refusal. |
| `test@okhdfcbank` | Simulates UPI Intent and Collect prompt. |
---
### 3. Test Netbanking
Select any major bank (e.g. HDFC, ICICI, SBI) in Sandbox Checkout. Cashfree displays a mock banking portal with options:
- **Success**: Simulates customer approving netbanking login and OTP.
- **Fail**: Simulates bank session timeout or incorrect credentials.
- **Pending**: Simulates delayed bank settlement.

View file

@ -0,0 +1,197 @@
# Cashfree Payments - Django Microservice Architecture Blueprint
## 1. Architecture Overview for Tradhox Payment Service
This blueprint outlines the recommended production architecture for integrating Cashfree Payment Gateway into the `paymentservice` Django app.
```mermaid
graph TD
A[Tradhox Customer Website / App] -->|1. Request Order| B[POST /api/payments/orders/create/]
B -->|2. Create Order via SDK| C[Cashfree API Gateway]
C -->|3. Return payment_session_id| B
B -->|4. Return session_id| A
A -->|5. Render Cashfree Checkout| D[Customer Completes Payment]
D -->|6. Direct S2S Webhook| E[POST /api/payments/webhooks/cashfree/]
E -->|7. Verify HMAC-SHA256| F[Update Database & Emit Events]
A -->|8. Redirect on Return| G[GET /api/payments/orders/status/]
```
---
## 2. Django Settings Configuration
Add the following to [`config/settings.py`](file:///d:/Tradhox_workspace/paymentservice/config/settings.py):
```python
# Cashfree Payment Gateway Configuration
CASHFREE_APP_ID = os.environ.get('CASHFREE_APP_ID', 'TEST103848...')
CASHFREE_SECRET_KEY = os.environ.get('CASHFREE_SECRET_KEY', 'cfsk_ma_test_...')
CASHFREE_ENVIRONMENT = os.environ.get('CASHFREE_ENVIRONMENT', 'sandbox') # 'sandbox' or 'production'
CASHFREE_API_VERSION = os.environ.get('CASHFREE_API_VERSION', '2023-08-01')
if CASHFREE_ENVIRONMENT == 'production':
CASHFREE_BASE_URL = 'https://api.cashfree.com/pg'
else:
CASHFREE_BASE_URL = 'https://sandbox.cashfree.com/pg'
```
---
## 3. Core Cashfree Client Class
Implement [`payments/cashfree_client.py`](file:///d:/Tradhox_workspace/paymentservice/payments/cashfree_client.py):
```python
import hmac
import hashlib
import base64
import time
import requests
import logging
from django.conf import settings
logger = logging.getLogger(__name__)
class CashfreeClient:
def __init__(self):
self.app_id = settings.CASHFREE_APP_ID
self.secret_key = settings.CASHFREE_SECRET_KEY
self.base_url = settings.CASHFREE_BASE_URL
self.api_version = settings.CASHFREE_API_VERSION
def _get_headers(self, idempotency_key=None):
headers = {
'Content-Type': 'application/json',
'x-api-version': self.api_version,
'x-client-id': self.app_id,
'x-client-secret': self.secret_key,
}
if idempotency_key:
headers['x-idempotency-key'] = idempotency_key
return headers
def create_order(self, order_id, amount, currency, customer, return_url, notify_url=None, note=None):
url = f"{self.base_url}/orders"
payload = {
"order_id": str(order_id),
"order_amount": float(amount),
"order_currency": currency,
"customer_details": {
"customer_id": str(customer.get("id")),
"customer_name": customer.get("name", "Customer"),
"customer_email": customer.get("email"),
"customer_phone": str(customer.get("phone")),
},
"order_meta": {
"return_url": return_url,
"notify_url": notify_url or return_url
}
}
if note:
payload["order_note"] = note
response = requests.post(url, json=payload, headers=self._get_headers(), timeout=10)
return response.status_code, response.json()
def get_order(self, order_id):
url = f"{self.base_url}/orders/{order_id}"
response = requests.get(url, headers=self._get_headers(), timeout=10)
return response.status_code, response.json()
def create_refund(self, order_id, refund_id, amount, note="Refund", speed="STANDARD"):
url = f"{self.base_url}/orders/{order_id}/refunds"
payload = {
"refund_id": str(refund_id),
"refund_amount": float(amount),
"refund_note": note,
"refund_speed": speed
}
response = requests.post(url, json=payload, headers=self._get_headers(), timeout=10)
return response.status_code, response.json()
def verify_webhook_signature(self, raw_body: bytes, signature: str, timestamp: str) -> bool:
if not signature or not timestamp or not self.secret_key:
return False
try:
ts_val = int(timestamp)
if ts_val > 10000000000:
ts_val = ts_val // 1000
if abs(int(time.time()) - ts_val) > 300:
logger.warning("Webhook timestamp older than 5 minutes")
return False
except ValueError:
return False
payload = str(timestamp).encode('utf-8') + raw_body
computed_hmac = hmac.new(
key=self.secret_key.encode('utf-8'),
msg=payload,
digestmod=hashlib.sha256
).digest()
computed_sig = base64.b64encode(computed_hmac).decode('utf-8')
return hmac.compare_digest(computed_sig, signature)
```
---
## 4. Suggested Database Models
Add to [`payments/models.py`](file:///d:/Tradhox_workspace/paymentservice/payments/models.py):
```python
from django.db import models
class PaymentOrder(models.Model):
STATUS_CHOICES = [
('ACTIVE', 'Active'),
('PAID', 'Paid'),
('EXPIRED', 'Expired'),
('TERMINATED', 'Terminated'),
]
order_id = models.CharField(max_length=64, unique=True, primary_key=True)
cf_order_id = models.CharField(max_length=64, blank=True, null=True)
amount = models.DecimalField(max_digits=12, decimal_places=2)
currency = models.CharField(max_length=3, default='INR')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='ACTIVE')
customer_id = models.CharField(max_length=64)
customer_email = models.EmailField()
customer_phone = models.CharField(max_length=15)
payment_session_id = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class PaymentTransaction(models.Model):
STATUS_CHOICES = [
('SUCCESS', 'Success'),
('FAILED', 'Failed'),
('PENDING', 'Pending'),
('USER_DROPPED', 'User Dropped'),
]
cf_payment_id = models.CharField(max_length=64, unique=True, primary_key=True)
order = models.ForeignKey(PaymentOrder, on_delete=models.CASCADE, related_name='transactions')
amount = models.DecimalField(max_digits=12, decimal_places=2)
status = models.CharField(max_length=20, choices=STATUS_CHOICES)
payment_method = models.CharField(max_length=50, blank=True, null=True)
bank_reference = models.CharField(max_length=100, blank=True, null=True)
payment_time = models.DateTimeField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
class PaymentRefund(models.Model):
STATUS_CHOICES = [
('SUCCESS', 'Success'),
('PENDING', 'Pending'),
('CANCELLED', 'Cancelled'),
('FAILED', 'Failed'),
]
refund_id = models.CharField(max_length=64, unique=True, primary_key=True)
cf_refund_id = models.CharField(max_length=64, blank=True, null=True)
order = models.ForeignKey(PaymentOrder, on_delete=models.CASCADE, related_name='refunds')
amount = models.DecimalField(max_digits=12, decimal_places=2)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING')
refund_arn = models.CharField(max_length=64, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
```

View file

@ -0,0 +1,46 @@
# Cashfree Knowledge Base
Complete end-to-end documentation for integrating and managing Cashfree across the Tradhox Payments Microservice (`paymentservice`).
---
## Documentation Index
| Module | Document | Description |
| :--- | :--- | :--- |
| **01** | [Overview & Quickstart](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/01_overview_and_quickstart.md) | Platform overview, environment endpoints (Sandbox & Production), architecture sequence diagram, and first API call. |
| **02** | [Authentication, Headers & Security](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/02_authentication_and_headers.md) | API key credentials (`x-client-id`, `x-client-secret`), versioning (`2023-08-01`), idempotency, rate limiting, and security compliance. |
| **03** | [Orders API Specification](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/03_orders_api.md) | Create Order (`POST /orders`), order lifecycle (`ACTIVE`, `PAID`, `EXPIRED`, `TERMINATED`), Get Order status, and payments lookup. |
| **04** | [Checkout Integrations & Payment Methods](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/04_payment_methods_and_checkout.md) | Hosted Checkout (redirect), Drop-in JS SDK v3, and Seamless API for UPI (Intent, Collect, QR), Cards (Token Vault), Netbanking, and Wallets. |
| **05** | [Webhooks & Signature Verification](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/05_webhooks_and_signature_verification.md) | Event schemas, HMAC-SHA256 signature verification algorithm, anti-replay timestamp validation, and production Django webhook view. |
| **06** | [Refunds & Disputes API](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/06_refunds_and_disputes.md) | Full/Partial refunds (`POST /orders/{order_id}/refunds`), Standard vs Instant refunds, ARN tracking, and chargeback dispute workflows. |
| **07** | [Settlements & Reconciliation](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/07_settlements_and_reconciliation.md) | T+1/T+2 banking settlement cycles, MDR fee & tax calculations, Order-level settlement API, UTR tracking, and settlement webhooks. |
| **08** | [Payouts & Automated Disbursements](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/08_payouts_and_transfers.md) | 24x7 automated disbursements via UPI, IMPS, NEFT, RTGS, Beneficiary onboarding, transfer status tracking, and balance inquiries. |
| **09** | [Secure ID & KYC Verification](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/09_secureid_and_kyc_verification.md) | Bank Account Penny Drop verification, UPI VPA verification, PAN validation, and GSTIN lookup for merchant & vendor compliance. |
| **10** | [Error Codes & Sandbox Test Data](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/10_error_codes_and_test_data.md) | Standard error formats, common error codes dictionary, test Visa/Mastercard/RuPay card numbers, test UPI handles, and simulated netbanking. |
| **11** | [Django Integration Blueprint](file:///d:/Tradhox_workspace/paymentservice/document/cashfree/11_django_integration_blueprint.md) | Production Python `CashfreeClient`, Django models (`PaymentOrder`, `PaymentTransaction`, `PaymentRefund`), settings configuration, and API views. |
---
## Quick Reference Summary
### Base URLs
- **Sandbox**: `https://sandbox.cashfree.com/pg`
- **Production**: `https://api.cashfree.com/pg`
### Standard Request Headers
```http
Content-Type: application/json
x-api-version: 2023-08-01
x-client-id: <CASHFREE_APP_ID>
x-client-secret: <CASHFREE_SECRET_KEY>
x-idempotency-key: <UUIDv4>
```
### Webhook Verification Formula
```text
payload = x-webhook-timestamp + raw_body
computed_hmac = HMAC_SHA256(secret_key, payload)
computed_signature = Base64Encode(computed_hmac)
valid = (computed_signature == x-webhook-signature)
```