VPay API Documentation
Fast, Secure, and Reliable Payment Solution
- RESTful API design, simple and easy to use
- Supports PHP, Python, Golang and more
- Complete payment flow: payment, query, disbursement, etc.
- Real-time callback notification
- RSA signature verification
Basic Rules
Production Environment:
https://api.vpayid.com/vpayid-apiapplication/jsonGenerating sign: Build the plaintext string src by concatenating the fields defined in the "Signature Rule" of each API, take its UTF-8 bytes, then perform a PKCS#1 v1.5 (type1) raw signing with the merchant RSA private key (signature padding block 0x00 0x01 FF..FF 0x00 + src, i.e. c = block^d mod n), and Base64 encode the result to get sign. This is NOT SHA256withRSA digest signing (do not use Signature/openssl_sign).
Verification: The platform recomputes sign with the merchant RSA public key in the same way (type1), recovering the 0x00 0x01 FF..FF 0x00 src block, and compares it byte-by-byte with the plaintext string src. It passes only when they are identical.
RSA Key Generation
1) Generate the private key (PKCS#8, header BEGIN PRIVATE KEY, works on all OpenSSL versions):
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out rsa_private_pkcs8.pem
2) Export the public key from the private key (X.509/SPKI, header BEGIN PUBLIC KEY):
openssl rsa -in rsa_private_pkcs8.pem -pubout -out rsa_public.pem
Note: on OpenSSL 3.x, openssl genrsa -out rsa_pkcs1_private.pem 2048 actually already outputs PKCS#8 (BEGIN PRIVATE KEY); but on OpenSSL 1.x genrsa outputs PKCS#1 (BEGIN RSA PRIVATE KEY), which the Java platform cannot load directly (algid parse error). It must be converted to PKCS#8 first:
openssl genrsa -out rsa_pkcs1_private.pem 2048 openssl pkcs8 -topk8 -nocrypt -in rsa_pkcs1_private.pem -out rsa_private_pkcs8.pem openssl rsa -in rsa_pkcs1_private.pem -pubout -out rsa_public.pem
- Private key: must be PKCS#8 (
-----BEGIN PRIVATE KEY-----). PKCS#1 (BEGIN RSA PRIVATE KEY) cannot be loaded. - Public key: X.509/SPKI format (
-----BEGIN PUBLIC KEY-----). When submitting to the platform, provide only the Base64 body (without the header/footer lines or line breaks). - Keep the private key safe by the merchant — never disclose it or commit it to a repository; submit the public key to the platform for your merchant profile.
Taking the Payment API as an example, the plaintext src = amount + callbackUrl + channelCode + merchantCode + orderNumber (no line breaks):
printf '%s' "100000http://localhost/callbackQRISTEST1604384758793" > src.txt # Sign with the private key (PKCS#1 v1.5 type1) to get sign openssl pkeyutl -sign -inkey rsa_private_pkcs8.pem -in src.txt -out sign.bin openssl base64 -in sign.bin # Base64 of this is the sign field in the request # Verify with the public key (simulating the platform). "Signature Verified Successfully" means the pair matches openssl pkeyutl -verify -pubin -inkey rsa_public.pem -in src.txt -sigfile sign.bin
You can also verify using the self-check methods in the code examples below.
- Request signing: the merchant signs the plaintext src with its own RSA private key using PKCS#1 v1.5 (type1) raw signing, Base64 encodes it, and puts it into the
signfield (signType=RSA). This is NOT SHA256withRSA digest signing. - Platform verification: the platform decrypts sign with the merchant's submitted RSA public key and compares it with the plaintext.
- Async callback verification: the
rsaSignin platform callbacks is verified by decrypting with the platform public key (please contact the platform to obtain it).
Payment API
Request Parameters
| Parameter Name | Description | Type | Required | Example |
|---|---|---|---|---|
| merchantCode | Merchant Code | String | Y | test |
| orderNumber | Order Number | String | Y | 1604384758793 |
| amount | Amount | Integer | Y | 40000 |
| phone | Phone | String | Y | 08123456789 |
| String | Y | test@test.com | ||
| userName | Name | String | Y | John Doe |
| walletNumber | Wallet Number | String | N | 08821900000 (Required for DANA, format 08********; Required for OVO cashier with real number, otherwise optional) |
| channelCode | Channel Code | String | Y | M1 (If CASH, will use cashier payment. See channel code table for other codes) |
| returnUrl | Return URL | String | N | http://test/return |
| callbackUrl | Callback URL | String | Y | http://test/callback |
| signType | Signature Type | String | Y | RSA |
| sign | Signature | String | Y | RSA(...) |
Signature Rule
RSA(amount + callbackUrl + channelCode + merchantCode + orderNumber)
Response Parameters
| Parameter Name | Description | Type | Example |
|---|---|---|---|
| code | Response Code | String | 000 |
| message | Response Message | String | success |
| orderNumber | Order ID | String | O3915CB3C93E445C9 |
| accountNumber | Bank Account | String | TEST0001 (VA only) |
| paymentUrl | Payment URL | String | http://xxx.xxx/ |
| qrcode | QR Code | String | Returned for QRIS payment |
| merchantCode | Merchant Code | String | test |
Request Example
{
"merchantCode": "test",
"orderNumber": "1604384758793",
"amount": 40000,
"phone": "08123456789",
"email": "test@test.com",
"userName": "John Doe",
"channelCode": "M1",
"returnUrl": "http://test/return",
"callbackUrl": "http://test/callback",
"sign": "a1b2c3d4e5f6..."
}Response Example
{
"code": "000",
"message": "success",
"orderNumber": "O3915CB3C93E445C9",
"paymentUrl": "http://xxx.xxx/",
"qrcode": "data:image/png;base64,..."
}import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* VPay /payment example (pure JDK, no third-party dependency).
*
* 1. PRIVATE_KEY_PATH is your merchant RSA private key (PKCS#8);
* PUBLIC_KEY_PATH is your merchant RSA public key (X.509) for self-verification.
* 2. sign = the merchant RSA private key does a PKCS#1 v1.5 type1 raw signing on src
* (signature padding block 0x00 0x01 FF..FF 0x00 + src, then c = block^d mod n)
* and Base64 encodes it. The platform verifies it by recomputing with the merchant
* RSA public key (type1) and comparing the recovered bytes with src.
* NOT SHA256withRSA (do not use Signature).
*/
public class PaymentDemo {
static final String BASE_URL = "https://api.vpayid.com/vpayid-api";
static final String PRIVATE_KEY_PATH = "YOUR_PRIVATE_KEY.pem"; // your PKCS#8 private key
static final String PUBLIC_KEY_PATH = "YOUR_PUBLIC_KEY.pem"; // your X.509 public key
static final String MERCHANT_CODE = "YOUR_MERCHANT_CODE";
static final String CALLBACK_URL = "http://localhost/callback";
public static void main(String[] args) throws Exception {
String orderNumber = "PAY_" + System.currentTimeMillis();
int amount = 100000;
// src = amount + callbackUrl + channelCode + merchantCode + orderNumber
String src = amount + CALLBACK_URL + "QRIS" + MERCHANT_CODE + orderNumber;
PrivateKey priv = loadPrivateKey(PRIVATE_KEY_PATH);
String sign = rsaPrivateEncryptBase64(src, priv);
boolean ok = rsaPublicDecryptVerify(src, sign, loadPublicKey(PUBLIC_KEY_PATH)); // self-check like the platform
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("merchantCode", MERCHANT_CODE);
payload.put("orderNumber", orderNumber);
payload.put("amount", amount);
payload.put("phone", "08123456789");
payload.put("email", "test@test.com");
payload.put("userName", "John Doe");
payload.put("channelCode", "QRIS");
payload.put("callbackUrl", CALLBACK_URL);
payload.put("signType", "RSA");
payload.put("sign", sign);
String body = toJson(payload);
System.out.println("src = " + src);
System.out.println("selfVerify = " + ok);
System.out.println("request = " + body);
// optional: POST BASE_URL + "/payment" with body
}
/**
* PKCS#1 v1.5 type1 raw signing of src with the merchant private key
* -> Base64 (the required sign).
*/
static String rsaPrivateEncryptBase64(String src, PrivateKey privateKey) {
RSAPrivateKey rsaPriv = (RSAPrivateKey) privateKey;
int k = rsaPriv.getModulus().bitLength() / 8;
byte[] data = src.getBytes(StandardCharsets.UTF_8);
if (data.length > k - 11) throw new IllegalArgumentException("src is too long");
byte[] block = new byte[k];
block[0] = 0x00; block[1] = 0x01; // type1: 00 01 FF..FF 00 src
java.util.Arrays.fill(block, 2, k - data.length - 1, (byte) 0xFF);
block[k - data.length - 1] = 0x00;
System.arraycopy(data, 0, block, k - data.length, data.length);
BigInteger m = new BigInteger(1, block);
BigInteger c = m.modPow(rsaPriv.getPrivateExponent(), rsaPriv.getModulus());
byte[] sig = c.toByteArray();
if (sig.length == k + 1 && sig[0] == 0) sig = java.util.Arrays.copyOfRange(sig, 1, k + 1); // strip BigInteger sign byte
if (sig.length > k) throw new IllegalStateException("signature is too long");
byte[] out = new byte[k];
System.arraycopy(sig, 0, out, k - sig.length, sig.length);
return Base64.getEncoder().encodeToString(out);
}
/**
* Self-check like the platform: recompute with the public key (type1),
* src is recovered from 0x00 0x01 FF..FF 0x00 src when valid.
*/
static boolean rsaPublicDecryptVerify(String src, String sign, PublicKey publicKey) {
try {
RSAPublicKey rsaPub = (RSAPublicKey) publicKey;
int k = rsaPub.getModulus().bitLength() / 8;
byte[] sig = Base64.getDecoder().decode(sign);
BigInteger c = new BigInteger(1, sig);
BigInteger m = c.modPow(rsaPub.getPublicExponent(), rsaPub.getModulus());
byte[] pad = new byte[k];
byte[] mb = m.toByteArray();
System.arraycopy(mb, 0, pad, k - mb.length, mb.length);
if (pad[0] != 0x00 || pad[1] != 0x01) return false; // needs 00 01 FF..00 data
int sep = -1;
for (int i = 2; i < k; i++) {
if (pad[i] == 0x00) { sep = i; break; }
}
if (sep < 0) return false;
return src.equals(new String(pad, sep + 1, k - sep - 1, StandardCharsets.UTF_8));
} catch (Exception e) {
return false;
}
}
// load PKCS#8 private key from a PEM file
static PrivateKey loadPrivateKey(String pemPath) throws Exception {
String pem = new String(Files.readAllBytes(Paths.get(pemPath)), StandardCharsets.UTF_8)
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.getMimeDecoder().decode(pem)));
}
// load X.509 public key from a PEM file (for self-verification)
static PublicKey loadPublicKey(String pemPath) throws Exception {
String pem = new String(Files.readAllBytes(Paths.get(pemPath)), StandardCharsets.UTF_8)
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(new X509EncodedKeySpec(Base64.getMimeDecoder().decode(pem)));
}
// minimal JSON builder (or use any JSON library)
static String toJson(Map<String, Object> m) {
StringBuilder sb = new StringBuilder("{");
boolean first = true;
for (Map.Entry<String, Object> e : m.entrySet()) {
if (!first) sb.append(",");
first = false;
sb.append("\"").append(e.getKey()).append("\":");
Object v = e.getValue();
if (v instanceof Number) {
sb.append(v);
} else {
sb.append("\"").append(v).append("\"");
}
}
return sb.append("}").toString();
}
}Payment Query
Request Parameters
| Parameter Name | Description | Type | Required | Example |
|---|---|---|---|---|
| merchantCode | Merchant Code | String | Y | test |
| orderNumber | Order Number | String | Y | 1604384758793 |
| signType | Signature Type | String | Y | RSA |
| sign | Signature | String | Y | RSA(...) |
Signature Rule
RSA(merchantCode + orderNumber)
Response Parameters
| Parameter Name | Description | Type | Example |
|---|---|---|---|
| code | Response Code | String | 000 |
| message | Response Message | String | complete |
| userOrderNumber | User Order Number | String | 1604384968942 |
| orderNumber | Platform Order Number | String | test16043849702603 |
| amount | Payment Amount | Integer | 40000 |
| merchantCode | Merchant Code | String | test |
| phone | Phone | String | Hidden (00) |
| String | Hidden (00) | ||
| userName | Name | String | John Doe |
| channelCode | Channel Code | String | M1 |
| status | Payment Status | int | 0 Pending, 1 Success, 2 Failed |
| createTime | Order Create Time | Date | 2020-11-03 13:29:30 |
| payTime | Payment Time | Date | 2020-11-03 13:39:30 (Returned on success) |
Request Example
{
"merchantCode": "test",
"orderNumber": "1604384758793",
"sign": "a1b2c3d4e5f6..."
}Response Example
{
"code": "000",
"status": 1,
"amount": 40000
}import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* VPay /checkPayment example (pure JDK, no third-party dependency).
*
* 1. PRIVATE_KEY_PATH is your merchant RSA private key (PKCS#8);
* PUBLIC_KEY_PATH is your merchant RSA public key (X.509) for self-verification.
* 2. sign = the merchant RSA private key does a PKCS#1 v1.5 type1 raw signing on src
* (signature padding block 0x00 0x01 FF..FF 0x00 + src, then c = block^d mod n)
* and Base64 encodes it. The platform verifies it by recomputing with the merchant
* RSA public key (type1) and comparing the recovered bytes with src.
* NOT SHA256withRSA (do not use Signature).
*/
public class CheckPaymentDemo {
static final String BASE_URL = "https://api.vpayid.com/vpayid-api";
static final String PRIVATE_KEY_PATH = "YOUR_PRIVATE_KEY.pem"; // your PKCS#8 private key
static final String PUBLIC_KEY_PATH = "YOUR_PUBLIC_KEY.pem"; // your X.509 public key
static final String MERCHANT_CODE = "YOUR_MERCHANT_CODE";
public static void main(String[] args) throws Exception {
String orderNumber = "PAY_" + System.currentTimeMillis();
// src = merchantCode + orderNumber
String src = MERCHANT_CODE + orderNumber;
PrivateKey priv = loadPrivateKey(PRIVATE_KEY_PATH);
String sign = rsaPrivateEncryptBase64(src, priv);
boolean ok = rsaPublicDecryptVerify(src, sign, loadPublicKey(PUBLIC_KEY_PATH)); // self-check like the platform
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("merchantCode", MERCHANT_CODE);
payload.put("orderNumber", orderNumber);
payload.put("signType", "RSA");
payload.put("sign", sign);
String body = toJson(payload);
System.out.println("src = " + src);
System.out.println("selfVerify = " + ok);
System.out.println("request = " + body);
// optional: POST BASE_URL + "/checkPayment" with body
}
/**
* PKCS#1 v1.5 type1 raw signing of src with the merchant private key
* -> Base64 (the required sign).
*/
static String rsaPrivateEncryptBase64(String src, PrivateKey privateKey) {
RSAPrivateKey rsaPriv = (RSAPrivateKey) privateKey;
int k = rsaPriv.getModulus().bitLength() / 8;
byte[] data = src.getBytes(StandardCharsets.UTF_8);
if (data.length > k - 11) throw new IllegalArgumentException("src is too long");
byte[] block = new byte[k];
block[0] = 0x00; block[1] = 0x01; // type1: 00 01 FF..FF 00 src
java.util.Arrays.fill(block, 2, k - data.length - 1, (byte) 0xFF);
block[k - data.length - 1] = 0x00;
System.arraycopy(data, 0, block, k - data.length, data.length);
BigInteger m = new BigInteger(1, block);
BigInteger c = m.modPow(rsaPriv.getPrivateExponent(), rsaPriv.getModulus());
byte[] sig = c.toByteArray();
if (sig.length == k + 1 && sig[0] == 0) sig = java.util.Arrays.copyOfRange(sig, 1, k + 1); // strip BigInteger sign byte
if (sig.length > k) throw new IllegalStateException("signature is too long");
byte[] out = new byte[k];
System.arraycopy(sig, 0, out, k - sig.length, sig.length);
return Base64.getEncoder().encodeToString(out);
}
/**
* Self-check like the platform: recompute with the public key (type1),
* src is recovered from 0x00 0x01 FF..FF 0x00 src when valid.
*/
static boolean rsaPublicDecryptVerify(String src, String sign, PublicKey publicKey) {
try {
RSAPublicKey rsaPub = (RSAPublicKey) publicKey;
int k = rsaPub.getModulus().bitLength() / 8;
byte[] sig = Base64.getDecoder().decode(sign);
BigInteger c = new BigInteger(1, sig);
BigInteger m = c.modPow(rsaPub.getPublicExponent(), rsaPub.getModulus());
byte[] pad = new byte[k];
byte[] mb = m.toByteArray();
System.arraycopy(mb, 0, pad, k - mb.length, mb.length);
if (pad[0] != 0x00 || pad[1] != 0x01) return false; // needs 00 01 FF..00 data
int sep = -1;
for (int i = 2; i < k; i++) {
if (pad[i] == 0x00) { sep = i; break; }
}
if (sep < 0) return false;
return src.equals(new String(pad, sep + 1, k - sep - 1, StandardCharsets.UTF_8));
} catch (Exception e) {
return false;
}
}
// load PKCS#8 private key from a PEM file
static PrivateKey loadPrivateKey(String pemPath) throws Exception {
String pem = new String(Files.readAllBytes(Paths.get(pemPath)), StandardCharsets.UTF_8)
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.getMimeDecoder().decode(pem)));
}
// load X.509 public key from a PEM file (for self-verification)
static PublicKey loadPublicKey(String pemPath) throws Exception {
String pem = new String(Files.readAllBytes(Paths.get(pemPath)), StandardCharsets.UTF_8)
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(new X509EncodedKeySpec(Base64.getMimeDecoder().decode(pem)));
}
// minimal JSON builder (or use any JSON library)
static String toJson(Map<String, Object> m) {
StringBuilder sb = new StringBuilder("{");
boolean first = true;
for (Map.Entry<String, Object> e : m.entrySet()) {
if (!first) sb.append(",");
first = false;
sb.append("\"").append(e.getKey()).append("\":");
Object v = e.getValue();
if (v instanceof Number) {
sb.append(v);
} else {
sb.append("\"").append(v).append("\"");
}
}
return sb.append("}").toString();
}
}Disbursement API
Request Parameters
| Parameter Name | Description | Type | Required | Example |
|---|---|---|---|---|
| merchantCode | Merchant Code | String | Y | test |
| orderNumber | Order Number | String | Y | 1604384758793 |
| amount | Amount | Integer | Y | 40000 |
| bankAccount | Bank Account | String | Y | 100100100 |
| bankCode | Bank Code | String | Y | PERMATA |
| phone | Phone | String | Y | 08123456789 |
| String | Y | test@test.com | ||
| userName | Recipient Name | String | Y | John Doe |
| channelCode | Channel Code | String | Y | ALL |
| returnUrl | Return URL | String | N | http://test/return |
| callbackUrl | Callback URL | String | Y | http://test/callback |
| signType | Signature Type | String | Y | RSA |
| sign | Signature | String | Y | RSA(amount + bankAccount + bankCode + callbackUrl + channelCode + merchantCode + orderNumber) |
Signature Rule
RSA(amount + bankAccount + bankCode + callbackUrl + channelCode + merchantCode + orderNumber)
PS
channelCode available: ALL for all channels, ID IDN US ZH for different countries, use ALL if not specified
Synchronous response: "code": "000" means success, any other value means the submission failed
If the request times out, you must use the query API to verify the order status
The final order status is subject to the query result or the "status" field of the asynchronous callback
Request Example
{
"merchantCode": "test",
"orderNumber": "1604384758793",
"amount": 50000,
"bankAccount": "1234567890",
"bankCode": "BCA",
"phone": "08123456789",
"email": "test@test.com",
"userName": "John Doe",
"channelCode": "ALL",
"returnUrl": "http://test/return",
"callbackUrl": "http://test/callback",
"sign": "a1b2c3d4e5f6..."
}Response Example
{
"code": "000",
"message": "success",
"amount": 40000,
"merchantCode": "test"
}import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* VPay /disbursement example (pure JDK, no third-party dependency).
*
* 1. PRIVATE_KEY_PATH is your merchant RSA private key (PKCS#8);
* PUBLIC_KEY_PATH is your merchant RSA public key (X.509) for self-verification.
* 2. sign = the merchant RSA private key does a PKCS#1 v1.5 type1 raw signing on src
* (signature padding block 0x00 0x01 FF..FF 0x00 + src, then c = block^d mod n)
* and Base64 encodes it. The platform verifies it by recomputing with the merchant
* RSA public key (type1) and comparing the recovered bytes with src.
* NOT SHA256withRSA (do not use Signature).
*/
public class DisbursementDemo {
static final String BASE_URL = "https://api.vpayid.com/vpayid-api";
static final String PRIVATE_KEY_PATH = "YOUR_PRIVATE_KEY.pem"; // your PKCS#8 private key
static final String PUBLIC_KEY_PATH = "YOUR_PUBLIC_KEY.pem"; // your X.509 public key
static final String MERCHANT_CODE = "YOUR_MERCHANT_CODE";
static final String CALLBACK_URL = "http://localhost/callback";
public static void main(String[] args) throws Exception {
String orderNumber = "OUT_" + System.currentTimeMillis();
int amount = 1000000;
String bankAccount = "7550390595";
String bankCode = "BCA";
// src = amount + bankAccount + bankCode + callbackUrl + channelCode + merchantCode + orderNumber
String src = amount + bankAccount + bankCode + CALLBACK_URL + "ALL" + MERCHANT_CODE + orderNumber;
PrivateKey priv = loadPrivateKey(PRIVATE_KEY_PATH);
String sign = rsaPrivateEncryptBase64(src, priv);
boolean ok = rsaPublicDecryptVerify(src, sign, loadPublicKey(PUBLIC_KEY_PATH)); // self-check like the platform
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("merchantCode", MERCHANT_CODE);
payload.put("orderNumber", orderNumber);
payload.put("amount", amount);
payload.put("bankAccount", bankAccount);
payload.put("bankCode", bankCode);
payload.put("phone", "08000000000");
payload.put("email", "noreply@test.com");
payload.put("userName", "Fandi");
payload.put("channelCode", "ALL");
payload.put("callbackUrl", CALLBACK_URL);
payload.put("signType", "RSA");
payload.put("sign", sign);
String body = toJson(payload);
System.out.println("src = " + src);
System.out.println("selfVerify = " + ok);
System.out.println("request = " + body);
// optional: POST BASE_URL + "/disbursement" with body
}
/**
* PKCS#1 v1.5 type1 raw signing of src with the merchant private key
* -> Base64 (the required sign).
*/
static String rsaPrivateEncryptBase64(String src, PrivateKey privateKey) {
RSAPrivateKey rsaPriv = (RSAPrivateKey) privateKey;
int k = rsaPriv.getModulus().bitLength() / 8;
byte[] data = src.getBytes(StandardCharsets.UTF_8);
if (data.length > k - 11) throw new IllegalArgumentException("src is too long");
byte[] block = new byte[k];
block[0] = 0x00; block[1] = 0x01; // type1: 00 01 FF..FF 00 src
java.util.Arrays.fill(block, 2, k - data.length - 1, (byte) 0xFF);
block[k - data.length - 1] = 0x00;
System.arraycopy(data, 0, block, k - data.length, data.length);
BigInteger m = new BigInteger(1, block);
BigInteger c = m.modPow(rsaPriv.getPrivateExponent(), rsaPriv.getModulus());
byte[] sig = c.toByteArray();
if (sig.length == k + 1 && sig[0] == 0) sig = java.util.Arrays.copyOfRange(sig, 1, k + 1); // strip BigInteger sign byte
if (sig.length > k) throw new IllegalStateException("signature is too long");
byte[] out = new byte[k];
System.arraycopy(sig, 0, out, k - sig.length, sig.length);
return Base64.getEncoder().encodeToString(out);
}
/**
* Self-check like the platform: recompute with the public key (type1),
* src is recovered from 0x00 0x01 FF..FF 0x00 src when valid.
*/
static boolean rsaPublicDecryptVerify(String src, String sign, PublicKey publicKey) {
try {
RSAPublicKey rsaPub = (RSAPublicKey) publicKey;
int k = rsaPub.getModulus().bitLength() / 8;
byte[] sig = Base64.getDecoder().decode(sign);
BigInteger c = new BigInteger(1, sig);
BigInteger m = c.modPow(rsaPub.getPublicExponent(), rsaPub.getModulus());
byte[] pad = new byte[k];
byte[] mb = m.toByteArray();
System.arraycopy(mb, 0, pad, k - mb.length, mb.length);
if (pad[0] != 0x00 || pad[1] != 0x01) return false; // needs 00 01 FF..00 data
int sep = -1;
for (int i = 2; i < k; i++) {
if (pad[i] == 0x00) { sep = i; break; }
}
if (sep < 0) return false;
return src.equals(new String(pad, sep + 1, k - sep - 1, StandardCharsets.UTF_8));
} catch (Exception e) {
return false;
}
}
// load PKCS#8 private key from a PEM file
static PrivateKey loadPrivateKey(String pemPath) throws Exception {
String pem = new String(Files.readAllBytes(Paths.get(pemPath)), StandardCharsets.UTF_8)
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.getMimeDecoder().decode(pem)));
}
// load X.509 public key from a PEM file (for self-verification)
static PublicKey loadPublicKey(String pemPath) throws Exception {
String pem = new String(Files.readAllBytes(Paths.get(pemPath)), StandardCharsets.UTF_8)
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(new X509EncodedKeySpec(Base64.getMimeDecoder().decode(pem)));
}
// minimal JSON builder (or use any JSON library)
static String toJson(Map<String, Object> m) {
StringBuilder sb = new StringBuilder("{");
boolean first = true;
for (Map.Entry<String, Object> e : m.entrySet()) {
if (!first) sb.append(",");
first = false;
sb.append("\"").append(e.getKey()).append("\":");
Object v = e.getValue();
if (v instanceof Number) {
sb.append(v);
} else {
sb.append("\"").append(v).append("\"");
}
}
return sb.append("}").toString();
}
}Disbursement Query
Request Parameters
| Parameter Name | Description | Type | Required | Example |
|---|---|---|---|---|
| merchantCode | Merchant Code | String | Y | test |
| orderNumber | Order Number | String | Y | 1604384758793 |
| signType | Signature Type | String | Y | RSA |
| sign | Signature | String | Y | RSA(...) |
Signature Rule
RSA(merchantCode + orderNumber)
Request Example
{
"merchantCode": "test",
"orderNumber": "1604384758793",
"sign": "a1b2c3d4e5f6..."
}Response Example
{
"code": "000",
"message": "complete",
"userOrderNumber": "1604384968942",
"orderNumber": "test16043849702603",
"amount": 40000,
"merchantCode": "test",
"phone": "00",
"email": "00",
"userName": "John Doe",
"bankCode": "PERMATA",
"bankAccount": "100100100",
"channelCode": "M1",
"status": 1,
"createTime": "2020-11-03 13:29:30",
"payTime": "2020-11-03 13:39:30"
}import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* VPay /checkDisbursement example (pure JDK, no third-party dependency).
*
* 1. PRIVATE_KEY_PATH is your merchant RSA private key (PKCS#8);
* PUBLIC_KEY_PATH is your merchant RSA public key (X.509) for self-verification.
* 2. sign = the merchant RSA private key does a PKCS#1 v1.5 type1 raw signing on src
* (signature padding block 0x00 0x01 FF..FF 0x00 + src, then c = block^d mod n)
* and Base64 encodes it. The platform verifies it by recomputing with the merchant
* RSA public key (type1) and comparing the recovered bytes with src.
* NOT SHA256withRSA (do not use Signature).
*/
public class CheckDisbursementDemo {
static final String BASE_URL = "https://api.vpayid.com/vpayid-api";
static final String PRIVATE_KEY_PATH = "YOUR_PRIVATE_KEY.pem"; // your PKCS#8 private key
static final String PUBLIC_KEY_PATH = "YOUR_PUBLIC_KEY.pem"; // your X.509 public key
static final String MERCHANT_CODE = "YOUR_MERCHANT_CODE";
public static void main(String[] args) throws Exception {
String orderNumber = "OUT_" + System.currentTimeMillis();
// src = merchantCode + orderNumber
String src = MERCHANT_CODE + orderNumber;
PrivateKey priv = loadPrivateKey(PRIVATE_KEY_PATH);
String sign = rsaPrivateEncryptBase64(src, priv);
boolean ok = rsaPublicDecryptVerify(src, sign, loadPublicKey(PUBLIC_KEY_PATH)); // self-check like the platform
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("merchantCode", MERCHANT_CODE);
payload.put("orderNumber", orderNumber);
payload.put("signType", "RSA");
payload.put("sign", sign);
String body = toJson(payload);
System.out.println("src = " + src);
System.out.println("selfVerify = " + ok);
System.out.println("request = " + body);
// optional: POST BASE_URL + "/checkDisbursement" with body
}
/**
* PKCS#1 v1.5 type1 raw signing of src with the merchant private key
* -> Base64 (the required sign).
*/
static String rsaPrivateEncryptBase64(String src, PrivateKey privateKey) {
RSAPrivateKey rsaPriv = (RSAPrivateKey) privateKey;
int k = rsaPriv.getModulus().bitLength() / 8;
byte[] data = src.getBytes(StandardCharsets.UTF_8);
if (data.length > k - 11) throw new IllegalArgumentException("src is too long");
byte[] block = new byte[k];
block[0] = 0x00; block[1] = 0x01; // type1: 00 01 FF..FF 00 src
java.util.Arrays.fill(block, 2, k - data.length - 1, (byte) 0xFF);
block[k - data.length - 1] = 0x00;
System.arraycopy(data, 0, block, k - data.length, data.length);
BigInteger m = new BigInteger(1, block);
BigInteger c = m.modPow(rsaPriv.getPrivateExponent(), rsaPriv.getModulus());
byte[] sig = c.toByteArray();
if (sig.length == k + 1 && sig[0] == 0) sig = java.util.Arrays.copyOfRange(sig, 1, k + 1); // strip BigInteger sign byte
if (sig.length > k) throw new IllegalStateException("signature is too long");
byte[] out = new byte[k];
System.arraycopy(sig, 0, out, k - sig.length, sig.length);
return Base64.getEncoder().encodeToString(out);
}
/**
* Self-check like the platform: recompute with the public key (type1),
* src is recovered from 0x00 0x01 FF..FF 0x00 src when valid.
*/
static boolean rsaPublicDecryptVerify(String src, String sign, PublicKey publicKey) {
try {
RSAPublicKey rsaPub = (RSAPublicKey) publicKey;
int k = rsaPub.getModulus().bitLength() / 8;
byte[] sig = Base64.getDecoder().decode(sign);
BigInteger c = new BigInteger(1, sig);
BigInteger m = c.modPow(rsaPub.getPublicExponent(), rsaPub.getModulus());
byte[] pad = new byte[k];
byte[] mb = m.toByteArray();
System.arraycopy(mb, 0, pad, k - mb.length, mb.length);
if (pad[0] != 0x00 || pad[1] != 0x01) return false; // needs 00 01 FF..00 data
int sep = -1;
for (int i = 2; i < k; i++) {
if (pad[i] == 0x00) { sep = i; break; }
}
if (sep < 0) return false;
return src.equals(new String(pad, sep + 1, k - sep - 1, StandardCharsets.UTF_8));
} catch (Exception e) {
return false;
}
}
// load PKCS#8 private key from a PEM file
static PrivateKey loadPrivateKey(String pemPath) throws Exception {
String pem = new String(Files.readAllBytes(Paths.get(pemPath)), StandardCharsets.UTF_8)
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.getMimeDecoder().decode(pem)));
}
// load X.509 public key from a PEM file (for self-verification)
static PublicKey loadPublicKey(String pemPath) throws Exception {
String pem = new String(Files.readAllBytes(Paths.get(pemPath)), StandardCharsets.UTF_8)
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(new X509EncodedKeySpec(Base64.getMimeDecoder().decode(pem)));
}
// minimal JSON builder (or use any JSON library)
static String toJson(Map<String, Object> m) {
StringBuilder sb = new StringBuilder("{");
boolean first = true;
for (Map.Entry<String, Object> e : m.entrySet()) {
if (!first) sb.append(",");
first = false;
sb.append("\"").append(e.getKey()).append("\":");
Object v = e.getValue();
if (v instanceof Number) {
sb.append(v);
} else {
sb.append("\"").append(v).append("\"");
}
}
return sb.append("}").toString();
}
}Available Channels
Request Parameters
| Parameter Name | Description | Type | Required | Example |
|---|---|---|---|---|
| merchantCode | Merchant Code | String | Y | test |
| timestamp | Timestamp | String | Y | 1604384758793 |
| signType | Signature Type | String | Y | RSA |
| sign | Signature | String | Y | RSA(merchantCode + timestamp) |
Response Parameters
| Parameter Name | Description | Type | Example |
|---|---|---|---|
| code | Response Code | String | 000 |
| message | Response Message | String | query success |
| channelCodeList | Channel List | LIST | |
| - channelCode | Channel Code | String | BT |
| - channelType | Channel Type | Integer | 0 Payment 1 Disbursement |
| - channelRate | Channel Rate | Double | 7.0 |
| - channelFee | Channel Fee | Integer | 200 |
Signature Rule
RSA(merchantCode + timestamp)
Request Example
{
"merchantCode": "test",
"timestamp": 1604384758793,
"sign": "a1b2c3d4e5f6..."
}Response Example
{
"code": "000",
"channels": [
{
"channelCode": "M1",
"channelName": "QRIS",
"minAmount": 1000,
"maxAmount": 10000000
}
]
}Balance Query
Request Parameters
| Parameter Name | Description | Type | Required | Example |
|---|---|---|---|---|
| merchantCode | Merchant Code | String | Y | test |
| timestamp | Timestamp | String | Y | 1604384758793 |
| signType | Signature Type | String | Y | RSA |
| sign | Signature | String | Y | RSA(merchantCode + timestamp) |
Response Parameters
| Parameter Name | Description | Type | Example |
|---|---|---|---|
| code | Response Code | String | 000 |
| message | Response Message | String | balance query success |
| merchantCode | Merchant Code | String | 1604384968942 |
| merchantBlance | Merchant Balance | Integer | 540000 |
| merchantFrozenAmount | Frozen Amount | Integer | 40000 |
| canUseAmount | Available Amount | Integer | 500000 |
| settlementList | Settlement List | LIST | |
| - settlementDate | Settlement Date | String | 2020-12-28 |
| - settlementAmount | Settlement Amount | Integer | 44500 |
Signature Rule
RSA(merchantCode + timestamp)
Request Example
{
"merchantCode": "test",
"timestamp": 1604384758793,
"sign": "a1b2c3d4e5f6..."
}Response Example
{
"code": "000",
"merchantBlance": 540000,
"merchantFrozenAmount": 40000,
"canUseAmount": 500000
}Async Callback
After payment is completed, the system will send a POST request to your configured callbackUrl
Callback Parameters
| Parameter Name | Description | Type | Example |
|---|---|---|---|
| code | Response Code | String | 000 |
| message | Response Message | String | complete |
| orderNumber | Merchant Order Number | String | 1604384758793 |
| amount | Payment Amount | Integer | 40000 |
| merchantCode | Merchant Code | String | test |
| phone | Phone (hidden as 00) | String | 00 |
| Email (hidden as 00) | String | 00 | |
| userName | User Name | String | John Doe |
| channelCode | Channel Code | String | M1 |
| status | Status: 0 Pending, 1 Success | Integer | 1 |
| createTime | Order Create Time (timestamp) | Long | 1617695193459 |
| payTime | Payment Time (timestamp, returned on success) | Long | 1617695193459 |
| rsaSign | RSA signature = RSA(amount + channelCode + email + merchantCode + orderNumber + phone + userName) | String | a1b2c3d4e5f6 |
Verification: Concatenate the callback fields above in the order shown to rebuild the plaintext string src, then decrypt rsaSign with the platform RSA public key using PKCS#1 v1.5 (type1) (recovering 0x00 0x01 FF..FF 0x00 + src) and compare it byte-by-byte with src. The callback is valid only when they match (same algorithm as request signing, not SHA256withRSA).
Callback Example
{
"code": "000",
"message": "complete",
"orderNumber": "1604384758793",
"amount": 40000,
"merchantCode": "test",
"phone": "00",
"email": "00",
"userName": "John Doe",
"channelCode": "M1",
"status": 1,
"createTime": 1617695193459,
"payTime": 1617695193459,
"rsaSign": "a1b2c3d4e5f6"
}Response: Please return HTTP 200 status code to confirm receipt of callback
After disbursement is completed, the system will send a POST request to your configured callbackUrl
Callback Parameters
| Parameter Name | Description | Type | Example |
|---|---|---|---|
| code | Response Code | String | 000 |
| message | Response Message | String | complete |
| orderNumber | Merchant Order Number | String | 1604384758793 |
| amount | Disbursement Amount | Integer | 40000 |
| merchantCode | Merchant Code | String | test |
| phone | Phone (hidden as 00) | String | 00 |
| Email (hidden as 00) | String | 00 | |
| userName | Recipient Name | String | John Doe |
| bankCode | Bank Code | String | PERMATA |
| bankAccount | Bank Account | String | 100100100 |
| channelCode | Channel Code | String | M1 |
| status | Status: 1 Success, 2 Failed | Integer | 1 |
| createTime | Order Create Time (timestamp) | Long | 1617695193459 |
| payTime | Payment Time (timestamp, returned on success) | Long | 1617695193459 |
| rsaSign | RSA signature = RSA(amount + bankAccount + bankCode + channelCode + email + merchantCode + orderNumber + phone + userName) | String | a1b2c3d4e5f6 |
Verification: Concatenate the callback fields above in the order shown to rebuild the plaintext string src, then decrypt rsaSign with the platform RSA public key using PKCS#1 v1.5 (type1) (recovering 0x00 0x01 FF..FF 0x00 + src) and compare it byte-by-byte with src. The callback is valid only when they match (same algorithm as request signing, not SHA256withRSA).
Callback Example
{
"code": "000",
"message": "complete",
"orderNumber": "1604384758793",
"amount": 40000,
"merchantCode": "test",
"phone": "00",
"email": "00",
"userName": "John Doe",
"bankCode": "PERMATA",
"bankAccount": "100100100",
"channelCode": "M1",
"status": 1,
"createTime": 1617695193459,
"payTime": 1617695193459,
"rsaSign": "a1b2c3d4e5f6"
}Response: Please return HTTP 200 status code to confirm receipt of callback
Payment Channel Code Reference
| channel_code | channel_name | Type |
|---|---|---|
| QRIS | QRIS | Collection |
| VC | Credit Card (Visa / Master) | Collection |
| BK | BCA KlikPay | Collection |
| BCA | BCA Virtual Account | Collection |
| BBC | BCA BC | Collection |
| M1 | Mandiri Virtual Account | Collection |
| ME | Mandiri eCash | Collection |
| MP | Mandiri PC | Collection |
| BT | Permata Bank Virtual Account | Collection |
| PN | Permata Net | Collection |
| A1 | ATM Bersama | Collection |
| B1 | CIMB Niaga Virtual Account | Collection |
| I1 | BNI Virtual Account | Collection |
| VA | Maybank Virtual Account | Collection |
| M2U | Maybank2U | Collection |
| FT | Ritel | Collection |
| OV | OVO | Collection |
| LA | LINKAJA | Collection |
| DANA | DANA | Collection |
| SP | Shopee Pay | Collection |
| SA | Shopee Pay Apps | Collection |
| AG | Bank Artha Graha | Collection |
| S1 | Sahabat Sampoerna | Collection |
| NI | Bank Negara Indonesia | Collection |
| RI | Bank Rakyat Indonesia | Collection |
| RIE | BRI ePay | Collection |
| IN | INDOMARET | Collection |
| AL | ALFAMART | Collection |
| DOB | Danamon Online Banking | Collection |
| DVA | Danamon Virtual Account | Collection |
| AI | Alto iDebit | Collection |
| JIB | Jenius IB | Collection |
| JA | JAKONE | Collection |
| OC | Octo Clicks | Collection |
| SK | Sakuku | Collection |
| ALF | Alfagroup | Collection |
| KR | Kredivo | Collection |
| BL | BTNONLINE | Collection |
| SVA | Sinarmas Virtual Account | Collection |
| CASH | Cashier Payment | Collection |
| ALL | Indonesia Disbursement All Channel | Disbursement |
| channel_code | channel_name | Type |
|---|---|---|
| PK_JAZZ | JAZZ Wallet | Collection |
| PK_EASY | EASY Wallet | Collection |
| PK_ALL | Pakistan Disbursement All Channel | Disbursement |
| channel_code | channel_name | Type |
|---|---|---|
| BR_BRL | QR Code Payment | Collection |
| BR_ALL | PIX Disbursement Channel | Disbursement |
| channel_code | channel_name | Type |
|---|---|---|
| TH_QR | Thailand QR Code Payment | Collection |
| TH_ALL | Thailand Disbursement Channel | Disbursement |
Bank Code Reference
🇮🇩 Indonesia
| bank_code | bank_name | activate |
|---|---|---|
| ACEH | BPD ACEH | ✓ |
| AGRIS | Bank Agris | |
| ARTOS | Bank Artos Indonesia | |
| BANK_NOBU | Bank Nobu | ✓ |
| BANK_SU | BPD SULAWESI TENGAH | ✓ |
| BCA | Bank BCA | ✓ |
| BCASYR | BANK BCA SYARIAH | ✓ |
| BJB | Bank BJB | ✓ |
| BJB_SYR | Bank BJB Syariah | ✓ |
| BNI | BANK BNI | ✓ |
| BNI_SYR | Bank BNI Syariah | ✓ |
| BRI | Bank BRI | ✓ |
| BRI_SYR | BANK BRI SYARIAH | ✓ |
| BTN | Bank BTN | ✓ |
| BTPN | Bank BTPN | ✓ |
| BUKOPIN | Bank Bukopin | ✓ |
| BUKOPIN_SYR | Bank Syariah Bukopin | ✓ |
| BUMI_ARTA | Bank Bumi Arta | ✓ |
| CHINATRUST | Bank Chinatrust Indonesia | ✓ |
| CIMB | BANK CIMB NIAGA | ✓ |
| COMMONWEALTH | Bank Commonwealth | ✓ |
| DANAMON | Bank Danamon | ✓ |
| DBS | Bank DBS Indonesia | ✓ |
| DINAR_INDONESIA | Bank Dinar Indonesia | |
| DKI | Bank DKI | ✓ |
| GANESHA | Bank Ganesha | ✓ |
| HARDA_INTERNASIONAL | Bank Harda Internasional | |
| ICBC | Bank ICBC Indonesia | ✓ |
| INDEX_SELINDO | Bank Index Selindo | ✓ |
| INDIA | Bank of India Indonesia | ✓ |
| JASA_JAKARTA | Bank Jasa Jakarta | ✓ |
| KESEJAHTERAAN_EKONOM | Bank Kesejahteraan Ekonomi | |
| MANDIRI | Bank Mandiri | ✓ |
| MANDIRI_SYR | Bank Syariah Mandiri | ✓ |
| MASPION | Bank Maspion Indonesia | ✓ |
| MAYBANK | BANK MAYBANK INDONESIA | ✓ |
| MAYORA | Bank Mayora | ✓ |
| MEGA | Bank Mega | ✓ |
| MUAMALAT | Bank Muamalat Indonesia | ✓ |
| NATIONALNOB | BANK NATIONALNOBU | ✓ |
| OCBC | Bank OCBC NISP | ✓ |
| PANIN | Bank Panin | ✓ |
| PERMATA | Bank Permata | ✓ |
| SAHABAT_SAMPOERNA | Bank Sahabat Sampoerna | ✓ |
| SINARMAS | Bank Sinarmas | ✓ |
| STANDARD_CHARTERED | Standard Chartered Bank | ✓ |
| SULUT | BANK SULUT | ✓ |
| UOB | Bank UOB Indonesia | ✓ |
| VICTORIA_SYR | Bank Victoria Syariah | ✓ |
| CITIBANK | Citibank | ✓ |
| ARTHA | Bank Artha Graha Internasional | ✓ |
| TOKYO | Bank of Tokyo Mitsubishi UFJ | ✓ |
| CAPITAL | Bank Capital Indonesia | ✓ |
| ANZ | ANZ Indonesia | ✓ |
| BOC | Bank of China (Hong Kong) Limited | ✓ |
| HSBC | HSBC Indonesia | ✓ |
| RABOBANK | Rabobank International Indonesia | ✓ |
| MAYAPADA | Bank Mayapada | ✓ |
| DAERAH_ISTIMEWA | BPD DIY | ✓ |
| JAWATIMUR | BPD JAWA TIMUR/SYARIAH | ✓ |
| JAMBI | Bank Jambi | ✓ |
| SUMUT | Bank Sumut | ✓ |
| SUMATERA_BARAT | BPD SUMATERA BARAT/NAGARI SYARIAH | ✓ |
| RIAU_DAN_KEPRI | Bank Riau Kepri | ✓ |
| SUMSEL_DAN_BABEL | Bank Sumsel Babel | ✓ |
| LAMPUNG | Bank Lampung | ✓ |
| KALIMANTAN_SELATAN | BPD KALIMANTAN SELATAN | ✓ |
| KALIMANTAN_TIMUR | BPD KALIMANTAN TIMUR DAN KALIMANTAN UTARA | ✓ |
| KALIMANTAN_TENGAH | BPD KALIMANTAN TENGAH | ✓ |
| SULSELBAR | Bank Sulselbar | ✓ |
| NUSA_TENGGARA_BARAT | BPD NUSA TENGGARA BARAT SYARIAH | ✓ |
| BALI | BPD Bali | ✓ |
| NUSA_TENGGARA_TIMUR | BPD NUSA TENGGARA TIMUR | ✓ |
| MALUKU | Bank Maluku | ✓ |
| PAPUA | Bank Papua | ✓ |
| BENGKULU | Bank Bengkulu | ✓ |
| SULAWESI | Bank Sulteng | ✓ |
| NUSANTARAPARAHYANGA | Bank Nusantara Parahyangan | |
| MESTIKADHARMA | Bank Mestika Dharma | ✓ |
| QNBKESAWAN | QNB Indonesia | ✓ |
| WOORI | Bank Woori Saudara | ✓ |
| BTPNSYR | Bank BTPN Syariah | ✓ |
| HANA | KEB Hana Bank Indonesia | ✓ |
| MNC_INTERNASIONAL | Bank MNC Internasional | ✓ |
| AGRONIAGA | AGRONIAGA | ✓ |
| SBIINDONESIA | SBI Indonesia | ✓ |
| ROYAL | Bank Royal Indonesia | ✓ |
| MEGASYR | Bank Mega Syariah | ✓ |
| INAPERDANA | Bank Ina Perdana | ✓ |
| VICTORIAINTERNASION | Bank Victoria International | ✓ |
| BANTEN | BPD Banten | ✓ |
| MUTIARA | Bank Mutiara | |
| PANINSYR | Panin Dubai Syariah | ✓ |
| ANTARDAERAH | Bank Antardaerah | |
| CCB | Bank China Construction Bank Indonesia | ✓ |
| CNB | Bank CNB (Centratama Nasional Bank) | ✓ |
| EKA | BPR EKA (Bank Eka) | ✓ |
| MANTAP | BANK MANTAP (Mandiri Taspen) | ✓ |
| MAS | Bank Multi Arta Sentosa (Bank MAS) | ✓ |
| PRIMA | Bank Prima Master | ✓ |
| SHINHAN | Bank Shinhan Indonesia | ✓ |
| YUDHA_BAKTI | Bank Yudha Bakti | |
| GOPAY | GoPay | ✓ |
| OVO | OVO | ✓ |
| SHOPEEPAY | ShopeePay | ✓ |
| DANA | Dana | ✓ |
| LINKAJA | LinkAja | |
| KALIMANTANBARAT | BPD Kalimantan Barat Bank Kalbar | ✓ |
| JAWATENGAH | BPD JAWA TENGAH | ✓ |
| ALTO | ALTO | |
| BSI | BSI (Bank Syariah Indonesia) | ✓ |
| AMAR | BANK AMAR INDONESIA | ✓ |
| CTBC | BANK CTBC INDONESIA | ✓ |
| IBK | BANK IBK INDONESIA | ✓ |
| BRA_CPF | BRA CPF | ✓ |
| BNEO | BANK NEO COMMERCE | |
| BSEAI | BANK SEABANK INDONESIA | ✓ |
| BJAGO | BANK JAGO | |
| ALADIN | BANK ALADIN SYARIAH | |
| BAC | BANK API CHANNEL |
🇵🇰 Pakistan
| bank_code | bank_name |
|---|---|
| PK_EASY | EASY Wallet |
| PK_JAZZ | JAZZ Wallet |
🇧🇷 Brazil
| bank_code | bank_name |
|---|---|
| BR_CPF | CPF Number |
| BR_PHONE | Phone Number |
| BR_EMAIL |
🇹🇭 Thailand
| bank_code | bank_name |
|---|---|
| TH_BAAC | BANK FOR AGRICULTURE AND AGRICULTURAL COOPERATIVES |
| TH_BAY | BANK OF AYUDHYA PUBLIC COMPANY LIMITED |
| TH_BBL | BANGKOK BANK PUBLIC COMPANY LTD. |
| TH_CIMBT | CIMB THAI BANK PUPBLIC COMPANY LTD. |
| TH_CITI | CITIBANK, N.A. |
| TH_GHB | THE GOVERNMENT HOUSING BANK |
| TH_GSB | THE GOVERNMENT SAVINGS BANK |
| TH_ISBT | ISLAMIC BANK OF THAILAND |
| TH_ICBCT | INDUSTRIAL AND COMMERCIAL BANK OF CHINA (THAI) PUBLIC COMPANY LIMITED |
| TH_KBANK | KASIKORNBANK PUBLIC COMPANY LTD. |
| TH_KK | KIATNAKIN BANK PUBLIC COMPANY LIMITED |
| TH_KTB | KRUNG THAI BANK PUBLIC COMPANY LTD. |
| TH_LHBANK | LAND AND HOUSES BANK PUBLIC COMPANY LIMITED |
| TH_MIZUHO | MIZUHO CORPORATE BANK, LTD. |
| TH_SCB | SIAM COMMERCIAL BANK PUBLIC COMPANY LTD |
| TH_SCBT | STANDARD CHARTERED BANK (THAI) PUBLIC COMPANY LIMITED |
| TH_SMBC | SUMITOMO MITSUI BANKING CORPORATION |
| TH_TBANK | THANACHART BANK PUBLIC COMPANY LTD. |
| TH_TCR | THE THAI CREDIT RETAIL BANK PUBLIC COMPANY LIMITED |
| TH_TISCO | TISCO BANK PUBLIC COMPANY LIMITED |
| TH_TMB | TMB BANK PUBLIC COMPANY LIMITED |
| TH_TTB | TMBTHANACHART BANK PUBLIC COMPANY LIMITED |
| TH_UOBT | UNITED OVERSEAS BANK (THAI) PUBLIC COMPANY LIMITED |
| TH_PROMPTPAY | PromptPay |