This is a Money In operation. Each charge pulls funds from the customer’s bound bank account into your Singapay merchant settlement account.
One-time setup
Customer authorizes their bank account once via a hosted webview. All future charges use the stored
binding_id — no customer redirect needed.OTP-aware charging
Some banks require an OTP per charge. SingaPay handles both flows automatically and signals when
/verify-otp is needed.Multi-bank support
Supports BRI, Mandiri, BNI, Danamon, BCA, CIMB, BNC, and more via BI/SNAP bank codes.
How Direct Debit Works
Direct Debit has two phases: Binding (one-time) and Charge (repeatable).- Binding — The customer authorizes their bank account via a hosted webview. On success, an
ACTIVEbinding is created and can be used for future charges indefinitely. - Charge — The merchant initiates a pull payment using the
binding_id. Depending on the bank’spayment_otp_mode, the charge either completes silently or requires the customer to submit an OTP.
Flow Diagram
Binding Status
| Status | Description |
|---|---|
PENDING_AUTH | Binding initiated — customer has been redirected to the bank webview for authorization. |
ACTIVE | Customer authorized successfully. This binding can be used for charges. |
UNBOUND | Binding was revoked by the merchant via the Unbind endpoint. |
FAILED | Customer cancelled or the bank rejected the authorization. |
EXPIRED | Customer did not complete authorization before expires_at. |
Transaction Status
| Status | Description |
|---|---|
PENDING | Charge accepted, awaiting settlement confirmation from the bank. |
PENDING_OTP | OTP required — waiting for customer to submit OTP via /verify-otp. |
SUCCESS | Funds credited to merchant settlement account. Fee breakdown available in fees. |
FAILED | Bank rejected the charge — check failure_code and failure_reason. |
EXPIRED | OTP not submitted in time, or upstream timeout. |
Recommended Flow
Create a binding
Call Binding Card with the customer’s phone number, bank code, and preferred OTP mode. Redirect the customer to the returned
redirect_url to complete bank authorization.Confirm binding is active
Listen for the
direct-debit.binding.succeeded webhook, or poll Binding Status until the status is ACTIVE. Store the binding_id — you will use it for all future charges.Charge the bound account
Call Charge with the
binding_id, amount, and a unique merchant_reference. If the bank requires OTP (requires_otp: true), proceed to the next step.Verify OTP (if required)
If the Charge response returns HTTP 202 with
requires_otp: true, prompt the customer to enter the OTP sent by their bank, then call Verify OTP with the transaction_id and OTP value.Supported Banks
| Bank | Bank Code |
|---|---|
| Bank Rakyat Indonesia (BRI) | 002 |
| Bank Mandiri | 008 |
| Bank Negara Indonesia (BNI) | 009 |
| Bank Danamon | 011 |
| Bank Central Asia (BCA) | 014 |
| CIMB Niaga | 022 |
| Bank Neo Commerce (BNC) | 490 |
Important Notes
Redirect URL is single-use and short-lived
Redirect URL is single-use and short-lived
The
redirect_url returned on binding creation is single-use and expires at expires_at (typically ~15 minutes). Treat it as opaque — do not parse or hardcode its host or path. If it expires before the customer completes authorization, create a new binding.OTP mode is bank-determined
OTP mode is bank-determined
The
payment_otp_mode parameter is a preferred hint — the bank may require OTP regardless of this setting. Always check the Charge response for requires_otp before assuming a silent charge will complete.Idempotency via merchant_reference
Idempotency via merchant_reference
The
merchant_reference field acts as an idempotency key for charges. Submitting the same reference twice returns SP_DD_DUPLICATE_REFERENCE — store the first transaction_id and query its status instead of retrying with a new request.Unbinding may also require OTP
Unbinding may also require OTP
Some banks require OTP verification when revoking a binding. If the Unbind response returns
otp_required: true, call Verify OTP with the binding_id and the unbind_context from the otp_handoff block.Signature required for charge
Signature required for charge
The Charge endpoint requires additional
X-Signature and X-Timestamp headers — separate from the standard Bearer token. See Authentication for signing requirements.IP whitelisting required
IP whitelisting required
All Direct Debit endpoints require IP whitelisting. Ensure your server IP is registered under the merchant account before going live.
Available Endpoints
Binding Card
Initiate a binding — returns a hosted webview
redirect_url for the customer to authorize their bank account.Binding Status
Check the current state of a binding by
binding_id.Unbind Card
Revoke an active binding. May require OTP verification for some banks.
Charge
Pull funds from a bound bank account. Returns
requires_otp: true when OTP verification is needed.Verify OTP
Submit the customer OTP to complete a charge or unbinding that requires verification.
Get Transaction
Retrieve the latest status of a Direct Debit charge by
transaction_id.All endpoints require authentication. The Charge endpoint additionally requires
X-Signature and X-Timestamp. See Authentication.