Search pages in the SMS Pay documentation.
A payment intent represents one payment attempt. It is created after the customer chooses a payment method and remains active for 5 minutes by default. The checkout page and reconciliation engine use the intent amount, receiver wallet, payment method, reference, and expiry to decide whether an SMS event can confirm the payment.
Current API route:
POST /v1/payments/intents
paymentMethod.id with your order.checkoutUrl.payment.paid webhook or poll the intent from your backend.PAID.POST /v1/payments/intents
X-Api-Key: sk_test_xxxxxxxxxxxxxxxxx
Content-Type: application/json
| Field | Required | Description |
|---|---|---|
amount | Yes | Payment amount. Supports up to two decimal places. |
currency | No | Must be BDT. Defaults to BDT. |
paymentMethod | Yes | One of BKASH_SEND_MONEY or BKASH_PAYMENT. Determines which receiver account is selected. |
customerReference | Yes | Merchant order or invoice reference shown on checkout. Must be globally unique for new intents. |
customerId | No | Optional merchant customer ID. |
expectedSenderMsisdn | No | Optional payer phone number for stronger scoring. |
expectedTrxId | No | Optional transaction ID if collected before matching. |
idempotencyKey | Yes | Retry-safe key for this create request. |
ttlSeconds | No | Intent lifetime in seconds. Defaults to 300. Minimum is 60. |
successUrl | No | Redirect URL after the customer's payment is confirmed (PAID). Must be http or https. |
failedUrl | No | Redirect URL on payment failure or rejection. Must be http or https. |
cancelUrl | No | Redirect URL when the customer clicks cancel on the checkout page. Must be http or https. |
expiredUrl | No | Redirect URL when the payment intent expires before confirmation. Must be http or https. |
Each merchant configures one or more receiver accounts per environment (Sandbox / Live) and payment method. When a payment intent is created, the system automatically picks the least-recently-used active receiver account for the requested paymentMethod and environment (round-robin). The selected account's MSISDN is snapshotted into receiverMsisdn and its receiverAccountId is stored for auditing.
If no active receiver account is found for the combination of merchant + environment + paymentMethod, the API returns a 400 Bad Request with error code RECEIVER_ACCOUNT_NOT_CONFIGURED. Configure receiver accounts in the organization settings before creating live intents.
Hosted checkout should only show methods that satisfy all three conditions:
Use the checkout availability endpoint before creating the intent:
GET /v1/merchants/:merchantId/checkout-payment-methods?environment=SANDBOX
X-Api-Key: sk_test_xxxxxxxxxxxxxxxxx
Example response:
{
"methods": [
{
"paymentMethod": "BKASH_PAYMENT",
"label": "bKash Payment",
"description": "Pay using bKash merchant payment.",
"available": true
},
{
"paymentMethod": "BKASH_SEND_MONEY",
"label": "bKash Send Money",
"description": "Send money to the merchant bKash number.",
"available": true
}
]
}
If no method is available, the checkout UI should show: No payment methods are currently available for this merchant.
If the customer submits a transaction ID from checkout, show user-friendly error text in the UI. Never render raw backend reason codes such as sms_event_not_found_for_trx_id directly to the customer.
export async function createPaymentIntent(order: {
id: string;
amount: number;
}) {
const response = await fetch(
"https://api.smspaybd.com/v1/payments/intents",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": process.env.SMS_PAY_SANDBOX_KEY!,
},
body: JSON.stringify({
amount: order.amount,
currency: "BDT",
paymentMethod: "BKASH_SEND_MONEY",
customerReference: order.id,
idempotencyKey: `payment_intent_${order.id}`,
ttlSeconds: 300,
successUrl: "https://smspaybd.com/payment/success",
failedUrl: "https://smspaybd.com/payment/failed",
cancelUrl: "https://smspaybd.com/checkout",
expiredUrl: "https://smspaybd.com/payment/expired",
}),
},
);
if (!response.ok) {
throw new Error(await response.text());
}
return response.json();
}
{
"amount": 500,
"currency": "BDT",
"paymentMethod": "BKASH_SEND_MONEY",
"customerReference": "ORDER-10045",
"idempotencyKey": "payment_intent_ORDER-10045",
"ttlSeconds": 300,
"successUrl": "https://smspaybd.com/payment/success",
"failedUrl": "https://smspaybd.com/payment/failed",
"cancelUrl": "https://smspaybd.com/checkout",
"expiredUrl": "https://smspaybd.com/payment/expired"
}
{
"id": "b5012f33-207e-4999-bf8d-5a1ebb10988e",
"merchantId": "merchant_123",
"environment": "SANDBOX",
"amount": "500",
"currency": "BDT",
"status": "PENDING",
"paymentMethod": "BKASH_SEND_MONEY",
"customerReference": "ORDER-10045",
"receiverMsisdn": "01700000001",
"receiverAccountId": "recv-acme-sandbox-bsm-1",
"expectedSenderMsisdn": null,
"expectedTrxId": null,
"successUrl": "https://smspaybd.com/payment/success",
"failedUrl": "https://smspaybd.com/payment/failed",
"cancelUrl": "https://smspaybd.com/checkout",
"expiredUrl": "https://smspaybd.com/payment/expired",
"checkoutUrl": "https://smspaybd.com/checkout/b5012f33-207e-4999-bf8d-5a1ebb10988e",
"expiresAt": "2026-05-05T10:05:00.000Z",
"createdAt": "2026-05-05T10:00:00.000Z",
"updatedAt": "2026-05-05T10:00:00.000Z"
}
GET /v1/payments/intents/:id
X-Api-Key: sk_test_xxxxxxxxxxxxxxxxx
{
"id": "b5012f33-207e-4999-bf8d-5a1ebb10988e",
"environment": "SANDBOX",
"amount": "500",
"currency": "BDT",
"status": "PAID",
"customerReference": "ORDER-10045",
"receiverMsisdn": "01700000001",
"checkoutUrl": "https://smspaybd.com/checkout/b5012f33-207e-4999-bf8d-5a1ebb10988e",
"expiresAt": "2026-05-05T10:05:00.000Z"
}
GET /v1/checkout/:id
{
"id": "b5012f33-207e-4999-bf8d-5a1ebb10988e",
"amount": "500",
"currency": "BDT",
"status": "PENDING",
"environment": "SANDBOX",
"customerReference": "ORDER-10045",
"receiverMsisdn": "01700000001",
"expiresAt": "2026-05-05T10:05:00.000Z"
}
When you provide redirect URLs, the hosted checkout page automatically navigates the customer after the payment resolves:
| Status reached | Redirect used |
|---|---|
PAID | successUrl |
FAILED, REJECTED | failedUrl |
EXPIRED | expiredUrl |
| Customer cancels | cancelUrl |
Behavior:
payment_intent_id — the intent UUIDstatus — final status string (e.g. PAID, EXPIRED, CANCELLED)customer_reference — the reference you provided at intent creationhttp and https URLs are accepted. URLs using javascript:, data:, file:, or any other protocol are rejected with a 400 error.Warning: Never fulfill orders solely based on browser redirects. Always verify payment status server-side using the webhook
payment.paidevent or by retrieving the intent viaGET /v1/payments/intents/:id. Redirect URLs can be manipulated by the customer.
idempotencyKey when retrying create requests.