Creates a deposit order and returns a PromptPay QR code for the customer. When the customer pays, you receive a webhook callback to your notify_url.
สร้างคำสั่งฝากเงินและส่งกลับ PromptPay QR ให้ — เมื่อลูกค้าจ่ายเงินสำเร็จ ระบบจะ POST callback ไปที่ notify_url
Use this endpoint to issue a scannable PromptPay QR code. If the receiving deposit account doesn't support PromptPay — or if a customer specifically needs to transfer by account number — use /payment/create-transfer instead.
ใช้ endpoint นี้สำหรับสร้าง PromptPay QR ที่ลูกค้าสแกนได้ — ถ้าบัญชีรับเงินไม่รองรับ PromptPay (หรือกรณีพิเศษที่ลูกค้าจำเป็นต้องโอนด้วยเลขบัญชี) ให้ใช้ /payment/create-transfer แทน
Request
Request
Body fields
Body fields
| Field | Type | Required | Notes | ฟิลด์ | ประเภท | บังคับ | หมายเหตุ |
|---|---|---|---|---|---|---|---|
| merchant_id | string | Yes | Your merchant identifier. | merchant_id | string | Yes | รหัส merchant ของคุณ |
| token | string | Yes | Your API token. | token | string | Yes | API token ของคุณ |
| time | number | string | Yes | Unix epoch (seconds). See Authentication. | time | number | string | Yes | Unix epoch (วินาที) — ดู Authentication |
| merchant_order_id | string | Yes | Your unique order identifier. Alphanumeric, max 40 chars. Must be unique per merchant for at least 7 days. | merchant_order_id | string | Yes | รหัสคำสั่งฝั่งคุณ ตัวอักษร/ตัวเลข ไม่เกิน 40 ตัว ห้ามซ้ำใน 7 วัน |
| amount | number | string | Yes | Amount in THB. Minimum 20.00. 2 decimal places. | amount | number | string | Yes | จำนวนเงิน (THB) ขั้นต่ำ 20.00 ทศนิยม 2 ตำแหน่ง |
| bank | string | Yes | Customer's bank code, uppercase. See the full list at Bank codes. | bank | string | Yes | รหัสธนาคารของลูกค้า ตัวพิมพ์ใหญ่ — ดูรายการทั้งหมดที่ Bank codes |
| account_name | string | Yes | Customer's name (for display / matching). | account_name | string | Yes | ชื่อลูกค้า (ใช้แสดง / จับคู่) |
| account_no | string | Yes | Customer's account number, 10–15 digits. Non-digits are stripped automatically. | account_no | string | Yes | เลขบัญชีลูกค้า 10–15 หลัก — อักขระที่ไม่ใช่ตัวเลขจะถูกตัดออก |
| notify_url | string | Optional | HTTPS URL we'll POST the callback to when status changes. If omitted, no callback is sent — you must poll /payment/query. | notify_url | string | Optional | HTTPS URL ที่จะให้ระบบ POST callback กลับ — ถ้าไม่ส่งจะไม่มี callback ต้องไป poll /payment/query |
Code samples
ตัวอย่างโค้ด
TIME=$(date +%s)
BODY="{\"merchant_id\":\"AA12345678\",\"token\":\"YOUR_TOKEN\",\"time\":$TIME,\"merchant_order_id\":\"ORDER-2026-001\",\"amount\":\"500.00\",\"bank\":\"KBANK\",\"account_name\":\"สมชาย ใจดี\",\"account_no\":\"1234567890\",\"notify_url\":\"https://<your-merchant-webhook-URL>/payment-callback\"}"
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "YOUR_SECRET" | awk '{print $2}')
curl -X POST https://<your-api-domain>/payment/create \
-H "Content-Type: application/json" \
-H "X-SIGNATURE: $SIG" \
-d "$BODY"
import crypto from 'node:crypto';
const SECRET = 'YOUR_SECRET';
const body = JSON.stringify({
merchant_id: 'AA12345678',
token: 'YOUR_TOKEN',
time: Math.floor(Date.now() / 1000),
merchant_order_id: 'ORDER-2026-001',
amount: '500.00',
bank: 'KBANK',
account_name: 'สมชาย ใจดี',
account_no: '1234567890',
notify_url: 'https://<your-merchant-webhook-URL>/payment-callback',
});
const sig = crypto.createHmac('sha256', SECRET).update(body).digest('hex');
const r = await fetch('https://<your-api-domain>/payment/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-SIGNATURE': sig },
body
});
console.log(await r.json());
<?php
$secret = 'YOUR_SECRET';
$body = json_encode([
'merchant_id' => 'AA12345678',
'token' => 'YOUR_TOKEN',
'time' => time(),
'merchant_order_id' => 'ORDER-2026-001',
'amount' => '500.00',
'bank' => 'KBANK',
'account_name' => 'สมชาย ใจดี',
'account_no' => '1234567890',
'notify_url' => 'https://<your-merchant-webhook-URL>/payment-callback',
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$sig = hash_hmac('sha256', $body, $secret);
$ch = curl_init('https://<your-api-domain>/payment/create');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"X-SIGNATURE: $sig",
],
]);
echo curl_exec($ch);
Response
Response
Success — HTTP 200
สำเร็จ — HTTP 200
{
"code": 200,
"message": "Success",
"data": {
"platform_order_id": "ABCP20260508abc123XYZ456",
"merchant_order_id": "ORDER-2026-001",
"uuid": "0190b4a2-7c1d-7000-9f3a-2c8e5b1a4d6f",
"order_datetime": "2026-05-08 10:30:00",
"expire_datetime": "2026-05-08 10:45:00",
"amount": 500.00,
"transfer_amount": 500.03,
"payment_type": "QR",
"qrcode": "00020101021129370016A0000006770101110113006611234567895802TH540...",
"payment_url": "https://<your-payment-page-domain>/p/0190b4a2-7c1d-7000-9f3a-2c8e5b1a4d6f"
},
"success": true
}
Response fields
Response fields
| Field | Type | Description | ฟิลด์ | ประเภท | คำอธิบาย |
|---|---|---|---|---|---|
| data.platform_order_id | string | Order ID assigned by the gateway (24 chars). Use this for /payment/query and matching the callback's platform_order_id. |
data.platform_order_id | string | Order ID ที่ออกโดย gateway (24 ตัวอักษร) — ใช้กับ /payment/query และจับคู่ platform_order_id ใน callback |
| data.merchant_order_id | string | Echo of your input. | data.merchant_order_id | string | Echo ค่าที่คุณส่งมา |
| data.uuid | string (UUID v7) | A globally-unique UUID for this order. Used as part of payment_url; you typically don't need to store it separately. |
data.uuid | string (UUID v7) | UUID v7 ของ order — ใช้ใน payment_url โดยทั่วไปฝั่งคุณไม่จำเป็นต้องเก็บแยก |
| data.order_datetime | string | Datetime when the order was created (YYYY-MM-DD HH:mm:ss in GMT+7). |
data.order_datetime | string | เวลาที่สร้าง order (YYYY-MM-DD HH:mm:ss GMT+7) |
| data.expire_datetime | string | Datetime after which this order is no longer accepted (YYYY-MM-DD HH:mm:ss GMT+7). Stop showing the QR to the customer once this passes. |
data.expire_datetime | string | เวลาที่ order หมดอายุ (YYYY-MM-DD HH:mm:ss GMT+7) — เลยเวลานี้ไม่ควรแสดง QR ให้ลูกค้าแล้ว |
| data.amount | number | The amount you requested. | data.amount | number | จำนวนเงินที่คุณ request มา |
| data.transfer_amount | number | The amount the customer must actually transfer. May be slightly higher than amount (e.g. +0.01 increments) so we can uniquely match incoming transfers to this order. |
data.transfer_amount | number | จำนวนเงินที่ลูกค้าต้องโอนจริง — อาจสูงกว่า amount เล็กน้อย (เช่น +0.01) เพื่อให้ระบบจับคู่เงินโอนกลับเข้าออเดอร์นี้ได้ |
| data.payment_type | enum | Always "QR" for this endpoint. |
data.payment_type | enum | เป็น "QR" เสมอสำหรับ endpoint นี้ |
| data.qrcode | string | EMV-formatted PromptPay QR string. Render as a QR image and show to your customer. The destination bank account is encoded inside the QR. | data.qrcode | string | PromptPay QR string ตามรูปแบบ EMV — ฝั่งคุณ render เป็น QR image แสดงให้ลูกค้า — ข้อมูลบัญชีปลายทางถูก encode ไว้ใน QR แล้ว |
| data.payment_url | string | null | A hosted payment page that displays the QR with a countdown. Useful if you'd rather redirect the customer than render the QR yourself. null if no payment-page domain has been configured for your account. |
data.payment_url | string | null | URL หน้า payment page ที่แสดง QR พร้อม countdown — ใช้แทนการ render QR เองโดย redirect ลูกค้าไปหน้านี้ — เป็น null ถ้าไม่ได้ตั้ง payment-page domain ไว้สำหรับบัญชีคุณ |
transfer_amount — not amount — to the customertransfer_amount ให้ลูกค้า — ไม่ใช่ amountIf the customer pays the displayed amount instead of transfer_amount, our settlement system may not match the deposit to this order.
ถ้าลูกค้าจ่ายตาม amount แทนที่จะเป็น transfer_amount ระบบของเราอาจจับคู่เงินกลับเข้า order นี้ไม่ได้
Errors
Errors
| HTTP | Error code | When it happens | Error code | เกิดเมื่อ |
|---|---|---|---|---|
| 400 | invalid-inputs |
Missing/invalid field, amount < 20, invalid bank, malformed URL. | invalid-inputs |
Field ขาด/ผิดรูปแบบ, amount < 20, bank ไม่ถูกต้อง, URL ผิด |
| 409 | duplicate-entry |
A previous order used the same merchant_order_id within the past 7 days. |
duplicate-entry |
merchant_order_id ซ้ำกับ order เดิมในช่วง 7 วันที่ผ่านมา |
| 403 | permission-denied |
Payment is disabled on your merchant account. | permission-denied |
Merchant ของคุณถูกปิดการรับชำระเงิน |
| 503 | service-unavailable |
No deposit account is currently available to accept this payment. Try again shortly. | service-unavailable |
ไม่มีบัญชีรับเงินที่ว่างขณะนี้ — ลองใหม่ในอีกสักครู่ |
| 429 | channel-limit-reached |
The deposit channel reached its hourly or daily limit. | channel-limit-reached |
Channel รับเงินถึงโควตารายชั่วโมงหรือรายวันแล้ว |
| 404 | not-found |
Partner configuration not found for this merchant — contact your admin. | not-found |
ไม่พบ Partner config ของ merchant — ติดต่อ admin |
Behavior & notes
พฤติกรรม & หมายเหตุ
- Idempotency by merchant_order_id. Calling with the same
merchant_order_idwithin 7 days returnsduplicate-entry. - Idempotency ด้วย merchant_order_id — เรียกซ้ำด้วย
merchant_order_idเดิมในช่วง 7 วันจะได้duplicate-entry - Order TTL. The order remains
openuntil the customer pays or until the configured order expiration window elapses. - Order TTL — order จะอยู่สถานะ
openจนกว่าลูกค้าจะจ่าย หรือเลยระยะเวลาหมดอายุที่ตั้งไว้สำหรับบัญชีคุณ - Slot allocation. The system reserves a unique
transfer_amountslot per deposit account for ~15 minutes to disambiguate concurrent deposits. - Slot allocation — ระบบจอง slot ของ
transfer_amountบนบัญชีรับเงิน ~15 นาที เพื่อแยกการโอนพร้อมกัน - Fees are calculated automatically and applied only when the order completes successfully — they don't appear in this response.
- ค่าธรรมเนียม คำนวณและตัดอัตโนมัติเฉพาะเมื่อ order สำเร็จ ไม่แสดงใน response นี้
- Display the destination account info to your customer in case the QR cannot be scanned (e.g., banking app issues, network).
- โชว์ข้อมูลบัญชีปลายทางให้ลูกค้าด้วย เผื่อกรณีสแกน QR ไม่ได้ (เช่น แอปธนาคารมีปัญหา, เน็ตช้า)