API v1.0

Payment Gateway API

Integrate deposits, payouts, and real-time webhooks into your platform with our simple REST API. Fast, secure, and reliable.

Base URL __BASE_URL__

🔒 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
The Deposit/Payout create endpoints also require an X-Idempotency-Key header — see the Idempotency section.
Keep your API credentials secure. Never expose them in client-side code or public repositories.

🔁 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-KeyRequired 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.

POST /api/v1/deposit/create Create Deposit

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 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)
The system selects the channel automatically — if the assigned System Bank has PromptPay it returns QR; otherwise it returns TRANSFER.
cURL
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
  }'
JSON
{
  "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"
  }
}
JSON
{
  "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.

POST /api/v1/payout/create Create Payout

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
Ensure your account has sufficient balance before creating a payout. Insufficient balance will result in an error.
cURL
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
  }'
JSON
{
  "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.

GET /api/v1/client/balance Check Wallet Balance

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
curl -X GET '__BASE_URL__/api/v1/client/balance' \
  -H 'X-API-Key: your_api_key' \
  -H 'X-API-Secret: your_api_secret'
JSON
{
  "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.

Your callback endpoint must respond with HTTP 200 within 10 seconds. Failed callbacks will be retried.
POST your_callback_url Deposit Callback
JSON
{
  "event": "deposit.paid",
  "txnId": "DEP1709123456ABC123",
  "amount": 1000.00,
  "status": "PAID",
  "paidAt": "2024-01-01T12:05:00Z",
  "signature": "callback_signature_here",
  "timestamp": 1709123456789
}
POST your_callback_url Payout Callback

Payout Callback Events: payout.success, payout.failed, payout.cancelled

JSON
// 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
}
Verify Callback Signature
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.

Fields included in the signature
  • accName
  • accountNo
  • amount
  • bankCode
  • callbackUrl
  • timestamp
Use exactly these 6 fields — no more, no less.
Fields NOT included in the signature
  • refCode (optional client reference)
  • signature (this is the output)
  • timeout (optional)
  • apiKey / apiSecret (HTTP headers only, never in signature)

Steps

  1. Pick the 6 fields listed above — do NOT include refCode/signature/timeout.
  2. Sort field names alphabetically A-Z (case-sensitive).
  3. Build the string: key1=value1&key2=value2&...
  4. Use raw values (no URL-encoding required — server accepts both).
  5. Compute HMAC-SHA256 using API Secret as the key.
  6. Output as a lowercase hex string (64 chars).
📅
timestamp = Unix epoch milliseconds. Server accepts a ±5 min window — generate the timestamp right before signing.
💰
amount: use the same value as in the body. The server normalizes 201, 201.00, 201.0 automatically.

Example (Node.js)

JavaScript
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
🧪
Verifiable example — test your code with these values
If your code produces the expected signature, your integration is ready.
JavaScript
// 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).

GET /api/v1/client/banks Get Banks List

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
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
POST /api/v1/deposit/:txnId/cancel

Cancel a pending deposit. Only deposits with pending status can be cancelled.

cURL
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

JSON
{
  "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.

Bank logo URL pattern
https://white-lable.sgp1.cdn.digitaloceanspaces.com/banks-logos/{CODE}.png
KBANK
KBANK004
SCB
SCB014
BBL
BBL002
KTB
KTB006
BAY
BAY025
TTB
TTB011
GSB
GSB030
CIMB
CIMB022
UOB
UOB024
TISCO
TISCO067
KKP
KKP069
CITI
CITI017
HSBC
HSBC031
ICBC
ICBC070
GHB
GHB033
IBANK
IBANK066
BAAC
BAAC034
LHB
LHB073
TCRB
TCRB071