中文
🤖 Want to integrate quickly with AI? Open AI integration prompt →
Download Markdown · Node.js demo · PHP demo · Java demo · Go demo · Python demo

Merchant OpenAPI Integration Guide

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

Versioning and Compatibility Policy

Changes in this version (1.2.0):

Merchant Information

Collection: Create Order (pay/create)

Request Details

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 (integer in minor unit, precision 0.0001; scale up by 10000, 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 HTTP 400 (code=400).
currency string Yes Currency (e.g. PHP/USDT; used for validation and routing)
pay_method string Yes Payment method (provided by the platform, 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 platform pushes 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 platform cannot infer the end-user origin)
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). The platform no longer rejects an order due to its absence (no longer returns 300405); however, upstream channels for mainstream payment methods such as gcash/maya effectively require name/phone, and missing them will cause the upstream to reject the order and the order to fail, so it is strongly recommended to include it. When provided, the platform validates the format and auto-maps it to each channel's format (a full name is auto-split into first/last): you may 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": "[email protected]",
      "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

Field Type Required Description
order_no string Yes Platform 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 (integer in minor unit, precision 0.0001; scaled up by 10000, 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

Common Errors

Collection: Query Order (pay/query)

Request Details

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 Platform 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

Field Type Required Description
order_no string Yes Platform order no.
out_order_no string Yes Merchant order no.
amount int Yes Order amount (integer in minor unit, precision 0.0001; scaled up by 10000, 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; unpaid-and-timed-out is no longer returned separately as expired and is normalized to pending.
channel_order_no string|null No Channel-side order no. (nullable)
paid_at string|null No Payment success time (ISO8601; may be empty when not yet successful)
notify_status string Yes Status of the platform pushing the callback to the merchant: 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,
    "currency": "USDT",
    "status": "success",
    "channel_order_no": "CH20250101XXX",
    "paid_at": "2025-01-01T12:15:00Z",
    "notify_status": "success"
  }
}

Additional Notes

Common Errors

Payout: Create Order (payout/create)

Request Details

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 (integer in minor unit, precision 0.0001; scale up by 10000, 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 HTTP 400 (code=400).
currency string Yes Currency (e.g. PHP/USDT; used for validation and routing)
pay_method string Yes Payment method (provided by the platform, 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 platform pushes 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; the platform translates it to the upstream code and backfills bank_name; 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). The platform no longer rejects an order due to its absence (no longer returns 300405); however, upstream channels for mainstream payment methods such as gcash/maya effectively require name/phone, and missing them will cause the upstream to reject the order and the order to fail, so it is strongly recommended to include it. When provided, the platform validates the format and auto-maps it to each channel's format (a full name is auto-split into first/last): you may 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": "[email protected]",
      "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

Field Type Required Description
payout_no string Yes Platform 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 (integer in minor unit, precision 0.0001; scaled up by 10000, 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

Common Errors

Payout: Query Order (payout/query)

Request Details

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 Platform 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

Field Type Required Description
payout_no string Yes Platform payout no.
out_payout_no string Yes Merchant payout no.
amount int Yes Payout amount (integer in minor unit, precision 0.0001; scaled up by 10000, 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 string|null No Channel-side order no. (nullable)
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 Status of the platform pushing the callback to the merchant: 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": "CHW20250101XXX",
    "finished_at": "2025-01-01T13:00:00Z",
    "failed_reason": null,
    "notify_status": "success"
  }
}

Additional Notes

Common Errors

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

Request Details

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

Field Type Required Description
banks array Yes Available bank list [{code,name}]; code is the valid value of bank_code for payout orders (a cross-channel unified code; the platform auto-translates to the upstream)

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

Common Errors

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

Request Details

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 Platform 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

Field Type Required Description
payout_no string Yes Platform 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 channel (nullable)

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

Common Errors

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

Request Details

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 Platform 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

Field Type Required Description
payout_no string Yes Platform 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=…; the merchant must prepend its own openapi_base, i.e. the upstream agent's dedicated domain, 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

Common Errors

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

Request Details

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

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

Common Errors

Common: Query Account Balance (balance/query)

Request Details

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

Field Type Required Description
balances array Yes Balance list [{currency,available,frozen}]; amounts are integers in the minor unit (10000 = 1 unit)

Response Example

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

Additional Notes

Common Errors

Common: Service Version (GET /version)

Request Details

None

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

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

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. A timestamp window of ±300 seconds is recommended; if nonce is used, the server performs replay protection within a short time window.

Multi-language Examples (pay/create)

Note: the examples already apply stable JSON serialization to nested fields such as extra before signing, and can be copied as-is (usable after replacing the placeholders and credentials).

JavaScript(Node.js)

// Demo of signing and order creation. sign = HMAC-SHA256(params sorted by key as k=v joined by "&", then append "&secret=<secret>") in hex. Nested objects (extra etc.) are serialized as compact JSON with keys sorted before signing. Replace placeholders with your real credentials.
const crypto = require('crypto');

const baseUrl = '<BASE_URL>';
const merchantNo = '<YOUR_MERCHANT_NO>';
const apiKey = '<YOUR_API_KEY>';
const apiSecret = '<API_SECRET>';

const payload = {
  merchant_no: merchantNo,
  api_key: apiKey,
  timestamp: Math.floor(Date.now() / 1000),
  nonce: 'random-xyz',
  out_order_no: '202501010001',
  amount: 10000,
  currency: 'PHP',
  pay_method: 'gcash',
  country: 'PH',
  notify_url: 'https://merchant.example.com/api/notify/pay',
  extra: {
    customer: {
      first_name: '<FIRST_NAME>',
      last_name: '<LAST_NAME>',
      email: '<EMAIL>',
      phone: '<PHONE>',
    },
  },
};

const stableStringify = (v) => {
  if (v === null) return 'null';
  if (typeof v !== 'object') return JSON.stringify(v);
  if (Array.isArray(v)) return '[' + v.map(stableStringify).join(',') + ']';
  return '{' + Object.keys(v).sort().map((k) => JSON.stringify(k) + ':' + stableStringify(v[k])).join(',') + '}';
};
const signPayload = (data, secret) => {
  const keys = Object.keys(data).filter((k) => k !== 'sign' && data[k] != null).sort();
  const raw = keys.map((k) => `${k}=${typeof data[k] === 'object' && data[k] !== null ? stableStringify(data[k]) : data[k]}`).join('&') + `&secret=${secret}`;
  return crypto.createHmac('sha256', secret).update(raw).digest('hex');
};

const sign = signPayload(payload, apiSecret);
const body = { ...payload, sign };

fetch(`${baseUrl}/merchant/pay/create`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(body),
})
  .then((r) => r.json())
  .then((d) => console.log(d));

PHP

<?php
// Demo of signing and order creation. sign = HMAC-SHA256(params sorted by key as k=v joined by "&", then append "&secret=<secret>") in hex. Nested objects (extra etc.) are serialized as compact JSON with keys sorted before signing. Replace placeholders with your real credentials.
$baseUrl = "<BASE_URL>";
$merchantNo = "<YOUR_MERCHANT_NO>";
$apiKey = "<YOUR_API_KEY>";
$apiSecret = "<API_SECRET>";

$payload = [
  'merchant_no' => $merchantNo,
  'api_key' => $apiKey,
  'timestamp' => time(),
  'nonce' => 'random-xyz',
  'out_order_no' => '202501010001',
  'amount' => 10000,
  'currency' => 'PHP',
  'pay_method' => 'gcash',
  'country' => 'PH',
  'notify_url' => 'https://merchant.example.com/api/notify/pay',
  'extra' => [
    'customer' => [
      'first_name' => '<FIRST_NAME>',
      'last_name' => '<LAST_NAME>',
      'email' => '<EMAIL>',
      'phone' => '<PHONE>',
    ],
  ],
];

function ksort_recursive(&$a) {
  if (is_array($a)) { ksort($a); foreach ($a as &$v) { ksort_recursive($v); } }
}
function stable_value($v) {
  if (is_array($v)) {
    ksort_recursive($v);
    return json_encode($v, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
  }
  return (string)$v;
}
ksort($payload);
$parts = [];
foreach ($payload as $k => $v) {
  if ($k === 'sign' || $v === null) { continue; }
  $parts[] = $k . "=" . stable_value($v);
}
$raw = implode("&", $parts) . "&secret=" . $apiSecret;
$sign = hash_hmac("sha256", $raw, $apiSecret);
$payload["sign"] = $sign;

$ch = curl_init($baseUrl . "/merchant/pay/create");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$resp = curl_exec($ch);
curl_close($ch);
echo $resp;

Java

// Demo of signing and order creation. sign = HMAC-SHA256(params sorted by key as k=v joined by "&", then append "&secret=<secret>") in hex. Nested objects (extra etc.) are serialized as compact JSON with keys sorted before signing. Replace placeholders with your real credentials.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TreeMap;
import java.util.HexFormat;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

String baseUrl = "<BASE_URL>";
String merchantNo = "<YOUR_MERCHANT_NO>";
String apiKey = "<YOUR_API_KEY>";
String apiSecret = "<API_SECRET>";

Map<String, Object> payload = new TreeMap<>();
payload.put("merchant_no", merchantNo);
payload.put("api_key", apiKey);
payload.put("timestamp", System.currentTimeMillis() / 1000);
payload.put("nonce", "random-xyz");
payload.put("out_order_no", "202501010001");
payload.put("amount", 10000);
payload.put("currency", "PHP");
payload.put("pay_method", "gcash");
payload.put("country", "PH");
payload.put("notify_url", "https://merchant.example.com/api/notify/pay");
Map<String, Object> extra = new TreeMap<>();
Map<String, Object> customer = new TreeMap<>();
customer.put("first_name", "<FIRST_NAME>");
customer.put("last_name", "<LAST_NAME>");
customer.put("email", "<EMAIL>");
customer.put("phone", "<PHONE>");
extra.put("customer", customer);
payload.put("extra", extra);

// signMapper: serializes nested objects with keys in ascending order, consistent with the server stable-signature convention
ObjectMapper signMapper = new ObjectMapper();
signMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
StringBuilder raw = new StringBuilder();
for (Map.Entry<String, Object> e : payload.entrySet()) {
  if ("sign".equals(e.getKey()) || e.getValue() == null) { continue; }
  Object val = e.getValue();
  String strVal = (val instanceof Map || val instanceof java.util.Collection)
      ? signMapper.writeValueAsString(val) : String.valueOf(val);
  raw.append(e.getKey()).append("=").append(strVal).append("&");
}
raw.append("secret=").append(apiSecret);

Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String sign = HexFormat.of().formatHex(mac.doFinal(raw.toString().getBytes(StandardCharsets.UTF_8)));
payload.put("sign", sign);

ObjectMapper mapper = new ObjectMapper();
String body = mapper.writeValueAsString(payload);

HttpRequest req = HttpRequest.newBuilder()
  .uri(URI.create(baseUrl + "/merchant/pay/create"))
  .header("Content-Type", "application/json")
  .POST(HttpRequest.BodyPublishers.ofString(body))
  .build();
HttpResponse<String> resp = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());

Go

// Demo of signing and order creation. sign = HMAC-SHA256(params sorted by key as k=v joined by "&", then append "&secret=<secret>") in hex. Nested objects (extra etc.) are serialized as compact JSON with keys sorted before signing. Replace placeholders with your real credentials.
package main

import (
  "bytes"
  "crypto/hmac"
  "crypto/sha256"
  "encoding/hex"
  "encoding/json"
  "fmt"
  "net/http"
  "sort"
  "strings"
  "time"
)

func main() {
  baseUrl := "<BASE_URL>"
  merchantNo := "<YOUR_MERCHANT_NO>"
  apiKey := "<YOUR_API_KEY>"
  apiSecret := "<API_SECRET>"

  payload := map[string]any{
    "merchant_no": merchantNo,
    "api_key": apiKey,
    "timestamp": time.Now().Unix(),
    "nonce": "random-xyz",
    "out_order_no": "202501010001",
    "amount": 10000,
    "currency": "PHP",
    "pay_method": "gcash",
    "country": "PH",
    "notify_url": "https://merchant.example.com/api/notify/pay",
    "extra": map[string]any{
      "customer": map[string]any{
        "first_name": "<FIRST_NAME>",
        "last_name": "<LAST_NAME>",
        "email": "<EMAIL>",
        "phone": "<PHONE>",
      },
    },
  }

  // stableJSON: disables HTML escaping (Go escapes <>& by default), consistent with the semantics of JS JSON.stringify
  stableJSON := func(v any) string {
    var buf bytes.Buffer
    enc := json.NewEncoder(&buf)
    enc.SetEscapeHTML(false)
    enc.Encode(v)
    return strings.TrimRight(buf.String(), "\n")
  }

  keys := make([]string, 0, len(payload));
  for k := range payload {
    if k == "sign" || payload[k] == nil { continue }
    keys = append(keys, k)
  }
  sort.Strings(keys)
  var b strings.Builder
  for i, k := range keys {
    if i > 0 { b.WriteString("&") }
    b.WriteString(k)
    b.WriteString("=")
    switch payload[k].(type) {
    case map[string]any, []any:
      b.WriteString(stableJSON(payload[k]))
    default:
      b.WriteString(fmt.Sprint(payload[k]))
    }
  }
  b.WriteString("&secret=")
  b.WriteString(apiSecret)

  mac := hmac.New(sha256.New, []byte(apiSecret))
  mac.Write([]byte(b.String()))
  sign := hex.EncodeToString(mac.Sum(nil))
  payload["sign"] = sign

  body, _ := json.Marshal(payload)
  req, _ := http.NewRequest("POST", baseUrl+"/merchant/pay/create", bytes.NewBuffer(body))
  req.Header.Set("Content-Type", "application/json")
  resp, _ := http.DefaultClient.Do(req)
  defer resp.Body.Close()
}

Python

# Demo of signing and order creation. sign = HMAC-SHA256(params sorted by key as k=v joined by "&", then append "&secret=<secret>") in hex. Nested objects (extra etc.) are serialized as compact JSON with keys sorted before signing. Replace placeholders with your real credentials.
import time
import hmac
import hashlib
import json
import requests

base_url = "<BASE_URL>"
merchant_no = "<YOUR_MERCHANT_NO>"
api_key = "<YOUR_API_KEY>"
api_secret = "<API_SECRET>"

payload = {
    "merchant_no": merchant_no,
    "api_key": api_key,
    "timestamp": int(time.time()),
    "nonce": "random-xyz",
    "out_order_no": "202501010001",
    "amount": 10000,
    "currency": "PHP",
    "pay_method": "gcash",
    "country": "PH",
    "notify_url": "https://merchant.example.com/api/notify/pay",
    "extra": {
        "customer": {
            "first_name": "<FIRST_NAME>",
            "last_name": "<LAST_NAME>",
            "email": "<EMAIL>",
            "phone": "<PHONE>",
        },
    },
}

def _sv(v):
    """Stable serialization: nested object/array -> compact JSON with keys in ascending order; scalar -> str(v)."""
    if isinstance(v, (dict, list)):
        return json.dumps(v, sort_keys=True, separators=(',', ':'), ensure_ascii=False)
    return str(v)

keys = sorted(k for k in payload if k != 'sign' and payload[k] is not None)
raw = "&".join(f"{k}={_sv(payload[k])}" for k in keys) + f"&secret={api_secret}"
sign = hmac.new(api_secret.encode(), raw.encode(), hashlib.sha256).hexdigest()
payload["sign"] = sign

resp = requests.post(f"{base_url}/merchant/pay/create", json=payload)
print(resp.text)

Amount Convention

All amounts are expressed as integers in the "minor accounting unit"; the current precision convention is 0.0001, so multiply values by 10000, e.g. pass 12345 for 1.2345, to avoid floating-point errors.

Callback Details

The platform 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 platform treats the callback as failed, retries it per policy, and triggers an alert when consecutive failures reach the threshold.

IP Allowlist/Blocklist Details

Common Authentication Error Codes

The following error codes are common to all authenticated endpoints; the "Common Errors" of each endpoint lists only high-frequency or business-related items and does not imply that other authentication codes will not be returned: