API Reference
Public REST API สำหรับ integrator — ตรวจสลิป, อ่านโควตา, รูปแบบ error + status ครบทุก endpoint
Public REST API สำหรับเชื่อมต่อระบบของคุณ (POS / ERP / บอท) เข้ากับ SlipBolt
ยืนยันตัวตนด้วย API key (sk_live_… / sk_test_…) — ดู Authentication
Base URL
https://api.slip-bolt.com/api/v1
ทุก request ต้องมี header:
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
เช็คก่อนยิงงานชุดใหญ่ — GET /api/v1/me (ร้าน · แพ็ก · โควตา live+test · บัญชีรับเงิน)
และ GET /api/v1/quota (โควตาอย่างเดียว) ไม่หักสลิป · ต้องมี scope read
ช่วง Trial ไม่มี API key — ดูกระเบื้องโควตาบน Dashboard
Test mode — ใช้ key sk_test_… เพื่อรันทั้ง pipeline จริงโดย ไม่กินโควตาของร้าน เหมาะกับตอน
dev/CI · แต่มีเพดานแยกของตัวเอง 200 ครั้ง/ร้าน/เดือน (ตรวจกับธนาคารจริงทุกครั้ง ไม่ใช่ของจำลอง)
เกินแล้วได้ 402 TEST_QUOTA_EXCEEDED เหมือนกัน — ดูรายละเอียดที่ Authentication
https://api.slip-bolt.com/api/v1/openapi.json
— import เข้า Postman / Insomnia / Bruno เพื่อลองยิงได้ทันที หรือใช้ generate client
ภาษาที่คุณถนัด (เช่น openapi-generator) ครอบคลุมทุก endpoint ในหน้านี้
Postman collection พร้อมใช้: https://api.slip-bolt.com/api/v1/postman.json — Import แล้วใส่ตัวแปร apiKey ก็กดส่งได้เลย · POST /verify แยกให้ครบ 5 แบบ (ไฟล์ดิบ · multipart · base64 · url · qr)
โค้ดก๊อปไปวางของ POST /verify ใน Node.js · Python · PHP · Go · Java · C# อยู่ที่ ตัวอย่างพร้อมใช้
ผลของ request ที่สำเร็จอยู่ใน data เสมอ — { "success": true, "data": { … }, "meta": { … } }
ส่วน error เป็นก้อนแบน (ดู Error envelope) · อ่าน verified ที่ data.verified ไม่ใช่ที่ราก
POST /api/v1/verify
ตรวจสลิปโอนเงิน 1 ใบ — ส่งได้ 5 แบบ: raw bytes (แนะนำ) · multipart/form-data · JSON image (base64) · JSON url · JSON qr
Request
| Method | POST |
| Body | ไฟล์รูปสลิป (JPEG/PNG) · ทั้งคำขอไม่เกิน 5 MB |
| Scope ที่ต้องมี | verify |
| ส่งแบบ | Content-Type | Body |
|---|---|---|
| raw bytes (แนะนำ) | application/octet-stream (หรือ image/jpeg · image/png) | ไฟล์รูปเป็น binary ดิบ |
| multipart | multipart/form-data | ไฟล์ในฟิลด์ชื่อ file (ไม่มีฟิลด์ชื่อนี้ → ใช้ไฟล์แรกที่เจอ) |
| JSON base64 | application/json | { "image": "<base64>" } — มี prefix data:image/…;base64, ก็ได้ |
| JSON URL | application/json | { "url": "https://…" } — เราดาวน์โหลดรูปให้ · https สาธารณะเท่านั้น · ไม่ตาม redirect · ไม่เกิน 5 MB |
| JSON QR | application/json | { "qr": "<ข้อความใน QR ของสลิป>" } — ไม่ต้องมีรูป |
qr เป็นข้อมูลขาเข้าเท่านั้น — คือข้อความที่สแกนได้จาก QR บนสลิป (QR ตรวจสอบรายการ) · API นี้ ไม่มี endpoint สร้าง QR / PromptPay สำหรับรับเงิน
- ส่ง QR รับเงิน (PromptPay / QR ร้านค้า — ข้อความขึ้นต้น
000201) มาแทน →400ไม่หักโควตา - ข้อความอื่นที่ไม่ใช่ QR ของสลิป → ธนาคารเป็นผู้ตัดสิน ผลออกมาเป็น
INVALIDหรือFRAUD(รายการไม่มีอยู่จริงที่ธนาคาร) — ไม่หักโควตา
JSON ใส่ได้ อย่างเดียว ใน image / url / qr · ตัวอย่างด้านล่างใช้ raw bytes · ตัวอย่างแบบอื่นครบทุกภาษาอยู่ที่ ตัวอย่างพร้อมใช้
curl -X POST https://api.slip-bolt.com/api/v1/verify \
-H "Authorization: Bearer $SLIPBOLT_API_KEY" \
-H "Content-Type: application/octet-stream" \
--data-binary "@slip.jpg"
Response 200 OK
{
"success": true,
"data": {
"id": "8f3c…",
"status": "SUCCESS",
"verified": true,
"source": "API",
"transRef": "015170106978META…",
"amount": 250.0,
"bankCode": "002",
"bankName": "ธนาคารกรุงเทพ",
"senderName": "นาย ก. …",
"senderAccount": "xxx-x-x1234-x",
"receiverName": "ร้าน ข.",
"receiverAccount": "xxx-x-x5678-x",
"receiverBankCode": "014",
"receiverBankName": "ธนาคารไทยพาณิชย์",
"transDate": "2026-06-17T03:21:05.000Z",
"errorCode": null,
"errorMessage": null,
"createdAt": "2026-06-17T03:21:06.412Z",
"receipt": { "code": "SB-7K2F-9QX4", "checkUrl": "https://slip-bolt.com/c/PyuMHlp9Tp-wwdLj9KW2xzQ1dGhVb2xr" },
"duplicate": false,
"cached": false,
"latencyMs": 480,
"environment": "live",
"quota": { "used": 132, "limit": 500, "remaining": 368 }
},
"meta": { "requestId": "3b1f0c9e-6a2d-4f5e-8b7c-1d2e3f4a5b6c", "timestamp": "2026-06-17T03:21:06.412Z" }
}
verified เป็น true เฉพาะเมื่อ status === "SUCCESS" — ดูตาราง status ด้านล่าง
meta.requestId ตรงกับ header X-Request-Id ของคำตอบ — เก็บลง log ไว้ใช้ตอนติดต่อทีมงาน
ตัวอย่างผลที่ไม่ผ่าน
ทุกกรณีด้านล่างเป็น HTTP 200 — คำขอสำเร็จ แต่คำตัดสินของสลิปคือไม่ผ่าน (ตัดเฉพาะฟิลด์ที่ต่างจากตัวอย่างข้างบน)
{
"id": "8f3c2a1e-…",
"status": "DUPLICATE",
"verified": false,
"transRef": "015170106978META…",
"amount": 250.0,
"errorCode": null,
"errorMessage": null,
"duplicate": true,
"cached": false
}
DUPLICATEคืนข้อมูลของ รายการเดิม (id·transRef·amountเป็นของครั้งแรกที่ตรวจ) — ใช้transRefหาว่าออเดอร์ไหนใช้สลิปนี้ไปแล้วRECEIVER_MISMATCH·AMOUNT_MISMATCHอ่านสลิปได้ครบ จึงมีtransRef/amount/ ผู้รับ ให้ดูว่าโอนไปที่ไหน ·errorCodeเป็นnull— แตกกิ่งด้วยstatusINVALID·FRAUDไม่มีข้อมูลบนสลิปให้เชื่อ —transRefเป็นnullและamountเป็น0
ฟิลด์ใน data
ฟิลด์ชุดแรก (ถึง createdAt) เป็นรูปร่างเดียวกับที่ GET /transactions คืน — parse ด้วยโค้ดชุดเดียวได้
| ฟิลด์ | ชนิด | ความหมาย |
|---|---|---|
id | string (uuid) | id ของรายการ — ใช้กับ GET /transactions/:id |
status | string | ผลตรวจ — ดู Transaction status |
verified | boolean | true เฉพาะเมื่อ status เป็น SUCCESS |
source | string | ช่องทางที่สลิปเข้ามา: API · LINE · LIFF · DASHBOARD |
transRef | string | null | เลขอ้างอิงธนาคาร — ใช้เป็น unique key ฝั่งคุณ · null เมื่ออ่านจากสลิปไม่ได้ |
amount | number | ยอดโอน (บาท) — 0 เมื่ออ่านไม่ได้ |
bankCode · bankName | string | null | ธนาคารผู้โอน — bankCode เป็นรหัส 3 หลัก ดู รหัสธนาคาร |
senderName · senderAccount | string | null | ผู้โอน — เลขบัญชีถูกปิดบางหลักตามที่ธนาคารให้มา |
receiverName · receiverAccount | string | null | ผู้รับ — receiverAccount คือบัญชีธนาคารที่เงินเข้า |
receiverProxyAccount | string | null | เลขพร้อมเพย์ของผู้รับ (เบอร์/เลขบัตร/e-Wallet) — เลขที่เห็นบนรูปสลิปเมื่อโอนผ่านพร้อมเพย์ · ต่างจาก receiverAccount เป็นปกติ · null เมื่อโอนเข้าบัญชีตรง |
receiverBankCode · receiverBankName | string | null | ธนาคารผู้รับ (รหัส 3 หลักเช่นกัน) |
transDate | string (ISO-8601 UTC) | null | เวลาที่โอนตามธนาคาร |
errorCode · errorMessage | string | null | เหตุผลเมื่อไม่ผ่าน — หรือคำเตือนเมื่อผ่าน (STALE_SLIP) · ดู errorCode |
warnings | array | คำเตือนของสลิปที่ผ่าน [{ code, message }] — ว่าง [] เมื่อไม่มีหรือเมื่อไม่ผ่าน · ตอนนี้มีแค่ STALE_SLIP (ค่าเดียวกับ errorCode คงไว้ทั้งคู่) |
createdAt | string (ISO-8601 UTC) | เวลาที่เราตรวจ |
receipt | object | null | ใบยืนยันการตรวจสลิป — code (เลขยืนยันบนใบ) · checkUrl (ลิงก์ใน QR) · มีเฉพาะ SUCCESS · รูป/PDF ดู receipt.png |
duplicate | boolean | transRef นี้เคยตรวจแล้ว (status = DUPLICATE) |
cached | boolean | ส่งรูปเดิมซ้ำใน 24 ชม. — คืนผลของรายการเดิม ไม่หักโควตา |
quotaCharged | boolean | คำขอนี้หักโควตา/เครดิตของร้านไหม — true เฉพาะ SUCCESS ที่ตรวจใหม่ด้วยคีย์ live · ไม่ผ่าน · DUPLICATE · cached · คีย์ sk_test_ = false |
latencyMs | integer | null | เวลาที่ใช้ตรวจกับธนาคาร |
environment | string | live หรือ test ตามชนิดคีย์ |
quota | object | used · limit · remaining หลังคำขอนี้ |
ชื่อผู้โอน/ผู้รับไม่ใช่ชื่อ-นามสกุลเต็ม — senderName / receiverName คือชื่อตามที่ธนาคารส่งมา (เราไม่ได้อ่านจากรูปสลิป) ซึ่งธนาคารปิดนามสกุลไว้เหลืออักษรตัวแรก เช่น นาย สมชาย ใ · บางธนาคารส่งเป็นภาษาอังกฤษ (MR. SOMCHAI J) · ชื่อร้าน/ชื่อบัญชีที่ยาวอาจถูกตัดท้าย
ขอชื่อเต็มไม่ได้ทุกช่องทาง (API · webhook · LINE) · อย่าใช้ชื่อเทียบตัวตนผู้รับ — เราเทียบเลขบัญชีกับบัญชีรับเงินของร้านให้แล้ว ผลอยู่ที่ status (RECEIVER_MISMATCH)
ส่งรูปเดิมซ้ำคนละรูปแบบ = รูปเดียวกัน — raw bytes / multipart / base64 / url ถูกแปลงเป็น bytes ชุดเดียวกันก่อนตรวจ
จึงได้ cached: true เหมือนส่งซ้ำแบบเดิม · qr นับแยกจากรูป (ข้อความเดิมซ้ำ = cached · รูปของสลิปใบเดียวกัน = DUPLICATE) — ดู ตัวอย่างพร้อมใช้
Quota — pool เดียวกับสลิป LINE
การเรียก API ใช้โควตารายเดือนเดียวกับสลิปจาก LINE ของร้าน (pool เดียว นับรวมกัน) ไม่ใช่โควตาแยก — ใช้ผ่านช่องไหนก็หักจากก้อนเดียวกัน
ทุก response (รวม GET /api/v1/quota) แนบ header บอกยอดคงเหลือ:
| Header | ความหมาย |
|---|---|
X-Slipbolt-Quota-Limit | เพดานสลิป/เดือนของแพ็กปัจจุบัน |
X-Slipbolt-Quota-Used | ใช้ไปแล้วในรอบเดือนนี้ |
X-Slipbolt-Quota-Remaining | คงเหลือ |
X-Slipbolt-Quota-Reset-Days | กี่วันก่อนรีเซ็ตรอบใหม่ |
โควตาและเครดิตหมด → 402 Payment Required (เฉพาะ key live · มีเครดิตเหลือ = ตัดเครดิตต่อ ไม่ได้ 402) — ไม่ใช่ 200 เงียบ ๆ ให้ integrator เช็คจาก HTTP status ได้เลย
{ "success": false, "statusCode": 402, "error": "Payment Required",
"message": "โควตาสลิปรายเดือนและเครดิตของร้านถูกใช้หมดแล้ว — ซื้อเครดิต อัปเกรดแพ็ก หรือรอรอบบิลถัดไป",
"code": "QUOTA_EXCEEDED", "quota": { "used": 500, "limit": 500, "remaining": 0 } }
key sk_test_ ไม่กินโควตาก้อนนี้ (ของร้าน) จึงไม่ได้ 402 ตัวนี้ — แต่มีเพดานแยกของตัวเอง
200 ครั้ง/เดือน เกินแล้วได้ 402 TEST_QUOTA_EXCEEDED เหมือนกัน (ดูหัวข้อ Idempotency/Test mode ด้านบน)
GET /api/v1/quota
อ่านโควตาคงเหลือ โดยไม่เปลือง verify — เหมาะให้ระบบ poll ก่อนยิงงานชุดใหญ่
| Method | GET |
| Scope ที่ต้องมี | read |
curl "https://api.slip-bolt.com/api/v1/quota" \
-H "Authorization: Bearer $SLIPBOLT_API_KEY"
{
"success": true,
"data": {
"used": 132,
"limit": 500,
"remaining": 368,
"percentUsed": 26,
"allowOverage": false,
"planCode": "STARTER",
"periodMonth": "2026-06",
"daysUntilReset": 13,
"creditsAvailable": 0
}
}
creditsAvailable = เครดิตสลิปคงเหลือ (แบบจ่ายตามใช้) — ใช้ต่อเมื่อโควตาแพ็กหมด ก่อนคิดค่าส่วนเกิน
ร้านที่ไม่มีแพ็กรายเดือนแต่มีเครดิตก็ตรวจสลิปได้ · remaining ยังเป็นโควตาของแพ็กอย่างเดียว
⇒ ตรวจได้อีกทั้งหมด ≈ remaining + creditsAvailable
GET /api/v1/me
ข้อมูลร้าน · คีย์ · แพ็ก · โควตาคงเหลือ — ไม่เปลืองโควตา เหมาะเรียกตอนบูตระบบของคุณ
เพื่อเช็คว่าคีย์ถูกร้าน scope ครบ เหลือสลิปเท่าไหร่ และบัญชีรับเงินที่ระบบใช้เทียบผู้รับคือบัญชีไหน
(สาเหตุอันดับหนึ่งของ RECEIVER_MISMATCH คือร้านยังไม่ได้เพิ่มบัญชีที่ลูกค้าโอนเข้า)
ช่วงทดลอง 7 วัน plan.code จะเป็น "TRIAL" และ quota.limit = 50 เสมอ
แม้ร้านจะเคยเลือกการ์ด Pro/Business ตอนสมัคร — สิทธิ์แพ็กจ่ายเริ่มหลังชำระแล้วเท่านั้น
| Method | GET |
| Scope ที่ต้องมี | read |
curl "https://api.slip-bolt.com/api/v1/me" \
-H "Authorization: Bearer $SLIPBOLT_API_KEY"
{
"success": true,
"data": {
"shop": { "id": "8f3c…", "name": "ร้าน ข." },
"apiKey": {
"name": "POS หน้าร้าน",
"prefix": "sk_live_a1b2c3",
"environment": "live",
"scopes": ["verify", "read"],
"lastUsedAt": "2026-06-17T03:21:06.000Z",
"createdAt": "2026-05-01T00:00:00.000Z"
},
"plan": { "code": "STARTER", "name": "Starter" },
"quota": { "used": 132, "limit": 500, "remaining": 368, "percentUsed": 26,
"overage": 0, "allowOverage": false, "planCode": "STARTER",
"periodMonth": "2026-06", "daysUntilReset": 13 },
"credits": { "available": 0, "usedThisMonth": 0, "nextExpiry": null },
"testQuota": { "used": 12, "limit": 200, "remaining": 188 },
"rateLimit": { "perKeyPerMinute": 120, "verifyPerMinute": 240 },
"payeeAccounts": [
{ "bankCode": "kbank", "kind": "BANK", "holder": "ร้าน ข.", "accountLast4": "5678", "isPrimary": true, "status": "ACTIVE" }
]
}
}
payeeAccounts[].bankCode เป็น slug (kbank) ไม่ใช่รหัส 3 หลักแบบในผลตรวจ (004) — เทียบด้วยตาราง รหัสธนาคาร
payeeAccounts ส่งแค่ 4 ตัวท้าย ของเลขบัญชี — คีย์ API รั่วได้ง่ายกว่ารหัสผ่านหน้าเว็บ
ไม่จำเป็นต้องให้เลขเต็มเพื่อรู้ว่า "บัญชีไหน" · testQuota.used: null = วัดไม่ได้ชั่วคราว
(คีย์ sk_test_ จะตกไปกินโควตาปกติของร้านแทน ไม่ใช่ error)
GET /api/v1/transactions
ลิสต์ผลตรวจสลิปของร้านย้อนหลัง — ใหม่สุดก่อน แบ่งหน้าแบบ cursor รวมทุกช่องทาง (API / LINE / Dashboard)
เหมาะกับตอน POST /verify หลุดกลางทาง (timeout/เน็ตตัด) แล้วอยากรู้ผลจริงโดยไม่ต้องส่งสลิปซ้ำ
| Method | GET |
| Scope ที่ต้องมี | read |
| Query | status · source · transRef · from/to (ISO-8601 — ใส่ +07:00 ถ้าคิดเป็นเวลาไทย) · cursor · limit (1-100, default 50) |
curl "https://api.slip-bolt.com/api/v1/transactions?status=SUCCESS&limit=20" \
-H "Authorization: Bearer $SLIPBOLT_API_KEY"
{
"success": true,
"data": {
"items": [ { "id": "8f3c…", "status": "SUCCESS", "verified": true, "…": "…" } ],
"nextCursor": "7ab1…"
}
}
ส่ง nextCursor กลับมาเป็น cursor เพื่อดึงหน้าถัดไป · null = หมดแล้ว
GET /api/v1/transactions/:id
ดึงผลตรวจสลิป 1 รายการ — ใช้คู่กับ webhook (รับ data.transactionId จาก event แล้วมาดึงรายละเอียดเต็ม)
รายการของร้านอื่น หรือ id ที่ไม่มีอยู่ → 404 เหมือนกัน (ไม่ยืนยันว่า id นั้นมีอยู่จริงในระบบ)
curl https://api.slip-bolt.com/api/v1/transactions/8f3c2a1e-… \
-H "Authorization: Bearer $SLIPBOLT_API_KEY"
GET /api/v1/transactions/:id/receipt.png · receipt.pdf
ใบยืนยันการตรวจสลิป ของรายการที่ผ่าน — รูปเดียวกับที่ลูกค้าได้จากบอท LINE / LIFF (หัว "ตรวจแล้ว · สลิปจริง" · ยอด · ผู้โอน → ผู้รับ · เลขอ้างอิง · QR + เลขยืนยัน) ใช้ส่งต่อให้ลูกค้าในระบบของคุณเอง (แชต · อีเมล · หน้าออเดอร์) ได้เลย
- QR บนใบเปิดหน้าเช็คบน
slip-bolt.com/c/…— ใครถือรูปก็สแกนเช็คได้ว่าใบออกจาก SlipBolt จริง (หน้านั้นโชว์ข้อมูลแบบปิดบางส่วน) · ลิงก์และเลขเดียวกันอยู่ในdata.receiptของผลตรวจ - ใบนี้ใช้แทนสลิปโอนเงินไม่ได้ และพิมพ์ไว้บนใบแล้ว — เป็นหลักฐานว่า "สลิปนี้ผ่านการตรวจ" ไม่ใช่หลักฐานการโอน
?maskSender=true— ปิดชื่อผู้โอนบางส่วน ("สมชาย ใจ***") และไม่พิมพ์เลขบัญชีผู้โอน · ใช้เมื่อจะโพสต์ในที่ที่หลายคนเห็น- สร้างใหม่ทุกครั้งที่ขอ เราไม่เก็บไฟล์ — เก็บรูปไว้เองถ้าต้องใช้ซ้ำ · PNG กว้าง 784px · PDF เป็นหน้าเดียวขนาดเท่าใบ
- รายการที่ไม่ผ่าน (
status≠SUCCESS) หรือข้อมูลถูกลบตามระยะเก็บของร้านแล้ว →409· ร้านอื่น/ไม่มีอยู่ →404· เพดาน 30 คำขอ/นาที ต่อ IP
| Required scope | read |
curl https://api.slip-bolt.com/api/v1/transactions/8f3c2a1e-…/receipt.png \
-H "Authorization: Bearer $SLIPBOLT_API_KEY" \
-o receipt.png
POST /api/v1/verify/async
ตรวจสลิปแบบ ไม่รอผล — ตอบ 202 ภายในไม่กี่มิลลิวินาที แล้วตรวจต่อเบื้องหลัง เหมาะกับร้านที่ส่งสลิปเป็นชุด
หรือไม่อยากให้ request ค้างรอระบบธนาคาร · body/header/scope เหมือน POST /verify ทุกอย่าง (รวม Idempotency-Key)
curl -i -X POST https://api.slip-bolt.com/api/v1/verify/async \
-H "Authorization: Bearer $SLIPBOLT_API_KEY" \
-H "Idempotency-Key: order-1001" \
-H "Content-Type: application/octet-stream" \
--data-binary @slip.jpg
HTTP/1.1 202 Accepted
Location: /api/v1/verifications/vrf_3f9a1c0b7e2d4a6f8b1c2d3e
{
"success": true,
"data": {
"id": "vrf_3f9a1c0b7e2d4a6f8b1c2d3e",
"status": "PENDING",
"createdAt": "2026-10-22T03:21:06.412Z",
"completedAt": null,
"statusUrl": "/api/v1/verifications/vrf_3f9a1c0b7e2d4a6f8b1c2d3e",
"result": null,
"error": null
},
"meta": { "requestId": "…", "timestamp": "…" }
}
รับผลได้ 2 ทาง (ใช้คู่กันได้):
- Webhook
verification.completed— ติ๊ก event นี้ที่ endpoint ใน Settings → Webhooks (รูปแบบ payload) - Poll
GET /api/v1/verifications/{id}ทุก 1–2 วินาที (ไม่เปลือง verify)
GET /api/v1/verifications/:id
status | ความหมาย |
|---|---|
PENDING · PROCESSING | ยังไม่จบ |
COMPLETED | ตรวจจบแล้ว — result คือ data ชุดเดียวกับที่ POST /verify ตอบ ทุกฟิลด์ (status ของสลิปดูที่ result.status — สลิปไม่ผ่านก็ยังเป็น COMPLETED) |
FAILED | ไม่ได้ตรวจ — ดู error · ไม่หักโควตา · ส่งใหม่ได้ |
error.code ของ FAILED (error.httpStatus = status ที่ POST /verify จะตอบในกรณีเดียวกัน):
error.code | เมื่อไร |
|---|---|
QUOTA_EXCEEDED · TEST_QUOTA_EXCEEDED | โควตา/เครดิตหมด (sync = 402) |
SPEND_CAP_EXCEEDED | ถึงเพดานต่อคีย์ที่ร้านตั้ง (sync = 429) |
VERIFY_TIMEOUT | ค้างคิวเกิน 5 นาที — ระบบไม่ได้ตรวจ |
API_KEY_REVOKED | คีย์ถูกยกเลิกก่อนตรวจเสร็จ |
INTERNAL_ERROR | ระบบเราขัดข้องหลังลองซ้ำแล้ว |
- เก็บผล 24 ชม. หลังจากนั้น
404— ผลถาวรอยู่ที่GET /transactions/{result.id} - id ของร้านอื่น / คีย์คนละ environment (
sk_live_vssk_test_) →404 Idempotency-Keyเดิม + สลิปเดิม = ได้202ชุดเดิม (idเดิม ·Idempotent-Replayed: true) ไม่สร้างงานซ้ำ · key ของ async แยกจากPOST /verify(ใช้ค่าเดียวกันสองเส้นได้ไม่ชนกัน)- งานรอตรวจต่อร้านมีเพดาน (ค่าเริ่มต้น 50 ใบ) — เต็มได้
429code: "ASYNC_QUEUE_FULL"+Retry-After - ระบบเราไม่มีคิวชั่วคราว →
503code: "ASYNC_UNAVAILABLE"— ส่งที่POST /verifyแทน
ผลตรวจ, DUPLICATE, STALE_SLIP, quotaCharged ของโหมด async เหมือนโหมดปกติทุกกรณี — ใช้เส้นตรวจตัวเดียวกัน
event slip.* เดิมก็ยังส่งตามปกติ (ได้ทั้ง slip.verified และ verification.completed ถ้าติ๊กไว้ทั้งคู่)
Scopes
แต่ละ key ถือ scope ได้หลายตัว — endpoint ต้องการ scope ครบทุกตัวที่ระบุ (ตั้งค่าได้ที่ Settings → API Keys)
| Scope | ใช้กับ |
|---|---|
verify | POST /api/v1/verify (default ของทุก key) |
read | GET /api/v1/quota · GET /api/v1/me · GET /api/v1/transactions[/:id] · GET /api/v1/transactions/:id/receipt.{png,pdf} |
webhook | สำรองไว้สำหรับอนาคต — ตอนนี้ยังไม่มี public endpoint ใช้งาน scope นี้ (ตั้งค่า outgoing webhook ทำผ่าน Dashboard → Settings → Webhooks เท่านั้น ไม่ใช่ API key) |
scope ไม่พอ → 403 Forbidden · message: "API key missing scope(s): verify" (ไม่มี code — รูปร่างเต็มอยู่ที่ Error envelope)
คีย์ที่ถูก revoke ได้ 401 ตั้งแต่คำขอถัดไปทันที — ไม่มีช่วง cache
Health check
ไม่ต้องใช้คีย์ · ไม่กินโควตาหรือ rate limit ของคีย์ · Cache-Control: no-store · จำกัด 120 คำขอ/นาที ต่อ IP
| Endpoint | ใช้ทำอะไร | ตอบ |
|---|---|---|
GET /api/v1/health | process ยังรับคำขอไหม (ไม่แตะ dependency) | 200 { "status": "ok" } |
GET /api/v1/ready | พร้อมตรวจสลิปจริงไหม — เช็คฐานข้อมูล · คิว · ระบบตรวจสลิป (ผลแคช 15 วินาที) | 200 ok / degraded · 503 down |
{ "status": "degraded", "checks": { "database": "ok", "queue": "ok", "verification": "degraded" }, "timestamp": "2026-10-21T08:00:00.000Z" }
degraded= ยังตรวจได้ แต่วิ่งเส้นสำรองอยู่ (อาจช้ากว่าปกติ) ·down= ตรวจไม่ได้ตอนนี้ — ระบบคุณควรพักการส่งแล้วลองใหม่- ตั้ง uptime monitor ที่
/api/v1/readyได้เลย · สถานะรวมและประวัติเหตุขัดข้องดูที่ หน้าสถานะระบบ
Rate limit
120 req/min ต่อ key (เท่ากันทุกแพ็ก) + เพดานรวมของ POST /api/v1/verify ที่ 240 req/min
เกินแล้ว → 429 Too Many Requests + header Retry-After + code: "RATE_LIMITED" (แยกจาก SPEND_CAP_EXCEEDED / ASYNC_QUEUE_FULL ด้วย code) · ดู Authentication
Header ที่มากับทุกคำตอบ:
| Header | ความหมาย | หน่วย |
|---|---|---|
X-RateLimit-Limit | เพดานของ endpoint นี้ ต่อ IP ต้นทาง — 120 ทุก endpoint · 240 บน POST /verify | คำขอ/นาที |
X-RateLimit-Remaining | เหลือในหน้าต่างปัจจุบัน (ต่อ IP) | คำขอ |
X-RateLimit-Reset | เวลาจนกว่าหน้าต่างจะรีเซ็ต | วินาที (ไม่ใช่ epoch) |
Retry-After | มาเฉพาะกับ 429 — ต้องรอเท่าไรก่อนยิงใหม่ | วินาที |
X-Request-Id | id ของคำขอ — ค่าเดียวกับ meta.requestId / requestId ใน error | UUID |
มีตัวนับสองชั้น — X-RateLimit-* คือชั้น ต่อ IP (จึงเห็น 240 บน /verify และ 120 ที่อื่น)
ส่วนเพดาน 120 req/min ต่อ key เป็นอีกตัวนับหนึ่งที่ ไม่มี header บอกยอดคงเหลือ — คีย์เดียวยิง /verify ได้จริง 120 ครั้ง/นาที
แม้ X-RateLimit-Limit จะเขียน 240 · ชนชั้นไหนก่อนก็ได้ 429 + Retry-After เหมือนกัน ⇒ ให้ throttle จาก 429 ไม่ใช่จาก Remaining
Transaction status
status | verified | หักโควตา/เครดิต | Webhook | ความหมาย · ทำอย่างไรต่อ |
|---|---|---|---|---|
SUCCESS | ✅ | หัก 1 (key live) | slip.verified | สลิปถูกต้อง โอนเข้าบัญชีร้าน → เทียบ amount กับยอดออเดอร์ แล้วปล่อยของ · เช็ค errorCode ด้วย — STALE_SLIP = สลิปเก่า |
DUPLICATE | ❌ | ไม่หัก | slip.duplicate | เลขอ้างอิง (transRef) นี้เคยตรวจแล้ว → ห้ามปล่อยของซ้ำ |
AMOUNT_MISMATCH | ❌ | ไม่หัก | slip.failed | ยอดบนสลิปไม่ตรงเงื่อนไขที่ธนาคารถูกขอให้เช็ค — POST /verify ไม่รับยอดที่คาดไว้ ⇒ ให้เทียบ amount กับยอดออเดอร์ฝั่งคุณเอง |
RECEIVER_MISMATCH | ❌ | ไม่หัก | slip.failed | โอนเข้าบัญชีที่ไม่ใช่ของร้าน → เช็ค payeeAccounts ใน GET /me ว่าเพิ่มบัญชีที่ลูกค้าโอนเข้าแล้วหรือยัง |
INVALID | ❌ | ไม่หัก | slip.failed (ยกเว้น NO_PAYEE_CONFIGURED — ไม่มี event) | อ่านสลิปไม่ได้ / ไม่ใช่สลิป → ให้ลูกค้าส่งรูปสลิปจากแอปธนาคารใหม่ · ดู errorCode |
FRAUD | ❌ | ไม่หัก | slip.fraud | สลิปปลอม/แก้ไข — ไม่ตรงข้อมูลธนาคาร → ไม่ปล่อยของ |
QUOTA_EXCEEDED | ❌ | ไม่หัก | ไม่มี | โควตาหมด — POST /verify ตอบ 402 ไม่ใช่ 200 (ดูด้านบน) · สถานะนี้เห็นได้ใน GET /transactions เท่านั้น |
PROVIDER_ERROR | ❌ | ไม่หัก | ไม่มี | ระบบตรวจกับธนาคารขัดข้องชั่วคราว (200 + สถานะนี้) — ยังไม่ใช่คำตัดสิน → ส่งสลิปเดิมใหม่ได้เลย (ตรวจใหม่จริง ไม่ถูกแคช) |
- โควตานับเฉพาะ
SUCCESS— สลิปที่ไม่ผ่านทุกแบบ · ของซ้ำ ·cached: true· และคีย์sk_test_ไม่หักโควตาหรือเครดิตของร้าน · ดูผลของคำขอนั้น ๆ ได้จากquotaCharged GET /transactions?status=รับได้ครบทั้ง 8 ค่าในตารางนี้ (ค่าอื่น →400)- คำขอที่ได้
cached: trueไม่ยิง webhook ซ้ำ — event ออกครั้งเดียวตอนตรวจครั้งแรก
errorCode ของผลตรวจ
มีโค้ดสองที่ซึ่ง คนละชุดกัน — data.errorCode (คำขอสำเร็จ 200 · บอกเหตุผลของผลตรวจ) กับ code ในซอง error (4xx)
data.errorCode — อยู่ในผลตรวจ (POST /verify · GET /transactions)
errorCode | มากับ status | เกิดเมื่อไร | ฝั่งคุณควรทำอะไร |
|---|---|---|---|
null | SUCCESS · DUPLICATE · RECEIVER_MISMATCH · AMOUNT_MISMATCH | ไม่มีเหตุผลเพิ่มเติม | แตกกิ่งด้วย status |
STALE_SLIP | SUCCESS (หรือ INVALID — ดู Stale slip) | สลิปจริง แต่เวลาโอนเก่าเกิน 24 ชม. หรือลงเวลาล่วงหน้า | ตัดสินใจเองว่าจะรับสลิปเก่าไหม — เป็น คำเตือน ไม่ใช่การปฏิเสธ |
NO_PAYEE_CONFIGURED | INVALID | ร้านยังไม่ได้เพิ่มบัญชีรับเงินสักบัญชี — ยังไม่ได้ส่งไปตรวจกับธนาคาร | เพิ่มบัญชีที่ Settings → Shop → บัญชีรับเงิน แล้วส่งสลิปเดิมใหม่ |
INVALID | INVALID | อ่านสลิปไม่ได้ / ไม่ใช่สลิป / ธนาคารไม่พบรายการ | ให้ลูกค้าส่งรูปสลิปใหม่ |
FRAUD | FRAUD | สลิปปลอมหรือถูกแก้ไข | ไม่ปล่อยของ |
PROVIDER_ERROR | PROVIDER_ERROR | ระบบตรวจขัดข้องชั่วคราว | ส่งสลิปเดิมใหม่แบบเว้นช่วง |
QUOTA_EXCEEDED | QUOTA_EXCEEDED | โควตา + เครดิตหมด (เห็นใน GET /transactions) | เติมเครดิต / อัปเกรดแพ็ก |
errorCode ไม่ใช่ null เสมอไปเมื่อ status เป็น SUCCESS — อย่าเขียน if (errorCode) reject() · ตัดสินผ่าน/ไม่ผ่านจาก verified (หรือ status) เท่านั้น
และถือ errorCode ที่ไม่รู้จักเป็น "ไม่ผ่านด้วยเหตุอื่น" — เราอาจเพิ่มค่าใหม่โดยไม่ถือเป็น breaking change · errorMessage เป็นข้อความสำหรับคนอ่าน ห้ามใช้แตกกิ่ง
code — อยู่ในซอง error (Error envelope) · มีเฉพาะสามกรณีนี้ error อื่นไม่มีฟิลด์ code
code | HTTP | เกิดเมื่อไร | หักโควตาไหม | ฝั่งคุณควรทำอะไร |
|---|---|---|---|---|
QUOTA_EXCEEDED | 402 | key live — โควตารายเดือนและเครดิตของร้านหมด | ไม่ | เติมเครดิต / อัปเกรดแพ็ก / รอรอบใหม่ — retry ไม่ช่วย |
TEST_QUOTA_EXCEEDED | 402 | key test — ใช้ครบ 200 ครั้ง/เดือน | ไม่ | ย้ายไปคีย์ sk_live_ หรือรอเดือนถัดไป |
PLAN_API_ACCESS_REQUIRED | 403 | แพ็กปัจจุบันไม่รวม REST API (Trial · แพ็กหมดอายุ · ร้านถูกพัก) | ไม่ | อัปเกรดเป็น Starter ขึ้นไป |
Stale slip
สลิป จริง ที่ถูกนำกลับมาใช้กับออเดอร์ใหม่ผ่านการตรวจกับธนาคารได้ (ธนาคารยืนยันว่ารายการมีจริง ไม่ได้ยืนยันว่า "เพิ่งโอน")
เราจึงเทียบเวลาโอน (transDate) กับเวลาที่ส่งมาตรวจ — เก่ากว่า 24 ชั่วโมง หรือลงเวลาล่วงหน้า = ติดธง STALE_SLIP
· เช็คเฉพาะสลิปที่ผ่าน — สลิปที่ไม่ผ่านด้วยเหตุอื่น (เช่น RECEIVER_MISMATCH) จะไม่มี STALE_SLIP แม้จะเก่า เพราะไม่ผ่านอยู่แล้ว
| โหมด | ผลที่ได้ | ใช้อยู่ไหม |
|---|---|---|
alert | status: "SUCCESS" · verified: true · errorCode: "STALE_SLIP" — เจ้าของร้านได้รับแจ้งเตือน · หักโควตาตามปกติ | ค่าที่ใช้อยู่ตอนนี้ |
reject | status: "INVALID" · verified: false · errorCode: "STALE_SLIP" — ไม่หักโควตา | ยังไม่เปิด (ตั้งที่ระดับแพลตฟอร์ม ไม่ใช่ต่อร้าน) |
{
"status": "SUCCESS",
"verified": true,
"transDate": "2026-06-15T03:21:05.000Z",
"errorCode": "STALE_SLIP",
"errorMessage": "สลิปลงวันที่ย้อนหลังเกิน 24 ชม. (เก่า 48 ชม.)",
"warnings": [{ "code": "STALE_SLIP", "message": "สลิปลงวันที่ย้อนหลังเกิน 24 ชม. (เก่า 48 ชม.)" }]
}
ถ้าธุรกิจของคุณไม่รับสลิปเก่า ต้องเช็คเอง — verified === true อย่างเดียวไม่พอ:
if (slip.verified && slip.warnings.some((w) => w.code === 'STALE_SLIP')) { /* ถือเป็นไม่ผ่าน หรือส่งให้คนตรวจ */ }
(โค้ดเดิมที่เช็ค slip.errorCode === 'STALE_SLIP' ยังใช้ได้)
หรือเทียบ transDate กับเวลาสร้างออเดอร์ตามกติกาของคุณเอง (เช่น ต้องโอนหลังเปิดออเดอร์)
Idempotency
ระบบ dedupe ให้อัตโนมัติ — สามเคสที่ต่างกัน:
- ส่ง "รูปเดิม" ซ้ำภายใน 24 ชม. →
cached: true·status/verifiedเป็นผลจริงของ รายการเดิม (retry ตอนเน็ตตัด/timeout ได้อย่างปลอดภัย — ไม่ตรวจซ้ำ ไม่หักโควตา) transRefเดียวกัน เคยตรวจผ่านไปแล้ว (คนละไฟล์รูป/ถ่ายซ้ำ) →duplicate: trueและstatusจะเป็น"DUPLICATE"เสมอในการตอบครั้งนี้ (verified: false) — ไม่ว่าผลตรวจ ครั้งแรกจะเป็นอะไรก็ตาม- ส่งรูปของสลิปที่ตอบ
DUPLICATEไปแล้วซ้ำอีก → ยังได้DUPLICATE(duplicate: trueพร้อมcached: true) — ผลเดิมแบบcachedมีให้เฉพาะรูปที่ API เป็นคนตรวจครั้งแรกเอง · สลิปที่เคยเข้าทาง LINE หรือ Dashboard มาก่อน ส่งเข้า API กี่ครั้งก็เป็นDUPLICATE
Idempotency-Key — retry คำขอเดิมแบบไม่มีผลข้างเคียง
cached ครอบเฉพาะ "รูปเดิมไบต์ต่อไบต์ที่ตรวจเสร็จแล้ว" · ถ้าระบบคุณ retry ตอน timeout ระหว่างที่คำขอแรกยังไม่จบ
หรืออยากได้คำตอบชุดเดิมเป๊ะ (รวม quotaCharged ของครั้งแรก) ให้ส่ง header Idempotency-Key — ค่าเฉพาะต่อคำขอ เช่น UUID หรือเลขออเดอร์
curl -X POST https://api.slip-bolt.com/api/v1/verify \
-H "Authorization: Bearer sk_live_xxx" \
-H "Idempotency-Key: order-1001" \
-H "Content-Type: application/octet-stream" \
--data-binary @slip.jpg
| เหตุการณ์ | ผล |
|---|---|
ส่ง key เดิม + สลิปเดิม หลังครั้งแรกตอบ 200 แล้ว | 200 พร้อม data ชุดเดิมทุกฟิลด์ + header Idempotent-Replayed: true · ไม่ตรวจซ้ำ ไม่หักโควตาซ้ำ ไม่กลายเป็น DUPLICATE |
| ส่ง key เดิม ระหว่างที่ครั้งแรกยังประมวลผลอยู่ | 409 code: "IDEMPOTENCY_IN_PROGRESS" + Retry-After — รอแล้วส่งซ้ำด้วย key เดิม |
| ส่ง key เดิม แต่เป็นสลิปคนละใบ | 422 code: "IDEMPOTENCY_KEY_REUSED" — ใช้ key ใหม่ต่อหนึ่งคำขอ |
| key ผิดรูป (ว่าง · เกิน 255 ตัว · ไม่ใช่ ASCII ที่พิมพ์ได้) | 400 code: "IDEMPOTENCY_KEY_INVALID" |
ครั้งแรกจบด้วย error (4xx/5xx เช่น 402 โควตาหมด) | ไม่ถูกจำ — ส่งซ้ำด้วย key เดิมได้ ประมวลผลจริงอีกรอบ |
- เก็บ 24 ชม. ต่อร้าน (คีย์
sk_live_กับsk_test_แยกกัน · ร้านอื่นใช้ค่าเดียวกันได้ไม่ชนกัน) - "สลิปเดิม" เทียบจากรูปหลังถอดรูปแบบแล้ว — ส่ง multipart ใหม่ (boundary เปลี่ยน) ก็ยังนับเป็นคำขอเดิม
meta.requestId/timestampของ envelope เป็นของคำขอปัจจุบัน · headerX-Slipbolt-Quota-*เป็นยอดปัจจุบัน- ไม่ส่ง header = พฤติกรรมเดิมทุกอย่าง (ไม่บังคับ)
ห้าม credit ออเดอร์จากแค่ verified === true — ต้องเช็ค duplicate ก่อนเสมอถ้าระบบคุณ
เคยประมวลผล transRef นั้นไปแล้ว ไม่งั้นการ retry/ส่งซ้ำโดยไม่ตั้งใจจะ credit ซ้ำ
Error envelope
ทุก error รูปแบบเดียวกัน:
{
"success": false,
"error": "Forbidden",
"message": "API key missing scope(s): read",
"statusCode": 403,
"path": "/api/v1/quota",
"requestId": "3b1f0c9e-6a2d-4f5e-8b7c-1d2e3f4a5b6c",
"timestamp": "2026-06-17T03:30:45.120Z"
}
code มีเฉพาะบาง error (402 ทั้งสองแบบ · 403 กรณีแพ็ก · IDEMPOTENCY_* ของ 400/409/422 · METHOD_NOT_ALLOWED ของ 405 — ดู ตาราง code) ·
error อื่นไม่มีฟิลด์นี้ ให้แยกด้วย statusCode · message เป็น string เสมอ ยกเว้น 400 จาก validation ที่ผิดหลายข้อจะเป็น array ของ string
· requestId ตรงกับ header X-Request-Id ของคำตอบนั้น — แนบมาด้วยเมื่อติดต่อทีมงาน
| Status | เมื่อไร | ลองใหม่ได้ไหม |
|---|---|---|
400 | ไม่มีรูปในคำขอ / multipart ไม่มีไฟล์ / image ไม่ใช่ base64 / ดาวน์โหลด url ไม่ได้ / qr ผิดรูปแบบหรือเป็น QR รับเงิน · query ที่ไม่รู้จัก (เช่น ?page=2 → property page should not exist) หรือค่าผิดชนิด (limit=500 · status=PASSED) | ไม่ — แก้ request |
401 | ไม่มี header Authorization: Bearer … (Missing API key — …) · คีย์ผิดหรือถูก revoke (Invalid or revoked API key) — X-API-Key และ Basic auth ใช้ไม่ได้ | ไม่ — เช็คคีย์ |
402 | โควตา + เครดิตหมด — QUOTA_EXCEEDED (key live) หรือ TEST_QUOTA_EXCEEDED (key test เกิน 200/เดือน) | ไม่ — เติมเครดิต/อัปเกรด หรือใช้คีย์ live |
403 | scope ไม่พอ (error: "Forbidden" · ไม่มี code — ตัวอย่างด้านบน) หรือแพ็กปัจจุบันไม่รวม REST API (code: "PLAN_API_ACCESS_REQUIRED") | ไม่ — เพิ่ม scope / อัปเกรดแพ็ก |
403 | IP ไม่อยู่ใน allowlist ของคีย์ — code: "IP_NOT_ALLOWED" (ตั้งค่า) | ไม่ — เพิ่ม IP หรือเรียกจากเครื่องที่อนุญาต |
404 | ไม่พบรายการ (GET /transactions/:id) — เป็นของร้านอื่น หรือ id ไม่ใช่ UUID ก็ได้ 404 เหมือนกัน (ไม่ใช่ 400) · path ที่ไม่มี ก็ 404 | ไม่ |
405 | path ถูก แต่ method ผิด (เช่น GET /api/v1/verify) — code: "METHOD_NOT_ALLOWED" · header Allow บอก method ที่ใช้ได้ | ไม่ — แก้ method |
409 | IDEMPOTENCY_IN_PROGRESS — คำขอที่ใช้ Idempotency-Key เดียวกันยังประมวลผลอยู่ | ได้ — รอตาม Retry-After แล้วส่งซ้ำด้วย key เดิม |
422 | IDEMPOTENCY_KEY_REUSED — Idempotency-Key นี้เคยใช้กับสลิปใบอื่น | ไม่ — ใช้ key ใหม่ |
429 | เกิน rate limit — รอตาม header Retry-After (วินาที) ก่อนยิงใหม่ | ได้ — หลังรอตาม Retry-After |
429 SPEND_CAP_EXCEEDED | ถึงเพดานต่อคีย์ที่ร้านตั้งเอง (spendCap.window = hour/day) | อย่า retry วน — รอรอบใหม่ตาม Retry-After |
5xx | ระบบเราขัดข้องชั่วคราว | ได้ — เว้นช่วงแบบ exponential (ส่งรูปเดิมซ้ำปลอดภัย ไม่หักโควตาซ้ำ) |
แยกกรณีด้วย statusCode และ code — อย่าเทียบจากข้อความใน message (ข้อความปรับได้โดยไม่ถือเป็น breaking change)
ไคลเอนต์ที่ทำ retry ตามตารางนี้ให้แล้วอยู่ที่ ตัวอย่างพร้อมใช้
รหัสธนาคาร
bankCode / receiverBankCode ในผลตรวจ (และใน webhook) เป็น รหัสธนาคาร 3 หลัก ตามที่เครือข่ายธนาคารส่งมา
ส่วน payeeAccounts[].bankCode ใน GET /me เป็น ชื่อย่อ (slug) ที่ร้านเลือกตอนเพิ่มบัญชีรับเงิน — สองรูปแบบนี้ไม่เท่ากันตรง ๆ ใช้ตารางนี้เทียบ
| รหัส 3 หลัก | slug ใน /me | ธนาคาร | Bank |
|---|---|---|---|
002 | bbl | กรุงเทพ | Bangkok Bank |
004 | kbank | กสิกรไทย | Kasikornbank |
006 | ktb | กรุงไทย | Krungthai Bank |
011 | ttb | ทีเอ็มบีธนชาต | TMBThanachart (ttb) |
014 | scb | ไทยพาณิชย์ | Siam Commercial Bank |
017 | citi | ซิตี้แบงก์ | Citibank |
020 | sc | สแตนดาร์ดชาร์เตอร์ด (ไทย) | Standard Chartered (Thai) |
022 | cimb | ซีไอเอ็มบี ไทย | CIMB Thai |
024 | uob | ยูโอบี | UOB |
025 | bay | กรุงศรีอยุธยา | Krungsri (Bank of Ayudhya) |
030 | gsb | ออมสิน | Government Savings Bank |
033 | ghb | อาคารสงเคราะห์ (ธอส.) | Government Housing Bank |
034 | baac | ธ.ก.ส. | BAAC |
067 | tisco | ทิสโก้ | TISCO |
069 | kkp | เกียรตินาคินภัทร | Kiatnakin Phatra |
070 | icbc | ไอซีบีซี (ไทย) | ICBC (Thai) |
071 | tcrb | ไทยเครดิต | Thai Credit |
073 | lhbank | แลนด์ แอนด์ เฮ้าส์ | LH Bank |
088 | clicx | คลิกซ์ | CLICX |
slug ที่ ไม่มีรหัส 3 หลัก (ไม่ใช่ธนาคาร): promptpay (kind: "PROMPTPAY") · truemoney (kind: "WALLET") · other (ธนาคารที่ร้านพิมพ์เอง)
- ถือรหัสเป็น string ทึบ — เก็บเลข
0นำหน้าไว้ ("002"ไม่ใช่2) และรองรับรหัสที่ไม่อยู่ในตาราง: เราส่งต่อค่าที่ธนาคารให้มา ธนาคารใหม่จึงโผล่ได้โดยไม่มีประกาศ · แสดงผลให้คนอ่านใช้bankName/receiverBankNameได้เลย - สลิปโอนเข้า PromptPay —
receiverBankCodeคือธนาคารที่บัญชีปลายทางผูกอยู่ (หรือnullถ้าสลิปไม่บอก) ไม่ใช่promptpay - แอปรับเงินร้านค้า (K SHOP · แม่มณี · ถุงเงิน · มั่งมี) ใช้ slug ของธนาคารเจ้าของ (
kbank·scb·ktb·bay) - อย่าเทียบผู้รับเองจากรหัสธนาคาร — เราเทียบกับบัญชีรับเงินของร้านให้แล้ว ผลอยู่ที่
status(RECEIVER_MISMATCH)
ถัดไป
- Webhooks — รับ event
slip.verified/slip.duplicate/slip.fraud/slip.failedแบบ HMAC-signed แทนการ poll - Authentication — รายละเอียด key · โควตาต่อแพ็ก · rate limit
