APIs integration

API reference

All endpoints are relative to your environment base URL:

Environment Base URL
Staging https://biz-app.staging.pingbusiness.org
Production https://biz-app.pingbusiness.org

Every request requires the three authentication headers. Requests and responses are JSON (Content-Type: application/json) unless noted. Conventions (money as decimal strings, status letters, error shapes) are collected in Errors & conventions.

Endpoint index

Area Endpoints
Merchant & store GET /merchants · GET /stores · GET /store/{id}
Customers GET /customers · GET /customer/{id} · POST /customer
Products & media GET /products · GET /product/{id} · GET /files · GET /file/{id} · GET /image/{id} · GET /download
Inventory GET /inventories
Checkout intents POST /intent · GET /intents · GET /intent/{id}
Hosted checkout POST /pa/checkout
Payment recording POST /paymentasia/record_payment
Subscriptions POST /recurring/checkout · POST /recurring/tokenization/record · POST /recurring/payment/record
Orders GET /orders · GET /order/{id}
Order items GET /order_items · GET /order_item/{id}
Payments GET /payments · GET /payment/{id}

Merchant & store

GET /merchants

Returns your own merchant record (integration credentials are scoped to one merchant).

Query parameters

Parameter Type Notes
identifier string Your merchant identifier. Recommended, for an unambiguous single-element result.

Returns200, an array with your merchant object:

[
  {
    "id": 7,
    "identifier": "9b2f0e64-…",
    "name": "Acme HK Limited",
    "status": "A",
    "kyb_status": "approved",
    "payment_gateway": "PAYMENT_ASIA",
    "payment_networks": "CreditCard,Fps,PayMe",
    "created_at": "…"
  }
]

payment_networks is a comma-separated string — split it to get the list your checkout may offer. The API key hash is never returned.

GET /stores

Returns stores in your scope. Filter with identifier (your store UUID) to resolve your configured store's numeric id.

Returns200, array of Store objects:

[
  {
    "id": 12,
    "identifier": "2c7a1d90-…",
    "merchant_id": 7,
    "name": "Web Store",
    "mode": "T",
    "details": null,
    "created_at": "…",
    "updated_at": null
  }
]

mode is T (test) or L (live), switched from the Merchant Portal.

GET /store/{id}

Returns one store by numeric ID. Must be your configured store — anything else is 403/404.


Customers

Customer records attach checkouts to a buyer. Map your site's user IDs to Ping Business customers via the username field and reuse the records.

The Customer object

Field Type Notes
id integer Numeric ID — used in customer_id fields.
identifier string Public UUID-like identifier.
merchant_id, store_id integer Your scope. Immutable.
first_name, last_name string Required on create.
email string, nullable
phone string, nullable Max 16 characters.
shipping_address, billing_address string, nullable Free-form.
username string, nullable Your stable external key for the buyer (e.g. your user ID).
details string, nullable Free text; may hold a JSON string.
created_at, updated_at date/time string Read-only.

POST /customer — create

Create mode is selected when the body has no id.

Body parameters

Parameter Required Notes
merchant_id Yes Your merchant's numeric ID; must match your credential scope.
store_id Yes Your store's numeric ID; must match your credential scope.
first_name Yes
last_name Yes
email, phone, shipping_address, billing_address, username, details No See the object table.

Returns201:

{ "id": 42 }

Errors400 missing/invalid fields or store not in merchant; 403 outside your credential scope; 404 merchant/store not found.

POST /customer — update

Update mode is selected when the body contains id. Send only the fields to change; identifier, merchant_id, store_id, created_at, updated_at are immutable.

Returns200 { "message": "Customer updated successfully" }.

GET /customers

With integration credentials this is a lookup, not a browse: all three parameters below are required and username is matched exactly, returning at most one mapping.

Parameter Required Notes
merchant_id Yes Must equal your scope.
store_id Yes Must equal your scope.
username Yes Exact match on your external key.

Returns200, array of matching Customer objects (empty if none).

GET /customer/{id}

Returns one customer by numeric ID, if in your scope. 404 when absent, 403 outside scope.


Products & media

Products are managed in the Merchant Portal; the API surface is read-only. Only approved products (state: "A") can be sold.

The Product object

Field Type Notes
id integer Used in cart lines and inventory/file filters.
identifier string, nullable Your SKU-like public identifier.
store_id integer
name string
description string, nullable
details string, nullable
amount decimal string Current unit price.
currency string 3 uppercase letters.
recurring_frequency string, nullable WEEKLY / MONTHLY / YEARLY; null for one-time products.
recurring_intervals integer, nullable
recurring_total_execution_times integer, nullable
state string A = approved/purchasable.
created_at, updated_at date/time string

A product is a subscription product when all three recurring fields are set. Never mix subscription products into an ordinary checkout.

GET /products

Parameter Type Notes
state string Use A to list only purchasable products.
name string Case-insensitive substring match.
description string Case-insensitive substring match.
identifier string Case-insensitive substring match.
recurring boolean-like true = subscription products only; false = one-time only.
store_id integer Optional; must equal your configured store.

Returns200, Product[] sorted by name.

GET /product/{id}

Returns one product. 404 when absent, 403 outside your store.

GET /files

Product file metadata (images, attachments).

Parameter Type Notes
product_id integer Filter to one product's files.

Returns200, array of ProductFile objects:

Field Type Notes
id integer Use with /image/{id} and /download?file_id={id}.
product_id integer
name string Filename.
description string, nullable The value __PINGBIZ_MAIN_TITLE_IMAGE__ conventionally marks the primary image.
size integer Bytes.
mime_type string Files with an image/* type can be served via /image/{id}.
location string Internal storage metadata — never build a public URL from it.

GET /file/{id}

One file's metadata.

GET /image/{id}

The image bytes (inline content disposition, stored MIME type). 400 if the file isn't an image. Proxy these through your backend or cache them — don't expose your API credentials to fetch them client-side.

GET /download

Parameter Required Notes
file_id Yes File to download.

Returns the file bytes as an attachment with the stored filename.


Inventory

GET /inventories

Parameter Required Notes
product_id Yes The product to check.
location No Case-insensitive substring filter.

Returns200, array of rows:

[
  { "id": 9, "product_id": 101, "quantity": 25, "location": "", "updated_at": "…" }
]

Total availability is the sum of all rows (quantities can be zero or negative; empty array = zero). Revalidate availability just before creating an intent.


Checkout intents

An intent is a checkout in progress. See the payments guide for the lifecycle.

The Intent object

Field Type Notes
id integer
identifier string The checkout ID — used in /pa/checkout and status polling.
order_id integer, nullable Set by the platform after verified success.
merchant_id, store_id, customer_id integer Scope; immutable.
currency string Immutable after creation.
amount decimal string Immutable after creation.
status string C / R / S / F / U — see statuses.
reference string, nullable Your merchant reference (unique, ≤ 36 chars).
intent_details string Your immutable JSON checkout snapshot.
details string, nullable Mutable workflow metadata.
expires_at date/time string, nullable Optional expiry you may set.
created_at, updated_at date/time string

POST /intent — create

Create mode is selected when the body has no id.

Body parameters

Parameter Required Notes
customer_id Yes The buyer.
store_id Yes Your store's numeric ID.
currency Yes 3-letter code; must match every cart product's currency.
amount Yes Decimal string — the checkout total your server computed from current approved prices.
status No Default C. Integration credentials may not create S intents (403).
reference Recommended Your merchant reference: globally unique, ≤ 36 characters. Use a fresh UUID4.
intent_details Yes for checkout JSON string. See below.
expires_at No ISO-8601 date/time.

Do not send merchant_id (derived from your credentials — 400 if present) or order_id (checkout intents are order-less; the order comes after payment).

intent_details schema (serialized as a JSON string):

{
  "source": "my_site_checkout",
  "merchant_reference": "<same value as reference>",
  "checkout_kind": "ordinary",
  "payment_network": "CreditCard",
  "cart": [ { "product_id": 101, "quantity": 2 } ],
  "line_items": [
    { "product_id": 101, "quantity": 2, "unit_amount": "150.00", "amount": "300.00" }
  ],
  "subject": "Order d3a9c2be"
}

For subscriptions use checkout_kind: "subscription", a single-line snapshot, and a subscription: {product_id, quantity} object (Subscriptions). The snapshot is immutable and is revalidated at launch: line totals must equal unit_amount × quantity, sum to the intent amount, and match current approved product prices.

Returns201:

{ "id": 555, "identifier": "0f0e2b1a-…" }

POST /intent — update

Update mode is selected when the body contains id. Integration credentials may update only status, details, and expires_at — and may never set status: "S" (403; success comes only from the verified payment flow). identifier, scope fields, currency, amount, and created_at are immutable (400).

Returns200 { "message": "Intent updated successfully" }.

GET /intents

Parameter Type Notes
identifier string Poll one checkout by its checkout ID.
status string Filter by status letter (e.g. R for reconciliation sweeps).
customer_id integer
created_from, created_to ISO-8601 Inclusive lower / exclusive upper creation-time bounds.

Returns200, Intent[] scoped to your store.

GET /intent/{id}

One intent by numeric ID.


Hosted checkout

POST /pa/checkout

Validates an ordinary checkout intent end-to-end and returns a signed launch form for the gateway's hosted payment page. One launch is signed per intent; identical retries return the stored response, conflicting or terminal checkouts return 409.

Body parameters

Parameter Required Notes
intent_identifier Yes The intent's identifier.
merchant_reference Yes Must equal the intent's reference.
currency Yes Must equal the intent currency.
amount Yes Decimal string; must equal the intent amount.
network Yes One of your enabled payment networks — exact value (403 if disabled).
return_url Yes Public HTTPS URL on your site the customer's browser returns to.
notify_url Yes Public HTTPS URL on your backend for the authoritative server callback.
customer_ip Yes The paying customer's IP.
customer_first_name, customer_last_name Yes
customer_email Yes
customer_phone Yes
customer_address Yes Billing or shipping address; "N/A" if genuinely unavailable.
customer_state, customer_country No Default HK.
customer_postal_code No Default 000000.
subject No Payment subject shown to the customer; default Order <merchant_reference>.
lang No Gateway page language hint.
generic No Send false.

Validation performed: intent exists, is yours, and is launchable; snapshot re-verified against current approved products and prices; network enabled; amounts consistent; Octopus totals an exact multiple of HKD 0.10; callback URLs sane. Any failure returns 400/403/409 before anything is signed.

Returns200:

{
  "action_url": "https://payment.paymentasia.com/…",
  "fields": { "…signed form fields…": "…", "sign": "…" }
}

Render fields as hidden inputs in a form POSTing to action_url in the customer's browser and auto-submit (quickstart step 5). Submit the fields exactly as returned — any change breaks the signature.


Payment recording

POST /paymentasia/record_payment

The only way a one-time checkout becomes successful. Forward the raw callback payload the gateway delivered to your notify_url (or a signed return_url hit); Ping Business verifies the signature and settles the checkout.

Body parameters

Parameter Required Notes
intent_identifier Yes The checkout being settled.
payload Yes The gateway's callback fields, unmodified — the signature covers them.

On verified success, atomically: the order is created, order items are materialized from the intent's line_items snapshot, a Payment (status: "S") is recorded, and the intent becomes S.

Returns200:

{
  "ok": true,
  "payment": { "payment_id": 77, "order_id": 123, "status": "S", "idempotent": false }
}

A verified failed result marks the intent F and returns a result without creating a payment. Exact replays return "idempotent": true — always safe to forward duplicates.

Errors400 invalid signature, or reference/amount/currency mismatch with the intent; 404 unknown intent; 409 conflicting terminal state.


Subscriptions

See the subscriptions guide for the full flow and constraints (HKD, CreditCard, single product, next-day start).

POST /recurring/checkout

Launches card tokenization + schedule enrollment for a subscription intent.

Parameter Required Notes
intent_identifier Yes A checkout_kind: "subscription" intent.
customer_ip Yes
return_url Yes Browser return after tokenization.
notify_url Yes Server-to-server tokenization result callback.
payment_notify_url Yes Server-to-server callback for every future scheduled charge — keep it live for the schedule's lifetime.
subject No
token_valid_date No YYYY-MM-DD card-token expiry hint.

Returns200 with accepted: true and a redirect link; send the customer's browser there:

{
  "accepted": true,
  "normalized_provider_result": { "redirect_link": "https://…" }
}

Read the link from normalized_provider_result.redirect_link, provider_response.payload.redirect_link, or top-level redirect_link, in that order.

POST /recurring/tokenization/record

Verifies and records the tokenization/enrollment result. Same forward-the-payload contract as record_payment; idempotent.

Parameter Required Notes
intent_identifier Yes
payload Yes Raw gateway tokenization callback.

Returns200:

{
  "ok": true,
  "checkout": {
    "order_id": 123,
    "status": "COMPLETE",
    "idempotent": false,
    "order_items": [ { "recurring_status": "ACTIVE", "recurring_merchant_reference": "…" } ]
  }
}

Creates the order with exactly one recurring order item. No one-time Payment row is created for enrollment.

POST /recurring/payment/record

Verifies and records one scheduled charge execution. Forward every payload your payment_notify_url receives; idempotent.

Parameter Required Notes
payload Yes Raw gateway execution callback (form fields or JSON).

Returns200:

{
  "ok": true,
  "recurring_payment": {
    "id": 801,
    "order_item_id": 901,
    "execution_number": 2,
    "amount": "29.90",
    "currency": "HKD",
    "status": "SUCCESS",
    "idempotent": false
  }
}

Orders

Orders are created by the platform after verified payment (or accepted subscription enrollment) and are read-only. There is no POST /order.

The Order object

Field Type Notes
id integer
identifier string Public order identifier.
merchant_id, store_id, customer_id integer
status string Single letter — see status letters.
currency string
subtotal_amount, total_amount decimal string
details string, nullable Server-generated metadata.
created_at, updated_at date/time string

GET /orders

Parameter Type Notes
customer_id integer
status string Single-letter filter.
created_from, created_to ISO-8601 Creation-time bounds.

Returns200, Order[] newest first, scoped to your store.

GET /order/{id}

One order. 404 absent; 403 outside scope.


Order items

The OrderItem object

Field Type Notes
id integer
order_id integer
order_status string, nullable Parent order's status, included for convenience.
product_id integer
quantity integer
unit_amount, amount decimal string Checkout-time prices (immutable history).
state string, nullable D = delivered; null = not marked delivered.
recurring_start_date date string, nullable Subscription lines only.
recurring_frequency, recurring_intervals, recurring_total_execution_times Copied plan (subscription lines).
recurring_merchant_reference string, nullable Provider schedule reference.
recurring_status string, nullable PENDING / ACTIVE / CANCELLED / COMPLETED / ERROR / UNKNOWN.
subscription_details object, nullable Sanitized audit data — raw card/tokenization material is never included.
created_at, updated_at date/time string

GET /order_items

Parameter Type Notes
order_id integer All lines of one order.
product_id integer All lines for one product.

Returns200, OrderItem[].

GET /order_item/{id}

One order item.


Payments

Verified successful one-time payments. Read-only; created exclusively by record_payment.

The Payment object

Field Type Notes
id integer
identifier string Public payment identifier.
order_id integer
merchant_id, store_id integer
currency string
amount decimal string Verified amount.
status string S on success.
reference string, nullable Gateway reference — use it to match settlement reports and in support requests.
details string, nullable Provider metadata (normally JSON text).
created_at date/time string

GET /payments

Parameter Type Notes
order_id integer
status string Single-letter filter (S).
currency string 3-letter code.
identifier string Case-insensitive substring match.
reference string Case-insensitive substring match.
created_from, created_to ISO-8601 Inclusive lower / exclusive upper bounds (timezone-aware input normalized to UTC).

Returns200, Payment[] newest first.

GET /payment/{id}

One payment.