中文
🤖 Want to integrate quickly with AI? Open AI integration prompt →
Download Markdown · Official SDKs (GitHub)

Merchant OpenAPI Integration Guide

  • Base URL: Log in to the merchant console "Security Center · OpenAPI" to obtain your actual integration address (this guide uses <BASE_URL> as a placeholder).
  • API version: v1 (server version 1.2.0; every response carries the X-Api-Version header).
  • Export date: 20260710

Note: This guide does not contain the merchant real credentials or allowlist information; replace the placeholders before integrating.

Versioning and Compatibility Policy

  • The major version is reflected in the path prefix: currently /api/open/v1.
  • v1 evolves only in a backward-compatible way (adding optional fields, fixing defects); it will not change the meaning of existing fields or tighten existing validation.
  • If a breaking change is required, a new /api/open/v2 will be added while v1 is retained for a period, allowing merchants to migrate smoothly.
  • Call GET <BASE_URL>/version to query the current server version (no authentication required).

Changes in this version (1.2.0):

  • Collection external status no longer returns expired: unpaid timeouts are normalized to pending (processing); rely on the order query result.
  • Collection only sends a callback to the merchant when the order succeeds (success); non-success states such as failed/timeout trigger no callback, and the merchant should obtain the final result via the order query API (Payout is not subject to this restriction).

Merchant Information

  • Merchant name: <YOUR_MERCHANT_NAME>
  • Merchant no.: <YOUR_MERCHANT_NO>

Official SDKs

Official SDKs for 5 languages: zero third-party dependencies (standard library only), with built-in HMAC-SHA256 request signing / callback verification, dual environments (SANDBOX local / PRODUCTION with your dedicated-domain baseUrl), full endpoint coverage, and byte-for-byte consistent signing across languages. Use the SDK directly to integrate — no need to implement signing or stable serialization yourself.

Language Install Package / Source
Node.js npm i @bebebus/merchant-openapi-sdk npm
Python pip install bebebus-merchant-openapi-sdk PyPI
PHP composer require bebebus/merchant-openapi-sdk Packagist
Go go get github.com/bebebus/SDK/go pkg.go.dev
Java From source (package cloud.cniia.openapi.sdk, not on Maven) GitHub
  • Repository and releases: https://github.com/bebebus/SDK (see SIGNING.md for the authoritative signing algorithm and INTERFACES.md for field-level request/response).

Collection: Create Order (pay/create)

  • Method: POST
  • Endpoint: <BASE_URL>/merchant/pay/create
  • Purpose: Create a collection order; returns the API order no. and a payment URL / QR code content.

Request Details

  • Content-Type: application/json
  • Required fields: merchant_no, api_key, timestamp, sign, out_order_no, amount, currency, pay_method, notify_url
  • Optional fields: nonce, country, return_url, subject, remark, client_ip, extra
Field Type Required Description
merchant_no string Yes Merchant no. (obtained from the merchant dashboard)
api_key string Yes API Key (obtained from the merchant dashboard)
timestamp int Yes Unix timestamp (seconds), used for expiry check (recommended ±300s window)
nonce string|null No Random string for replay protection; when unused, omit it or pass null (do not pass an empty string)
out_order_no string Yes Merchant order no. (idempotency key; must be unique per merchant)
amount int Yes Order amount (= actual value × 10000 as an integer, e.g. pass 12345 for 1.2345). Must be a positive JSON integer; strings (e.g. "12345") or decimals are not accepted; invalid input returns code=100001 (HTTP 200, with the specific field validation error in message).
currency string Yes Currency (e.g. PHP/USDT; used for validation and routing)
pay_method string Yes Payment method (choose from the payment-method dictionary, e.g. gcash/maya; for crypto use the chain name such as trc20/erc20)
country string|null No Country ISO code (e.g. PH; required for fiat, optional for crypto)
notify_url string Yes Callback URL (the service sends the result to this URL when the order reaches a final state)
return_url string|null No Frontend return URL (redirect back to the merchant page after payment; whether it redirects depends on the payment method/scenario)
subject string|null No Order subject (can be used for display or reconciliation notes)
remark string|null No Remark (can be used for merchant custom notes / reconciliation)
client_ip string|null No End-user IP (can be used for risk control; if omitted, the service cannot obtain this information)
extra object|null No Extension field (top-level field, included in the signature; the object is serialized with stable JSON). ⚠️ It is strongly recommended to include the end-user info extra.customer (see the "Extension info and channel requirements" section below)
extra.customer object No End-user info (optional). If omitted, the request is not rejected solely because this field is missing; however, some payment methods require name/phone and the order may fail without them, so including them is recommended. The service validates the format and maps the fields (a full name is split into first/last): provide the full name (or first_name+last_name), email, and phone
sign string Yes Signature (computed per the documented rules; the pay endpoint uses api_secret_pay)

Request Example

{
  "merchant_no": "<YOUR_MERCHANT_NO>",
  "api_key": "<YOUR_API_KEY>",
  "timestamp": 1736073600,
  "nonce": "random-xyz",
  "sign": "<SIGN>",
  "out_order_no": "202501010001",
  "amount": 10000,
  "currency": "PHP",
  "pay_method": "gcash",
  "country": "PH",
  "notify_url": "https://merchant.example.com/api/notify/pay",
  "return_url": "https://merchant.example.com/pay/result",
  "subject": "Order subject (optional)",
  "remark": "Remark (optional)",
  "client_ip": "1.2.3.4",
  "extra": {
    "user_id": "u123456",
    "customer": {
      "first_name": "San",
      "last_name": "Zhang",
      "email": "user@example.com",
      "phone": "13800000000"
    }
  }
}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
order_no string Yes API order no. (used for subsequent query and reconciliation)
out_order_no string Yes Merchant order no. (echoed as-is, to help the merchant link its business order)
amount int Yes Order amount (= actual value × 10000 as an integer, e.g. 12345 for 1.2345)
currency string Yes Currency (echoes the requested currency as-is, e.g. PHP/USDT)
pay_url string|null No Payment URL (on a successful order with code=0, at least one of pay_url/qrcode_content/pay_params is non-empty; this field may be empty, in which case use the other fields to launch payment)
qrcode_content string|null No QR code content (on a successful order with code=0, at least one of pay_url/qrcode_content/pay_params is non-empty; this field may be empty)
pay_params string|null No Native payment string (optional): some payment methods return native app launch parameters; when pay_url is empty and this field is non-empty, use it to launch payment on the client
expire_at string|null No Expiry time (ISO8601; nullable). Unpaid-and-timed-out is no longer represented separately as an expired state; externally it is normalized to pending (processing) — rely on the order query result.
status string Yes Order status: pending (processing) / success / failed. Only success/failed are final states; unpaid-and-timed-out is no longer returned separately as expired and is normalized to pending.

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "order_no": "P20250101000001",
    "out_order_no": "202501010001",
    "amount": 10000,
    "currency": "PHP",
    "pay_url": "https://pay.example.com/h5/P20250101000001",
    "qrcode_content": "https://pay.example.com/h5/P20250101000001",
    "pay_params": null,
    "expire_at": "2025-01-01T12:30:00Z",
    "status": "pending"
  }
}

Additional Notes

  • Amount = actual value × 10000 as an integer, e.g. pass 12345 for 1.2345, transmitted as an integer to avoid floating-point error.
  • Idempotency key: out_order_no is globally unique per merchant; a repeated order with identical parameters is treated as an idempotent success, while mismatched parameters return an idempotency conflict.
  • If the order is not accepted, the endpoint returns code=100000 in real time and sets the order to the failed final state; you cannot retry with the same out_order_no (idempotency will only return that failed order), so use a new out_order_no to place a new order.
  • [End-user info (strongly recommended)] Some payment methods require the end-user name and phone (email may be optional); missing them may cause the order to fail. Use these generic fields: name — full name in extra.customer.name (or explicit extra.customer.first_name+last_name); email — extra.customer.email (or extra.email); phone — extra.customer.phone (or extra.phone). The service validates and maps these fields, so the merchant does not need separate integrations for different payment methods.
  • [Native payment string] When pay_url is empty and pay_params is non-empty (some payment methods only return native launch parameters), use pay_params to launch payment on the client.

Common Errors

  • 100101 Request expired
  • 100102 IP not in allowlist
  • 100104 Invalid signature
  • 100105 IP in blocklist
  • 100000 Failed to create order (the order was not accepted and was set to failed in real time)
  • 300404 No payment method is available for the request
  • 100001 Parameter validation failed (e.g. notify_url points to an internal network / illegal protocol)
  • 300402 Payment method configuration unavailable
  • 300403 Fee rate not configured

Collection: Query Order (pay/query)

  • Method: POST
  • Endpoint: <BASE_URL>/merchant/pay/query
  • Purpose: Query a collection order status by API order no. or merchant order no.

Request Details

  • Content-Type: application/json
  • Required fields: merchant_no, api_key, timestamp, sign, order_no/out_order_no (either one)
  • Optional fields: nonce
Field Type Required Description
merchant_no string Yes Merchant no. (obtained from the merchant dashboard)
api_key string Yes API Key (obtained from the merchant dashboard)
timestamp int Yes Unix timestamp (seconds), used for expiry check (recommended ±300s window)
nonce string|null No Random string for replay protection; when unused, omit it or pass null (do not pass an empty string)
order_no string|null No API order no. (provide either this or out_order_no; at least one is required)
out_order_no string|null No Merchant order no. (provide either this or order_no; at least one is required)
sign string Yes Signature (computed per the documented rules; the pay endpoint uses api_secret_pay)

Request Example

{
  "merchant_no": "<YOUR_MERCHANT_NO>",
  "api_key": "<YOUR_API_KEY>",
  "timestamp": 1736073600,
  "nonce": "random-xyz",
  "sign": "<SIGN>",
  "out_order_no": "202501010001",
  "order_no": null
}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
order_no string Yes API order no.
out_order_no string Yes Merchant order no.
amount int Yes Reference order amount (= actual value × 10000 as an integer); reference only — credit by actual_amount
actual_amount int|null No End-user's actual paid amount (the primary basis for collection crediting and reconciliation; use it first; null if unknown)
fee_amount int|null No Fee (recomputed from the actual paid amount; null if unknown)
net_amount int|null No Net credited amount = actual paid − fee (null if unknown)
currency string Yes Currency
status string Yes Order status: pending (processing) / success / failed. Only success/failed are final states; unpaid-and-timed-out is no longer returned separately as expired and is normalized to pending.
channel_order_no null No Always returns null. Use order_no or out_order_no for order queries and business correlation.
paid_at string|null No Payment success time (ISO8601; may be empty when not yet successful)
notify_status string Yes Callback delivery status: pending/success/failed (not the same as the payment result)

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "order_no": "P20250101000001",
    "out_order_no": "202501010001",
    "amount": 10000,
    "actual_amount": 9500,
    "fee_amount": 500,
    "net_amount": 9000,
    "currency": "USDT",
    "status": "success",
    "channel_order_no": null,
    "paid_at": "2025-01-01T12:15:00Z",
    "notify_status": "success"
  }
}

Additional Notes

  • Provide at least one of order_no/out_order_no.
  • The final states for status are success/failed; pending means processing (including unpaid-and-timed-out, which is normalized to pending).
  • Collection callbacks are sent to the merchant only when the order succeeds (success); non-success states such as failure/timeout are not called back, so the merchant should learn the final result via this order query endpoint.

Common Errors

  • 100104 Invalid signature
  • 300301 Order does not exist

Payout: Create Order (payout/create)

  • Method: POST
  • Endpoint: <BASE_URL>/merchant/payout/create
  • Purpose: Create a payout order and freeze the balance (a successful creation does not mean the payout will ultimately succeed).

Request Details

  • Content-Type: application/json
  • Required fields: merchant_no, api_key, timestamp, sign, out_payout_no, amount, currency, pay_method, notify_url, account_no
  • Optional fields: nonce, country, account_name, bank_name, bank_code, remark, client_ip, extra
Field Type Required Description
merchant_no string Yes Merchant no. (obtained from the merchant dashboard)
api_key string Yes API Key (obtained from the merchant dashboard)
timestamp int Yes Unix timestamp (seconds), used for expiry check (recommended ±300s window)
nonce string|null No Random string for replay protection; when unused, omit it or pass null (do not pass an empty string)
out_payout_no string Yes Merchant payout no. (idempotency key; must be unique per merchant)
amount int Yes Payout amount (= actual value × 10000 as an integer, e.g. pass 12345 for 1.2345). Must be a positive JSON integer; strings (e.g. "12345") or decimals are not accepted; invalid input returns code=100001 (HTTP 200, with the specific field validation error in message).
currency string Yes Currency (e.g. PHP/USDT; used for validation and routing)
pay_method string Yes Payment method (choose from the payment-method dictionary, e.g. gcash/maya; for crypto use the chain name such as trc20/erc20)
country string|null No Country ISO code (e.g. PH; required for fiat, optional for crypto)
notify_url string Yes Callback URL (the service sends the result to this URL when the payout reaches a final state)
account_name string|null No Payee name / account name (interpreted per channel type; nullable for on-chain address types)
account_no string Yes Payee account / address (interpreted per channel type; e.g. bank card number / on-chain address)
bank_name string|null No Bank / chain name (interpreted per channel type; nullable)
bank_code string|null No Bank code (corresponds to the code returned by the "Query Available Banks" endpoint; may be omitted when no bank selection is needed; if bank_name is also provided, the result resolved from bank_code takes precedence)
remark string|null No Remark (can be used for merchant custom notes / reconciliation)
client_ip string|null No End-user IP (can be used for risk control)
extra object|null No Extension field (included in the signature; the object is serialized with stable JSON). ⚠️ It is strongly recommended to include the end-user info extra.customer (see the "Extension info and channel requirements" section below)
extra.customer object No End-user info (optional). If omitted, the request is not rejected solely because this field is missing; however, some payment methods require name/phone and the order may fail without them, so including them is recommended. The service validates the format and maps the fields (a full name is split into first/last): provide the full name (or first_name+last_name), email, and phone
sign string Yes Signature (computed per the documented rules; the payout endpoint uses api_secret_payout)

Request Example

{
  "merchant_no": "<YOUR_MERCHANT_NO>",
  "api_key": "<YOUR_API_KEY>",
  "timestamp": 1736073600,
  "nonce": "random-xyz",
  "sign": "<SIGN>",
  "out_payout_no": "WD202501010001",
  "amount": 5000,
  "currency": "PHP",
  "pay_method": "gcash",
  "country": "PH",
  "notify_url": "https://merchant.example.com/api/notify/payout",
  "account_name": "San Zhang (optional)",
  "account_no": "09171234567",
  "bank_name": "Optional: fill in the bank name for bank-card payouts (can be omitted for wallet types such as gcash)",
  "client_ip": "1.2.3.4",
  "remark": "Merchant withdrawal (optional)",
  "extra": {
    "user_id": "u123456",
    "customer": {
      "first_name": "San",
      "last_name": "Zhang",
      "email": "user@example.com",
      "phone": "13800000000"
    }
  }
}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
payout_no string Yes API payout no. (used for subsequent query and reconciliation)
out_payout_no string Yes Merchant payout no. (echoed as-is, to help the merchant link its business)
amount int Yes Payout amount (= actual value × 10000 as an integer, e.g. 12345 for 1.2345)
currency string Yes Currency (echoes the requested currency as-is, e.g. PHP/USDT)
status string Yes Order status: pending on creation (processing; payouts always enter the processing flow first); success/failed are final states.
review_status string|null No Payout approval status (when merchant payout approval is enabled): pending (under review) / approved / rejected; null when no approval is required.
fee_amount int|null No Fee (nullable)
freeze_amount int|null No Frozen amount (nullable; recommended convention amount + fee_amount)

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "payout_no": "W20250101000001",
    "out_payout_no": "WD202501010001",
    "amount": 5000,
    "currency": "PHP",
    "status": "pending",
    "review_status": "pending",
    "fee_amount": 20,
    "freeze_amount": 5020
  }
}

Additional Notes

  • A successful creation means "accepted and balance frozen". A payout usually goes through review/dispatch and other steps; the final result is determined by the async notification and the query.
  • Idempotency key: out_payout_no is globally unique per merchant; a repeated order with identical parameters is treated as an idempotent success.
  • [End-user info (strongly recommended)] Some payment methods require the end-user name and phone (email may be optional); missing them may cause the order to fail. Use these generic fields: account_name for the payee or account name, or the full name in extra.customer.name (or explicit extra.customer.first_name+last_name); email — extra.customer.email (or extra.email); phone — extra.customer.phone (or extra.phone). The service validates and maps these fields, so the merchant does not need separate integrations for different payment methods.
  • [Bank-type payout] When pay_method is a bank type, bank_code is required; its value uses the code returned by the "Query Available Banks (payout/banks/query)" endpoint; an invalid code returns 300407.

Common Errors

  • 100104 Invalid signature
  • 300201 Payout order idempotency conflict
  • 300501 Insufficient balance
  • 300404 No payment method is available for the request
  • 300403 Fee rate not configured
  • 300401 Payment method unavailable / does not exist
  • 100001 Parameter validation failed (e.g. notify_url points to an internal network / illegal protocol)
  • 300402 Payment method configuration unavailable
  • 300406 Missing required order parameter (e.g. bank_code)
  • 300407 Invalid bank code

Payout: Query Order (payout/query)

  • Method: POST
  • Endpoint: <BASE_URL>/merchant/payout/query
  • Purpose: Query a payout order status by API payout no. or merchant payout no.

Request Details

  • Content-Type: application/json
  • Required fields: merchant_no, api_key, timestamp, sign, payout_no/out_payout_no (either one)
  • Optional fields: nonce
Field Type Required Description
merchant_no string Yes Merchant no. (obtained from the merchant dashboard)
api_key string Yes API Key (obtained from the merchant dashboard)
timestamp int Yes Unix timestamp (seconds), used for expiry check (recommended ±300s window)
nonce string|null No Random string for replay protection; when unused, omit it or pass null (do not pass an empty string)
payout_no string|null No API payout no. (provide either this or out_payout_no; at least one is required)
out_payout_no string|null No Merchant payout no. (provide either this or payout_no; at least one is required)
sign string Yes Signature (computed per the documented rules; the payout endpoint uses api_secret_payout)

Request Example

{
  "merchant_no": "<YOUR_MERCHANT_NO>",
  "api_key": "<YOUR_API_KEY>",
  "timestamp": 1736073600,
  "nonce": "random-xyz",
  "sign": "<SIGN>",
  "payout_no": null,
  "out_payout_no": "WD202501010001"
}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
payout_no string Yes API payout no.
out_payout_no string Yes Merchant payout no.
amount int Yes Payout amount (= actual value × 10000 as an integer, e.g. 12345 for 1.2345)
currency string Yes Currency
status string Yes Order status: pending (processing) / success / failed; only success/failed are final states.
sub_state string|null No Processing sub-state (normalized): accepted / reviewing / processing (payout in progress) / verifying (payout result being verified) (non-final, keep waiting for the callback or polling); null in a final state
channel_order_no null No Always returns null. Use payout_no or out_payout_no for order queries and business correlation.
finished_at string|null No Completion time (ISO8601; may be empty when not yet in a final state)
failed_reason string|null No Failure reason summary (may have a value only on failure)
notify_status string Yes Callback delivery status: pending/success/failed (not the same as the payout result)

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "payout_no": "W20250101000001",
    "out_payout_no": "WD202501010001",
    "amount": 5000,
    "currency": "USDT",
    "status": "success",
    "sub_state": null,
    "channel_order_no": null,
    "finished_at": "2025-01-01T13:00:00Z",
    "failed_reason": null,
    "notify_status": "success"
  }
}

Additional Notes

  • Provide at least one of payout_no/out_payout_no.
  • The final states for status are success/failed; pending means processing (keep waiting for the callback or polling the order query).
  • sub_state is a normalized breakdown of the processing state (verifying means the payout result is being verified, still non-final); sub_state is only for display/troubleshooting — base business decisions on the final status (success/failed).

Common Errors

  • 100104 Invalid signature
  • 300301 Order does not exist

Payout: Query Available Banks (payout/banks/query)

  • Method: POST
  • Endpoint: <BASE_URL>/merchant/payout/banks/query
  • Purpose: Query the currently available bank list by country + payment method (the valid values of bank_code for bank-type payout orders).

Request Details

  • Content-Type: application/json
  • Required fields: merchant_no, api_key, timestamp, sign, pay_method
  • Optional fields: nonce, country, currency
Field Type Required Description
merchant_no string Yes Merchant no. (obtained from the merchant dashboard)
api_key string Yes API Key (obtained from the merchant dashboard)
timestamp int Yes Unix timestamp (seconds), used for expiry check (recommended ±300s window)
nonce string|null No Random string for replay protection; when unused, omit it or pass null (do not pass an empty string)
pay_method string Yes Payment method (bank type such as bank; wallet types such as gcash usually return an empty list)
country string|null No Country ISO code (e.g. PH)
currency string|null No Currency (e.g. PHP)
sign string Yes Signature (computed per the documented rules; the payout endpoint uses api_secret_payout)

Request Example

{
  "merchant_no": "<YOUR_MERCHANT_NO>",
  "api_key": "<YOUR_API_KEY>",
  "timestamp": 1736073600,
  "nonce": "random-xyz",
  "sign": "<SIGN>",
  "pay_method": "bank",
  "country": "PH",
  "currency": "PHP"
}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
banks array Yes Available bank list [{code,name}]; code is the valid value of bank_code for payout orders.

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "banks": [
      {
        "code": "BDO",
        "name": "BDO Unibank"
      },
      {
        "code": "BPI",
        "name": "Bank of the Philippine Islands"
      },
      {
        "code": "GCASH",
        "name": "GCash"
      },
      {
        "code": "MAYA",
        "name": "Maya"
      }
    ]
  }
}

Additional Notes

  • The available bank list may change over time; query it shortly before placing an order or use a short-lived cache.
  • Pure wallet payment methods (pay_method=gcash/maya themselves) do not need a destination selection and return empty banks; for bank-type (pay_method=bank), banks may include e-wallets (such as GCASH/MAYA) as payout destinations, and bank_code may take their code.

Common Errors

  • 100104 Invalid signature

Payout: Query Payment Proof (payout/proof/query)

  • Method: POST
  • Endpoint: <BASE_URL>/merchant/payout/proof/query
  • Purpose: Query the payment-proof URL of a successfully paid-out order (not all payment methods/currencies provide proof).

Request Details

  • Content-Type: application/json
  • Required fields: merchant_no, api_key, timestamp, sign, payout_no/out_payout_no (either one)
  • Optional fields: nonce
Field Type Required Description
merchant_no string Yes Merchant no. (obtained from the merchant dashboard)
api_key string Yes API Key (obtained from the merchant dashboard)
timestamp int Yes Unix timestamp (seconds), used for expiry check (recommended ±300s window)
nonce string|null No Random string for replay protection; when unused, omit it or pass null (do not pass an empty string)
payout_no string|null No API payout no. (provide either this or out_payout_no; at least one is required)
out_payout_no string|null No Merchant payout no. (provide either this or payout_no; at least one is required)
sign string Yes Signature (computed per the documented rules; the payout endpoint uses api_secret_payout)

Request Example

{
  "merchant_no": "<YOUR_MERCHANT_NO>",
  "api_key": "<YOUR_API_KEY>",
  "timestamp": 1736073600,
  "nonce": "random-xyz",
  "sign": "<SIGN>",
  "payout_no": null,
  "out_payout_no": "WD202501010001"
}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
payout_no string Yes API payout no.
out_payout_no string Yes Merchant payout no.
proof_url string Yes Payment-proof URL (has an access expiry, see expires_in; use it immediately and do not persist the URL)
expires_in int|null No Proof URL validity (seconds, decided by the channel, e.g. 1800=30 minutes; null means the channel did not declare it)
queried_at string|null No Query time returned by the service (nullable; the format may not be ISO8601)

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "payout_no": "W20250101000001",
    "out_payout_no": "WD202501010001",
    "proof_url": "https://proof.example.com/xxx.pdf",
    "expires_in": 1800,
    "queried_at": "2025-01-01 13:00:00"
  }
}

Additional Notes

  • Only successfully paid-out orders (status=success) can query proof; processing/failed orders return 300409.
  • Not all channels/currencies provide proof; some channels only support querying orders from the last few days (returns 300409 beyond the window).
  • The proof URL has an access expiry (expires_in seconds); download and re-store the file rather than saving the URL.

Common Errors

  • 100104 Invalid signature
  • 300301 Order does not exist
  • 300408 Channel does not support proof query
  • 300409 Proof temporarily unavailable

Payout: Query Payment Receipt Image (payout/receipt/query)

  • Method: POST
  • Endpoint: <BASE_URL>/merchant/payout/receipt/query
  • Purpose: Query/generate a payment receipt image (PNG) for a payout order. Only payout orders with status=success can be generated; render-once persists and reuses it.

Request Details

  • Content-Type: application/json
  • Required fields: merchant_no, api_key, timestamp, sign, payout_no/out_payout_no (either one)
  • Optional fields: nonce, lang, inline
Field Type Required Description
merchant_no string Yes Merchant no. (obtained from the merchant dashboard)
api_key string Yes API Key (obtained from the merchant dashboard)
timestamp int Yes Unix timestamp (seconds), used for expiry check (recommended ±300s window)
nonce string|null No Random string for replay protection; when unused, omit it or pass null (do not pass an empty string)
payout_no string|null No API payout no. (provide either this or out_payout_no; at least one is required)
out_payout_no string|null No Merchant payout no. (provide either this or payout_no; at least one is required)
lang string|null No Receipt language (optional, enum: en / zh-CN / zh-TW; when omitted it is auto-derived from the order country)
inline int|null No When set to 1, returns the base64 image data directly (image_base64 + mime); when omitted or set to 0, returns a time-limited signed download link (receipt_url)
sign string Yes Signature (computed per the documented rules; the payout endpoint uses api_secret_payout)

Request Example

{
  "merchant_no": "<YOUR_MERCHANT_NO>",
  "api_key": "<YOUR_API_KEY>",
  "timestamp": 1736073600,
  "nonce": "random-xyz",
  "sign": "<SIGN>",
  "payout_no": null,
  "out_payout_no": "WD202501010001",
  "lang": "zh-CN",
  "inline": 0
}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
payout_no string Yes API payout no.
out_payout_no string Yes Merchant payout no.
lang string Yes Language actually used for the receipt (en / zh-CN / zh-TW)
receipt_url string|null No Receipt image download URL (relative path, e.g. /api/open/v1/payout/receipt/file?token=…; prepend your openapi_base to form the full download URL; a GET downloads it; returned only when inline=0 or omitted; the link has an expiry, see expires_in; use it immediately and do not persist the URL)
expires_in int|null No Download link validity (seconds, e.g. 3600=1 hour; null means no declared expiry); meaningful only when inline=0 or omitted
mime string|null No Image MIME type (e.g. image/png); returned only when inline=1
image_base64 string|null No Base64-encoded receipt image data (without the data URI prefix); returned only when inline=1

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "payout_no": "W20250101000001",
    "out_payout_no": "WD202501010001",
    "lang": "zh-CN",
    "receipt_url": "/api/open/v1/payout/receipt/file?token=eyJhbGciOiJIUzI1NiJ9...",
    "expires_in": 3600
  }
}

Additional Notes

  • Only successfully paid-out orders (status=success) can generate a receipt; a non-success status returns 300410.
  • render-once: after the first generation the image is persisted and reused; repeated queries with the same parameters return the cached result directly.
  • When lang is omitted, the service selects a language from the order country (e.g. PH → en); an explicit value overrides this.
  • When inline=1, the response body directly contains the Base64 image (image_base64 + mime), suitable for immediate server-side processing; when inline=0 (default), it returns a time-limited signed receipt_url download link, suitable for frontend redirect/embed.
  • The download link has an expiry (expires_in seconds); do not persist the URL; for long-term retention, download the image and re-store it in your own storage.

Common Errors

  • 100104 Invalid signature
  • 300301 Order does not exist
  • 300410 Payout receipt temporarily unavailable (order is not success)
  • 300411 Receipt generation failed

Common: Query Available Payment Methods (pay-methods/query)

  • Method: POST
  • Endpoint: <BASE_URL>/merchant/pay-methods/query
  • Purpose: Query the available payment-method dictionary (the valid values of pay_method/country for placing orders).

Request Details

  • Content-Type: application/json
  • Required fields: merchant_no, api_key, timestamp, sign
  • Optional fields: nonce, country
Field Type Required Description
merchant_no string Yes Merchant no. (obtained from the merchant dashboard)
api_key string Yes API Key (obtained from the merchant dashboard)
timestamp int Yes Unix timestamp (seconds), used for expiry check (recommended ±300s window)
nonce string|null No Random string for replay protection; when unused, omit it or pass null (do not pass an empty string)
country string|null No Country ISO code (e.g. PH; when omitted, all are returned)
sign string Yes Signature (computed per the documented rules; this endpoint uses api_secret_pay)

Request Example

{
  "merchant_no": "<YOUR_MERCHANT_NO>",
  "api_key": "<YOUR_API_KEY>",
  "timestamp": 1736073600,
  "nonce": "random-xyz",
  "sign": "<SIGN>",
  "country": "PH"
}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
methods array Yes Payment method list [{pay_method,name,country,currency}]; pay_method is the valid value for the order endpoints

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "methods": [
      {
        "pay_method": "gcash",
        "name": "GCash",
        "country": "PH",
        "currency": "PHP"
      },
      {
        "pay_method": "maya",
        "name": "Maya",
        "country": "PH",
        "currency": "PHP"
      }
    ]
  }
}

Additional Notes

  • Returns the currently available payment-method dictionary; whether a payment method can currently be used is determined by the actual response of the order-creation endpoint (300404 is returned when none is available).

Common Errors

  • 100104 Invalid signature

Common: Query Account Balance (balance/query)

  • Method: POST
  • Endpoint: <BASE_URL>/merchant/balance/query
  • Purpose: Query the available/frozen balance of each currency in the merchant transaction account.

Request Details

  • Content-Type: application/json
  • Required fields: merchant_no, api_key, timestamp, sign
  • Optional fields: nonce, currency
Field Type Required Description
merchant_no string Yes Merchant no. (obtained from the merchant dashboard)
api_key string Yes API Key (obtained from the merchant dashboard)
timestamp int Yes Unix timestamp (seconds), used for expiry check (recommended ±300s window)
nonce string|null No Random string for replay protection; when unused, omit it or pass null (do not pass an empty string)
currency string|null No Currency (e.g. PHP; when omitted, all currencies are returned)
sign string Yes Signature (computed per the documented rules; this endpoint uses api_secret_pay)

Request Example

{
  "merchant_no": "<YOUR_MERCHANT_NO>",
  "api_key": "<YOUR_API_KEY>",
  "timestamp": 1736073600,
  "nonce": "random-xyz",
  "sign": "<SIGN>",
  "currency": "PHP"
}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
balances array Yes Balance list [{currency,available,frozen}]; amount = actual value × 10000 as an integer

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "balances": [
      {
        "currency": "PHP",
        "available": 12345600,
        "frozen": 50000
      }
    ]
  }
}

Additional Notes

  • available is the available balance, frozen is the frozen amount (including in-flight payout freezes); both are integers in the minor unit.

Common Errors

  • 100104 Invalid signature

Common: Service Version (GET /version)

  • Method: GET
  • Endpoint: <BASE_URL>/version
  • Purpose: Query the server-side OpenAPI version (no authentication required); all /api/open/ responses carry an X-Api-Version header.

Request Details

  • Content-Type: application/json
  • Required fields: None
  • Optional fields: None

None

Request Example

{}

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
major string Yes Major version (path prefix, e.g. v1)
version string Yes Semantic version (major.minor.patch)
base_path string Yes OpenAPI path prefix

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "major": "v1",
    "version": "1.2.0",
    "base_path": "/api/open/v1"
  }
}

Additional Notes

  • No authentication required; used for liveness probing and version canary troubleshooting.

Payout: Download Payment Receipt File (GET /payout/receipt/file)

  • Method: GET
  • Endpoint: <BASE_URL>/payout/receipt/file
  • Purpose: Download the payout payment receipt image (binary PNG). This endpoint is not called directly; it is derived from the receipt_url returned by "Query Payment Receipt Image (payout/receipt/query)" — the merchant prepends its dedicated domain and simply performs a GET to download.

Request Details

  • Content-Type: application/json
  • Required fields: token
  • Optional fields: None
Field Type Required Description
token string Yes Download token (query parameter). Issued by payout/receipt/query (the receipt_url returned when inline=0 / omitted); it embeds the payout no. / merchant / language / expiry and is time-limited; do not construct it yourself or persist it long-term.

Request Example

{}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.

None

Response Example

{}

Additional Notes

  • This endpoint returns a binary image (Content-Type: image/png, content-disposition: inline), not a {code,message,data} JSON envelope; a successful download yields the PNG byte stream directly.
  • Usage: first call "Query Payment Receipt Image (payout/receipt/query)" (inline=0 or omitted) to obtain receipt_url (a relative path), prepend your openapi_base to form the full URL, then simply GET that URL to download.
  • The official SDK does not wrap this endpoint: receipt_url is a time-limited signed download link — use any HTTP client to GET it yourself (no further signing/verification needed); an expired link (invalid token) returns 100001, so just call payout/receipt/query again for a fresh link.
  • The authentication differs from other endpoints: this endpoint does not use merchant_no/api_key/sign — it is authenticated solely by the token in the query (HMAC-issued), so signature fields are neither needed nor should be attached.

Common Errors

  • 100001 Invalid or expired token (call payout/receipt/query again for a fresh link)
  • 300410 Payout receipt temporarily unavailable (order is not success)

Test Sandbox: Manually Complete a Collection Test Order (pay/test/complete)

  • Method: POST
  • Endpoint: <BASE_URL>/merchant/pay/test/complete
  • Purpose: [Test credentials only] Manually push a collection TEST order to a final state (success/failed) for integration testing without a live payment flow; production credentials are rejected.

Request Details

  • Content-Type: application/json
  • Required fields: merchant_no, api_key, timestamp, sign, out_order_no/order_no (either one), result
  • Optional fields: nonce, actual_amount
Field Type Required Description
merchant_no string Yes Merchant no. (obtained from the merchant dashboard)
api_key string Yes API Key (must be the test environment key; calling with a production key is rejected)
timestamp int Yes Unix timestamp (seconds), used for expiry check (recommended ±300s window)
nonce string|null No Random string for replay protection; when unused, omit it or pass null (do not pass an empty string)
out_order_no string|null No Merchant order no. (provide either this or order_no; at least one is required)
order_no string|null No API order no. (provide either this or out_order_no; at least one is required)
result string Yes Desired final state: success or failed
actual_amount int|null No End-user actual paid amount (= actual value × 10000 as an integer, optional; effective only when result=success — defaults to the order amount if omitted; ignored when result=failed)
sign string Yes Signature (computed per the documented rules; this endpoint uses the api_secret_pay test credential)

Request Example

{
  "merchant_no": "<YOUR_MERCHANT_NO>",
  "api_key": "<YOUR_API_KEY>",
  "timestamp": 1736073600,
  "nonce": "random-xyz",
  "sign": "<SIGN>",
  "out_order_no": "202501010001",
  "order_no": null,
  "result": "success",
  "actual_amount": 10000
}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
ok bool Yes Whether the operation succeeded (always true; failures are expressed via code)
order_no string Yes API order no.
status string Yes The order final state after pushing: success/failed

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "ok": true,
    "order_no": "P20250101000001",
    "status": "success"
  }
}

Additional Notes

  • [Test credentials only] Only test-environment credentials (env=test) can call this; production credentials return 100001 (only test credentials can call the test-complete endpoint).
  • Applicable only to test orders (is_test=1): orders created with test credentials are marked is_test=1, use the mock flow, do not move real funds, and are excluded from statistics. Calling it on a non-test order, a non-existent order, or an already-final order returns 100001 (order not found / non-test order cannot be completed manually / order already in a final state).
  • After setting it to success, the merchant callback is enqueued and delivered as usual (the sandbox "fires callbacks too"), which is useful for testing callback verification and crediting logic; the pushed result is also queryable via pay/query.

Common Errors

  • 100104 Invalid signature
  • 100001 Test credentials only / order not found / not a test order / order already final (message carries the specific reason)

Test Sandbox: Manually Complete a Payout Test Order (payout/test/complete)

  • Method: POST
  • Endpoint: <BASE_URL>/merchant/payout/test/complete
  • Purpose: [Test credentials only] Manually push a payout TEST order to a final state (success/failed) for integration testing without a live payout flow; production credentials are rejected.

Request Details

  • Content-Type: application/json
  • Required fields: merchant_no, api_key, timestamp, sign, out_payout_no/payout_no (either one), result
  • Optional fields: nonce
Field Type Required Description
merchant_no string Yes Merchant no. (obtained from the merchant dashboard)
api_key string Yes API Key (must be the test environment key; calling with a production key is rejected)
timestamp int Yes Unix timestamp (seconds), used for expiry check (recommended ±300s window)
nonce string|null No Random string for replay protection; when unused, omit it or pass null (do not pass an empty string)
out_payout_no string|null No Merchant payout no. (provide either this or payout_no; at least one is required)
payout_no string|null No API payout no. (provide either this or out_payout_no; at least one is required)
result string Yes Desired final state: success or failed
sign string Yes Signature (computed per the documented rules; this endpoint uses the api_secret_payout test credential)

Request Example

{
  "merchant_no": "<YOUR_MERCHANT_NO>",
  "api_key": "<YOUR_API_KEY>",
  "timestamp": 1736073600,
  "nonce": "random-xyz",
  "sign": "<SIGN>",
  "out_payout_no": "WD202501010001",
  "payout_no": null,
  "result": "success"
}

Signature note: sign is computed over "all top-level fields of the request JSON except sign"; object/array fields are serialized with stable JSON.

Response Fields and Example

  • Unified response structure: { code, message, data }; business failures usually still return HTTP 200.
Field Type Required Description
ok bool Yes Whether the operation succeeded (always true; failures are expressed via code)
payout_no string Yes API payout no.
status string Yes The payout final state after pushing: success/failed

Response Example

{
  "code": 0,
  "message": "ok",
  "data": {
    "ok": true,
    "payout_no": "W20250101000001",
    "status": "success"
  }
}

Additional Notes

  • [Test credentials only] Only test-environment credentials (env=test) can call this; production credentials return 100001 (only test credentials can call the test-complete endpoint). Payout test completion has no actual_amount parameter (payout uses the order amount).
  • Applicable only to test orders (is_test=1): test orders never move real funds (the freeze at creation is not unfrozen here), do not enter the live payment flow, and are excluded from statistics. Calling it on a non-test order, a non-existent order, or an already-final order returns 100001 (order not found / non-test order cannot be completed manually / order already in a final state).
  • Both payout final states (success/failed) enqueue and deliver the merchant callback as usual (the sandbox "fires callbacks too"), useful for testing payout callback verification; the pushed result is also queryable via payout/query.

Common Errors

  • 100104 Invalid signature
  • 100001 Test credentials only / order not found / not a test order / order already final (message carries the specific reason)

Signature and Common Fields

Common fields (recommended to place in the JSON request body): merchant_no, api_key, timestamp, nonce (optional), sign. Recommended algorithm: HMAC-SHA256. Sort all signing fields (excluding sign) in ascending ASCII order by field name, join them as key=value with &, append &secret=... at the end, run HMAC-SHA256 over the raw string, and use the lowercase hexadecimal result as sign. The timestamp window is ±300 seconds (a request whose time differs from the server by more than this returns 100101). Replay protection: when nonce is provided it is de-duplicated by nonce, otherwise a "signature fingerprint" fallback is used; both de-duplication records are retained for 300 seconds, i.e. the same nonce / the same signed request cannot be repeated within 300 seconds (a repeat returns 100103). Generate a globally unique nonce per request (e.g. a UUID) and ensure it is not reused within at least 300 seconds.

Tip: the signing rules above are already implemented in the Official SDKs — call the SDK directly when integrating, no need to hand-write signing or stable serialization.

Amount Convention

All amounts follow the convention amount = actual value × 10000 as an integer (minor accounting unit 0.0001), e.g. pass 12345 for 1.2345, transmitted as integers to avoid floating-point errors. This applies to every request, response and callback amount field, regardless of currency (do not interpret it as any specific currency unit).

Test Credential Sandbox

Test-environment credentials (env=test) are available for integration testing: collection/payout orders created with a test credential are marked as test orders (is_test=1), short-circuit through a mock — no live payment flow, no real funds moved, and excluded from statistics/reconciliation — while callbacks are still fired (so you can test callback verification and crediting logic). Since test orders never enter the live payment flow, two manual-completion endpoints let you push a test order to a final state yourself: POST /merchant/pay/test/complete (collection, uses api_secret_pay, optional actual_amount) and POST /merchant/payout/test/complete (payout, uses api_secret_payout), with result of success/failed; see the corresponding endpoint sections above.

Note: the manual-completion endpoints are callable only with a test credential (env=test); a production credential is always rejected (returns 100001, message "only test credentials can call the test-complete endpoint"); and they apply only to test orders (is_test=1) — calling them on a production order, a non-existent order, or an already-final order also returns 100001. Production (env=prod) credentials have no sandbox behavior: production orders cannot be completed manually.

Callback Details

When an order reaches a final state, the service sends the collection/payout result via POST application/json to the notify_url provided when the order was created. The callback also carries signature fields; the merchant should verify the signature using the respective secret for the corresponding business (pay/payout).

Collection callback timing: Collection sends a callback to the merchant only when the order succeeds (status=success); non-success states such as failed/timeout trigger no callback, and the merchant should obtain the final result via the order query API (pay/query). (Payout is not subject to this restriction: Payout sends a callback at both final states success/failed.)

Success acknowledgement rule: After processing successfully, the merchant must return HTTP 200 with a response body of success or ok (case-insensitive); JSON formats are also accepted: {"success":true} / {"code":0} / {"message":"success"} / {"message":"ok"}. If the response does not follow this rule (even an HTTP 200 with a different body, such as an empty response or an HTML error page), the callback is treated as failed, retries it per policy, and triggers an alert when consecutive failures reach the threshold.

Collection Callback Fields

Field Type Required Description
merchant_no string Yes Merchant no.
order_no string Yes API order no.
out_order_no string Yes Merchant order no.
amount int Yes Reference order amount (= actual value × 10000 as an integer); reference only — credit by actual_amount
actual_amount int|null No End-user's actual paid amount (the primary basis for collection crediting and reconciliation; use it first; null if unknown)
fee_amount int|null No Fee (recomputed from the actual paid amount; null if unknown)
net_amount int|null No Net credited amount = actual paid − fee (null if unknown)
currency string Yes Currency
status string Yes Normalized status; collection only sends a callback on success, so this is always success
channel_order_no null No Always returns null; use order_no or out_order_no for order correlation. Because it is null, this field is excluded from the signature.
paid_at string|null No Payment success time (ISO8601; may be empty if not successful)
sign string Yes Signature (computed with api_secret_pay for collection callbacks)

Payout Callback Fields

Field Type Required Description
merchant_no string Yes Merchant no.
payout_no string Yes API payout no.
out_payout_no string Yes Merchant payout no.
amount int Yes Payout amount (= actual value × 10000 as an integer)
currency string Yes Currency
status string Yes Normalized status: success/failed (payout sends a callback at every final state)
fee_amount int|null No Fee (null if unknown)
channel_order_no null No Always returns null; use payout_no or out_payout_no for order correlation. Because it is null, this field is excluded from the signature.
finished_at string|null No Completion time (ISO8601)
failed_reason string|null No Failure reason (returned when failed)
sign string Yes Signature (computed with api_secret_payout for payout callbacks)

Note: the payout callback body does not contain notify_status. notify_status is returned only by the order query API and never appears in the callback body.

Refund Callback Fields

Field Type Required Description
merchant_no string Yes Merchant no.
order_no string|null No API order no. of the source collection order being refunded (may be null in edge cases)
out_order_no string|null No Merchant order no. of the source collection order being refunded (may be null in edge cases)
source_channel_order_no null No Always returns null; the refund callback does not provide another order number for the source collection order. Because it is null, this field is excluded from the signature.
refund_no string Yes API refund no.
out_refund_no string Yes Merchant refund no.
amount int Yes Refund amount (= actual value × 10000 as an integer)
currency string Yes Currency
status string Yes Normalized status: success/failed (refund sends a callback at every final state, consistent with the payout final-state set)
channel_order_no null No Always returns null; use refund_no or out_refund_no for refund correlation. Because it is null, this field is excluded from the signature.
finished_at string|null No Completion time (ISO8601)
failed_reason string|null No Failure reason (a desensitized generic message; returned when failed, otherwise null)
sign string Yes Signature (computed with api_secret_pay for refund callbacks, shared with collection)

Refund callback note: the callback body is verified with api_secret_pay (shared with collection, so no separate refund callback secret is needed); the verification algorithm is the same as collection/payout callbacks (see "Callback Signature Verification" below). channel_order_no / source_channel_order_no are always null and are excluded from the signature. refund_no / out_refund_no are the refund order numbers, while order_no / out_order_no link to the original collection order being refunded.

Callback Signature Verification

The callback signature algorithm is identical to the request signature (see the "Signature and Common Fields" section):

  1. Take all top-level fields of the callback JSON except sign and whose value is not null;
  2. Sort by field name in ascending ASCII (code-point) order, and join them as key=value with &;
  3. Append &secret=<corresponding secret>, compute HMAC-SHA256 over that raw string, and output lowercase hex;
  4. Compare against the callback body's sign (use a constant-time comparison such as crypto.timingSafeEqual to prevent timing attacks).

Secret selection: collection callbacks use api_secret_pay, payout callbacks use api_secret_payout. Fields whose value is null (such as channel_order_no) are excluded from the signature (consistent with the request signature, which skips null).

Security red lines (must follow): (1) Verify the signature first, then process the business and credit funds — never act on callback content before verifying; (2) Credit/reconcile using actual_amount (the actual amount paid by the user) and net_amount (net credited = actual paid − fee), never amount (only a reference order amount); (3) Callbacks may be delivered more than once due to retries — handle them idempotently by out_order_no / out_payout_no; a duplicate notification must not credit twice; (4) The callback source IP is not fixeddo not restrict the callback source by IP allowlist; signature verification is the only trustworthy check.

Retry / Timeout / Security

  • Timeout: the callback service waits 8000ms for the merchant response; no response counts as a failure for this attempt.
  • Retry: up to 6 attempts (including the first), with backoff intervals of approximately 1min / 2min / 5min / 10min / 30min / 60min (the last value is used beyond that); if it still fails after the cap, the callback task is set to failed, and the merchant can fall back to the order query API to obtain the final state.
  • No redirect following: the callback service does not follow 30x redirects for notify_url.
  • SSRF protection: a notify_url pointing to internal / loopback / link-local addresses is blocked, counted as a failure with no retry (the address will not change on its own); at order creation, such a notify_url also returns 100001.

Field Conventions and External Contract

  • Callback amounts follow the same convention as requests: actual value × 10000 as an integer.
  • status is the unified status returned by the API: collection is always success; payout is success/failed.

Collection vs Payout Differences

  • Trigger timing: collection triggers a callback only on success; payout triggers at both final states success/failed.
  • Field differences: collection includes actual_amount / net_amount / paid_at; payout includes finished_at / failed_reason.

IP Allowlist/Blocklist Details

  • The allowlist is disabled by default (all source IPs are allowed to access the OpenAPI).
  • Once the policy is enabled, a blocklist match is rejected outright; when an allowlist exists, access is allowed only if it matches the allowlist.
  • The exported document does not include the merchant real allowlist configuration; please check it in the merchant console.

Error Code Reference

All business errors return HTTP 200 with the envelope { code, message, data }; HTTP 4xx/5xx is never used to express a business failure (a transport-layer 4xx/5xx only indicates a network/gateway issue — investigate the link or retry). Each endpoint Common Errors list shows only high-frequency items; this table is the complete reference. Authentication errors (100xxx) are common to all authenticated endpoints.

Code Meaning HTTP Retryable Notes / Troubleshooting
100000 Generic business failure / unified auth-failure code 200 Depends When the order is not accepted, it is set to failed in real time — do not retry with the same out_order_no; during auth it is a unified authentication-failed message
100001 Parameter validation failed 200 No message carries the specific field validation error (in English, e.g. "amount" must be a number); amount must be a JSON integer — strings/decimals return this code. Fix params and resend
100101 Request expired 200 Yes timestamp is outside the ±300s window; calibrate local time and retry
100102 IP not in allowlist 200 No Add the source IP to the allowlist in the merchant console and retry
100103 Replay / duplicate request 200 No nonce or signature fingerprint repeated within 300s; use a new nonce (or change params) and retry
100104 Invalid signature 200 No Check the secret and signing algorithm (pay uses api_secret_pay, payout uses api_secret_payout)
100105 IP in blocklist 200 No Remove the source IP from the blocklist and retry
100106 Too many auth failures (rate limited) 200 Yes Triggered when the same merchant_no + source IP fails auth 60 times within 60s; wait ~60s and retry
200002 Service account disabled 200 No Contact your service provider
210002 Merchant disabled 200 No Contact your service provider
300101 Collection idempotency conflict 200 No out_order_no exists with mismatched params; align params or use a new order no.
300201 Payout idempotency conflict 200 No out_payout_no exists with mismatched params; align params or use a new payout no.
300301 Order not found 200 No Check the order no. (order_no / out_order_no / payout_no / out_payout_no)
300401 Payment method unavailable / missing 200 No The payment method is unavailable or does not exist
300402 Payment method configuration unavailable 200 Yes No valid configuration matched the request; change parameters or retry later
300403 Fee rate not configured 200 No Contact your service provider to configure the fee rate, then retry
300404 No payment method is available for the request 200 Yes No available combination for country + currency + pay_method; change parameters or retry later. Use pay-methods/query to list payment methods
300405 Missing required extra info (extra.customer) 200 No Fill the fields listed in data.missing_fields under extra.customer
300406 Missing required order parameter (e.g. bank_code) 200 No Fill the required field for that route and retry
300407 Invalid bank code 200 No Use a code returned by Query Available Banks (payout/banks/query)
300408 Payment method does not support payout proof query 200 No The payment method does not support proof queries
300409 Payout proof temporarily unavailable 200 Yes Order is not successful / proof is not ready / outside the query window; confirm success and retry within the window
300410 Payout receipt temporarily unavailable (order not success) 200 Yes Retry after the payout succeeds
300411 Receipt generation failed 200 Yes Rendering error; retry later
300501 Insufficient balance 200 Yes Available balance < frozen amount (amount + fee); top up and retry

Rate Limiting and Polling Guidance

  • Rate limiting only applies to auth failures: once the same merchant_no + source IP accumulates 60 auth failures within 60 seconds, further requests are rejected with 100106 and recover as the window rolls (~60s). Successfully authenticated business requests have no hard QPS cap.
  • Do not retry auth failures without backoff (signature error, clock skew, etc.), or you will easily hit 100106; fix the cause before retrying.
  • Query polling guidance: rely on the callback for the final state first; only poll the query API when no callback arrives, using exponential backoff (e.g. first delay 3–5s, then 5s → 15s → 30s → 60s up to the final state or a reasonable cap). Avoid high-frequency empty polling on non-final orders.

Integration Notes

  • Credit by actual_amount: collection credits and reconciles by the amount the user actually paid; use actual_amount (actual paid) and net_amount (net credited = actual paid − fee) from the callback / query, not amount, which is only a reference order amount and must not be used for crediting.
  • Amount limits: min/max amounts for each pay_method are validated in real time and are not published as fixed values; an out-of-range order returns the corresponding error code (e.g. 300402 / 300404). Do not hard-code amount ranges on the client.
  • Order validity: use the expire_at returned by the order-creation endpoint instead of hard-coding a fixed duration. An unpaid timeout is normalized to pending; rely on the order query result.
  • Callback source: the callback egress IP is not fixed; do not restrict the callback source by IP allowlist — use signature verification as the only trustworthy check (see Callback Signature Verification and the security red lines above).