Search pages in the SMS Pay documentation.
This page answers the questions developers and operators most often hit while integrating SMS Pay.
When a payment is not becoming paid:
PENDING and not EXPIRED.receiverMsisdn.customerReference.Most failed matches are caused by one of these:
No. The SMS Simulator only sends sandbox SMS events. Create a sandbox payment intent first, open checkout, then send a matching simulator SMS event.
If the intent is expired, the gateway prevents auto-confirmation. Your operations team can inspect the SMS event and order manually, but your system should not auto-fulfill expired payments.
Wrong amount does not auto-confirm the payment. Depending on other matching signals, it may remain pending or move to REVIEW_REQUIRED.
The instant match needs the reference. If the SMS has a valid transaction ID and the customer enters that transaction ID on checkout, fallback confirmation can still succeed when amount, receiver, active intent, and unused SMS checks pass.
The reference and receiver prevent accidental matching. If data is ambiguous, the gateway routes to review instead of marking the wrong intent paid.
Webhook delivery is at-least-once. Store X-Webhook-Id or your own idempotency record keyed by payment intent and event type.
Safe fulfillment check:
async function fulfillFromWebhook(event: {
event: string;
data: { payment_intent_id: string; customer_reference: string };
}) {
if (event.event !== "payment.paid") return;
await db.transaction(async (tx) => {
const order = await tx.orders.findUnique({
where: { reference: event.data.customer_reference },
});
if (!order || order.status === "paid") return;
await tx.orders.update({
where: { id: order.id },
data: { status: "paid" },
});
});
}
Check a payment intent:
GET /v1/payments/intents/b5012f33-207e-4999-bf8d-5a1ebb10988e
X-Api-Key: sk_test_xxxxxxxxxxxxxxxxx
{
"id": "b5012f33-207e-4999-bf8d-5a1ebb10988e",
"status": "REVIEW_REQUIRED",
"amount": "500",
"currency": "BDT",
"customerReference": "ORDER-10045",
"receiverMsisdn": "01700000001"
}