Skip to content
Appearance

Turn your ABA payment link into a payment API.

One POST returns a KHQR, a deeplink, and an expiry. We watch the status with you until the payment settles or the QR expires.

Get an API keyRead the docs
Create a payment
POST /payment
Authorization: Bearer sk_live_…
Content-Type: application/json

{
  "merchantStoreId": "branch-2",
  "amount": "12.50",
  "currency": "USD",
  "tranId": "INV-1042"
}
KHQR

Bongluy Demo Store

12.50USD

Scannable KHQR to pay Bongluy Demo Store
$
Awaiting payment3:00 remaining

An ABA payment link is an interface for a human, not for your software.

Today the link is the whole integration surface. You send it, the customer types the amount themselves, and your system finds out it was paid when somebody tells it. There is no call to make, no identifier to correlate, and no event to subscribe to.

What the link gives you

  • An amount typed by the payer, so wrong often enough.
  • No id to reconcile against your own order.
  • No callback, no status endpoint, nothing to poll.
  • Confirmation by a person reading a banking app.
  • No history you can query after the fact.

What the API gives you

  • An amount your backend sets, exact by construction.
  • A payment id, and your own tranId carried alongside it.
  • A polling endpoint, plus a durable record you can read back.
  • A terminal state your code can branch on.
  • Permanent, paged, filterable history.

The link already works. It just had no API. We gave it one.

Create a payment. Render the QR. Poll for the outcome.

Four steps carry the integration from one-time setup to a durable result. The returned expiry keeps the polling loop bounded.

  1. 01

    Register the link you already have

    Once

    Add a store, paste its existing ABA PayWay link, and create an API key after signing in with Google or GitHub. There is no sandbox key today.

  2. 02

    POST an amount

    Per order

    One authenticated request carrying the store and a decimal amount string, plus your own tranId if you have one. It returns a payment id, a KHQR payload, ABA Mobile deeplinks, and an expiry timestamp.

  3. 03

    Render the QR wherever you like

    Your surface

    The payload is a plain KHQR string. Draw it on a web checkout, a POS screen, a printed slip, or a chat message — any KHQR-capable banking app can pay it.

  4. 04

    Poll, then reconcile

    Until expiry

    Poll the status route every two or three seconds until payment succeeds or the returned expiry passes. Read the detail route when you need the durable record.

Your request
POST /payment
Authorization: Bearer sk_live_…
Content-Type: application/json

{
  "merchantStoreId": "branch-2",
  "amount": "12.50",
  "currency": "USD",
  "tranId": "INV-1042"
}
What comes back
{
  "id": "8435481a-48a-4bb2-91d2-bcd1e604fb17",
  "status": "PENDING",
  "amount": "12.50",
  "currency": "USD",
  "tranId": "INV-1042",
  "qrString": "00020101021229400015kh.gov.nbc…",
  "deeplink": {
    "scheme": "…",
    "android": "…"
  },
  "expireAt": "2026-08-14T09:32:11Z"
}

The checkout you build, confirming itself.

Render the qrString and the amount, then show the status you already have. It moves to Success without a refresh because the checkout polls the public status route every two or three seconds.

On a phone, the deeplink in the same payload hands the customer straight to ABA Mobile with the amount already filled in. The QR always stays on screen as the fallback.

Bongluy Demo Store3:00
KHQR

Bongluy Demo Store

12.50USD

Scannable KHQR to pay Bongluy Demo Store
$
PendingChecking every 2.5s

Demo payment is pending.

REST, API keys, and bounded polling.

Authenticated store and payment routes take a server-side bearer key. Keys are prefixed sk_live_, shown once at creation, rate limited to 600 requests a minute, expire after 90 days, and carry fixed scopes. Public checkout reads use a separate server-side key.

Status after settlement
{
  "paymentId": "8435481a-48a-4bb2-91d2-bcd1e604fb17",
  "status": "SUCCESS",
  "expireAt": 1786763662418,
  "settledTranId": "1234567890",
  "receipt": "https://…",
  "at": 1786763501992
}

Polling without an endless checkout

Poll /payment/status every two or three seconds until the state leaves PENDING or the returnedexpireAt passes. A stale pending payment past that deadline is unpaid.

Read /payment/detail for the durable record. Merchant webhooks are not available yet, even though webhook fields can be stored on a store.

Everything around the payment, not just the payment.

KHQR from a single call

A store id and a decimal amount string return a KHQR payload ready to render anywhere. Currency is record metadata only; the actual charge follows the store's PayWay link.

A deeplink in the same payload

A payment with a QR also carries ABA Mobile deeplinks for checkout without a second device. Android can fall through to the Play Store; iOS may do nothing when the app is absent, so the QR stays visible as the source of truth.

A status route built for polling

Poll one lightweight endpoint every two or three seconds until the state changes or expireAt passes. A separate detail route returns the durable payment record for reconciliation.

Idempotency keys

Send a tranId and a repeated request for that store returns the original payment instead of issuing a second QR. Retrying a timed-out create is then safe.

Stores under one account

One account holds many stores, each with its own PayWay link, name, and on/off switch. Payments and history are scoped per store; an account's API keys reach every store it owns.

A queryable payment record

Every payment keeps its amount, currency, status, your tranId, the settled transaction id, receipt link, and timestamps. Lists are paged and filterable by status; detail supports exact tranId lookup.

Checkout-scoped reads

Public payment and status routes use a separate server-held checkout key. They disclose only the store name, amount, status, and QR, keeping account API keys and account data out of checkout code.

The money goes straight to the merchant’s bank. We only tell you it arrived.

The money never touches us

Funds move from the payer straight into the merchant's own ABA account. Bongluy holds no balance, runs no payout schedule, and carries no float.

No card data in the system

The service handles QR payments only. There are no card numbers collected, transmitted, or stored anywhere, so there is nothing sensitive to leak.

Reads are scoped to the account

Authenticated reads report a foreign payment or store exactly like one that does not exist, so another account's key cannot use responses to probe ids.

Keys stay out of browser code

Account and checkout keys belong on your server. Public checkout responses are payment-scoped and never expose store ids, transaction ids, receipts, or internal errors.

The questions that come up in review.

What do I need from ABA?

An existing ABA PayWay payment link. Register that link on a Bongluy store, then create payments against the store from your server.

How does my server learn the outcome?

Poll POST /payment/status every two or three seconds until the state leaves PENDING or expireAt passes. Use POST /payment/detail for the durable record. Merchant webhooks are not available yet.

What happens if the QR expires?

Nothing is charged. The bank sets the window and Bongluy returns the exact expireAt. Treat expiry as an unpaid outcome and use a new tranId when you create the replacement payment.

Expired
How do API keys scope to stores?

Keys belong to the account, not to an individual store, so one key reaches every store that account owns. Keys are prefixed sk_live_, shown once at creation, rate limited to 600 requests a minute, and expire after 90 days.

Which banking apps can pay it?

Any KHQR-capable banking app can scan the code. On a phone, the deeplink in the payment payload can hand the customer straight into ABA Mobile with the amount already filled in.

Your first payment, in about ten minutes.

Add the store, paste the PayWay link, create a key, and POST your first amount.