Complete reference for every SabPaisa PG 3.0 endpoint, including request/response schemas, error codes, and rate limits.
All API requests must include the X-Api-Key header. Refund endpoints also require the X-Merchant-Id header.
| Header | Required | Description |
|---|---|---|
| X-Api-Key | Yes | Your API key provided by SabPaisa |
| Content-Type | Yes | application/json |
| X-Merchant-Id | Yes (Refunds) | Your client code — required for all refund endpoints (create, status, list) |
| Environment | URL |
|---|---|
| Staging | https://staging-sb-merchant-api.sabpaisa.in |
| Production | https://merchant-api.sabpaisa.in |
Test API endpoints directly from your browser. Select an endpoint, fill in parameters, and see the response. This is a frontend-only simulation — no actual API calls are made.
Creates a new payment session and returns a checkoutUrl to which you redirect the customer. The session expires after 30 minutes.
| Field | Type | Validation | Description |
|---|---|---|---|
| merchantId | String | Max 50 chars | Your merchant/client code |
| merchantTxnId | String | Max 100 chars, [a-zA-Z0-9_-] | Your unique transaction ID |
| amount | Long | Min: 100, Max: 100000000 | Amount in paise (100 = Rs 1) |
| currency | String | INR or USD | Currency code |
| customerName | String | 2-100 chars, letters & spaces | Customer's full name |
| customerEmail | String | Valid email, max 255 chars | Customer's email (no disposable emails) |
| customerPhone | String | 10 digits, starts with 6-9 | Indian mobile number |
| returnUrl | String | Valid URL, max 2000 chars | URL to redirect after payment |
| checksum | String | Exactly 64 hex chars | HMAC-SHA256 signature (see Checksum) |
| timestamp | Long | Unix seconds, max 5 min old | Current timestamp in seconds |
| Field | Type | Description |
|---|---|---|
| description | String | Order description (max 500 chars) |
| metadata | Object | Custom key-value pairs (max 50 keys, 4KB total) |
| language | String | Checkout page language (max 10 chars) |
| udf1 - udf20 | String | User-defined fields (max 255 chars each) |
| billingAddress | Object | Billing address (name, line1, line2, landmark, city, state, postalCode, country, phone) |
| shippingAddress | Object | Shipping address (same fields as billing). Set shippingSameAsBilling: true to copy |
| lineItems[] | Array | Order items (name, sku, quantity, unitPrice, tax, etc.) — max 100 items |
| orderSummary | Object | Order totals (subtotal, shippingAmount, discountAmount, taxAmount, totalAmount) |
1curl -X POST "https://staging-sb-merchant-api.sabpaisa.in/api/v2/payments" \
2 -H "X-Api-Key: your_api_key" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "merchantId": "AJME84",
6 "merchantTxnId": "TESTING06032610564854",
7 "amount": 50000,
8 "currency": "INR",
9 "customerName": "Bhargav",
10 "customerEmail": "[email protected]",
11 "customerPhone": "9876543210",
12 "returnUrl": "https://yoursite.com/payment/return",
13 "checksum": "your_generated_checksum_here",
14 "timestamp": 1707123456
15 }'1curl -X POST "https://staging-sb-merchant-api.sabpaisa.in/api/v2/payments" \
2 -H "X-Api-Key: your_api_key" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "merchantId": "AJME84",
6 "merchantTxnId": "TESTING06032610564854",
7 "amount": 172000,
8 "currency": "INR",
9 "customerName": "Bhargav",
10 "customerEmail": "[email protected]",
11 "customerPhone": "9876543210",
12 "returnUrl": "https://yoursite.com/payment/return",
13 "description": "Order #12345 - 2x Blue T-Shirt",
14 "checksum": "your_generated_checksum_here",
15 "timestamp": 1707123456,
16 "metadata": { "orderId": "ORD-12345", "source": "mobile_app" },
17 "billingAddress": {
18 "name": "Bhargav",
19 "line1": "123 MG Road",
20 "city": "Bengaluru",
21 "state": "KA",
22 "postalCode": "560001",
23 "country": "IN"
24 },
25 "lineItems": [{
26 "name": "Blue T-Shirt",
27 "sku": "TSH-BLU-L",
28 "quantity": 2,
29 "unitPrice": 75000,
30 "taxPercent": 18.0
31 }],
32 "orderSummary": {
33 "subtotal": 150000,
34 "shippingAmount": 5000,
35 "discountAmount": 10000,
36 "taxAmount": 27000,
37 "totalAmount": 172000
38 }
39 }'1{
2 "success": true,
3 "paymentId": "sppay_f2c2f710ecd34427a710",
4 "checkoutUrl": "https://staging-sb-checkout.sabpaisa.in/checkout/sppay_f2c2f710ecd34427a710",
5 "status": "PENDING",
6 "amount": 50000,
7 "currency": "INR",
8 "merchantTxnId": "TESTING06032610564854",
9 "expiresAt": "2026-02-13T12:30:00",
10 "clientSecret": "sppay_f2c2f710ecd34427a710_secret_08bf1afa68fb...",
11 "message": "Payment created successfully. Redirect customer to checkoutUrl.",
12 "traceId": "abc123def456"
13}| Field | Description |
|---|---|
| paymentId | Unique payment identifier — use this to track the payment |
| checkoutUrl | Redirect your customer to this URL to complete payment |
| expiresAt | Session expiry (30 min from creation) |
| clientSecret | Secret token for checkout auth. Returned only once — store securely |
| traceId | Trace ID for debugging — share with SabPaisa support if needed |
After creating a payment session, redirect the customer to the checkout page using checkoutUrl and clientSecret from the response.
1{checkoutUrl}?clientSecret={clientSecret}1app.post('/pay', async (req, res) => {
2 const response = await createPaymentSession(req.body);
3 const redirectUrl = `${response.checkoutUrl}?clientSecret=${response.clientSecret}`;
4 res.redirect(redirectUrl);
5});1@PostMapping("/pay")
2public ResponseEntity<Void> pay(@RequestBody OrderRequest order) {
3 CreatePaymentResponse response = createPaymentSession(order);
4 String redirectUrl = response.getCheckoutUrl() + "?clientSecret=" + response.getClientSecret();
5 return ResponseEntity.status(HttpStatus.FOUND)
6 .header("Location", redirectUrl)
7 .build();
8}1@app.route('/pay', methods=['POST'])
2def pay():
3 response = create_payment_session(request.json)
4 redirect_url = f"{response['checkoutUrl']}?clientSecret={response['clientSecret']}"
5 return redirect(redirect_url)1$response = createPaymentSession($orderData);
2$redirectUrl = $response['checkoutUrl'] . '?clientSecret=' . $response['clientSecret'];
3header("Location: " . $redirectUrl);
4exit;1// After receiving response from your backend
2const redirectUrl = `${response.checkoutUrl}?clientSecret=${response.clientSecret}`;
3window.location.href = redirectUrl;returnUrl. Always verify payment status server-side after redirect.After payment completes (success, failure, or timeout), the customer is redirected back to your returnUrl with 8 query parameters appended, including a cryptographic signature. You must verify this signature server-side before trusting the result.
| # | Parameter | Example | Description |
|---|---|---|---|
| 1 | transaction_id | SP_3129835 | SabPaisa transaction ID |
| 2 | merchant_txn_id | ORDER001 | Your order ID |
| 3 | status | SUCCESS | Final status: SUCCESS, FAILED, EXPIRED, TIMEOUT, CANCELLED |
| 4 | amount | 1000.00 | Requested amount in rupees |
| 5 | paid_amount | 1000.00 | Actual paid amount in rupees |
| 6 | payment_mode | UPI | Payment mode: UPI, CARD, NETBANKING, etc. |
| 7 | timestamp | 1739540000000 | Epoch milliseconds when URL was generated |
| 8 | signature | a3f8b2c1... | HMAC-SHA256 hex signature (64 chars) |
1https://yoursite.com/payment/return?transaction_id=SP_3129835&merchant_txn_id=ORDER001&status=SUCCESS&amount=1000.00&paid_amount=1000.00&payment_mode=UPI×tamp=1739540000000&signature=a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1The signature is a one-way HMAC hash — you cannot “decrypt” it. Instead, regenerate the same signature on your server and compare. If they match, the redirect is authentic.
signature)key=value separated by pipe |HMAC-SHA256(dataString, secretKey) → lowercase hexsignature parameter. If equal, the redirect is valid.1Step 1: Sort parameters alphabetically by key:
2 amount = 1000.00
3 merchant_txn_id = ORDER001
4 paid_amount = 1000.00
5 payment_mode = UPI
6 status = SUCCESS
7 timestamp = 1739540000000
8 transaction_id = SP_3129835
9
10Step 2: Join as key=value with pipe separator:
11 amount=1000.00|merchant_txn_id=ORDER001|paid_amount=1000.00|payment_mode=UPI|status=SUCCESS|timestamp=1739540000000|transaction_id=SP_3129835
12
13Step 3: HMAC-SHA256 with your secret key:
14 HMAC-SHA256(dataString, secretKey) → 64-char lowercase hex1const crypto = require('crypto');
2
3app.get('/payment/return', (req, res) => {
4 const { signature, ...params } = req.query;
5
6 // Step 1: Sort keys alphabetically and build data string
7 const dataString = Object.keys(params)
8 .sort()
9 .map(key => `${key}=${params[key]}`)
10 .join('|');
11
12 // Step 2: Compute HMAC-SHA256
13 const expectedSignature = crypto
14 .createHmac('sha256', process.env.SECRET_KEY)
15 .update(dataString)
16 .digest('hex');
17
18 // Step 3: Compare signatures
19 if (expectedSignature !== signature) {
20 return res.status(400).send('Invalid signature — possible tampering');
21 }
22
23 // Signature valid — now check status
24 if (params.status === 'SUCCESS') {
25 // IMPORTANT: Also verify via Transaction Enquiry API or Webhooks
26 res.send('Payment successful!');
27 } else {
28 res.send(`Payment ${params.status.toLowerCase()}`);
29 }
30});1@GetMapping("/payment/return")
2public ResponseEntity<String> handleReturn(@RequestParam Map<String, String> params) {
3 String receivedSignature = params.remove("signature");
4
5 // Sort keys and build data string
6 String dataString = params.entrySet().stream()
7 .sorted(Map.Entry.comparingByKey())
8 .map(e -> e.getKey() + "=" + e.getValue())
9 .collect(Collectors.joining("|"));
10
11 // Compute HMAC-SHA256
12 Mac mac = Mac.getInstance("HmacSHA256");
13 mac.init(new SecretKeySpec(secretKey.getBytes(UTF_8), "HmacSHA256"));
14 byte[] hash = mac.doFinal(dataString.getBytes(UTF_8));
15 StringBuilder hex = new StringBuilder();
16 for (byte b : hash) hex.append(String.format("%02x", b));
17 String expectedSignature = hex.toString();
18
19 if (!expectedSignature.equals(receivedSignature)) {
20 return ResponseEntity.badRequest().body("Invalid signature");
21 }
22
23 // Signature valid — verify status via API and show result
24 return ResponseEntity.ok("Payment " + params.get("status"));
25}1import hmac, hashlib
2
3@app.route('/payment/return')
4def payment_return():
5 params = dict(request.args)
6 received_signature = params.pop('signature', '')
7
8 # Sort keys and build data string
9 data_string = '|'.join(
10 f'{k}={params[k]}' for k in sorted(params.keys())
11 )
12
13 # Compute HMAC-SHA256
14 expected_signature = hmac.new(
15 SECRET_KEY.encode(), data_string.encode(), hashlib.sha256
16 ).hexdigest()
17
18 if expected_signature != received_signature:
19 return 'Invalid signature', 400
20
21 # Signature valid — verify status via API and show result
22 return f"Payment {params['status'].lower()}"1<?php
2$params = $_GET;
3$receivedSignature = $params['signature'];
4unset($params['signature']);
5
6// Sort keys alphabetically
7ksort($params);
8
9// Build data string
10$dataString = implode('|', array_map(
11 fn($k, $v) => "$k=$v",
12 array_keys($params), array_values($params)
13));
14
15// Compute HMAC-SHA256
16$expectedSignature = hash_hmac('sha256', $dataString, $secretKey);
17
18if ($expectedSignature !== $receivedSignature) {
19 http_response_code(400);
20 die('Invalid signature');
21}
22
23// Signature valid — verify status via API and show result
24echo "Payment " . strtolower($params['status']);Transaction Enquiry API or Webhooks. A user could bookmark or replay the return URL.amount and paid_amount are in rupees (not paise). The timestamp is in milliseconds (not seconds).The checksum protects each payment request from tampering. Build the message string by concatenating the following fields with a pipe separator:
1message = merchantId|merchantTxnId|amount|currency|timestamp
2checksum = HMAC-SHA256(secretKey, message) → lowercase hex (64 characters)
3
4Example:
5message = "AJME84|TESTING06032610564854|100|INR|1772774819"
6checksum = HMAC-SHA256("your_secret_key", message) → "e3b0c44298fc1c149afbf4c8..."1const crypto = require('crypto');
2
3function generateChecksum(secretKey, merchantId, merchantTxnId, amount, currency, timestamp) {
4 const message = `${merchantId}|${merchantTxnId}|${amount}|${currency}|${timestamp}`;
5 return crypto.createHmac('sha256', secretKey).update(message).digest('hex');
6}
7
8// Usage
9const timestamp = Math.floor(Date.now() / 1000);
10const checksum = generateChecksum(
11 'your_secret_key', 'AJME84', 'TESTING06032610564854', 100, 'INR', timestamp
12);1import javax.crypto.Mac;
2import javax.crypto.spec.SecretKeySpec;
3import java.nio.charset.StandardCharsets;
4
5public String generateChecksum(String secretKey, String merchantId, String merchantTxnId,
6 long amount, String currency, long timestamp) {
7 String message = merchantId + "|" + merchantTxnId + "|" + amount + "|" + currency + "|" + timestamp;
8 Mac mac = Mac.getInstance("HmacSHA256");
9 mac.init(new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
10 byte[] hash = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
11 StringBuilder hex = new StringBuilder();
12 for (byte b : hash) hex.append(String.format("%02x", b));
13 return hex.toString();
14}1import hmac, hashlib, time
2
3def generate_checksum(secret_key, merchant_id, merchant_txn_id, amount, currency, timestamp):
4 message = f"{merchant_id}|{merchant_txn_id}|{amount}|{currency}|{timestamp}"
5 return hmac.new(secret_key.encode(), message.encode(), hashlib.sha256).hexdigest()
6
7# Usage
8timestamp = int(time.time())
9checksum = generate_checksum(
10 'your_secret_key', 'AJME84', 'TESTING06032610564854', 100, 'INR', timestamp
11)1function generateChecksum($secretKey, $merchantId, $merchantTxnId, $amount, $currency, $timestamp) {
2 $message = "{$merchantId}|{$merchantTxnId}|{$amount}|{$currency}|{$timestamp}";
3 return hash_hmac('sha256', $message, $secretKey);
4}
5
6// Usage
7$timestamp = time();
8$checksum = generateChecksum(
9 'your_secret_key', 'AJME84', 'TESTING06032610564854', 100, 'INR', $timestamp
10);1using System.Security.Cryptography;
2using System.Text;
3
4public string GenerateChecksum(string secretKey, string merchantId, string merchantTxnId,
5 long amount, string currency, long timestamp) {
6 string message = $"{merchantId}|{merchantTxnId}|{amount}|{currency}|{timestamp}";
7 using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey));
8 byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
9 return BitConverter.ToString(hash).Replace("-", "").ToLower();
10}Server-to-server UPI integration for direct QR code generation and UPI Intent flows. No redirect required — your server calls SabPaisa directly and gets a QR string or intent URL.
Generate a UPI QR code for desktop checkout or a UPI Intent URL for mobile. Requires X-Merchant-Id header and S2S to be enabled on your account.
Additional headers: X-Merchant-Id (required), X-Idempotency-Key (optional for safe retries).
1{
2 "merchantId": "LPSD1",
3 "merchantTxnId": "ORDER_20260317_001",
4 "amount": 50000,
5 "currency": "INR",
6 "customerName": "Rahul Sharma",
7 "customerEmail": "[email protected]",
8 "customerPhone": "9876543210",
9 "paymentMode": "UPI_QR",
10 "timestamp": 1742198400,
11 "checksum": "a1b2c3d4e5f6...64_hex_chars"
12}1{
2 "success": true,
3 "paymentId": "sppay_abc123xyz",
4 "txnId": "SP_12345",
5 "merchantTxnId": "ORDER_20260317_001",
6 "status": "PROCESSING",
7 "amount": 50000,
8 "currency": "INR",
9 "paymentMode": "UPI_QR",
10 "upiQrString": "upi://pay?pa=merchant@upi&pn=MerchantName&am=500.00",
11 "intentUrl": null,
12 "message": "Payment initiated",
13 "traceId": "abc-def-123"
14}1{
2 "merchantId": "LPSD1",
3 "merchantTxnId": "ORDER_20260317_002",
4 "amount": 50000,
5 "currency": "INR",
6 "customerName": "Rahul Sharma",
7 "customerEmail": "[email protected]",
8 "customerPhone": "9876543210",
9 "paymentMode": "UPI_INTENT",
10 "timestamp": 1742198400,
11 "checksum": "a1b2c3d4e5f6...64_hex_chars"
12}1{
2 "success": true,
3 "paymentId": "sppay_def456uvw",
4 "txnId": "SP_67890",
5 "merchantTxnId": "ORDER_20260317_002",
6 "status": "PROCESSING",
7 "amount": 50000,
8 "currency": "INR",
9 "paymentMode": "UPI_INTENT",
10 "upiQrString": null,
11 "intentUrl": "upi://pay?pa=merchant@upi&pn=MerchantName&am=500.00&tr=SP_67890",
12 "message": "Payment initiated",
13 "traceId": "def-ghi-456"
14}Fetch detailed transaction information for a completed or in-progress payment, including bank details, payment instrument, and timestamps.
| Field | Type | Required | Description |
|---|---|---|---|
| clientCode | String | Yes | Your merchant/client code (must match authenticated merchant) |
| merchantTxnId | String | Yes | Your unique transaction ID |
| spTxnId | String | No | SabPaisa transaction ID (for direct lookup) |
1curl -X POST "https://staging-sb-merchant-api.sabpaisa.in/api/v2/payments/enquiry" \
2 -H "X-Api-Key: your_api_key" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "clientCode": "DJ021",
6 "merchantTxnId": "TESTING06032610564854"
7 }'1{
2 "success": true,
3 "traceId": "abc123def456",
4 "txnId": "SP_TXN_ABC123",
5 "merchantId": "AJME84",
6 "merchantTxnId": "TESTING06032610564854",
7 "amountPaise": 50000,
8 "currency": "INR",
9 "status": "SUCCESS",
10 "paymentMode": "UPI",
11 "sessionId": "sppay_f2c2f710ecd34427a710",
12 "paymentInstrumentType": "UPI",
13 "paymentScheme": "UPI",
14 "paymentVariant": "BHIM UPI QR",
15 "issuerEntity": "HDFC Bank",
16 "requestAmount": 500.0,
17 "paidAmount": 500.0,
18 "bankTxnId": "BANK_TXN_789",
19 "bankRrn": "123456789012",
20 "bankCode": "HDFC",
21 "bankName": "HDFC Bank",
22 "customerName": "Bhargav",
23 "customerEmail": "[email protected]",
24 "customerMobile": "9876543210",
25 "refundStatus": "NONE",
26 "initiatedAt": "2026-02-13T10:00:00Z",
27 "completedAt": "2026-02-13T10:02:30Z",
28 "metadata": { "orderId": "ORD-12345" }
29}requestAmount and paidAmount are in rupees (not paise), while amountPaise is in paise.Initiate a full or partial refund for a successful payment. Refunds are processed asynchronously and typically settle within 5-7 business days.
| Field | Type | Required | Description |
|---|---|---|---|
| txnId | String | Yes | SabPaisa transaction ID of the original payment |
| amount | Long | Yes | Refund amount in paise (min: 100 = Rs 1) |
| reason | String | Yes | Reason for the refund (max 500 chars) |
1curl -X POST "https://staging-sb-merchant-api.sabpaisa.in/api/v2/refunds" \
2 -H "X-Api-Key: your_api_key" \
3 -H "X-Merchant-Id: YOUR_MERCHANT_ID" \
4 -H "Content-Type: application/json" \
5 -d '{
6 "txnId": "SP_TXN_ABC123",
7 "amount": 25000,
8 "reason": "Customer requested cancellation"
9 }'1{
2 "success": true,
3 "data": {
4 "refundId": "rfnd_a1b2c3d4e5",
5 "txnId": "SP_TXN_ABC123",
6 "originalAmount": 50000,
7 "refundAmount": 25000,
8 "amount": 25000,
9 "status": "SUCCESS",
10 "bankRefundId": "BANK_REF_XYZ789",
11 "refundedAt": "2026-02-13T10:35:00Z",
12 "createdAt": "2026-02-13T10:30:00Z",
13 "message": "Refund processed successfully"
14 }
15}Check the status of a specific refund by its refund ID.
1curl -X GET "https://staging-sb-merchant-api.sabpaisa.in/api/v2/refunds/rfnd_a1b2c3d4e5" \
2 -H "X-Api-Key: your_api_key" \
3 -H "X-Merchant-Id: YOUR_MERCHANT_ID"1{
2 "success": true,
3 "data": {
4 "refundId": "rfnd_a1b2c3d4e5",
5 "txnId": "SP_TXN_ABC123",
6 "originalAmount": 50000,
7 "refundAmount": 25000,
8 "amount": 25000,
9 "status": "SUCCESS",
10 "bankRefundId": "BANK_REF_XYZ789",
11 "refundedAt": "2026-02-13T10:35:00Z",
12 "createdAt": "2026-02-13T10:30:00Z",
13 "message": "Refund processed successfully"
14 }
15}| Status | Description |
|---|---|
| INITIATED | Refund request received, processing started |
| SUCCESS | Refund completed, amount credited to customer |
| FAILED | Refund failed — check error details |
List all refunds with optional filters and pagination.
| Parameter | Type | Default | Description |
|---|---|---|---|
| txnId | String | — | Filter by original transaction ID |
| status | String | — | Filter by status (INITIATED, SUCCESS, FAILED) |
| fromDate | String | — | Start date (yyyy-MM-dd) |
| toDate | String | — | End date (yyyy-MM-dd) |
| page | Integer | 0 | Page number (0-based) |
| size | Integer | 20 | Page size (max 100) |
1curl -X GET "https://staging-sb-merchant-api.sabpaisa.in/api/v2/refunds?status=SUCCESS&fromDate=2026-01-01&toDate=2026-02-13&page=0&size=10" \
2 -H "X-Api-Key: your_api_key" \
3 -H "X-Merchant-Id: YOUR_MERCHANT_ID"1{
2 "success": true,
3 "data": {
4 "refunds": [
5 {
6 "refundId": "rfnd_a1b2c3d4e5",
7 "txnId": "SP_TXN_ABC123",
8 "amount": 25000,
9 "status": "SUCCESS",
10 "reason": "Customer requested cancellation",
11 "createdAt": "2026-02-13T10:30:00Z",
12 "refundedAt": "2026-02-13T10:35:00Z"
13 }
14 ],
15 "pagination": {
16 "page": 0,
17 "size": 20,
18 "totalElements": 1,
19 "totalPages": 1,
20 "hasNext": false,
21 "hasPrevious": false
22 }
23 }
24}When a request fails, the API returns a structured error response:
1{
2 "success": false,
3 "error": {
4 "code": "INVALID_SIGNATURE",
5 "message": "Checksum verification failed"
6 },
7 "traceId": "abc123def456"
8}| HTTP | Error Code | Description | How to Fix |
|---|---|---|---|
| 401 | UNAUTHORIZED | Invalid or missing API key | Check your X-Api-Key header |
| 400 | INVALID_SIGNATURE | Checksum verification failed | Verify HMAC-SHA256 formula and secret key |
| 400 | REQUEST_EXPIRED | Timestamp older than 5 minutes | Use current timestamp, sync server clock (NTP) |
| 400 | INVALID_TIMESTAMP | Timestamp missing or >1 min in future | Use seconds not milliseconds |
| 400 | INVALID_AMOUNT | Amount < 100 or > 100000000 paise | Amount in paise: Rs 500 = 50000 |
| 400 | INVALID_CURRENCY | Unsupported currency | Use INR or USD |
| 400 | DUPLICATE_TRANSACTION | merchantTxnId already used | Generate unique ID per transaction |
| 400 | INVALID_RETURN_URL | Return URL missing or invalid | Provide valid HTTPS URL |
| 400 | INVALID_EMAIL | Invalid email format | Valid email, no disposable domains |
| 400 | INVALID_PHONE | Invalid phone number | 10-digit Indian mobile starting with 6/7/8/9 |
| 400 | INVALID_CUSTOMER_NAME | Name too short/long or invalid chars | 2-100 characters, letters and spaces only |
| 400 | INVALID_TXN_ID | Invalid merchantTxnId format | Only alphanumeric, hyphen, underscore |
| 403 | MERCHANT_INACTIVE | Merchant account not active | Contact SabPaisa support |
| 503 | AUTH_CONFIG_ERROR | Merchant config issue on server | Contact SabPaisa support |
| HTTP | Error Code | Description | How to Fix |
|---|---|---|---|
| 400 | TRANSACTION_NOT_FOUND | Transaction ID not found | Verify the txnId is correct |
| 400 | AMOUNT_EXCEEDED | Refund exceeds original payment | Partial refund must be <= paid amount |
| 400 | REFUND_NOT_ALLOWED | Refund not enabled for merchant | Contact SabPaisa to enable refunds |
| 400 | REFUND_PERIOD_EXPIRED | Refund window has passed | Refund within allowed days from payment |
| 400 | DUPLICATE_REFUND | Duplicate refund request | A refund is already in progress |
| 400 | INVALID_AMOUNT | Amount less than minimum (100 paise) | Minimum refund is Rs 1 (100 paise) |
| HTTP | Error Code | Description | How to Fix |
|---|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key | Check X-Api-Key header |
| 403 | CLIENT_CODE_MISMATCH | clientCode doesn't match authenticated merchant | Use your own merchant code |
| 404 | TRANSACTION_NOT_FOUND | No transaction found for given criteria | Verify merchantTxnId is correct |
| 400 | TRANSACTION_SERVICE_ERROR | Transaction service temporarily unavailable | Retry after some time |
| 400 | INTERNAL_ERROR | Unexpected internal error | Contact SabPaisa support with traceId |
All amounts in the API are in paise (1 Rupee = 100 Paise), except requestAmount and paidAmount in the enquiry response which are in rupees.
| Rupees | Paise (API value) |
|---|---|
| Rs 1 | 100 |
| Rs 500 | 50000 |
| Rs 10,000 | 1000000 |
| Language | Get Current Timestamp (seconds) |
|---|---|
| JavaScript | Math.floor(Date.now() / 1000) |
| Python | int(time.time()) |
| Java | System.currentTimeMillis() / 1000 |
| PHP | time() |
| C# | DateTimeOffset.UtcNow.ToUnixTimeSeconds() |
| Mistake | Fix |
|---|---|
| Timestamp in milliseconds | Divide by 1000: Math.floor(Date.now() / 1000) |
| Amount in rupees instead of paise | Multiply by 100: Rs 500 = 50000 |
| Checksum generated on frontend | Move checksum logic to your backend server |
| Reusing merchantTxnId | Generate a new unique ID for every transaction |
| Not verifying status after redirect | Always verify via Transaction Enquiry API or Webhooks |
| Sending API key in Authorization without Bearer | Use X-Api-Key header directly |
| Scope | Limit | Window |
|---|---|---|
| Merchant API (overall) | 100,000 requests | Per minute |
| Checkout per IP | 30 requests | Per minute |
| Per payment status poll | 100 requests | Total per payment |
| Header | Description |
|---|---|
| X-RateLimit-Limit | Maximum requests allowed in the current window |
| X-RateLimit-Remaining | Requests remaining in the current window |
| X-RateLimit-Reset | Unix timestamp when the window resets |
| Retry-After | Seconds to wait before retrying (only on 429 responses) |
SabPaisa sends real-time HTTP POST notifications (webhooks) to your server whenever a payment reaches a terminal state. Every webhook includes an X-SabPaisa-Signature header for verification. For complete documentation, see the Webhook Integration Guide.
| Event | Status | Description |
|---|---|---|
| payment.success | SUCCESS | Payment completed successfully. Funds have been captured. |
| payment.failed | FAILED | Payment failed due to insufficient funds, bank decline, etc. |
| payment.expired | EXPIRED | Payment session expired before customer completed payment. |
| payment.timeout | TIMEOUT | Payment processing timed out at the bank/aggregator level. |
Header format: X-SabPaisa-Signature = timestamp.base64_signature
. to extract timestamp and Base64-encoded signature.timestamp.raw_request_body (use the raw JSON body, do NOT parse and re-serialize).HMAC-SHA256(signed_payload, webhook_secret) and Base64-encode the result.SabPaisa retries with exponential backoff and jitter. 10 attempts over approximately 8.5 minutes:
| Attempt | Delay | Approx. Time After First |
|---|---|---|
| 1 (initial) | Immediate | 0s |
| 2 | ~1s | ~1s |
| 3 | ~2s | ~3s |
| 4 | ~4s | ~7s |
| 5 | ~8s | ~15s |
| 6-10 | ~16s to ~256s | up to ~8.5 min |
After 10 failed attempts, webhooks are moved to a Dead Letter Queue. A circuit breaker pauses delivery for 5 minutes if your endpoint has >50% failure rate over 20 requests.
1{
2 "event": "payment.success",
3 "txn_id": "TXN202602150001",
4 "merchant_txn_id": "ORDER-12345",
5 "status": "SUCCESS",
6 "request_amount": 1500.00,
7 "paid_amount": 1500.00,
8 "currency": "INR",
9 "payment_mode": "UPI",
10 "bank_txn_id": "BANK123456789",
11 "bank_rrn": "432109876543",
12 "completed_at": "2026-02-15T10:30:00Z",
13 "timestamp": "2026-02-15T10:30:01.234Z",
14 "idempotency_key": "TXN202602150001_SUCCESS"
15}Use the following test data in the staging environment to simulate various payment outcomes.
| Field | Value |
|---|---|
| Base URL | https://staging-sb-merchant-api.sabpaisa.in |
| Merchant ID | Contact SabPaisa for staging credentials |
| API Key | Contact SabPaisa for staging API key |
| Secret Key | Contact SabPaisa for staging secret key |
| VPA | Result |
|---|---|
| success@upi | Payment succeeds |
| failure@upi | Payment fails |
| timeout@upi | Payment times out |
| Card Number | Network | Result |
|---|---|---|
| 4111 1111 1111 1111 | Visa | Payment succeeds |
| 5500 0000 0000 0004 | Mastercard | Payment succeeds |
| 4000 0000 0000 0002 | Visa | Payment declined |
| 5105 1051 0510 5100 | Mastercard | Insufficient funds |
Was this page helpful?