Integration guide

PaymentSwitch has three integration points. All requests go to https://api.pswitch.live and use JSON over HTTPS.

#WhatDirection
1Payments API: create a payment and get a UPI intentYou → PaymentSwitch
2Status Check API: fetch a payment by transactionIdYou → PaymentSwitch
3Webhooks: final status delivered to your serverPaymentSwitch → You

Overview

How a payment flows

Your server ──(1) POST /upi/intent──▶ PaymentSwitch ──▶ chosen gateway
                                          │                    │
Customer pays in their UPI app ◀──────────┘ (UPI intent)       │
                                                               ▼
Gateway ──── webhook ───▶ PaymentSwitch ──(3) signed webhook──▶ Your server
Your server ──(2) GET /transactions/{transactionId}──▶ PaymentSwitch (any time)

You integrate with PaymentSwitch only. Gateways report outcomes to PaymentSwitch, and PaymentSwitch delivers them to you, so you never build a separate webhook per gateway.

Two IDs for every order

FieldWhose IDUse it for
transactionIdPaymentSwitch (switch) ID, created by us for every paymentThe Status Check API, support requests, matching webhooks to orders. Always present.
gatewayTransactionIdGateway ID, issued by the gateway that processes the paymentReconciling against your gateway’s dashboard and settlement reports.

Both appear in the payment response, the status response and every webhook. Your own merchantOrderId is echoed back too.

Set up

  1. Register a merchant account and wait for approval.
  2. In the dashboard, add and enable your payment gateway(s) under Gateways.
  3. Under API Integration: copy your API key, generate your API secret (shown once, keep it on your server only), and set your Callback URL.

PaymentSwitch routes payments to the gateways you configured. Settlement, refunds and compliance remain between you and your gateway.

Authentication: signing requests

Every API request carries three headers:

HeaderValue
X-PS-KeyYour API key (starts with psk_).
X-PS-TimestampCurrent time in epoch milliseconds. Must be within 5 minutes of our clock.
X-PS-SignatureLowercase hex of HMAC-SHA256(apiSecret, signingString).

The signing string is four parts joined by dots:

{timestamp}.{METHOD}.{path-and-query}.{sha256hex(body)}

Use the exact path and query string you send (for example /api/v1/upi/intent), and the SHA-256 of the exact body bytes. For a request without a body, hash the empty string. Each signature can be used once, so sign every retry with a fresh timestamp.

import crypto from 'node:crypto';

function sign(method, path, body, apiKey, apiSecret) {
  const ts = Date.now().toString();
  const bodyHash = crypto.createHash('sha256').update(body ?? '').digest('hex');
  const signature = crypto.createHmac('sha256', apiSecret)
    .update(`${ts}.${method}.${path}.${bodyHash}`).digest('hex');
  return { 'X-PS-Key': apiKey, 'X-PS-Timestamp': ts, 'X-PS-Signature': signature,
           'Content-Type': 'application/json' };
}

1. Payments API: create a payment

POSThttps://api.pswitch.live/api/v1/upi/intent

Creates a payment, chooses the best gateway for it, and returns a UPI intent link to open in the customer’s UPI app.

Request body

FieldTypeDescription
merchantOrderIdstring, requiredYour unique order ID (max 64 chars: letters, digits, . _ - /). Used for idempotency.
amountnumber, requiredAmount in INR, 1.00 to 10,000,000.00, up to 2 decimals.
currencystringINR (default).
customer.mobilestring, required10-digit Indian mobile number.
customer.namestring, requiredCustomer name.
customer.emailstringCustomer email.
customer.addressstringOptional.
deviceIdstring, requiredA stable identifier for the customer’s device. Used to recognise returning customers and detect unusual activity.

There is no callback field on the request: your callback URL is part of your merchant configuration.

{
  "merchantOrderId": "ORD-12345",
  "amount": 1250.00,
  "currency": "INR",
  "customer": { "mobile": "9999999999", "email": "customer@example.com", "name": "Customer Name" },
  "deviceId": "device-abc-123"
}

Response 201 Created

{
  "success": true,
  "transactionId": "PSMUYPQVPQ020001OTT0",
  "gatewayTransactionId": "MG1A2B3C4D5E6F70",
  "merchantOrderId": "ORD-12345",
  "status": "PROCESSING",
  "amount": 1250.00,
  "currency": "INR",
  "gateway": "GATEWAY_B",
  "upiIntentUri": "upi://pay?pa=…&am=1250.00&cu=INR&tr=MG1A2B3C4D5E6F70",
  "createdAt": "2026-10-08T10:15:30Z",
  "replayed": false
}

Open upiIntentUri on the customer’s device. The payment is not complete until you receive a final status. Store transactionId with your order.

Safe retries

The call is idempotent on merchantOrderId. If you retry the same order with the same body (for example after a timeout), you get the original payment back with 200 and "replayed": true. Reusing an order ID with a different body returns 409 DUPLICATE_ORDER.

2. Status Check API

GEThttps://api.pswitch.live/api/v1/transactions/{transactionId}

transactionId is the PaymentSwitch ID returned by the Payments API. Sign the request like any other (empty body).

{
  "transactionId": "PSMUYPQVPQ020001OTT0",
  "gatewayTransactionId": "MG1A2B3C4D5E6F70",
  "merchantOrderId": "ORD-12345",
  "status": "SUCCESS",
  "amount": 1250.00,
  "currency": "INR",
  "gateway": "GATEWAY_B",
  "failureCode": null,
  "createdAt": "2026-10-08T10:15:30Z",
  "updatedAt": "2026-10-08T10:16:02Z"
}
StatusMeaning
PROCESSINGIntent created; waiting for the customer and the gateway.
SUCCESSPaid. Final.
FAILEDNot paid. Final. failureCode says why (for example BANK_DECLINED, EXPIRED).
PENDINGThe gateway has not confirmed an outcome yet. It can still become SUCCESS or FAILED. Do not fulfil yet.
CANCELLEDCancelled before payment. Final.

An unknown transactionId, or one that belongs to another merchant, returns 404. Use webhooks as the primary signal and this API to confirm or recover.

3. Webhooks

Payment gateways send their notifications to PaymentSwitch. PaymentSwitch updates the payment and then distributes the result to your callback URL, the one you saved in the dashboard (API Integration → Callback URL), as an HTTPS POST.

POST https://yourstore.com/payments/callback
Content-Type: application/json
X-PS-Timestamp: 1791420890404
X-PS-Signature: 9f2c…

{
  "event": "payment.succeeded",
  "transactionId": "PSMUYPQVPQ020001OTT0",
  "gatewayTransactionId": "MG1A2B3C4D5E6F70",
  "merchantOrderId": "ORD-12345",
  "status": "SUCCESS",
  "amount": 1250.00,
  "currency": "INR",
  "gateway": "GATEWAY_B",
  "occurredAt": "2026-10-08T10:16:02Z"
}

Events: payment.succeeded and payment.failed (the latter adds failureCode). Match on transactionId (switch ID); gatewayTransactionId is the gateway’s own reference.

Verify the signature

Compute HMAC-SHA256(apiSecret, timestamp + "." + rawBody) over the raw request body, hex-encode it, and compare it to X-PS-Signature in constant time. Reject requests whose timestamp is more than 5 minutes old.

const expected = crypto.createHmac('sha256', API_SECRET)
  .update(`${req.headers['x-ps-timestamp']}.${rawBody}`).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers['x-ps-signature']));

Delivery and retries

Errors

Errors share one shape, with the HTTP status reflecting the problem:

{ "success": false, "code": "NO_ELIGIBLE_GATEWAY", "message": "No eligible payment gateway is available.",
  "details": { "transactionId": "…", "reasons": { "GATEWAY_A": ["DAILY_AMOUNT_LIMIT"] } }, "requestId": "…" }
HTTPCodeMeaning
400INVALID_REQUESTValidation failed. details.fields names each problem field.
401UNAUTHENTICATEDMissing or invalid signature, stale timestamp, or a replayed signature.
403MERCHANT_INACTIVEYour account is not active.
404NOT_FOUNDUnknown transactionId.
409DUPLICATE_ORDER / CONFLICTOrder ID reused with a different body, or still being processed (retry shortly).
422NO_ELIGIBLE_GATEWAYNo gateway can take this payment (disabled, amount limits, usage limits). details.reasons lists why per gateway.
422BLOCKED_BY_POLICYDeclined by a risk flag. details.reasons names the exact flag, for example USER_HIGH_VELOCITY. The payment was not sent to any gateway.
429RATE_LIMITEDToo many requests. Back off and retry.
502GATEWAY_ERROREvery eligible gateway failed to create the payment.

Include the requestId when contacting support.