React Native · npm · Pure JavaScript
Overview
The official React Native SDK is written in pure JavaScript — no AAR / XCFramework to build and no native module bridging. It talks to SabPaisa’s API directly and uses React Native’s own Linking and react-native-webview for the on-device parts. The app picks the right entry point per transaction: payUpi for native UPI, or <SabPaisaGatewayCheckout> for cards / net banking / wallets.
No Native Modules
Pure JS — npm install and you're done
Native UPI
Intent (opens UPI app) or QR string via payUpi
WebView Checkout
Cards / net banking / wallets via a component
No-Secret Status
fetchStatus confirms outcome with the public key only
Refunds
Merchant-scoped — public key only, no secret
Typed Errors
e.kind: config / validation / api / network
Requirements
sabpaisa-react-native on npm. crypto-js (HMAC) installs automatically; react-native-webview is a peer dependency only needed for the gateway-checkout component. License: MIT.Installation
1npm install sabpaisa-react-native
2
3# Only if you use the gateway-checkout component:
4npm install react-native-webviewTo open UPI apps via Linking.openURL('upi://…') on Android 11+, add a <queries> entry to your manifest:
1<!-- android/app/src/main/AndroidManifest.xml -->
2<queries>
3 <intent>
4 <action android:name="android.intent.action.VIEW" />
5 <data android:scheme="upi" />
6 </intent>
7</queries>Credentials
There is no global config — every call takes credentials as named arguments. Most apps wrap them once in a service module.
| Field | Format | Used as |
|---|---|---|
| apiKey | sp_… | X-Api-Key header |
| secretKey | sec_… | HMAC signing key — only used by payUpi |
| merchantId | short code | X-Merchant-Id header + request body field |
| environment | 'staging' / 'production' | selects the base URL |
1// app/sabpaisa.js
2const SP = {
3 apiKey: process.env.SABPAISA_API_KEY,
4 secretKey: process.env.SABPAISA_SECRET_KEY, // only used by payUpi
5 merchantId: process.env.SABPAISA_MERCHANT_ID,
6 environment: __DEV__ ? 'staging' : 'production',
7};
8
9export default SP;payUpi puts secretKey on the device (PCI-scoped, same as the native Android SDK’s in-SDK mode). To keep the secret off-device, mint the S2S session on your backend and just Linking.openURL(intentUrl) yourself.secretKey (sec_…) and a legacy hmac_api_key (64-char hex). The SDK signs with secretKey — using hmac_api_key produces INVALID_SIGNATURE. Also note fetchStatus and the refund calls reject sec_… keys at runtime — pass the public apiKey only.Native UPI — payUpi
payUpi signs the request, calls POST /api/v2/payments/s2s, and — in UpiMode.INTENT — opens the customer’s UPI app via Linking. In UpiMode.QR it returns a upi://pay?… string for your app to render as a QR. Amounts are in paise.
1import { payUpi, UpiMode } from 'sabpaisa-react-native';
2
3try {
4 const r = await payUpi({
5 ...SP,
6 merchantTxnId: orderId,
7 amountPaise: 50000, // paise — 50000 = Rs 500.00
8 customerName: 'Shubham Saurav',
9 customerEmail: '[email protected]',
10 customerPhone: '9876543210',
11 mode: UpiMode.INTENT, // or UpiMode.QR
12 webhookUrl: 'https://yoursite.com/sabpaisa/webhook',
13 });
14 // r.status is typically 'PROCESSING' — initiated, NOT paid.
15 // r.upiAppOpened tells you whether a UPI app launched.
16 // INTENT: SDK opened the UPI app. QR: render r.upiQrString as a QR.
17 // Confirm the real outcome via fetchStatus / your backend.
18} catch (e) {
19 // e.kind = 'config' | 'validation' | 'api' | 'network'
20}payUpi resolving is not proof of payment — status is typically 'PROCESSING' (initiated). UPI is asynchronous; the customer may approve minutes later. Confirm via fetchStatus or your backend webhook.payUpi also accepts the same optional order fields as the other SDKs — customerId, description, language, metadata, udf, billingAddress / shippingAddress, shippingSameAsBilling, lineItems, and orderSummary. When an address is sent, country must be a 2-letter ISO code (e.g. IN) and state a valid state code (e.g. KA).
Checksum (reference)
The SDK signs every request for you. Documented here for support escalations — HMAC-SHA256, lowercase hex, over merchantId|merchantTxnId|amount|currency|timestamp.
1import CryptoJS from 'crypto-js';
2
3const base = `${merchantId}|${merchantTxnId}|${amount}|${currency}|${timestamp}`;
4const checksum = CryptoJS.HmacSHA256(base, secretKey).toString(CryptoJS.enc.Hex);Gateway Checkout (cards / net banking / wallets)
Your backend creates the session (POST /api/v2/payments) and returns a checkoutUrl — the SDK does not call hosted-create itself. Render <SabPaisaGatewayCheckout> (usually inside a <Modal>); it hosts the page in a WebView and intercepts the terminal redirect on your returnUrlScheme or returnUrlPrefix.
1import { SabPaisaGatewayCheckout, CheckoutFlowStatus } from 'sabpaisa-react-native';
2
3// Your BACKEND creates the session (POST /api/v2/payments) and returns checkoutUrl.
4<SabPaisaGatewayCheckout
5 checkoutUrl={checkoutUrlFromBackend}
6 returnUrlScheme="mymerchantapp" // your app's deep-link scheme
7 returnUrlPrefix="https://yoursite.com/sabpaisa-return" // the https returnUrl your backend registered
8 merchantTxnId={orderId}
9 onResult={(res) => {
10 switch (res.flowStatus) {
11 case CheckoutFlowStatus.RETURNED:
12 // res.reportedStatus ('SUCCESS' / 'FAILED') is an instant hint — confirm via backend
13 confirmFromBackend(res.merchantTxnId);
14 break;
15 case CheckoutFlowStatus.CANCELLED: showCancelled(); break;
16 case CheckoutFlowStatus.FAILED_TO_OPEN: showError(); break;
17 }
18 }}
19/>CheckoutFlowStatus.RETURNED only means the WebView’s URL matched your scheme/prefix — not that the customer paid. reportedStatus is the gateway’s claim (good for an instant UI hint); always confirm via your backend or fetchStatus before fulfilling.Confirm Status — fetchStatus
fetchStatus is a no-secret server-side enquiry (POST /api/v2/payments/enquiry) using the public apiKey + merchantId only — it rejects sec_… keys at runtime, so the signing secret stays on your backend.
1import { fetchStatus } from 'sabpaisa-react-native';
2
3const s = await fetchStatus({
4 apiKey: SP.apiKey, // PUBLIC key only — sec_… is rejected at runtime
5 merchantId: SP.merchantId,
6 environment: SP.environment,
7 merchantTxnId: orderId,
8});
9
10if (s.isSuccess) fulfilOrder();
11else if (s.isFailed) showFailed();
12else showStillPending(); // poll again shortlystatus, isSuccess, isFailed, paymentMode, paidAmount, bankTxnId, and traceId. Match by merchantTxnId, not amount — SabPaisa may add a convenience fee.Refunds
Refunds are merchant-scoped — public apiKey + merchantId only, no secret, no checksum (the SDK rejects a sec_… key here too).
1import { createRefund, getRefundStatus, listRefunds } from 'sabpaisa-react-native';
2
3const refund = await createRefund({
4 apiKey, merchantId, environment: 'production',
5 txnId: sabpaisaTxnId, // SabPaisa txn id of the original payment
6 amountPaise: 50000, // >= 100 (Rs 1)
7 reason: 'Customer cancelled',
8 idempotencyKey: `refund-${sabpaisaTxnId}`, // recommended
9}); // -> { refundId, status: 'INITIATED', isPending: true, ... }
10
11const latest = await getRefundStatus({ apiKey, merchantId, refundId: refund.refundId });
12const page = await listRefunds({ apiKey, merchantId, txnId: sabpaisaTxnId, size: 20 });
13// -> { refunds: [...], pagination: { page, size, total, totalPages }, traceId }createRefund throws SabPaisaError (with e.code) for structured gateway errors — AMOUNT_EXCEEDED, DUPLICATE_REFUND, REFUND_NOT_ALLOWED, REFUND_PERIOD_EXPIRED, TRANSACTION_NOT_FOUND. A declined refund is not a throw — it’s a refund with status: 'FAILED'. Prefer issuing refunds from your backend and using this only to read status.Verify Return URL
If you handle a checkout return URL yourself, verify its HMAC signature before trusting any status it carries. It recomputes the HMAC over the sorted query parameters (all except signature / checksum) and compares constant-time.
1import { verifyReturnUrl } from 'sabpaisa-react-native';
2
3if (verifyReturnUrl(callbackUrl, secretKey)) {
4 // Signature authentic — but still confirm with fetchStatus before fulfilling.
5}fetchStatus before fulfilling.Error Handling
The SDK never throws for normal user actions (decline / cancel / timeout). It throws only for integration mistakes or transport failures, with a typed kind. Capture e.code + e.traceId on API errors when escalating to support.
| e.kind | When |
|---|---|
| config | Missing / malformed creds (also: secretKey passed to fetchStatus or a refund call) |
| validation | Local field validation failed (amount range, name length, …) |
| api | Structured server error — e.code + e.traceId populated (S2S_NOT_ENABLED, INVALID_SIGNATURE, ORCHESTRATOR_ERROR, PAYMENT_FAILED, …) |
| network | Transport failure reaching SabPaisa |
Webhooks
Webhooks are a backend concern — the RN SDK doesn’t ship a verifier (mobile apps shouldn’t be webhook receivers; the URL must be reachable from SabPaisa’s servers). Verify them on your server with one of the server SDKs (PHP or Python), and have the app call fetchStatus (or poll your own status endpoint) to read the result the webhook recorded. See the full Webhooks guide.
Production Best Practices
- •Backend-mode if you can — mint the S2S / hosted session on your server and pass the intentUrl / upiQrString / checkoutUrl to the app. Avoids shipping secretKey to devices.
- •Always HTTPS for returnUrlPrefix — SabPaisa won't accept http://.
- •Register a deep-link returnUrlScheme for your app, otherwise the WebView component can't intercept the return.
- •Set both returnUrlScheme and returnUrlPrefix when possible.
- •Unique merchantTxnId per attempt — a fresh ID on retry of a failed payment.
- •fetchStatus and refunds use the public key only — the SDK rejects sec_… keys at runtime; never wrap them to bypass the check.
- •Don't trust SDK return values for fulfilment — 'PROCESSING' / RETURNED only mean the flow happened.
- •Implement a backend webhook receiver — required in practice for UPI.
- •Capture traceId on every API error — it's what SabPaisa support needs.
- •Pin to a tested version; read the CHANGELOG before bumping.
fetchStatus / backend enquiry). Never mark an order paid on an SDK return alone.Need Help?
For integration support, contact the SabPaisa technical team with:
- •Your merchant ID
- •The trace ID from the error (
e.traceId) - •The SDK version (
1.0.1)
Was this page helpful?