Skip to content
Appearance

One API for ABA PayWay and Bakong KHQR.

Choose the rail, POST an amount, and receive a KHQR with its exact deadline. We watch the status with you until the outcome is known.

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",
  "provider": "BAKONG",
  "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 where you collect

    Once

    Add a store with its required USD PayWay link, optional KHR link, and optional Bakong account ID. Then create an API key after signing in with Google or GitHub.

  2. 02

    POST an amount

    Per order

    One authenticated request carries the store, amount, currency, provider, and your own tranId. It returns a payment id, KHQR payload, and exact 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 decided

    Poll the status route every two or three seconds until the state leaves PENDING. 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",
  "provider": "BAKONG",
  "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…",
  "paywayLink": null,
  "khqrMd5": "e0f3…",
  "deeplink": null,
  "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.

ABA payments can offer a mobile deeplink when present. Bakong payments stay QR-first, so the scannable KHQR is always the source of truth.

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. Read the returned clocks rather than assuming a duration, and never manufacture a terminal state from the local clock.

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, provider, currency, and decimal amount string return a KHQR payload ready to render anywhere. ABA uses PayWay; Bakong builds the code locally.

An ABA deeplink when available

ABA payments can carry ABA Mobile deeplinks for checkout without a second device. Bakong returns no deeplink, and the QR remains the source of truth on every rail.

A status route built for polling

Poll one lightweight endpoint every two or three seconds until the state changes. 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 PayWay links, an optional Bakong account, a name, and an on/off switch. Payments and history remain scoped per store.

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 bank 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 remains required on every store. Add a Bakong account ID when that store should also accept Bakong payments.

How does my server learn the outcome?

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

What happens if the QR expires?

Bongluy checks the provider once more at the deadline before marking a payment EXPIRED. Only that terminal status is an expiry verdict; a PENDING result still needs reconciliation.

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. ABA payments may also include an ABA Mobile deeplink; Bakong payments are paid from the QR.

Your first payment, in about ten minutes.

Add the store, configure its payment rails, create a key, and POST your first amount, currency, and provider.