Capabilities & Constraints
- Flow type: Redirect to bank or bank switch; result confirmed by webhook.
- Capture model: Direct capture only (no separate auth/capture).
- Customer experience: Bank login → approve payment → redirect/return.
- Settlement: According to the connected provider/bank switch.
- Refunds: Supported by many providers after capture (rules vary; see matrix below). No “reversal”/void once bank has posted the debit.
- Retries: If abandoned/expired, create a new charge with a new
Idempotency-Key.
Typical Use Cases
- E-commerce checkout where card conversion is low
- High-trust domestic bank rails (e.g., Malaysia FPX-style, SG pay-by-bank)
- Desktop/mobile flows where redirect is acceptable
1. Create a Charge
Initiate the payment by creating a charge in your system with the transaction details.
2. Redirect the Customer
Use the
return_url from Monxa to send the customer to their bank app.3. Customer Approves at Bank App
The customer confirms the transaction by entering their PIN/OTP or using biometric authentication in their bank app.
4. Handle Webhooks
Once the e-wallet provider processes the payment, Monxa sends a webhook notification to update your system with the final status.
Supported Channels
- 🇲🇾 Malaysia
- 🇹🇭 Thailand
| Channel | Code | Currency | Refund | Settlement | Min Amount | Max Amount |
|---|---|---|---|---|---|---|
| Affin Bank | bank_affin | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Agro Bank | bank_agro | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Alliance Bank | bank_alliance | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Am Bank | bank_am | MYR | N.A | T+2 | 1 | 1000,000,000 |
| BNP Bank | bank_bnp | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Bank Of China | bank_boc | MYR | N.A | T+2 | 1 | 1000,000,000 |
| BSN | bank_bsn | MYR | N.A | T+2 | 1 | 1000,000,000 |
| CIMB | bank_cimb | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Citibank | bank_citi | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Deutsche Bank | bank_deutsche | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Hong Leong Bank | bank_hlb | MYR | N.A | T+2 | 1 | 1000,000,000 |
| HSBC | bank_hsbc | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Islam Bank | bank_islam | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Kuwait Finance House | bank_kuwait | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Maybank | bank_maybank | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Bank Mualamat | bank_mualamat | MYR | N.A | T+2 | 1 | 1000,000,000 |
| OCBC | bank_ocbc | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Public Bank | bank_public | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Bank Rakyat | bank_rakyat | MYR | N.A | T+2 | 1 | 1000,000,000 |
| RHB Bank | bank_rhb | MYR | N.A | T+2 | 1 | 1000,000,000 |
| Standard Chartered | bank_scb | MYR | N.A | T+2 | 1 | 1000,000,000 |
| UOB | bank_uob | MYR | N.A | T+2 | 1 | 1000,000,000 |
Payment Flow
Status Lifecycle
| Status | When | Merchant Action |
|---|---|---|
| pending | Charge created; awaiting bank result | Redirect customer; wait for webhook |
| succeeded | Bank confirmed & funds captured | Fulfill order |
| failed | Bank/customer canceled or error | Offer retry / alternate method |
| expired | Session timed out at bank/switch | Create a new charge if needed |
Field Reference
| Field | Type | Required | Notes |
|---|---|---|---|
| amount | integer | ✓ | Minor units. |
| currency | string | ✓ | ISO-4217 (e.g., MYR, SGD, THB). |
| reference_id | string | ✓ | Your order/invoice reference. |
| channel_code | string | ✓ | e.g., bank_redirect_fpx, bank_redirect_sgpaybybank, bank_redirect_th. |
| channel_properties.return_url | string(url) | ✓ | Where we return the customer after bank flow. |
| channel_properties.cancel_url | string(url) | Where to send customer if they cancel (if supported). | |
| channel_properties.bank_code | string | Preselect a bank (provider-dependent). | |
| metadata | object | Freeform key-value pairs. | |
| Header: Idempotency-Key | string | ✓ | Unique per create attempt to avoid duplicates. |
Step 1: Create a Charge
Create a charge with a bank-redirectchannel_code. Include a return_url (required) to bring the customer back to your site/app.
Request Example : Malaysia - FPX-like switch)
Request Example : Malaysia - FPX-like switch)
Response Example
Response Example
Why pending? The payment is not approved yet. Final state is delivered via webhook after the customer completes at their bank.
Step 2: Redirect the Customer
Send the customer toactions.redirect_url.
- Web: Perform a 302 redirect or render a “Continue to your bank” button linking to
redirect_url. - Mobile: Open in external browser or in-app webview. Handle the
return_urlto resume your flow.
Step 3: Customer Approves at Bank
The customer authenticates with their bank and approves the payment. The bank/switch posts the result back to Monxa.- If successful → funds are captured and status becomes
succeeded. - If rejected/canceled → status becomes
failed. - If not completed in time → status becomes
expired.
Step 4: Handle Webhooks & Update Payment Status
Sample Webhook (charge.succeeded)
Sample Webhook (charge.succeeded)
Sample Webhook (charge.failed)
Sample Webhook (charge.failed)
Implementation Tips
- UI: Always show a “Continue to your bank” CTA and a fallback “Try another method.”
- Timeouts: Respect
actions.expires_at; show session countdowns to reduce abandonment. - Mobile deep-linking: Some providers return app deep links; handle gracefully with browser fallback.
- Reconciliation: Store
charge.id,reference_id, bankbank_code(if present), and webhook timestamps.
Error Handling
| Error | Meaning | How to Resolve |
|---|---|---|
400 invalid_channel_properties | Missing/invalid return_url or bank_code | Provide required fields; validate URLs |
400 unsupported_channel | Channel not enabled or not available in corridor | Enable channel or choose supported one |
402 bank_declined | Bank rejected the payment | Offer retry/alternative method |
409 duplicate_reference | reference_id uniqueness conflict (if enforced) | Use a unique reference or update config |
422 amount_mismatch | Amount not accepted by provider | Ensure correct minor units and limits |