Checkout Mode
psp, platform, and self. The merchant's preferred mode shapes next_action in every payment intent response.
A payment intent's next_action depends on the merchant's checkout mode and the routed payment method. The mode is configured in the dashboard and resolved at intent creation time.
| Mode | next_action shape | Who hosts the customer-facing flow |
|---|---|---|
psp | redirect_to_url when the PSP returns a hosted checkout. | The PSP. |
platform | redirect_to_url pointing at the platform checkout host. | The platform's hosted page. |
self | display_qr_code or display_bank_transfer_instructions. | You. |
When each fits
psp is the simplest. The PSP runs the customer-facing checkout. Redirect the payer to next_action.url when the response type is redirect_to_url.
platform keeps the payer on a platform-hosted page that wraps the PSP. The merchant gets a stable redirect target and a single look and feel across PSPs. Requires a platform checkout URL to be configured.
self is the most flexible. The platform returns QR data or bank transfer instructions and you render the action inside your own UI. Used when you control the payer surface, such as a mobile app or custom checkout flow.
The fallback rules
The merchant's preferred mode is intersected with the checkout modes the routed method supports.
- Method has not opted into multi-mode ->
psp. - Preference supported -> that mode.
- Preference not supported -> method's first supported mode.
platformselected but no base URL is configured -> falls back topspor the method's first supported mode.
Fallback is silent. An unsupported preference never errors the request — it degrades to a
supported mode. Drive your customer-facing flow off next_action.type, not off the mode you
configured.
What you receive
The next_action.type tells you which shape arrived. Each mode resolves to one of these three.
{
"id": "dord_01HZX...",
"object": "payment_intent",
"status": "requires_action",
"next_action": {
"type": "redirect_to_url",
"url": "https://pay.example-psp.com/session/abc123",
"expires_at": "2026-05-09T12:00:00.000Z"
}
}Send the payer to next_action.url. Produced by psp and platform modes.
{
"id": "dord_01HZX...",
"object": "payment_intent",
"status": "requires_action",
"next_action": {
"type": "display_qr_code",
"qr_data": "00020101021229370016A...",
"qr_image_data_url": "data:image/png;base64,iVBORw0K...",
"expires_at": "2026-05-09T12:00:00.000Z"
}
}Render qr_image_data_url directly, or generate your own QR from qr_data. Produced by self mode.
{
"id": "dord_01HZX...",
"object": "payment_intent",
"status": "requires_action",
"next_action": {
"type": "display_bank_transfer_instructions",
"instructions": "Bank: Maybank\nAccount: 1234567890\nReference: dord_01HZX...",
"expires_at": "2026-05-09T12:00:00.000Z"
}
}Show instructions verbatim to the payer. Produced by self mode.
Notes
- Mode is a merchant-level setting, not a per-request flag. There is no
modefield on the create-payment-intent body. expires_atis the action-payload validity window. Past it, create a fresh payment intent.next_actionisnullwhen the intent is processing without a customer action, or once it reaches a terminal state.