Use the lightweight status route
The status endpoint is the observation channel for an active checkout; the detail endpoint is the durable record used afterward.
Send a store identifier plus either Bongluy's payment UUID inid or your owntranId. When both payment identifiers are present, id wins.
curl -X POST https://api.bongluy.com/payment/status \
-H "Authorization: Bearer $BONGLUY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"merchantStoreId": "branch-2",
"tranId": "INV-1042"
}'The status response uses epoch milliseconds forexpireAt and at. The payment object returned by create and detail uses an ISO 8601 expiry, so do not parse the two response shapes as if they were identical.
Branch on four payment states
State names are stable machine values. Only a terminal value from the server decides whether payment succeeded or expired.
PENDING- Created and awaiting payment.
SUCCESS- Paid and settled.
EXPIRED- Expired without payment.
FAILED- Reserved for a future non-success terminal state.
Use a bounded polling loop
Poll every two or three seconds, stop when the state leaves PENDING, or pause when your checkout has waited long enough.
async function waitForPayment(payment, giveUpAfterMs = 10 * 60_000) {
const deadline = Date.now() + giveUpAfterMs;
while (Date.now() < deadline) {
const result = await readStatus(payment.tranId);
if (result.status !== "PENDING") return result.status;
await new Promise((resolve) => setTimeout(resolve, 2500));
}
// Undecided, not unpaid. Reconcile later.
return "PENDING";
}Poll once per payment from your server or one active checkout, not from every browser tab. Authenticated routes are limited per API key, so duplicate loops waste capacity without making settlement arrive sooner.
Read detail for the durable record
After success, use POST /payment/detail to retrieve the full record required for receipts and order reconciliation.
The detail response contains the amount, currency, your transaction id, provider transaction reference, settlement time, QR, optional receipt and deeplinks, and timestamps. Match it to your order using the sametranId supplied during creation.
A store webhook can deliver the same terminal result without a polling loop. Keep this detail read as the durable reconciliation path for any expected webhook that never arrives.
Retry without creating a duplicate
A network timeout does not prove the original create failed, so retry the create request with the same store and transaction id.
Bongluy uses tranId as the idempotency key within a store. Reusing it resolves to the original payment; changing it requests a new payment. On authentication or rate-limit errors, fix the key or back off before polling again rather than replacing the transaction id.
Continue the integration
Read KHQR API guide next, or use the complete API reference for request fields, response shapes, limits, and error behavior.