Skip to main content

Accept a payment

Taking an in-store payment is two steps: start it, then follow it to its outcome.

Step 1 — Start the payment

Send the amount to a store and terminal you have registered:

curl https://api-live.kcpboundless.com/api/in-store/payments \
-H "X-API-Key: bndl_live_..." \
-H "Content-Type: application/json" \
-d '{
"merchantExternalId": "your-merchant-id",
"storeExternalId": "your-store-id",
"terminalExternalId": "your-terminal-id",
"amount": 100.00,
"currency": "SGD",
"reference": "order-4821"
}'
FieldRequiredDescription
merchantExternalIdyesYour business identifier.
storeExternalIdyesThe store the payment is taken at.
terminalExternalIdyesThe device at that store.
amountyesDecimal amount in the payment's currency, greater than zero.
currencyyesISO 4217 code, for example SGD.
referencerecommendedYour own reference for the bill. Use it to match the outcome back to your records.
sequenceNumberoptionalPosition of this tender when one bill is split across several payments.
requestedModesoptionalRestrict what the terminal offers, for example ["CARD"]. Omit to allow every mode the device supports.
terminalTimeoutMinutesoptionalHow long the device waits for the customer.
cashierId, customerMobile, customerEmailoptionalPassed through for the receipt and your records.

The response is the payment, waiting at the terminal:

{
"id": 90210,
"pspReference": "BNDL7K2P9QW1ABCD",
"status": "AWAITING_TERMINAL",
"amount": 100.00,
"currency": "SGD"
}
  • id — the payment id. Use it to check status.
  • pspReference — Boundless's stable reference for the payment.
  • statusAWAITING_TERMINAL means the bill is on the device and no money has moved yet.

The customer now taps their card at the terminal.

Step 2 — Follow it to the outcome

You learn the result two ways. Use webhooks as your primary path; use status polling as a fallback and for an on-demand "check status" button.

Register a webhook endpoint (see Webhooks) and Boundless will call it when the payment reaches a final state — captured, declined, cancelled. You do not have to poll.

By status call

Two calls answer "where is this payment now", and they differ in cost:

Read the current state — a plain lookup of what Boundless already knows. Cheap; use this for routine polling:

curl https://api-live.kcpboundless.com/api/transactions/90210 \
-H "X-API-Key: bndl_live_..."

Refresh from the terminal — asks the acquirer what happened at the device and applies the answer before responding. Use it when you suspect Boundless's view is behind the device — for example the customer says they paid but the payment still reads as waiting:

curl -X POST https://api-live.kcpboundless.com/api/in-store/payments/90210/sync-status \
-H "X-API-Key: bndl_live_..."

Either way the response is the payment, with its status advanced if the customer has finished:

{
"id": 90210,
"status": "CAPTURED",
"amount": 100.00,
"currency": "SGD",
"cardBrand": "VISA",
"last4": "4242"
}

Both calls are safe to make repeatedly. If the payment is still open, you get AWAITING_TERMINAL again; once it is final, the state does not change on you. A sensible point of sale combines a webhook subscription with one sync-status behind its "check payment" button.

Outcomes

StatusMeaning
AWAITING_TERMINALOn the device, waiting for the customer. Not yet paid.
CAPTUREDPaid. Funds are captured.
AUTHORIZEDApproved and held, awaiting completion. Applies only to terminals configured for a two-step authorize-then-complete flow.
REFUSEDDeclined at the terminal.
CANCELLEDThe bill was cancelled before the customer paid.
EXPIREDNobody completed the bill in time; Boundless closed it automatically.
ERROR, FAILEDA processing problem prevented a decision. Safe to retry with the same reference.

Retries, duplicates, and split bills

Point-of-sale software retries: networks drop, cashiers double-press, apps restart mid-request. The API is built for that.

  • One open payment per reference. While a payment carrying your reference is still open — waiting at the terminal or authorized — sending the same create request again returns that same payment rather than creating a duplicate. Retry a timed-out create with the same reference with confidence. Once the payment reaches a final state, the same reference may be used again for a genuinely new bill.
  • A failed payment is retry-safe. ERROR and FAILED are final, so re-creating with the same reference starts a fresh payment — the guarantee above ensures you never end up with two live ones.
  • Split bills. When one bill is settled by several tenders, keep the same reference and give each tender its own sequenceNumber (1, 2, 3…). Each reference-and-sequence pair gets its own one-open-payment guarantee.
  • One bill on the device at a time. A terminal shows one bill. Starting a second payment (different reference) while the first is still on the device does not return an HTTP error — the create responds normally with the new payment already REFUSED and the terminal provider's explanation attached. Complete or cancel the open bill, then create again. Always branch on the returned status, never on the HTTP code alone.

Good practice

  • Match on your reference. Every outcome carries the payment back to the bill you started, so set a reference you can look up. The reference is also printed on the customer's payment slip, which lets store staff read a bill's reference straight off the paper when reconciling.
  • Do not assume from the start call. A 201 on the start request means the bill reached the device, not that the customer paid. The payment is only paid at CAPTURED.
  • A bill nobody completes is closed automatically. If a customer walks away, Boundless resolves the payment on its own after the terminal's timeout — you do not have to reconcile abandoned bills yourself.

Cancelling an open bill

If the customer changes their mind before paying, withdraw the bill from the device:

curl -X POST https://api-live.kcpboundless.com/api/in-store/payments/90210/cancel \
-H "X-API-Key: bndl_live_..."

The response is the payment in its resulting state — normally CANCELLED.

A cancellation never overrides a completed payment

The cancel request and the customer's tap can race each other. If the customer completed the payment first, their payment stands and the response comes back CAPTURED, not CANCELLED. Always read the returned status rather than assuming the cancellation took effect.

You can only cancel a payment that is still waiting at the terminal. Cancelling one that already reached a final state returns that state unchanged.

Unresponsive terminals

Forcing a bill off a device that cannot be reached is a separate, operator-only action, because it interrupts the terminal itself rather than simply withdrawing a bill. Contact Boundless if a terminal is stuck with a bill you cannot cancel normally.