Payment Gateway API
Integrate deposits, payouts, and real-time webhooks into your platform with our simple REST API. Fast, secure, and reliable.
Authentication
All API requests must include the following headers for authentication.
| Header | Value | Description |
|---|---|---|
| X-API-Key | your_api_key |
Your API key from the Client Management page |
| X-API-Secret | your_api_secret |
Your API secret from the Client Management page |
| Content-Type | application/json |
Request body format |
Idempotency — Prevent Duplicate Transactions
Every Deposit/Payout create request must include the X-Idempotency-Key header to prevent duplicate transactions caused by retries or double-submits.
X-Idempotency-Key — Required header on create endpoints
- X-Idempotency-Key: use a UUID v4 — a string unique per transaction, up to 256 characters (generated by the client).
- 1 transaction = 1 key; never reuse a key for a different transaction.
- A retry must reuse the same key + identical body (every byte) → the server replays the original response without creating a duplicate.
- Keys live for 60 minutes; after expiry the request is treated as new.
Error Responses
| HTTP | Code | When |
|---|---|---|
| 400 | IDEMPOTENCY_KEY_REQUIRED |
X-Idempotency-Key header was not sent |
| 409 | IDEMPOTENCY_IN_PROGRESS |
The original request is still processing — wait, then retry with the same key |
| 422 | IDEMPOTENCY_BODY_MISMATCH |
Same key but a different body — a client bug; use a new key |
Deposit API
Create deposit requests to receive funds from customers via QR code or bank transfer.
Request Fields
| Field | Type | Description |
|---|---|---|
| accountNo Required | string | Customer bank account number (10-15 digits) |
| accName Required | string | Customer account name (2-100 characters) |
| bankCode Required | string | Bank code (e.g. KBANK, SCB, BBL) |
| amount Required | number | Amount in THB — integer baht only; any decimal portion is silently floored (e.g. 100.99 → 100). Range 1 - 2,000,000. |
| callbackUrl Required | string | HTTPS URL for status callbacks |
| refCode Optional | string | Client-side reference code (not included in signature, up to 100 chars) |
| signature Required | string | HMAC-SHA256 signature over the sorted query string (see Signature) |
| timestamp Required | number | Unix timestamp in milliseconds (must be within 5 minutes) |
Response Fields
| Field | Type | Description |
|---|---|---|
| txnId | string | Transaction ID (format: DEP{timestamp}{random}) |
| status | string |
Transaction status
pending
paid
expired
cancelled
|
| channel | string | Payment channel: QR (PromptPay) or TRANSFER (bank transfer) |
| amount | number | Amount the customer must pay |
| fee | number | Transaction fee (from MDR Profile) |
| feePercent | number | Fee percentage |
| netAmount | number | Net amount after fee (amount - fee) |
| expireDate | string | Expiration date/time (ISO 8601, UTC) |
| qrcode | string | PromptPay EMV payload for client-side QR rendering (only when channel=QR) |
| bankData | object | Bank account for transfer: bankName, bankCode, accountName, accountNumber (only when channel=TRANSFER) |
curl -X POST '__BASE_URL__/api/v1/deposit/create' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: your_api_key' \
-H 'X-API-Secret: your_api_secret' \
-H 'X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000' \
-d '{
"accountNo": "1234567890",
"accName": "John Doe",
"bankCode": "KBANK",
"amount": 1000,
"callbackUrl": "https://your-domain.com/callback",
"refCode": "ORDER-12345",
"signature": "abc123...",
"timestamp": 1709123456789
}'
{
"status": "success",
"message": "Create Success",
"data": {
"txnId": "DEP1709123456ABC123",
"status": "pending",
"channel": "QR",
"amount": 1000.00,
"fee": 15.00,
"feePercent": 1.5,
"netAmount": 985.00,
"expireDate": "2024-01-01T12:10:00Z",
"customerData": {
"accountNo": "1234567890",
"accName": "John Doe",
"bankCode": "KBANK"
},
"qrcode": "00020101021229370016A000000677010111011300105568164630530376454041000802TH5910COMPANY CO6304ABCD"
}
}
{
"status": "success",
"message": "Create Success",
"data": {
"txnId": "DEP1709123456ABC123",
"status": "pending",
"channel": "TRANSFER",
"amount": 1000.00,
"fee": 15.00,
"feePercent": 1.5,
"netAmount": 985.00,
"expireDate": "2024-01-01T12:10:00Z",
"customerData": {
"accountNo": "1234567890",
"accName": "John Doe",
"bankCode": "KBANK"
},
"bankData": {
"bankName": "KASIKORNBANK",
"bankCode": "KBANK",
"accountName": "Company ABC",
"accountNumber": "xxx-x-xxxxx-x"
}
}
}
Payout API
Create payout requests to transfer funds to customer bank accounts.
Request Fields
| Field | Type | Description |
|---|---|---|
| accountNo Required | string | Destination bank account number |
| accName Required | string | Destination account name (must match bank records) |
| bankCode Required | string | Destination bank code (e.g. KBANK, SCB, BBL) |
| amount Required | number | Amount in THB (must have sufficient balance). Range 1 - 2,000,000. |
| callbackUrl Required | string | HTTPS URL for status callbacks |
| refCode Optional | string | Client-side reference code (not included in signature, up to 100 chars) |
| signature Required | string | HMAC-SHA256 signature over the sorted query string (see Signature) |
| timestamp Required | number | Unix timestamp in milliseconds (must be within 5 minutes) |
Response Fields
| Field | Type | Description |
|---|---|---|
| txnId | string | Transaction ID (format: PAY{timestamp}{random}) |
| status | string |
Transaction status
pending
processing
completed
failed
cancelled
|
| amount | number | Payout amount |
| fee | number | Withdrawal fee (from MDR Profile) |
| feePercent | number | Fee percentage |
| customerData | object | Destination account details: accountNo, accName, bankCode |
curl -X POST '__BASE_URL__/api/v1/payout/create' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: your_api_key' \
-H 'X-API-Secret: your_api_secret' \
-H 'X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000' \
-d '{
"accountNo": "9876543210",
"accName": "Jane Doe",
"bankCode": "SCB",
"amount": 5000.00,
"callbackUrl": "https://your-domain.com/callback",
"refCode": "WD-98765",
"signature": "def456...",
"timestamp": 1709123456789
}'
{
"status": "success",
"message": "Create Success",
"data": {
"txnId": "PAY1709123456XYZ789",
"amount": 5000.00,
"fee": 75.00,
"feePercent": 1.5,
"status": "pending",
"customerData": {
"accountNo": "9876543210",
"accName": "Jane Doe",
"bankCode": "SCB"
}
}
}
Balance API
Check your wallet balance including available and frozen amounts.
Response Fields
| Field | Type | Description |
|---|---|---|
| clientId | string | Client ID |
| name | string | Client name |
| balance | number | Total balance |
| available | number | Available balance (balance - frozen) |
| frozen | number | Frozen/locked amount |
| updatedAt | string | Last updated timestamp (Asia/Bangkok) |
curl -X GET '__BASE_URL__/api/v1/client/balance' \
-H 'X-API-Key: your_api_key' \
-H 'X-API-Secret: your_api_secret'
{
"status": "success",
"data": {
"clientId": "65a1b2c3d4e5f6a7b8c9d0e1",
"name": "ACME-01",
"balance": 150000.00,
"available": 148500.00,
"frozen": 1500.00,
"updatedAt": "2024-01-01T19:05:00+07:00"
}
}
Callback / Webhook
The system sends a POST request to your callback URL whenever a transaction status changes.
{
"event": "deposit.paid",
"txnId": "DEP1709123456ABC123",
"amount": 1000.00,
"status": "PAID",
"paidAt": "2024-01-01T12:05:00Z",
"signature": "callback_signature_here",
"timestamp": 1709123456789
}
Payout Callback Events: payout.success, payout.failed, payout.cancelled
// Payout Success
{
"event": "payout.success",
"txnId": "PAY1709123456XYZ789",
"amount": 5000.00,
"fee": 15.00,
"status": "SUCCESS",
"completedAt": "2024-01-01T14:30:00Z",
"signature": "callback_signature_here",
"timestamp": 1709123456789
}
// Payout Failed
{
"event": "payout.failed",
"txnId": "PAY1709123456XYZ789",
"amount": 5000.00,
"fee": 15.00,
"status": "FAILED",
"signature": "callback_signature_here",
"timestamp": 1709123456789
}
// Payout Cancelled
{
"event": "payout.cancelled",
"txnId": "PAY1709123456XYZ789",
"amount": 5000.00,
"fee": 15.00,
"status": "CANCELLED",
"signature": "callback_signature_here",
"timestamp": 1709123456789
}
Always verify the callback signature by re-generating the HMAC-SHA256 hash from the callback data using your API Secret. This ensures the callback is genuinely from our system.
Signature Generation
Generate the signature with HMAC-SHA256 over a sorted query string of exactly these 6 fields.
accNameaccountNoamountbankCodecallbackUrltimestamp
- refCode (optional client reference)
- signature (this is the output)
- timeout (optional)
- apiKey / apiSecret (HTTP headers only, never in signature)
Steps
- Pick the 6 fields listed above — do NOT include refCode/signature/timeout.
- Sort field names alphabetically A-Z (case-sensitive).
- Build the string: key1=value1&key2=value2&...
- Use raw values (no URL-encoding required — server accepts both).
- Compute HMAC-SHA256 using API Secret as the key.
- Output as a lowercase hex string (64 chars).
Example (Node.js)
const crypto = require('crypto');
// Allowed fields for signature — IMPORTANT: must be exactly these 6.
// Do NOT include refCode, signature, timeout, apiKey, or any other field.
const SIG_FIELDS = ['accName', 'accountNo', 'amount', 'bankCode', 'callbackUrl', 'timestamp'];
function generateSignature(data, apiSecret) {
// 1. Pick only the allowed fields + sort alphabetically (case-sensitive)
const sortedKeys = SIG_FIELDS.slice().sort();
// 2. Build query string: key=value joined by '&', raw values (no URL encoding)
const queryString = sortedKeys
.map(key => `${key}=${data[key]}`)
.join('&');
// 3. HMAC-SHA256 with API Secret as key, output as lowercase hex
return crypto
.createHmac('sha256', apiSecret)
.update(queryString)
.digest('hex');
}
// Usage (Deposit example)
const data = {
accName: 'John Doe',
accountNo: '1234567890',
bankCode: 'KBANK',
amount: 1000,
callbackUrl: 'https://your-site.com/callback',
refCode: 'ORDER-123', // stays in body but NOT in signature
timestamp: Date.now(), // Unix epoch ms, generate right before signing
};
data.signature = generateSignature(data, 'your_api_secret');
// data is now ready to POST as JSON
If your code produces the expected signature, your integration is ready.
// Test inputs (use these exact values in your code)
const apiSecret = 'test_secret_for_docs';
const data = {
accName: 'John Doe',
accountNo: '1234567890',
amount: 1000,
bankCode: 'KBANK',
callbackUrl: 'https://your-site.com/callback',
timestamp: 1709123456789,
};
// Step 2 output (sorted query string):
// accName=John Doe&accountNo=1234567890&amount=1000&bankCode=KBANK&callbackUrl=https://your-site.com/callback×tamp=1709123456789
// Expected HMAC-SHA256 output (lowercase hex):
// 7fcd73b8eb6a5e2f5250ab0011caa230171b11ca6af65b0954567f3dc9e78e25
const sig = generateSignature(data, apiSecret);
console.log(sig);
// if your code prints the same value the server computes, your integration is ready.
Banks API
Retrieve the list of supported banks with pagination and search (uses API Key like Deposit/Payout).
Query Parameters
| Field | Type | Description |
|---|---|---|
| page Optional | number | Page number (default: 1) |
| limit Optional | number | Items per page (default: 100) |
| search Optional | string | Search by bank name, bank code, or international code |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | string | Bank ID (MongoDB ObjectId) |
| bank_name | string | Bank name |
| bank_code | string | Bank code (e.g. KBANK, SCB) |
| int_code | string | International code (e.g. 004, 014) |
| bank_logo | string | Bank logo URL |
curl -X GET '__BASE_URL__/api/v1/client/banks?page=1&limit=100' \
-H 'X-API-Key: your_api_key' \
-H 'X-API-Secret: your_api_secret'
Other Endpoints
Additional endpoints for managing deposits and payouts.
Deposit Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/deposit/:requestId |
Get deposit details by ID |
| GET | /api/v1/deposit/status/:requestId |
Get deposit status (public, no auth required) |
| GET | /api/v1/deposits |
List all deposits with pagination |
| POST | /api/v1/deposit/:requestId/cancel |
Cancel a pending deposit |
/api/v1/deposit/:txnId/cancel
Cancel a pending deposit. Only deposits with pending status can be cancelled.
curl -X POST '__BASE_URL__/api/v1/deposit/DEP1709123456ABC123/cancel' \
-H 'X-API-Key: your_api_key' \
-H 'X-API-Secret: your_api_secret'
Response
{
"status": "success",
"message": "Deposit cancelled successfully"
}
Payout Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/payout/:requestId |
Get payout details by ID |
| GET | /api/v1/payout/status/:requestId |
Get payout status (public, no auth required) |
| GET | /api/v1/payouts |
List all payouts with pagination |
Bank Codes
Supported bank codes and their logos.
https://white-lable.sgp1.cdn.digitaloceanspaces.com/banks-logos/{CODE}.png


















