Merchant API
References

Checkout mode

Choose hosted or merchant checkout per payment intent and present the correct payer action.

checkout_mode decides who owns the payer-facing presentation. It is selected when you create a payment intent and must also be used when you discover eligible payment methods.

ModeDefaultCreate requestReturned action
hostedYespayment_method is optionalSigned hosted checkout URL
merchantNopayment_method is requiredCanonical next_action for your application to show

The merchant's Team policy must allow the requested mode. A denied mode returns 403 checkout_mode_not_allowed; the API never silently changes it. The selected mode is retained for the lifetime of the payment intent.

Discover and map eligible methods

Discover

Use the same amount, country, currency, and checkout_mode for discovery and payment creation. The response contains public method codes, the action types each method can produce, available bank codes, and any payer fields required by the selected route.

curl -s "$API_BASE/payment_methods/options?amount=250000&country=TH&currency=THB&checkout_mode=merchant" \
  -u "$API_PUBLIC_KEY:$API_SECRET_KEY"

A discovery response can look like this:

{
  "country": "TH",
  "currency": "THB",
  "environment": "test",
  "methods": [
    {
      "category": "bank_transfer",
      "code": "BANK_TRANSFER",
      "label": "Bank transfer",
      "next_action_types": ["display_bank_transfer_instructions"],
      "options": {
        "banks": [{ "code": "BANK_CODE", "label": "Example bank" }],
        "payer": {
          "account_name": { "required": true },
          "account_number": { "required": true },
          "bank": { "required": true },
          "customer_name": { "required": true },
          "customer_phone": { "required": true }
        }
      }
    }
  ]
}

If methods is empty, no eligible public method is available for those request constraints. Do not guess a method or its fields; use the discovery response and retry with a supported combination.

Map payer fields

options.payer describes what the selected route requires. It is a discovery shape, not a create request object. Map each field to its canonical request path:

Discovery fieldCreate request field
customer_namecustomer.name
customer_phonecustomer.phone
account_namepayment_method_options.account_name
account_numberpayment_method_options.account_number
bankpayment_method_options.bank
name_thpayment_method_options.name_th

The top-level customer object is required on every payment-intent request. The payment_method_options object is optional and only applies when payment_method is set.

Create the payment intent

Use the mapped fields in the request. Do not send options.payer directly and do not put customer_name or customer_phone inside payment_method_options. The complete hosted and merchant examples are available on Create a payment intent.

Hosted checkout

Hosted checkout always enters the signed checkout surface, whether or not the request already names a payment method.

Let the payer choose

Omit payment_method and payment_method_options when the payer should choose on the hosted page:

{
  "amount": "10000",
  "currency": "MYR",
  "country": "MY",
  "checkout_mode": "hosted",
  "merchant_reference": "order_123",
  "customer": { "email": "aisyah@example.com" }
}

The 201 response contains checkout_mode: "hosted", payment_method: null, and a next_action.type of redirect_to_url. Redirect the payer to that URL. The initial intent has no frozen method selection; the hosted page completes that selection before payment processing starts.

Start with a known method

You can include payment_method when the method is known, but the payer still completes the flow in the hosted surface. Use payment_method_options only when the selected method requires payer fields. Hosted routes that need a QR code, bank instructions, or a form submission are presented through the signed hosted URL rather than exposed as merchant-rendered fields.

For example, add this field to the hosted request when the payer should use a known method:

{
  "checkout_mode": "hosted",
  "payment_method": "FPX"
}

return_url is valid only in hosted mode. The terminal checkout page receives it unmodified and no payment status is appended, so retrieve the payment intent or consume its webhook before treating the payment as final.

Merchant-owned checkout

Merchant checkout requires one public payment_method; omitting it returns 400 validation_failed. return_url is rejected in this mode. Create the intent with the method and the canonical options required by discovery:

{
  "amount": "250000",
  "currency": "THB",
  "country": "TH",
  "checkout_mode": "merchant",
  "payment_method": "BANK_TRANSFER",
  "payment_method_options": {
    "bank": "BANK_CODE",
    "account_name": "Somchai",
    "account_number": "1234567890"
  },
  "merchant_reference": "order_124",
  "customer": {
    "name": "Somchai",
    "phone": "+66812345678"
  }
}

Replace BANK_CODE with one of the codes returned in options.banks.

payment_method_options without payment_method, unknown option values, or missing route-required customer fields also return 400 validation_failed.

Canonical next actions

For merchant checkout, next_action is a closed union:

TypeWhat your application does
redirect_to_urlOpen the returned URL with a browser GET.
display_qr_codeRender qr_data, qr_image_url, or qr_image_data_url when present.
display_bank_transfer_instructionsShow the structured bank, account, and reference values, then collect payment evidence if requested.

The action type is determined by the selected method and eligible route. Do not infer a form post, local method code, or bank field from the action; use only the public response shape.

Lifecycle and errors

  • next_action is present when the payer must do something. A pending intent without a current action is returned as status: "processing".
  • next_action is null after a terminal result such as succeeded, failed, or expired. Poll GET /payment_intents/:id or consume the webhook until the status is terminal.
  • 403 checkout_mode_not_allowed means Team policy denies the requested mode.
  • 422 payment_configuration_unavailable means the chosen method or option has no configured execution.
  • 422 routing_unavailable means no healthy route is available for the request.

On this page