Pay-In · Indonesia
Overview
The Pay-In API lets merchants initiate collection transactions from customers. This page covers Indonesia (country code id) and supports two order placement modes:
| Mode | Path | Description |
|---|---|---|
| Cashier mode | /pay/prePay | Default mode. Returns cashierUrl for redirecting the customer to the platform H5 cashier to complete payment |
| API mode | /pay/transOrder | Specify method in the request to receive payData / payDataType directly (VA, QRIS, etc.) |
method (Pay-In payment codes for Indonesia) is listed in Regional Payment Guide — Indonesia.
Integration note
Cashier mode is enabled by default. To integrate API mode, contact support to request merchant whitelist access before integration.
Request Header
Shared by both modes:
| Field | Required | Type | Description |
|---|---|---|---|
| Content-Type | M | String | HTTP content type specification Fixed value: application/json Required for proper request parsing |
Callback Version (shared by both modes)
Both order placement modes accept notifyVersion to specify the async notification payload version:
| Field | Required | Type | Description |
|---|---|---|---|
| notifyVersion | O | String(8) | Callback version Optional v1 or v2 (case-insensitive)Omitted or v1: callback contains base fields onlyv2: when status is SUCCESS and the upstream channel supports it, the success callback may include actual payer information (see Notification below)Default: v1 |
Cashier Mode (prePay)
After placing a pre-order, the merchant receives cashierUrl and redirects the customer to the platform cashier to select a payment method and complete payment.
Request URL
Domain: Prefer unified host
openapi.toppayment.com(path unchanged); legacy country domain (e.g.global-id-openapi.toppayment.com) remains available.
| Environment | URL |
|---|---|
| Sandbox | https://openapi.toppayment.com/sandbox/id/pay/prePay |
| Production | https://openapi.toppayment.com/id/pay/prePay |
Request Body
| Field | Required | Type | Description |
|---|---|---|---|
| mchNo | M | String(32) | Merchant number Unique merchant identifier assigned by the platform Used for merchant authentication and transaction routing |
| orderNum | M | String(64) | Merchant order number Unique transaction identifier Format: Alphanumeric string Used for transaction tracking and reference |
| amount | M | Number(32,8) | Transaction amount Numeric type Decimals are not allowed for Indonesia — pass an integer Example: 100000 |
| productDetail | M | String(100) | Product detail Transaction purpose or description UTF-8 string |
| method | O | String(16) | Payment method (Indonesia) Optional; if omitted, the customer selects on the cashier. Examples: QRIS, DANA, BNI — full list: Regional Payment Guide — Indonesia |
| timestamp | M | String(13) | Timestamp Request timestamp in milliseconds Example: 1749451858772 |
| customerName | M | String(64) | Customer name Payer name UTF-8 string |
| customerEmail | M | String(64) | Customer email Payer email address Valid email format |
| customerPhone | M | String(32) | Customer phone Payer phone number Valid phone number |
| expiryPeriod | O | Number(1–9999) | Expiry period Order validity in minutes Example: 1440 (24 hours) |
| downNotifyUrl | M | String(255) | Async notification URL Webhook URL for transaction status updates Valid HTTP/HTTPS URL |
| redirectUrl | O | String(512) | Redirect URL Browser redirect after successful payment (cashier mode) Valid absolute http:// or https:// URLMust share the same origin as a URL on the merchant redirect whitelist; if omitted or invalid, fallback rules apply (see below) |
| notifyVersion | O | String(8) | Callback version See Callback Version above |
| sign | M | String | Signature Digital signature for request authentication See Signature |
Request Body Example
Content-type: application/json
{
"mchNo": "{{mchNo}}",
"orderNum": "ID1234561",
"amount": 100000,
"productDetail": "Product",
"method": "DANA",
"timestamp": "1749451858772",
"customerName": "Budi",
"customerEmail": "[email protected]",
"customerPhone": "081234567890",
"expiryPeriod": 1440,
"downNotifyUrl": "https://example.com/notify",
"redirectUrl": "https://example.com/return",
"notifyVersion": "v2",
"sign": "Generate per signature rules and replace"
}
Response Body Parameters
| Field | Required | Type | Description |
|---|---|---|---|
| success | M | Boolean | Request success status true: success, false: failure |
| code | M | String | Response status code 9999: success; other values: failure |
| msg | O | String | Response message Human-readable status description null on success |
| timeStamp | M | Number | Response timestamp Server response time in milliseconds |
| data | M | Object | Response data object Contains transaction details |
| orderNum | M | String | Merchant order number Same as orderNum in the request |
| platOrderNum | M | String | Platform order number System-generated internal transaction reference |
| amount | M | Number | Transaction amount |
| fee | M | Number | Fee |
| method | M | String | Payment method (Indonesia) As returned by the platform — see Regional Payment Guide — Indonesia |
| productDetail | M | String | Product detail |
| customerName | M | String | Customer name |
| customerEmail | M | String | Customer email |
| customerPhone | M | String | Customer phone |
| validTime | M | Number | Expiry timestamp Order expiry time in milliseconds |
| cashierUrl | M | String | Cashier URL URL to open the payment cashier |
Response Example
Content-type: application/json
{
"success": true,
"code": "9999",
"msg": null,
"timeStamp": 1767840272829,
"data": {
"orderNum": "ID1234561",
"platOrderNum": "PRE2009093829059153920",
"amount": 100000,
"fee": 2000.0,
"method": "DANA",
"productDetail": "Product",
"customerName": "Budi",
"customerEmail": "[email protected]",
"customerPhone": "081234567890",
"validTime": 1767926672819,
"cashierUrl": "https://example.cashier/pay"
}
}
Cashier success redirect
After a successful payment, the payer’s browser can be redirected to a merchant-configured page. This capability is disabled by default. To enable it, contact support via Telegram; support will turn it on and configure a redirect URL whitelist (multiple URLs allowed).
You may pass optional redirectUrl when placing an order:
- Must be an absolute
http://orhttps://URL (relative paths and other schemes are not supported) - Redirect occurs only on payment success; failed or incomplete payments do not redirect
- If a valid
redirectUrlis provided and its origin matches the merchant whitelist: redirect as-is after success (no automaticorderNumappend) - If omitted, empty, or invalid: redirect to the first whitelist URL with query parameter
orderNumappended (same as the order requestorderNum)
Example:https://merchant.example.com/pay/result?orderNum=ID1234561 - If
redirectUrlis provided but its origin is not on the whitelist: no redirect after success (does not fall back to the first whitelist URL)
API Mode (transOrder)
The merchant specifies the payment method in the request and initiates the transaction directly with the payment channel. The response returns payData / payDataType (such as VA account number, QRIS code, or redirect link) without redirecting to the platform H5 cashier.
Request URL
| Environment | URL |
|---|---|
| Sandbox | https://openapi.toppayment.com/sandbox/id/pay/transOrder |
| Production | https://openapi.toppayment.com/id/pay/transOrder |
Request Body
Compared with cashier mode, the difference is:
| Field | Required | Type | Description |
|---|---|---|---|
| method | M | String(16) | Payment method (Indonesia) Required in API mode. Examples: QRIS, DANA, BNI — full list: Regional Payment Guide — Indonesia |
All other fields (including notifyVersion) are the same as cashier mode.
Request Body Example
Content-type: application/json
{
"mchNo": "{{mchNo}}",
"orderNum": "IDAPI1234561",
"amount": 100000,
"productDetail": "Product",
"method": "QRIS",
"timestamp": "1749451858772",
"customerName": "Budi",
"customerEmail": "[email protected]",
"customerPhone": "081234567890",
"expiryPeriod": 1440,
"downNotifyUrl": "https://example.com/notify",
"sign": "Generate per signature rules and replace"
}
{
"mchNo": "{{mchNo}}",
"orderNum": "IDAPI7890121",
"amount": 100000,
"productDetail": "Product",
"method": "BNI",
"timestamp": "1749451858772",
"customerName": "Budi",
"customerEmail": "[email protected]",
"customerPhone": "081234567890",
"expiryPeriod": 1440,
"downNotifyUrl": "https://example.com/notify",
"sign": "Generate per signature rules and replace"
}
Response Body Parameters
Common response fields (success, code, msg, timeStamp, and orderNum, platOrderNum, amount, fee, method, productDetail, customerName, customerEmail, customerPhone, validTime in data) are the same as cashier mode. API mode focuses on:
| Field | Required | Type | Description |
|---|---|---|---|
| payData | M | String | Payment data VA account number, QRIS string, redirect link, etc. — meaning depends on payDataType |
| payDataType | M | String | Payment data type Values: VA, QR_CODE, QR_URL, CASHIER_URL |
| cashierUrl | O | String | Cashier URL Some channels may return this as well; in API mode, use payData as the primary field |
payDataType Reference
| payDataType | Description | Typical method |
|---|---|---|
VA | Virtual account — merchant guides the customer to transfer to this account | BNI, BCA, MANDIRI, BRI bank VA |
QR_CODE | QRIS QR code content string — render as QR code on your side | QRIS |
QR_URL | QR code image or scan page URL | Some QRIS / wallet scenarios |
CASHIER_URL | Upstream payment page redirect link | DANA, OVO wallets |
Content-type: application/json
{
"success": true,
"code": "9999",
"msg": null,
"timeStamp": 1767840272829,
"data": {
"orderNum": "IDAPI1234561",
"platOrderNum": "PRE2009093829059153920",
"amount": 100000,
"fee": 2000.0,
"method": "QRIS",
"productDetail": "Product",
"customerName": "Budi",
"customerEmail": "[email protected]",
"customerPhone": "081234567890",
"validTime": 1767926672819,
"payData": "00020101021226650016COM.DANA.WWW0118936009140812345678900303UMM5144005405100000050205035406100000005802ID5913Test Merchant6007Jakarta61051234062070703A0304140212345678901234567890123456789012345678901234567890123456789012345678906304ABCD",
"payDataType": "QR_CODE"
}
}
{
"success": true,
"code": "9999",
"msg": null,
"timeStamp": 1767840272829,
"data": {
"orderNum": "IDAPI7890121",
"platOrderNum": "PRE2009093829059153921",
"amount": 100000,
"fee": 2000.0,
"method": "BNI",
"productDetail": "Product",
"customerName": "Budi",
"customerEmail": "[email protected]",
"customerPhone": "081234567890",
"validTime": 1767926672819,
"payData": "8812345678901234",
"payDataType": "VA"
}
}
Notification
Async notifications are identical for both order placement modes. The callback payload version is determined by notifyVersion at order placement (defaults to v1 if omitted). The version field in the callback body matches it.
HTTP Request
| Field | Required | Type | Description |
|---|---|---|---|
| Content-Type | M | String | HTTP request content type specification Fixed value: application/json |
Notification Body (v1 / base fields)
| Field | Required | Type | Description |
|---|---|---|---|
| platOrderNum | M | String | Platform order number System-generated internal transaction reference |
| version | M | String | Callback version Matches notifyVersion at order placementExamples: v1, v2 |
| orderNum | M | String | Merchant order number Same as orderNum in the original request |
| amount | M | Number | Transaction amount |
| fee | M | Number | Fee |
| customerName | M | String | Customer name Payer name provided at order placement (not the actual payer account name) |
| customerEmail | M | String | Customer email |
| customerPhone | M | String | Customer phone |
| status | M | String | Transaction status See Transaction Status Codes |
| sign | M | String | Signature Digital signature for callback verification |
Notification Body (v2 extended fields)
When notifyVersion is v2 and status is SUCCESS, if the upstream channel returns payer information, the callback may additionally include the following fields (channel-dependent — omitted or empty when unavailable):
| Field | Required | Type | Description |
|---|---|---|---|
| payerName | O | String | Payer name Actual payer account name (may differ from customerName at order placement) |
| payerAccountNo | O | String | Payer account number Actual payer account |
| payerAccountBank | O | String | Payer bank Actual payer bank or channel name |
v2 notes
v2only affects whether successful callbacks attempt to include payer information; non-success callbacks (e.g. failure) have the same structure asv1without the extended fields.- Whether
payerName/payerAccountNo/payerAccountBankare returned depends on whether the payment channel provides them in the upstream callback. Merchants should handle both cases.
Response
Important
Return only the string SUCCESS to acknowledge receipt of the notification.
{
"platOrderNum": "PRE2009165141186183168",
"version": "v1",
"orderNum": "ID12345621",
"amount": 100000,
"fee": 2000,
"customerName": "Budi",
"customerEmail": "[email protected]",
"customerPhone": "081234567890",
"status": "SUCCESS",
"sign": "m5++HHEOfaVL3opFSuihVE4kkdLaCyhpFVSSLJld8WeEhlH93Ido5MQQ6peWrf+8eCkQd127jesL9esQDdFAiGKkem5BwvqTAvZGQm9v7M33Sy+W58OkGkb3/8BxQwCLTIUouhwpj1TwIeqP3JWo3AFMm5qezH3JbVfOd1IZ9Gw="
}
{
"platOrderNum": "PRE2009165141186183168",
"version": "v2",
"orderNum": "ID12345621",
"amount": 100000,
"fee": 2000,
"customerName": "Budi",
"customerEmail": "[email protected]",
"customerPhone": "081234567890",
"status": "SUCCESS",
"payerName": "BUDI SANTOSO",
"payerAccountNo": "081234567890",
"payerAccountBank": "BNI",
"sign": "m5++HHEOfaVL3opFSuihVE4kkdLaCyhpFVSSLJld8WeEhlH93Ido5MQQ6peWrf+8eCkQd127jesL9esQDdFAiGKkem5BwvqTAvZGQm9v7M33Sy+W58OkGkb3/8BxQwCLTIUouhwpj1TwIeqP3JWo3AFMm5qezH3JbVfOd1IZ9Gw="
}
SUCCESS
