sabpaisa 1.1.0
sabpaisa: ^1.1.0 copied to clipboard
Official Flutter/Dart SDK for SabPaisa Payment Gateway 2.0 — payments, refunds, transaction enquiry, webhook verification.
SabPaisa Flutter SDK #
Official Flutter/Dart SDK for SabPaisa Payment Gateway 2.0.
Features #
- Payment session creation with checkout URL
- In-app WebView checkout with UPI intent handling (
SabPaisaCheckout) - In-app browser redirect (
redirectToCheckout) - Return URL signature verification
- Transaction enquiry
- Refund creation, status, and listing
- User-defined fields (udf1–udf20)
- Retry with exponential backoff on transient failures
- Idempotency key support for safe retries
Installation #
Add to your pubspec.yaml:
dependencies:
sabpaisa: ^1.1.0
Then run:
flutter pub get
Quick Start #
import 'package:sabpaisa/sabpaisa.dart';
final sabpaisa = SabPaisaClient(SabPaisaConfig(
apiKey: 'sp_your_api_key',
merchantId: 'YOUR_MERCHANT_ID',
secretKey: 'sec_your_secret_key',
clientCode: 'YOUR_CLIENT_CODE',
env: Environment.staging, // or Environment.production
));
Usage #
Create a Payment #
final payment = await sabpaisa.payments.createSession(
const CreatePaymentRequest(
merchantTxnId: 'ORDER_123',
amount: 50000, // Rs 500.00 in paise
customerName: 'John Doe',
customerEmail: 'john@example.com',
customerMobile: '9876543210',
// returnUrl is optional for in-app checkout — SDK provides a default.
// Provide one if you handle the redirect yourself:
// returnUrl: 'https://yoursite.com/callback',
udfFields: { // optional user-defined fields (udf1–udf20)
'udf1': 'plan-premium',
'udf2': 'campaign-summer',
},
),
);
print(payment.checkoutUrl); // Ready-to-use URL with clientSecret
print(payment.paymentId);
With idempotency key (recommended — prevents duplicate payments on retry):
final payment = await sabpaisa.payments.createSession(
request,
options: const RequestOptions(idempotencyKey: 'unique-order-key-123'),
);
In-App WebView Checkout (Recommended) #
Opens the checkout page in a WebView inside your app. Handles UPI intent launching (gpay://, phonepe://, paytm://, etc.) and intercepts the return URL automatically:
final response = await sabpaisa.payments.createSession(
const CreatePaymentRequest(
merchantTxnId: 'ORDER_123',
amount: 50000,
customerName: 'John Doe',
customerEmail: 'john@example.com',
customerMobile: '9876543210',
returnUrl: 'https://yoursite.com/callback',
),
);
final result = await Navigator.push<CheckoutResult>(
context,
MaterialPageRoute(
builder: (_) => SabPaisaCheckout(
checkoutUrl: response.checkoutUrl,
returnUrl: 'https://yoursite.com/callback',
),
),
);
switch (result) {
case CheckoutCompleted(callbackUri: final uri):
// Payment reached return URL — inspect uri.queryParameters
final params = ReturnUrlParams.fromJson(
uri.queryParameters.map((k, v) => MapEntry(k, v as dynamic)),
);
final isValid = sabpaisa.payments.verifyReturnUrl(params);
print('Status: ${params.status}, Valid: $isValid');
case CheckoutError(message: final msg):
print('Error: $msg');
case CheckoutCancelled():
print('User cancelled');
case null:
break; // pop without result
}
SabPaisaCheckout also supports optional callbacks and customization:
SabPaisaCheckout(
checkoutUrl: response.checkoutUrl,
returnUrl: 'https://yoursite.com/callback',
title: 'Pay Now',
showAppBar: true,
onPaymentComplete: (uri) => print('Done: $uri'),
onError: (msg, err) => print('Error: $msg'),
onCancel: () => print('Cancelled'),
)
Redirect to Checkout (Flutter) #
Opens the checkout page in an in-app browser (Chrome Custom Tabs on Android, SFSafariViewController on iOS). No returnUrl needed:
await sabpaisa.payments.redirectToCheckout(
const CreatePaymentRequest(
merchantTxnId: 'ORDER_123',
amount: 50000,
customerName: 'John Doe',
customerEmail: 'john@example.com',
customerMobile: '9876543210',
),
);
Or get the URL and handle redirection yourself:
final url = sabpaisa.payments.getCheckoutUrl(payment);
Verify Return URL Callback #
After checkout, SabPaisa redirects to your returnUrl with signed query parameters:
final isValid = sabpaisa.payments.verifyReturnUrl(
ReturnUrlParams.fromJson(callbackQueryParams),
);
if (isValid) {
// Signature is authentic — safe to process
}
ReturnUrlParams fields: transactionId, merchantTxnId, status, amount, paidAmount, paymentMode, timestamp, signature.
Transaction Enquiry #
final txn = await sabpaisa.transactions.enquiry(
merchantTxnId: 'ORDER_123',
);
print(txn.data.status); // SUCCESS, FAILED, PENDING
print(txn.data.amountPaise); // Amount in paise
print(txn.data.paymentMode); // UPI, CARD, etc.
print(txn.data.bankTxnId); // Bank transaction reference
print(txn.data.bankRrn); // Bank RRN
print(txn.data.udfData); // User-defined fields (Map<String, String>)
Refunds #
Create a refund:
final refund = await sabpaisa.refunds.create(
const CreateRefundRequest(
txnId: 'SP_TXN_456',
amount: 25000, // Partial refund: Rs 250.00
reason: 'Customer request',
),
options: const RequestOptions(idempotencyKey: 'refund_ORDER_123_1'),
);
print(refund.data.refundId);
Check refund status:
final status = await sabpaisa.refunds.getStatus('REFUND_ID');
print(status.data.status); // PENDING, COMPLETED, FAILED
List refunds:
final list = await sabpaisa.refunds.list(
const RefundListParams(page: 0, size: 20),
);
for (final r in list.data) {
print('${r.refundId}: ${r.status}');
}
// Pagination
if (list.pagination != null) {
print('Page ${list.pagination!.page + 1} of ${list.pagination!.totalPages}');
print('Total: ${list.pagination!.total}');
}
Configuration #
| Parameter | Required | Description |
|---|---|---|
apiKey |
Yes | API key from SabPaisa dashboard |
merchantId |
Yes | Merchant ID for authentication |
secretKey |
Yes | HMAC secret for checksums |
clientCode |
Yes | Client code for API requests |
env |
Yes | Environment.staging or Environment.production |
baseUrl |
No | Custom URL override (takes precedence over env) |
Error Handling #
SabPaisaError (base)
├── ApiError — API returned an error (4xx, 5xx)
├── ChecksumError — Checksum verification failed
└── ValidationError — Invalid input (caught before API call)
try {
await sabpaisa.payments.createSession(params);
} on ValidationError catch (e) {
// Client-side validation failed
print('${e.field}: ${e.message}');
} on ApiError catch (e) {
// API returned an error
print('${e.message} (HTTP ${e.statusCode})');
print('Retryable: ${e.retryable}'); // true for 5xx, 429
print('Trace ID: ${e.traceId}'); // share with SabPaisa support
} on SabPaisaError catch (e) {
// Network error, timeout, etc.
print('${e.code}: ${e.message}');
}
Request Options #
Customize timeout or add an idempotency key per request:
// Idempotency key (prevents duplicate operations on retry)
const options = RequestOptions(idempotencyKey: 'unique-key');
// Custom timeout (milliseconds)
const options = RequestOptions(timeoutMs: 60000);
// Both
const options = RequestOptions(idempotencyKey: 'unique-key', timeoutMs: 60000);
// Use with any method
await sabpaisa.payments.createSession(request, options: options);
await sabpaisa.refunds.create(refundRequest, options: options);
await sabpaisa.refunds.getStatus('RFD_123', options: options);
Important Notes #
| Topic | Detail |
|---|---|
| Amounts | Always in paise. Rs 500.00 = 50000. The SDK does not convert. |
| Customer Mobile | Use customerMobile in the SDK. It is automatically mapped to the API's customerPhone field. |
| Idempotency Keys | Always use for createSession() and refunds.create() in production to prevent duplicates. |
| Timeout | Default 30 seconds per request. Override with RequestOptions(timeoutMs: ...). |
| Config Immutability | Config cannot be changed after creation. Create a new SabPaisaClient for different credentials. |
Support #
For integration support, contact the SabPaisa technical team with:
- Your merchant ID
- The trace ID from the error response (
ApiError.traceId) - The SDK version (
1.1.0)