APIs integration

Errors & conventions

Error shape

Errors are JSON:

{ "error": "Human-readable message" }

Some proxied or provider-adjacent responses use message, details, or raw instead. Resolve a display message in this order: errormessageraw → HTTP status text. Some not-found reads return a bodyless 204 No Content — tolerate it.

HTTP status codes

Code Meaning Typical causes
200 / 201 Success 201 on resource creation.
204 No content Some not-found reads.
400 Bad request Missing/invalid fields, snapshot mismatch, currency mismatch, invalid signature payload, Octopus increment violation, attempts to change immutable fields.
401 Unauthenticated Missing API key header.
403 Forbidden Invalid key, out-of-scope merchant/store/resource, disabled payment network, forbidden write (e.g. setting an intent to S, product writes).
404 Not found Resource absent or not visible in your scope.
409 Conflict KYB gate (API key before approval), conflicting or terminal checkout launch, duplicate unique values.
500 Server error Report with timestamp + request path.
502 Upstream error Payment-gateway or internal service failure; safe to retry idempotent reads.

Client guidance: treat 4xx as "fix the request" (don't blind-retry), 502 on reads as retryable with backoff, and callback forwards (record_payment, recurring/*/record) as always safe to retry — they're idempotent.

Idempotency & retries

There is no idempotency-key header; idempotency is built into the money-moving endpoints themselves:

Endpoint Retry behavior
POST /pa/checkout One signed launch per intent. An identical retry returns the stored response; a conflicting or terminal checkout returns 409.
POST /paymentasia/record_payment Exact replays return the recorded result with "idempotent": true. Forward duplicates freely.
POST /recurring/tokenization/record, POST /recurring/payment/record Same replay semantics as record_payment.
POST /intent (create) Not idempotent — a retry after a network timeout creates a second intent. Use your unique reference (merchant reference) to detect and discard duplicates on your side.
All GETs Safe to retry with backoff.

Pagination

List endpoints return the full scoped result set as a JSON array — there is no cursor or page parameter. Keep result sets bounded with the documented filters (identifier, status, created_from / created_to, order_id, …), especially on intents, orders, and payments reconciliation sweeps.

Data conventions

Convention Rule
Money Fixed-point decimal strings with 2 decimals ("300.00"). Never compute money in binary floating point.
Currency 3 uppercase letters (HKD). One currency per checkout.
IDs Numeric id = internal integer key (route paths, foreign keys). identifier = public UUID-like string (≤ 36 chars). A checkout ID is an intent's identifier.
Merchant reference Your unique-per-checkout reference: globally unique, ≤ 36 characters. UUID4 recommended.
Timestamps Server-formatted date/time strings; parse leniently. Range filters accept ISO-8601 (created_from inclusive, created_to exclusive).
Headers Auth via the three X-PingBiz-* headers on every call; never in query strings.

Status letters

Intent (checkout) status

Letter Meaning Terminal
C Created No
R Redirected to gateway / processing No
S Succeeded (verified) Yes
F Failed (verified) Yes
U Unknown — reconcile Yes (for UI)

Order status — single letters; the set includes N, P, C, R, S, F, U. Orders you receive from checkout success are payment-complete; use order status for display and ops filtering.

Payment statusS = successful. Payments are only written after verification, so this is the value you'll see.

Order item stateD = delivered, null = not marked delivered (delivery marking happens in the Merchant Portal / back office, not via this API).

Store modeT = test, L = live.

Product stateA = approved/purchasable.

Recurring statusPENDING, ACTIVE, CANCELLED, COMPLETED, ERROR, UNKNOWN (subscriptions).

Merchant KYB statusin_review, approved, rejected (getting started).