คู่มือเชื่อมต่อ API
คู่มือการเชื่อมต่อ API ตรวจสอบสลิปธนาคารอัตโนมัติ สำหรับร้านค้าและนักพัฒนา
Base URL
ใช้เป็นต้นทางของทุก endpoint ในเอกสารนี้
https://backend-slip2try.lilsky-owner.workers.devAPI เวอร์ชัน v1 (ทุก endpoint อยู่ใต้ /api/v1) · อัปเดตล่าสุด 2026-10-23
- IP ที่ส่งคีย์ผิดเกิน 30 ครั้งต่อนาที: คีย์ที่ไม่ถูกต้องได้ HTTP 429 reason ใหม่ auth_locked (เดิม rate_limited — โค้ดที่หน่วงเวลาเมื่อได้ rate_limited ให้รองรับ auth_locked ด้วย) · คีย์ที่ถูกต้องที่ใช้งานอยู่จาก IP เดียวกันใช้ได้ตามปกติ
- ขีดจำกัดต่อนาทีนับแยกคีย์จริง (live) กับคีย์ทดสอบ (test) — ทดสอบหนักแค่ไหนก็ไม่กินโควต้าของคีย์จริง · ทุกคำตอบที่ผ่านด่านคีย์มี header X-RateLimit-Limit, X-RateLimit-Remaining และ X-RateLimit-Reset (วินาที)
- /usage: today.calls นับเฉพาะสลิปที่ธนาคารตัดสินแล้ว (ไม่รวมคำขอที่ถูกปฏิเสธและช่วงระบบขัดข้อง) · เพิ่ม environment และ breakdown { denied, errors } · คีย์ทดสอบนับผลจำลองของคีย์ทดสอบ
- Webhook: ช่วง 24 ชม. หลังหมุน Secret มีหัว X-VerifySlips-Signature-Previous (ลายเซ็นรุ่น 1 ด้วย Secret เดิม) ให้ระบบที่ยังตรวจ V1 ไม่สะดุด · ส่งครั้งแรกทันทีหลังตอบคำขอที่ทำให้เกิด webhook (ถึงหลังคำตอบ API เสมอ)
- Webhook: หมุน Secret ได้จากหน้า Webhook — Secret เดิมยังลงลายเซ็นคู่ในหัวใหม่ X-VerifySlips-Signature-V2-Previous อีก 24 ชั่วโมง อัปเดต Secret ฝั่งคุณได้โดยไม่ขาดช่วง
- Webhook: พักการส่งชั่วคราวได้ — ระหว่างพักไม่เข้าคิวเหตุการณ์ใหม่ (ไม่ส่งย้อนหลังเมื่อเปิดอีกครั้ง) · ปุ่มส่งทดสอบและรายการที่รอลองใหม่ยังส่งตามปกติ
- พอร์ทัล: หน้าประวัติการเรียก API (ทุกคำขอที่ยืนยันคีย์ได้ เก็บ 14 วัน ค้นด้วย requestId) · หน้าทดลองเรียก API ด้วยคีย์ทดสอบ · หน้าภาพรวมนักพัฒนาพร้อมเช็กลิสต์เชื่อมต่อ
- คำตอบที่ไม่ใช่ 2xx ของ /api/v1/* ทุกตัวใช้รูปแบบเดียวกัน: success: false, status, code, reason, message และ requestId (400 ที่ข้อมูลไม่ถูกต้องมี param บอกชื่อฟิลด์) · path ที่ไม่มีอยู่ได้ 404 not_found รูปแบบเดียวกัน · 401 ยังมีฟิลด์ error: invalid api key แบบเดิมอีก 1 รุ่น
- POST /api/v1/verify/slip รับ multipart/form-data แล้ว: รูปในฟิลด์ file (หรือส่ง qrData แทน) และ expectedAmount ได้ตามต้องการ
- เพิ่ม reason: invalid_api_key (401), payload_too_large (413), unsupported_media_type (415), too_many_failed (429) และ internal_error (500)
- ทุกคำตอบ 429 และ 503 มี header Retry-After (วินาที) รวมคีย์ทดสอบ: rate_limited = 60 · temporarily_unavailable = 5 · too_many_failed และเพดานรายวันของคีย์ทดสอบ = จำนวนวินาทีจนรีเซ็ต
- Webhook: body มี id (คงเดิมทุกครั้งที่ลองส่งใหม่ — ใช้กันประมวลผลซ้ำ) และ environment · header ใหม่ X-VerifySlips-Delivery · verify.* มี channel และ requestId
- /balance, /usage และ /ping มี requestId · /balance มี environment · /usage นับการตรวจจากทุกช่องทาง (API + LINE + เว็บ)
- คำว่า Bearer ใน Authorization ไม่สนตัวพิมพ์เล็ก/ใหญ่ · ตรวจ expectedAmount ก่อนอ่านรูป · รูป WebP ได้ 415 · Callback URL ต้องเป็น https · endpoint ค้นข้อมูลตอบ 503 temporarily_unavailable เมื่อระบบต้นทางขัดข้อง
- ฟิลด์หลักของรายละเอียดการโอนคือ data.transfer — data.slipVerification.transfer ยังมีให้แต่เลิกแนะนำแล้ว (deprecated)
สร้าง API Key → ส่งคำขอตรวจสอบสลิป → ดูประวัติย้อนหลัง
- ขั้นตอนที่ 1
สร้าง API Key
หลังสมัคร ไปที่หน้า API Keys แล้วกดสร้างคีย์ (ระบบไม่สร้างคีย์ให้อัตโนมัติ) รหัสลับแสดงครั้งเดียว — คัดลอกเก็บไว้ฝั่งเซิร์ฟเวอร์
- ขั้นตอนที่ 2
ส่ง QR จากสลิปมาตรวจสอบ
อ่านค่า QR จากสลิป (ขึ้นต้นด้วย 00...) ส่งมาที่
POST /api/v1/verify/slipคำตอบมีdata.transferบอกยอดเงินจริง และduplicateบอกว่าเป็นสลิปซ้ำหรือไม่ - ขั้นตอนที่ 3
ตรวจผลย้อนหลังได้ทุกใบ
ทุกคำขอถูกบันทึกไว้ในระบบ กดที่แถวเพื่อดูรายละเอียดสลิป ผู้โอน ผู้รับ จำนวนเงิน และ IP ต้นทาง
8 ข้อที่ต้องทำให้ครบก่อนเปิดรับเงินจริง
- 1เรียก API จากเซิร์ฟเวอร์ของคุณเท่านั้น ห้ามฝังคีย์ในหน้าเว็บหรือแอป · พัฒนาด้วยคีย์ s2t_test_ แล้วเปลี่ยนเป็น s2t_live_ ตอนใช้งานจริง
- 2ส่ง expectedAmount = ยอดของออเดอร์ ทุกครั้งที่ตรวจสลิป
- 3ส่งของ/เติมเงินเฉพาะเมื่อ success && !duplicate && warnings.length === 0 && environment === "live" — นอกนั้นให้ปฏิเสธหรือส่งให้คนตรวจ
- 4อ่านรายละเอียดการโอนจาก data.transfer · เก็บ transactionRef เป็นคีย์ไม่ซ้ำ (unique) และเก็บ verificationId คู่กับออเดอร์
- 5แยกกรณีด้วย success + reason เท่านั้น — ห้ามตัดสินจากข้อความใน message
- 6ตั้ง timeout ฝั่งคุณ 60 วินาที · หมดเวลาหรือหลุด ให้ค้นผลด้วย GET /api/v1/verifications?qrData= (หรือ ?requestId=) ก่อนส่งซ้ำ
- 7ได้ 429 หรือ 503 ให้รอตาม Retry-After แล้วค่อยลองใหม่ · งานจำนวนมากให้ทำคิว (ค่าเริ่มต้น 120 คำขอ/นาทีต่อร้าน)
- 8Webhook: ตรวจ X-VerifySlips-Signature-V2 จาก Raw Body และปฏิเสธเวลาที่ห่างเกิน 5 นาที · กันซ้ำด้วย id ใน body · ตอบ 2xx ภายใน 10 วินาที
ฝั่งซ้ายคือโค้ดส่งคำขอ ฝั่งขวาคือการถอดรหัสฟิลด์สลิปและโลโก้ธนาคารที่ได้รับยืนยัน
โค้ดส่งคำขอ
POST /api/v1/verify/slip
# ให้ของเมื่อ success: true, duplicate: false, warnings: [] และ environment: "live" เท่านั้น · expectedAmount = ยอดออเดอร์
curl -X POST https://backend-slip2try.lilsky-owner.workers.dev/api/v1/verify/slip \
--max-time 60 \
-H "Authorization: Bearer s2t_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"qrData":"0038000600000101030060217A3828f7b4f3244ff35102TH910480CE","expectedAmount":100000}'
# หมดเวลาหรือหลุด: ค้นผลก่อนส่งซ้ำ (ไม่หักโควต้า)
curl -G https://backend-slip2try.lilsky-owner.workers.dev/api/v1/verifications \
--max-time 60 \
-H "Authorization: Bearer s2t_live_xxxxxxxx" \
--data-urlencode "qrData=0038000600000101030060217A3828f7b4f3244ff35102TH910480CE"เก็บคีย์ไว้ฝั่งเซิร์ฟเวอร์เท่านั้น
s2t_live_xxxxxxxx ด้วยคีย์จริงของคุณ และอย่าฝังคีย์ไว้ในโค้ดฝั่งหน้าเว็บหรือแอปมือถือ — เรียกจากเซิร์ฟเวอร์หลังบ้าน (Backend Server) ของคุณเท่านั้นตัวอย่างการถอดรหัสสลิป

Krungthai Business · ธนาคารกรุงไทย
ยืนยันความถูกต้องตรงกับธนาคารแล้ว
ktbจำนวนเงินที่โอน
100,000.00 บาท

บัญชีผู้โอน
น.ส.สมศรี ใจดี

บัญชีผู้รับ
EXAMPLE CO., LTD.
- transactionRef
- A3828f7b4f3244ff3
- transactionDateTime
- 2026-09-01T17:51:01+07:00
- amount.amount
- 100,000.00 บาท
- duplicate
- false · สลิปใหม่
โครงสร้างเหมือนกันทุก Provider
data.transfer ครั้งเดียว ใช้ได้ทั้ง KTB, CIMB, SCB และ Slip2Go ทันทีเช็ค duplicate ทุกครั้งก่อนเติมเงินหรือส่งของ
success: true ไม่ได้แปลว่าเป็นการจ่ายเงินครั้งใหม่
success บอกแค่ว่าธนาคารยืนยันว่าสลิปจริง ถ้า duplicate: true แปลว่าร้านคุณเคยตรวจสลิปใบนี้สำเร็จแล้ว ลูกค้าอาจส่งสลิปเดิมมาใช้ซ้ำอ่านผลอย่างไร
| คำตอบ | ผล | สิ่งที่ต้องทำ |
|---|---|---|
| success: true · duplicate: false | สลิปใหม่ | เติมเงิน / ส่งของได้ |
| success: true · duplicate: true | สลิปซ้ำ | ปฏิเสธ — ลูกค้าอาจใช้สลิปเดิม |
| success: false | ตรวจไม่ผ่าน | ไม่ให้ของ แจ้งลูกค้าให้ตรวจสลิป |
ตรวจครั้งแรกจะได้ duplicate: false และ duplicateInfo: null
ตัวอย่างคำตอบเมื่อสลิปซ้ำ
{
"success": true,
"cached": true,
"duplicate": true,
"duplicateInfo": {
"firstVerifiedAt": "2026-10-14T04:14:49.145Z",
"previousCount": 2
},
"data": { "...": "รายละเอียดการโอนเหมือนตอนตรวจครั้งแรก" }
}นับเฉพาะร้านคุณ
ร้านอื่นเคยตรวจสลิปใบนี้ ไม่ทำให้ซ้ำสำหรับร้านคุณ — ร้านคุณตรวจครั้งแรกยังหัก 1 สลิปตามปกติ
ซ้ำด้วย QR หรือเลขอ้างอิง
QR เดิม หรือ QR ต่างกันแต่
transactionRefเดียวกัน นับว่าซ้ำ และสลิปซ้ำไม่หักสลิปครั้งที่ไม่ผ่านไม่นับ
ตรวจแล้วไม่ผ่าน จะไม่ถูกนับเป็นการตรวจครั้งก่อน
ส่งพร้อมกันได้ผลเดียว
ส่งสลิปเดียวกันหลายคำขอพร้อมกัน มีเพียงคำขอเดียวที่ได้
duplicate: falseWebhook ก็บอกด้วย
verify.successมีdata.duplicateและdata.firstVerifiedAtดูย้อนหลังในพอร์ทัล
หน้า ประวัติตรวจสอบ มีป้าย “สลิปซ้ำ” และตัวกรอง
กันอีกชั้นในระบบของคุณ
transactionRef เป็นคีย์ไม่ซ้ำ (unique) ในฐานข้อมูลออเดอร์ของคุณด้วย เผื่อกรณีโค้ดฝั่งคุณเรียกซ้ำหรือรับ webhook ซ้ำลองเชื่อมต่อและทดสอบทุกกรณีด้วยคีย์ทดสอบก่อนใช้งานจริง — ผลจำลอง ไม่เรียกธนาคาร ไม่หักโควต้า
- คีย์ทดสอบขึ้นต้นด้วย
s2t_test_— สร้างที่หน้า API Keys โดยเลือกประเภท “ทดสอบ” ใช้ Base URL และ endpoint เดียวกับคีย์จริง - ได้ผลจำลองทันที ไม่ส่งไปตรวจกับธนาคารหรือระบบตรวจ ไม่หักโควต้า และใช้ได้แม้ร้านยังไม่มีแพ็กเกจ
- ตรวจสลิปด้วยคีย์ทดสอบได้ 1,000 ครั้งต่อวันต่อร้าน (เกินได้ HTTP 429 reason: rate_limited) · ผลทดสอบเก็บไว้ 7 วัน
- Webhook ยังส่งตาม event ที่เลือกไว้ แต่มี header
X-VerifySlips-Test: trueและ"environment": "test"ใน body และไม่ลองส่งใหม่เมื่อล้มเหลว - ส่ง QR เดิมซ้ำ = duplicate: true เหมือนของจริง · ส่งรูปสลิปได้ (ระบบอ่าน QR จากรูปแล้วใช้กติกาเดียวกัน) · QR รับเงิน (000201…) ยังได้ 400 payment_qr
- คีย์ทดสอบเห็นเฉพาะผลทดสอบ คีย์จริงเห็นเฉพาะผลจริง (GET /api/v1/verifications) · ping ได้ environment: test และ ready: true
ผลทดสอบไม่ใช่เงินเข้าจริง
environment ทั้งในคำตอบและ webhook และไม่ส่งของเมื่อเป็นผลทดสอบ — พร้อมใช้งานจริงเมื่อไร เปลี่ยนเป็นคีย์ s2t_live_กำหนดผลจากท้ายข้อความ QR
| ท้าย QR | ผลที่ได้ | ในคำตอบ |
|---|---|---|
| …FAIL | ธนาคารตรวจไม่ผ่าน (HTTP 200 ไม่มี reason) — webhook verify.failed | success: false |
| …ERROR | ระบบตรวจขัดข้องชั่วคราว — ส่งใหม่ได้ | HTTP 503 · reason: temporarily_unavailable |
| …DUP | สลิปซ้ำ — duplicateInfo บอกเวลาตรวจครั้งแรก | duplicate: true · warnings: ["duplicate"] |
| …MISMATCH | โอนเข้าบัญชีอื่น (checks.receiver: mismatch) | warnings: ["receiver_mismatch"] |
| …AMOUNT | ยอดต่างจาก expectedAmount (checks.amount: mismatch) — ต้องส่ง expectedAmount มาด้วย | warnings: ["amount_mismatch"] |
| …OLD | เวลาโอนย้อนไป 48 ชม. (checks.stale: true) | warnings: ["stale_slip"] |
| อื่น ๆ | ผ่าน — ยอด = expectedAmount (ไม่ส่ง = 100 บาท) · ผู้รับ = บัญชีรับเงินบัญชีแรกของร้าน (ยังไม่ตั้งบัญชี = not_configured) | success: true · warnings: [] |
ระบบดูตัวอักษรท้ายสุดของ qrData (ไม่สนตัวพิมพ์เล็ก/ใหญ่) เช่น ORDER-1001-FAIL — qrData ยังต้องยาว 10–1000 ตัวอักษรเหมือนของจริง
ตัวอย่าง
Request · คีย์ทดสอบ
curl -X POST https://backend-slip2try.lilsky-owner.workers.dev/api/v1/verify/slip \
-H "Authorization: Bearer s2t_test_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"qrData":"TEST-ORDER-1001-AMOUNT","expectedAmount":250}'คลิกที่แต่ละรายการเพื่อพับ/ขยาย ดูพารามิเตอร์ที่ต้องส่ง ตัวอย่าง Request และหน้าตา Response ทั้งตอนสำเร็จและไม่สำเร็จ
ส่งรูปสลิปหรือข้อความ QR บนสลิป ระบบจะยืนยันกับธนาคารและคืนรายละเอียดการโอนเงินตัวจริง พร้อมผลเช็คว่าเป็นสลิปเก่าไหมและโอนเข้าบัญชีร้านไหม
Scope: verify:slip
พารามิเตอร์ที่ส่ง
| ฟิลด์ | ประเภท | จำเป็น | คำอธิบาย |
|---|---|---|---|
qrData | string | ค่าที่อ่านได้จาก QR บนสลิป ปกติขึ้นต้นด้วย 00 — ส่งทั้งสตริงตามที่อ่านได้ อย่าแปลงตัวพิมพ์หรือตัดตัวอักษรออก (ช่องว่างหัวท้ายระบบตัดให้เอง) · ส่ง qrData หรือ image อย่างใดอย่างหนึ่ง | |
image | string (base64) | รูปสลิป JPEG/PNG แบบ base64 (มี data:image/…;base64, นำหน้าได้) ระบบอ่าน QR ให้ — รูปไม่เกินราว 3 MB (body รวมไม่เกิน 4 MB) · หรือส่งไฟล์รูปเป็น body ตรงๆ พร้อม Content-Type: image/jpeg หรือ image/png · หรือ multipart/form-data ฟิลด์ file — คำขอทั้งก้อนไม่เกิน 4 MB · WebP, GIF, HEIC, AVIF และ PDF ได้ 415 unsupported_media_type | |
expectedAmount | number | string | ยอดของออเดอร์ (บาท) ให้ระบบเทียบกับยอดในสลิป — ควรส่งทุกครั้ง · ตัวเลขหรือสตริงตัวเลข มากกว่า 0 ทศนิยมไม่เกิน 2 ตำแหน่ง · ส่งไฟล์รูปเป็น body ตรงๆ ให้ใส่เป็น ?expectedAmount= ใน URL · multipart ใส่เป็นฟิลด์ข้อความ expectedAmount · ตรวจก่อนอ่านรูป ค่าไม่ถูกต้องได้ 400 invalid_request (param: expectedAmount) |
ตัวอย่าง Request
{
"qrData": "0038000600000101030060217A3828f7b4f3244ff35102TH910480CE",
"expectedAmount": 100000
}ตัวอย่าง Response
{
"status": "Success",
"code": "200",
"success": true,
"message": "Bank slip QR code verified successfully",
"qrData": "0038000600000101030060217A3828f7b4f3244ff35102TH910480CE",
"verificationId": "6f1c2a9e-4b7d-4c1e-9a52-0d3f8e7b1c24",
"environment": "live",
"source": "qr",
"requestId": "3b1f0c9e-6a2d-4f5e-8b7c-1d2e3f4a5b6c",
"provider": "ktb",
"cached": false,
"duplicate": false,
"duplicateInfo": null,
"warnings": [],
"checks": {
"stale": false,
"slipAgeHours": 0.1,
"receiver": "matched",
"receiverLabel": "บัญชีหลัก",
"amount": "matched",
"expectedAmount": 100000
},
"data": {
"type": "SLIP_VERIFICATION",
"payment": null,
"transfer": {
"transactionRef": "A3828f7b4f3244ff3",
"transactionDateTime": "2026-09-01T17:51:01+07:00",
"fromBankCode": "006",
"fromBankName": "กรุงไทย",
"fromAccountNo": "XXX-X-XX123-4",
"fromAccountName": "น.ส.สมศรี ใจดี",
"fromAccountNameTh": "น.ส.สมศรี ใจดี",
"fromAccountNameEn": "MS SOMSRI JAIDEE",
"toBankCode": "014",
"toBankName": "ไทยพาณิชย์",
"toAccountNo": "XXX-X-XX567-8",
"toAccountName": "EXAMPLE CO., LTD.",
"toAccountNameTh": "บริษัท ตัวอย่าง จำกัด",
"toAccountNameEn": "EXAMPLE CO., LTD.",
"amount": {
"amount": 100000,
"currency": { "code": "THB", "displayName": "บาท", "symbol": "฿" }
}
},
"slipVerification": {
"transfer": { "...": "เลิกแนะนำแล้ว ข้อมูลเดียวกับ data.transfer" }
}
}
}ฟิลด์สำคัญในคำตอบ
| ชื่อฟิลด์ | ประเภท | ความหมาย |
|---|---|---|
success | boolean | ตัวชี้ขาดว่าธนาคารยืนยันสลิปหรือไม่ — ให้เช็คค่านี้ ไม่ใช่ HTTP status |
verificationId | string | รหัสผลตรวจครั้งนี้ — ตรงกับ data.verificationId ใน webhook ใช้ดึงผลย้อนหลังด้วย GET /api/v1/verifications/:id (ไม่มีเมื่อถูกปฏิเสธพร้อม reason) |
environment | "live" | "test" | live = คีย์จริง ตรวจกับธนาคาร · test = คีย์ทดสอบ ผลจำลอง (ห้ามใช้ตัดสินการส่งของ) |
warnings | string[] | สิ่งที่ต้องเช็คก่อนส่งของ: duplicate · receiver_mismatch · amount_mismatch · stale_slip — อาร์เรย์ว่าง = ไม่มีอะไรต้องระวัง |
checks.receiver | string | matched = เข้าบัญชีรับเงินที่ร้านตั้งไว้ · mismatch = โอนเข้าบัญชีอื่น · not_configured = ร้านยังไม่ได้ตั้งบัญชีรับเงิน (ตั้งได้ที่หน้า LINE Bot) |
checks.amount | string | matched = ยอดตรงกับ expectedAmount · mismatch = ไม่ตรง (warnings มี amount_mismatch) · not_checked = ไม่ได้ส่ง expectedAmount |
checks.expectedAmount | number | null | ยอดที่ส่งมาให้เทียบ (null = ไม่ได้ส่ง) |
checks.stale | boolean | true = เวลาโอนเก่ากว่า 24 ชม. หรือเป็นเวลาในอนาคต — อาจเป็นสลิปจริงที่เอากลับมาใช้กับออเดอร์ใหม่ |
requestId | string | รหัสคำขอ ตรงกับ header X-Request-Id — แจ้งทีมงานพร้อมรหัสนี้ เราหาคำขอนั้นเจอทันที |
source | string | qr = ส่งข้อความ QR มาเอง · image = ระบบอ่าน QR จากรูปให้ |
duplicate | boolean | true = ร้านคุณเคยตรวจสลิปใบนี้สำเร็จแล้ว ต้องเช็คก่อนเติมเงินหรือส่งของทุกครั้ง |
duplicateInfo.firstVerifiedAt | string | null | เวลาที่ตรวจสำเร็จครั้งแรก (ISO 8601) — เป็น null ทั้งก้อนเมื่อ duplicate: false |
duplicateInfo.previousCount | number | จำนวนครั้งที่ร้านคุณเคยตรวจสลิปใบนี้สำเร็จมาก่อน |
cached | boolean | true = ผลมาจากแคชกลางของระบบ (สลิปนี้เคยถูกตรวจสำเร็จแล้ว ไม่ต้องถามธนาคารซ้ำ) — ไม่หักสลิปเฉพาะกรณีสลิปซ้ำของร้านคุณ (duplicate: true) ถ้าร้านอื่นเคยตรวจ ร้านคุณยังเสีย 1 สลิป |
provider | string | null | ผู้ตรวจที่ตอบคำขอนี้ เช่น ktb (อาจเป็น null) |
data.transfer | object | รายละเอียดการโอนทั้งหมด — อ่านจาก path นี้ |
…transfer.transactionRef | string | เลขอ้างอิงธุรกรรม ใช้กันสลิปซ้ำและใช้ค้นในหน้าประวัติ |
…transfer.transactionDateTime | string | เวลาทำรายการตามที่ธนาคารส่งมา (สตริง ISO-8601) — รูปแบบและโซนเวลาอาจต่างกันตามธนาคาร |
…transfer.amount.amount | number | null | จำนวนเงิน (บาท) — อยู่ซ้อนอีกชั้นใต้ amount เช่น 100000 คือหนึ่งแสนบาท (ไม่ใช่สตางค์) อาจเป็น null |
…transfer.fromAccountNo | string | เลขบัญชีผู้โอน ธนาคารปิดบังบางหลักมาให้แล้ว |
data.slipVerification.transfer | object | ชื่อเดิมของ data.transfer (ข้อมูลเดียวกัน) — เลิกแนะนำแล้ว (deprecated) โค้ดใหม่ให้อ่าน data.transfer |
ข้อควรทราบและคำแนะนำ
- โครงสร้างคำตอบเหมือนกันทุก provider เขียนโค้ดชุดเดียวใช้ได้ทุกเจ้า — แต่ค่าในฟิลด์เป็นไปตามที่ธนาคารส่งมา
- transactionDateTime ส่งต่อตามที่ธนาคารให้มา (ISO-8601) — ใช้ไลบรารีวันที่ parse อย่าตัดสตริงเอง และอย่าสมมติโซนเวลาตายตัว
- provider และ amount.amount อาจเป็น null ได้ — ถ้าจะใช้ยอดเงินตัดสินใจ ให้เช็คว่ามีค่าก่อนเสมอ
- toBankCode อาจว่างได้เมื่อปลายทางเป็นพร้อมเพย์หรือ e-wallet — อย่าถือว่าต้องมีค่าเสมอ
- การปิดบังเลขบัญชีเป็นของธนาคารแต่ละเจ้า รูปแบบจึงต่างกันได้ เช่น xxxx-xx328-0 กับ xxxxxx3280 — ใช้ transactionRef เป็นตัวอ้างอิงแทน
- ชื่อบัญชีมีทั้ง Th และ En บางธนาคารส่งมาไม่ครบ ให้เผื่อกรณีเป็น null ไว้
- cached: true แปลว่าตอบจากแคชกลาง (เร็วกว่า) — สลิปซ้ำของร้านคุณไม่หักสลิปเพิ่มและจะติด duplicate: true ส่วนสลิปที่ร้านอื่นเคยตรวจยังหัก 1 สลิป — ดูหัวข้อ 'ป้องกันสลิปซ้ำ'
- คำตอบตอนธนาคารตรวจไม่ผ่านจะไม่มีฟิลด์ reason — reason มีเฉพาะคำตอบที่ไม่ใช่ 2xx (ถูกปฏิเสธหรือผิดพลาด ดูหัวข้อ 'เมื่อถูกปฏิเสธและข้อผิดพลาด') ถ้า success: false และไม่มี reason แปลว่าธนาคารตรวจสลิปใบนั้นไม่ผ่าน เช่น สลิปเก่าเกินไปหรือไม่พบรายการ
- ข้อความใน message เป็นภาษาไทยและอาจเปลี่ยนได้ — ให้ระบบของคุณตัดสินจาก success, duplicate, warnings และ reason เท่านั้น
- success: true แปลว่าธนาคารยืนยันว่าสลิปจริง — ยังต้องเช็ค warnings: receiver_mismatch = เงินไม่ได้เข้าบัญชีร้าน · amount_mismatch = ยอดไม่ตรงกับ expectedAmount · stale_slip = สลิปเก่า (ระบบไม่ปฏิเสธให้ ร้านตัดสินตามกติกาของร้าน)
- ส่ง expectedAmount มาด้วย ระบบจะเทียบยอดให้ — ยอดไม่ตรงยังได้ success: true แต่มี warnings: amount_mismatch และ checks.amount: mismatch (ธนาคารไม่ส่งยอดมา ถือว่าไม่ตรง)
- ส่งไฟล์รูปตรงๆ: curl -X POST "…/api/v1/verify/slip?expectedAmount=250" -H "Authorization: Bearer $API_KEY" -H "Content-Type: image/jpeg" --data-binary @slip.jpg (ไม่เทียบยอดก็ไม่ต้องใส่ ?expectedAmount=)
- ตั้ง timeout ฝั่งคุณ 60 วินาที · หมดเวลาหรือหลุดกลางทาง อย่าเพิ่งส่งสลิปซ้ำ — เรียก GET /api/v1/verifications?qrData=… (หรือ ?requestId=… ถ้าได้ header X-Request-Id มาแล้ว) ดูผลก่อน ไม่หักโควต้า · ถ้าส่งซ้ำแล้วได้ duplicate: true ให้เช็คว่าครั้งแรกคือคำขอของคุณเองที่หมดเวลาไปหรือไม่
- ส่งแบบ multipart/form-data: curl -X POST "…/api/v1/verify/slip" -H "Authorization: Bearer $API_KEY" -F "file=@slip.jpg" -F "expectedAmount=250" — รูป JPEG/PNG ในฟิลด์ file (หรือฟิลด์ข้อความ qrData แทนรูป) · ทั้งคำขอไม่เกิน 4 MB · คำตอบมี source: image
- คีย์ทดสอบ (s2t_test_…) ได้ผลจำลองที่มี environment: test — ดูหัวข้อ 'โหมดทดสอบ'
- QR รับเงิน (พร้อมเพย์ / QR ร้านค้า ขึ้นต้นด้วย 000201) ไม่ใช่ QR บนสลิป — ได้ reason: payment_qr ทันที ไม่หักสลิป
ดึงผลที่เคยตรวจแล้วด้วย verificationId (จากคำตอบตรวจสลิปหรือจาก webhook) — ไม่ตรวจซ้ำและไม่หักโควต้า
Scope: verify:slip
พารามิเตอร์ที่ส่ง
| ฟิลด์ | ประเภท | จำเป็น | คำอธิบาย |
|---|---|---|---|
id | string (path) | จำเป็น | verificationId จากคำตอบ POST /api/v1/verify/slip หรือ data.verificationId ใน webhook — ใส่ใน path |
ตัวอย่าง Request
// GET /api/v1/verifications/6f1c2a9e-4b7d-4c1e-9a52-0d3f8e7b1c24
// Authorization: Bearer s2t_live_xxxxxxxxตัวอย่าง Response
{
"success": true,
"data": {
"verificationId": "6f1c2a9e-4b7d-4c1e-9a52-0d3f8e7b1c24",
"environment": "live",
"status": "success",
"channel": "api",
"createdAt": "2026-10-14T04:20:11.512Z",
"requestId": "3b1f0c9e-6a2d-4f5e-8b7c-1d2e3f4a5b6c",
"qrData": "0038000600000101030060217A3828f7b4f3244ff35102TH910480CE",
"transactionRef": "A3828f7b4f3244ff3",
"amount": 100000,
"message": null,
"reason": null,
"duplicate": false,
"cached": false,
"warnings": [],
"checks": {
"stale": false,
"slipAgeHours": 0.1,
"receiver": "matched",
"receiverLabel": "บัญชีหลัก",
"amount": "matched",
"expectedAmount": 100000
},
"transfer": { "...": "ข้อมูลเดียวกับ data.transfer" }
},
"requestId": "9d4e2b17-5c6a-4f3e-8b1d-2a7c0e9f6b35"
}ฟิลด์สำคัญในคำตอบ
| ชื่อฟิลด์ | ประเภท | ความหมาย |
|---|---|---|
data | object | ผลตรวจ 1 รายการ (ฟิลด์ตามแถวด้านล่าง) |
…verificationId | string | รหัสผลตรวจ ตรงกับ verificationId ในคำตอบตรวจสลิปและใน webhook |
…environment | "live" | "test" | live = ผลจริง · test = ผลจำลองจากคีย์ทดสอบ |
…status | "success" | "failed" | success = ธนาคารยืนยันสลิป · failed = ตรวจไม่ผ่านหรือถูกปฏิเสธ |
…channel | "api" | "line" | "web" | ช่องทางที่ส่งสลิปมา: api · line (บอท LINE ของร้าน) · web (หน้าตรวจสลิปในพอร์ทัล) |
…createdAt | string | เวลาที่ตรวจ (ISO 8601) |
…amount | number | null | ยอดเงิน (บาท) — null เมื่อตรวจไม่ผ่าน |
…reason | string | null | เหตุที่ถูกปฏิเสธก่อนถึงธนาคารหรือระบบขัดข้อง — null = ธนาคารตัดสินแล้ว |
…warnings | string[] | เหมือนในคำตอบตรวจสลิป: duplicate · receiver_mismatch · amount_mismatch · stale_slip |
…checks | object | null | ผลเช็คชุดเดียวกับตอนตรวจ (stale · receiver · amount) — อาจเป็น null |
…transfer | object | null | รายละเอียดการโอน รูปแบบเดียวกับ data.transfer ของการตรวจสลิป — null เมื่อตรวจไม่ผ่าน |
ข้อควรทราบและคำแนะนำ
- คืนผลตามที่บันทึกไว้ตอนตรวจ ไม่ถามธนาคารซ้ำและไม่หักโควต้า
- ไม่ใช่ผลของร้านคุณหรือไม่พบ → HTTP 404 พร้อม reason: not_found
- คีย์ทดสอบเห็นเฉพาะผลทดสอบ (เก็บ 7 วัน) และคีย์จริงเห็นเฉพาะผลจริง — ใช้คีย์คนละประเภทจะได้ 404
- ใช้สิทธิ์ verify:slip และผ่านด่านเดียวกับการตรวจสลิป (IP Whitelist · rate limit)
ค้นผลตรวจของร้านด้วย qrData, transactionRef หรือ requestId — เช่น คำขอหมดเวลากลางทาง ให้ถามผลจากที่นี่แทนการส่งสลิปซ้ำ
Scope: verify:slip
พารามิเตอร์ที่ส่ง
| ฟิลด์ | ประเภท | จำเป็น | คำอธิบาย |
|---|---|---|---|
qrData | string (query) | ข้อความ QR ที่เคยส่งตรวจ (ต้องตรงทุกตัวอักษร · URL-encode ก่อนใส่ใน query) | |
transactionRef | string (query) | เลขอ้างอิงธุรกรรมจากผลตรวจ | |
requestId | string (query) | requestId (header X-Request-Id) ของคำขอตรวจครั้งนั้น | |
limit | number (query) | จำนวนรายการสูงสุด 1–50 (ค่าเริ่มต้น 20) |
ตัวอย่าง Request
// GET /api/v1/verifications?transactionRef=A3828f7b4f3244ff3&limit=5
// Authorization: Bearer s2t_live_xxxxxxxxตัวอย่าง Response
{
"success": true,
"data": [
{
"verificationId": "6f1c2a9e-4b7d-4c1e-9a52-0d3f8e7b1c24",
"environment": "live",
"status": "success",
"channel": "api",
"createdAt": "2026-10-14T04:20:11.512Z",
"requestId": "3b1f0c9e-6a2d-4f5e-8b7c-1d2e3f4a5b6c",
"qrData": "0038000600000101030060217A3828f7b4f3244ff35102TH910480CE",
"transactionRef": "A3828f7b4f3244ff3",
"amount": 100000,
"message": null,
"reason": null,
"duplicate": false,
"cached": false,
"warnings": [],
"checks": {
"stale": false,
"slipAgeHours": 0.1,
"receiver": "matched",
"receiverLabel": "บัญชีหลัก",
"amount": "matched",
"expectedAmount": 100000
},
"transfer": { "...": "ข้อมูลเดียวกับ data.transfer" }
}
],
"requestId": "1f7a3c9e-2d4b-4e8a-b6c5-9e0d1a2b3c4d"
}ฟิลด์สำคัญในคำตอบ
| ชื่อฟิลด์ | ประเภท | ความหมาย |
|---|---|---|
data | object[] | ผลตรวจที่ตรงเงื่อนไข เรียงใหม่สุดก่อน — ไม่พบ = อาร์เรย์ว่าง |
…verificationId | string | รหัสผลตรวจ ตรงกับ verificationId ในคำตอบตรวจสลิปและใน webhook |
…environment | "live" | "test" | live = ผลจริง · test = ผลจำลองจากคีย์ทดสอบ |
…status | "success" | "failed" | success = ธนาคารยืนยันสลิป · failed = ตรวจไม่ผ่านหรือถูกปฏิเสธ |
…channel | "api" | "line" | "web" | ช่องทางที่ส่งสลิปมา: api · line (บอท LINE ของร้าน) · web (หน้าตรวจสลิปในพอร์ทัล) |
…createdAt | string | เวลาที่ตรวจ (ISO 8601) |
…amount | number | null | ยอดเงิน (บาท) — null เมื่อตรวจไม่ผ่าน |
…reason | string | null | เหตุที่ถูกปฏิเสธก่อนถึงธนาคารหรือระบบขัดข้อง — null = ธนาคารตัดสินแล้ว |
…warnings | string[] | เหมือนในคำตอบตรวจสลิป: duplicate · receiver_mismatch · amount_mismatch · stale_slip |
…checks | object | null | ผลเช็คชุดเดียวกับตอนตรวจ (stale · receiver · amount) — อาจเป็น null |
…transfer | object | null | รายละเอียดการโอน รูปแบบเดียวกับ data.transfer ของการตรวจสลิป — null เมื่อตรวจไม่ผ่าน |
ข้อควรทราบและคำแนะนำ
- ใส่ตัวกรองหลายตัวพร้อมกันได้ (ต้องตรงทุกตัว) · ไม่ใส่ตัวกรอง = ผลล่าสุดของร้าน
- เรียงใหม่สุดก่อน · ไม่พบ = HTTP 200 พร้อม data เป็นอาร์เรย์ว่าง
- ตัวกรองผิดรูปแบบหรือ limit นอกช่วง 1–50 → HTTP 400 พร้อม reason: invalid_request
- ไม่หักโควต้า · คีย์ทดสอบเห็นเฉพาะผลทดสอบ คีย์จริงเห็นเฉพาะผลจริง
ยังไม่เปิดให้บริการ — ทุกคำขอถูกด่านตรวจสิทธิ์ของคีย์ปฏิเสธด้วย HTTP 403 reason: service_disabled
Scope: verify:name
พารามิเตอร์ที่ส่ง
| ฟิลด์ | ประเภท | จำเป็น | คำอธิบาย |
|---|---|---|---|
accountNumber | string | จำเป็น | เลขบัญชี ใส่เฉพาะตัวเลข ไม่ต้องมีขีด |
bankCode | string | จำเป็น | รหัสธนาคาร 3 หลัก เช่น 014 = ไทยพาณิชย์, 004 = กสิกรไทย, 006 = กรุงไทย |
ตัวอย่าง Request
{
"accountNumber": "1234567890",
"bankCode": "014"
}ตัวอย่าง Response
{
"success": true,
"message": "Account name verified",
"data": {
"accountNumber": "1234567890",
"accountName": "นาย สมชาย ใจดี",
"accountNameTh": "นาย สมชาย ใจดี",
"accountNameEn": "MR. SOMCHAI JAIDEE",
"bankCode": "014",
"bankName": "ไทยพาณิชย์",
"iconUrl": "https://raw.githubusercontent.com/casperstack/thai-banks-logo/master/icons/SCB.png"
}
}ฟิลด์สำคัญในคำตอบ
| ชื่อฟิลด์ | ประเภท | ความหมาย |
|---|---|---|
data.accountName | string | ชื่อภาษาไทย เท่ากับ accountNameTh เก็บไว้เพื่อความเข้ากันได้ย้อนหลัง |
data.accountNameEn | string | ชื่อภาษาอังกฤษ อาจว่างถ้าธนาคารไม่ส่งมา |
data.bankName | string | ชื่อธนาคารที่ตรงกับ bankCode ที่ส่งไป |
data.iconUrl | string | URL โลโก้ธนาคารอย่างเป็นทางการ นำไปแสดงผลในหน้าเว็บได้ทันที |
ข้อควรทราบและคำแนะนำ
- บริการนี้ยังไม่เปิดให้บริการ — ตัวอย่างด้านบนคือรูปแบบที่จะใช้เมื่อเปิด
- ตอนนี้ทุกคำขอไม่ผ่านด่านตรวจสิทธิ์ (permission) ของคีย์ ได้ HTTP 403 พร้อม reason: service_disabled (รูปแบบคำตอบเดียวกับข้อผิดพลาดอื่น)
- รหัส bankCode ใช้ชุดเดียวกับส่วน 'รหัสธนาคารในผลตรวจ'
เช็คว่าคีย์ · IP Whitelist · แพ็กเกจพร้อมใช้งานไหม โดยไม่ตรวจสลิปจริงและไม่หักโควต้า — เหมาะใช้ตอนติดตั้งครั้งแรก
Scope: verify:slip
พารามิเตอร์ที่ส่ง
ไม่ต้องส่ง Request Body — แนบเฉพาะ Authorization Header: Authorization: Bearer s2t_live_xxxxxxxx
ตัวอย่าง Request
// GET Request Header:
// Authorization: Bearer s2t_live_xxxxxxxxตัวอย่าง Response
{
"success": true,
"message": "เชื่อมต่อสำเร็จ พร้อมตรวจสลิป",
"ready": true,
"environment": "live",
"merchant": { "code": "M0001", "name": "ร้านของคุณ" },
"apiKey": { "name": "เซิร์ฟเวอร์หลัก", "keyPrefix": "s2t_live_ab12" },
"yourIp": "203.0.113.10",
"plan": { "name": "BASIC", "remainingSlips": 1850, "expiresAt": "2026-11-13T17:00:00.000Z", "expired": false },
"requestId": "e3a7c1f9-5b2d-4e8a-9c6f-1d4b7e0a3c52"
}ฟิลด์สำคัญในคำตอบ
| ชื่อฟิลด์ | ประเภท | ความหมาย |
|---|---|---|
ready | boolean | true = ส่งตรวจสลิปได้ทันที (คีย์ทดสอบเป็น true เสมอ) · false = แพ็กเกจหมดอายุหรือสลิปหมด (แบบเครดิต: เครดิตน้อยกว่าค่าตรวจ 1 ครั้ง) |
environment | "live" | "test" | live = คีย์จริง · test = คีย์ทดสอบ (ผลจำลอง ไม่ต้องมีแพ็กเกจ) |
yourIp | string | IP ที่ระบบเห็นจากเซิร์ฟเวอร์ของคุณ — นำไปใส่ IP Whitelist ได้เลย |
plan.remainingSlips | number | null | สลิปคงเหลือ (null = ไม่จำกัด) |
requestId | string | รหัสคำขอ ตรงกับ header X-Request-Id — แจ้งทีมงานพร้อมรหัสนี้ เราหาคำขอนั้นเจอทันที |
ข้อควรทราบและคำแนะนำ
- ผ่านด่านเดียวกับการตรวจสลิปทุกข้อ (คีย์ · ร้าน · IP · ความถี่) จึงใช้หาสาเหตุที่ถูกปฏิเสธได้
- ไม่เรียกธนาคารและไม่หักโควต้า
- คีย์ทดสอบได้ environment: test และ ready: true เสมอ แม้ร้านยังไม่มีแพ็กเกจ
เครดิตคงเหลือของร้าน ใช้เช็คก่อนยิงงานจำนวนมาก (ไม่ต้องส่ง body — ใส่แค่ header ของคีย์)
Scope: verify:slip
พารามิเตอร์ที่ส่ง
ไม่ต้องส่ง Request Body — แนบเฉพาะ Authorization Header: Authorization: Bearer s2t_live_xxxxxxxx
ตัวอย่าง Request
// GET Request Header:
// Authorization: Bearer s2t_live_xxxxxxxxตัวอย่าง Response
{
"success": true,
"environment": "live",
"balance": 1850,
"currency": "credit",
"plan": {
"name": "BASIC",
"billingMode": "package",
"remainingSlips": 1850,
"expiresAt": "2026-11-13T17:00:00.000Z"
},
"requestId": "6d0b3f8e-2a5c-4e9b-a7d1-8c4f1e6b0a27"
}ฟิลด์สำคัญในคำตอบ
| ชื่อฟิลด์ | ประเภท | ความหมาย |
|---|---|---|
environment | "live" | "test" | live = คีย์จริง · test = คีย์ทดสอบ |
balance | number | ยอดคงเหลือ — แบบแพ็กเกจคือจำนวนสลิปที่ยังตรวจได้ (1 เครดิต = 1 สลิป) |
currency | string | หน่วย (credit) |
plan | object | null | แพ็กเกจปัจจุบัน — null = ยังไม่มีแพ็กเกจ |
plan.remainingSlips | number | null | สลิปคงเหลือ (null = ไม่จำกัด) |
plan.expiresAt | string | null | วันหมดอายุแพ็กเกจ (ISO 8601) |
requestId | string | รหัสคำขอ ตรงกับ header X-Request-Id — แจ้งทีมงานพร้อมรหัสนี้ เราหาคำขอนั้นเจอทันที |
ข้อควรทราบและคำแนะนำ
- ไม่ต้องส่ง Request Body — แนบเฉพาะ Authorization Header
- ต้องใช้คีย์ที่มีสิทธิ์ verify:slip และผ่านด่านเดียวกับการตรวจสลิป (IP Whitelist · rate limit)
- คีย์ผิดหรือถูกเพิกถอน → HTTP 401 reason: invalid_api_key (ฟิลด์ error: invalid api key แบบเดิมยังอยู่อีก 1 รุ่น)
จำนวนการตรวจและสลิปที่ใช้ไปวันนี้จากทุกช่องทาง (API + LINE + เว็บ) นับตามเวลาไทย (ไม่ต้องส่ง body — ใส่แค่ header ของคีย์)
Scope: verify:slip
พารามิเตอร์ที่ส่ง
ไม่ต้องส่ง Request Body — แนบเฉพาะ Authorization Header: Authorization: Bearer s2t_live_xxxxxxxx
ตัวอย่าง Request
// GET Request Header:
// Authorization: Bearer s2t_live_xxxxxxxxตัวอย่าง Response
{
"success": true,
"environment": "live",
"today": {
"calls": 312,
"credits": 298
},
"breakdown": {
"denied": 4,
"errors": 1
},
"requestId": "0f6c9a2e-8d4b-4b1f-a3e7-5c2d9f6a1b84"
}ฟิลด์สำคัญในคำตอบ
| ชื่อฟิลด์ | ประเภท | ความหมาย |
|---|---|---|
environment | "live" | "test" | live = คีย์จริง (นับประวัติจริงของร้าน) · test = คีย์ทดสอบ (นับผลจำลองของคีย์ทดสอบเท่านั้น) |
today.calls | number | สลิปที่ธนาคารตัดสินแล้ววันนี้ (ผ่าน + ไม่ผ่าน) จากทุกช่องทาง (API + บอท LINE + หน้าเว็บ) — ไม่นับคำขอที่ถูกปฏิเสธก่อนถึงธนาคาร (แพ็กเกจหมด/หมดอายุ ฯลฯ) และช่วงระบบผู้ตรวจขัดข้อง |
today.credits | number | สลิปที่ถูกหักจริงวันนี้ (ตรวจผ่านและไม่ใช่สลิปซ้ำของร้านคุณ) · คีย์ทดสอบเป็น 0 เสมอ |
breakdown.denied | number | คำขอที่ถูกปฏิเสธก่อนถึงธนาคารวันนี้ (เช่น no_plan, plan_expired, quota_exceeded, insufficient_credit, feature_disabled) — ไม่หักสลิป |
breakdown.errors | number | คำขอที่ระบบผู้ตรวจขัดข้องวันนี้ (ตอบ 503 temporarily_unavailable) — ไม่หักสลิป ส่งซ้ำได้ |
requestId | string | รหัสคำขอ ตรงกับ header X-Request-Id — แจ้งทีมงานพร้อมรหัสนี้ เราหาคำขอนั้นเจอทันที |
ข้อควรทราบและคำแนะนำ
- รีเซ็ตสถิติอัตโนมัติทุกเที่ยงคืนตามเวลาประเทศไทย (UTC+7)
- คำขอที่ไม่ผ่านด่านคีย์ (401, 403, 429 ฯลฯ) ไม่ถูกบันทึกเป็นการตรวจ จึงไม่อยู่ในตัวเลขใดเลย — ดูได้ที่หน้าประวัติการเรียก API
- นับรวมสลิปที่ตรวจผ่านบอท LINE และหน้าตรวจสลิปบนเว็บ ไม่ใช่เฉพาะ API · สลิปที่ไม่ผ่านหรือถูกปฏิเสธไม่ถูกหัก credits
- ต้องใช้คีย์ที่มีสิทธิ์ verify:slip — คีย์ผิดได้ HTTP 401 reason: invalid_api_key เหมือน endpoint อื่น
ฟิลด์ reason ช่วยให้ระบบของคุณแยกแยะและแจ้งเตือนผู้ใช้งานได้อย่างถูกต้อง
| ฟิลด์ reason | HTTP | ความหมาย | สิ่งที่ต้องทำ |
|---|---|---|---|
| invalid_request | 400 | ไม่ได้ส่ง qrData หรือ image (หรือส่งมาทั้งคู่), qrData ยาวไม่อยู่ในช่วง 10–1000 ตัวอักษร, expectedAmount ไม่ถูกต้อง, JSON เสีย หรือตัวกรองของ GET /api/v1/verifications ไม่ถูกต้อง — ฟิลด์ param บอกชื่อฟิลด์ที่ผิด (ถ้ามี) | ส่ง { "qrData": "…" } หรือ { "image": "<base64>" } อย่างใดอย่างหนึ่ง · expectedAmount ต้องมากกว่า 0 ทศนิยมไม่เกิน 2 ตำแหน่ง |
| payment_qr | 400 | ส่ง QR รับเงิน (พร้อมเพย์ / QR ร้านค้า) มาแทน QR บนสลิป | ให้ลูกค้าส่งสลิปหลังโอนเงิน ไม่ใช่ QR ที่ใช้สแกนจ่าย |
| qr_not_found | 422 | อ่าน QR บนรูปที่ส่งมาไม่ได้ | ใช้รูปสลิปจากแอปธนาคารที่เห็น QR ชัด ไม่ครอป ไม่ถ่ายหน้าจอซ้อน |
| invalid_api_key | 401 | ไม่มี Authorization header, รูปแบบผิด, คีย์ผิด หรือคีย์ถูกเพิกถอน | ตรวจคีย์ / สร้างคีย์ใหม่ (คำว่า Bearer ไม่สนตัวพิมพ์) · ฟิลด์ error: invalid api key แบบเดิมจะเอาออกในรุ่นถัดไป |
| no_plan | 402 | ร้านยังไม่มีแพ็กเกจ | ซื้อแพ็กเกจ / รับทดลองฟรี |
| plan_expired | 402 | แพ็กเกจหมดอายุ | ซื้อแพ็กเกจใหม่ |
| quota_exceeded | 402 | ใช้สลิปในแพ็กเกจครบแล้ว | ซื้อแพ็กเกจเพิ่ม (สลิปที่เหลือยกไปรวมเฉพาะเมื่อแพ็กเดิมยังไม่หมดอายุ) |
| insufficient_credit | 402 | เครดิตไม่พอ (ร้านที่ใช้แบบเครดิต) | เติมที่หน้ากระเป๋าเงิน |
| ip_not_allowed | 403 | IP ของเซิร์ฟเวอร์ไม่อยู่ใน IP Whitelist | เพิ่ม IP ในหน้า IP Whitelist |
| service_disabled | 403 | คีย์หรือร้านนี้ไม่มีสิทธิ์ใช้บริการนี้ (เช่น verify/name ที่ยังไม่เปิด) | ใช้คีย์ที่มีสิทธิ์ verify:slip |
| feature_disabled | 403 | บัญชีของร้านยังไม่เปิดใช้การตรวจสอบสลิป | ติดต่อฝ่ายเทคนิค |
| merchant_suspended | 403 | ร้านค้าถูกระงับการใช้งาน | ติดต่อฝ่ายเทคนิค |
| not_found | 404 | ไม่พบผลตรวจนี้ในร้านคุณ (GET /api/v1/verifications/:id), ใช้คีย์คนละประเภทกับตอนตรวจ หรือเรียก path ที่ไม่มีอยู่ | เช็ค verificationId และใช้คีย์ประเภทเดียวกับตอนตรวจ (ทดสอบ / ใช้งานจริง) · ผลทดสอบเก็บไว้ 7 วัน |
| payload_too_large | 413 | body ใหญ่เกิน 4 MB (รูปแบบ base64 ควรไม่เกินราว 3 MB) หรือเกิน 64 KB ใน endpoint อื่น | ย่อ/บีบอัดรูปก่อนส่ง หรือส่ง qrData แทน |
| unsupported_media_type | 415 | Content-Type ไม่รองรับ หรือรูปไม่ใช่ JPEG/PNG (WebP, GIF, HEIC, AVIF, PDF) — multipart ได้ param: file | ใช้ application/json, image/jpeg, image/png หรือ multipart/form-data ที่แนบ JPEG/PNG |
| rate_limited | 429 | ส่งคำขอเกินขีดจำกัดต่อนาทีของร้าน (ค่าเริ่มต้น 120 ครั้ง/นาที แยกคีย์จริง/คีย์ทดสอบ) หรือคีย์ทดสอบตรวจเกิน 1,000 ครั้งต่อวัน | รอตาม Retry-After (ขีดจำกัดต่อนาที = ไม่เกิน 60 วินาที · เพดานรายวันของคีย์ทดสอบ = วินาทีจนรีเซ็ต) แล้วลองใหม่ ควรทำคิวฝั่งระบบของคุณ |
| auth_locked | 429 | IP นี้ (หรือวงเดียวกัน /24) ส่ง API key ที่ไม่ถูกต้องเกิน 30 ครั้งใน 1 นาที — ตอบคีย์ที่ไม่ถูกต้อง ส่วนคีย์ที่ถูกต้องที่ใช้งานอยู่ยังใช้ได้ตามปกติ · ก่อนมีรหัสนี้ กรณีนี้ตอบ rate_limited — โค้ดที่หน่วงเวลาเมื่อได้ rate_limited ให้ทำแบบเดียวกันกับ auth_locked | ตรวจว่าทุกเซิร์ฟเวอร์ใช้คีย์ปัจจุบัน (คีย์เก่าหลังรีเซ็ต / พิมพ์ผิด) — ส่งซ้ำด้วยคีย์เดิมไม่ช่วย รอตาม Retry-After แล้วใช้คีย์ที่ถูกต้อง (ถ้าคีย์ที่ถูกต้องได้รหัสนี้ระหว่างที่วง IP เดียวกันส่งคีย์ผิดรัวๆ ให้รอตาม Retry-After แล้วลองใหม่) |
| too_many_failed | 429 | สลิปที่ธนาคารตรวจไม่ผ่านเกิน 300 ใบต่อวันต่อร้าน | หยุดส่ง QR ที่ไม่ใช่สลิปจริง แล้วรอตาม Retry-After (วินาทีจนรีเซ็ต) |
| internal_error | 500 | ระบบขัดข้องโดยไม่คาดคิด | ลองใหม่ภายหลัง (ถ้าเป็นการตรวจสลิป ให้ค้นผลก่อนส่งซ้ำ) · ถ้ายังเกิดซ้ำให้แจ้ง requestId กับฝ่ายเทคนิค |
| temporarily_unavailable | 503 | ระบบตรวจของธนาคาร หรือแหล่งข้อมูลของ endpoint ค้นข้อมูล ขัดข้องชั่วคราว | ลองใหม่หลัง Retry-After (5 วินาที) — สลิปเดิมส่งซ้ำได้ |
ให้โค้ดของคุณตัดสินจาก success และ reason เท่านั้น — ข้อความใน message เป็นภาษาไทยและอาจปรับถ้อยคำได้ ห้ามนำมาเทียบ
รูปแบบคำตอบเมื่อผิดพลาด
ทุกคำตอบที่ไม่ใช่ 2xx ของ /api/v1/* ใช้รูปแบบเดียวกัน (reason ตามตารางด้านบน)
HTTP/1.1 400 Bad Request
Content-Type: application/json
X-Request-Id: 2c9f4e1a-7b3d-4a8e-9f61-5d0c2b7a8e14
{
"success": false,
"status": "Failed",
"code": "400",
"reason": "invalid_request",
"message": "expectedAmount ต้องเป็นตัวเลขมากกว่า 0 ทศนิยมไม่เกิน 2 ตำแหน่ง",
"param": "expectedAmount",
"requestId": "2c9f4e1a-7b3d-4a8e-9f61-5d0c2b7a8e14"
}| ชื่อฟิลด์ | ประเภท | ความหมาย |
|---|---|---|
success | false | false เสมอ |
status | "Failed" | Failed เสมอ |
code | string | HTTP status เป็นสตริง เช่น 400 |
reason | string | รหัสเหตุผลตามตารางด้านบน — ใช้ตัวนี้แยกกรณี |
message | string | ข้อความภาษาไทยสำหรับคนอ่าน อาจเปลี่ยนถ้อยคำได้ |
requestId | string | ตรงกับ header X-Request-Id — แจ้งฝ่ายเทคนิคพร้อมรหัสนี้ |
param | string? | เฉพาะ 400 ที่ข้อมูลไม่ถูกต้อง: ชื่อฟิลด์ที่ผิด |
error | string? | เฉพาะ 401: ค่าเดิม invalid api key — เก็บไว้อีก 1 รุ่น ให้เปลี่ยนไปใช้ reason |
คำขอที่ถูกปฏิเสธหรือล้มเหลวไม่กินโควต้า
success: false โดยไม่มี reasonจำนวนคำขอสูงสุดต่อนาที และวิธีรับมือเมื่อเกิน
- ค่าเริ่มต้น 120 คำขอต่อนาทีต่อร้าน (ปรับได้รายร้าน — ดูค่าของร้านคุณที่หน้า Callback / Webhook) นับรวมทุกคีย์จริงของร้านเดียวกัน สร้างคีย์เพิ่มไม่ได้เพิ่มโควต้าความถี่ · คีย์ทดสอบนับแยกอีกก้อน (เพดานเท่ากัน) จึงไม่กินโควต้าของคีย์จริง
- ทุกคำตอบที่ผ่านด่านคีย์มี
X-RateLimit-Limit·X-RateLimit-Remaining·X-RateLimit-Reset— Limit = เพดานต่อนาที · Remaining = เหลืออีกกี่ครั้งในนาทีนี้ · Reset = อีกกี่วินาทีจะเริ่มนับใหม่ - นับทุก endpoint ที่ใช้ API key: verify/slip, verifications, ping, balance และ usage
- นอกจากขีดจำกัดต่อนาที คีย์ทดสอบยังตรวจสลิปได้ไม่เกิน 1,000 ครั้งต่อวันต่อร้าน
- สลิปที่ธนาคารตรวจไม่ผ่านเกิน 300 ใบต่อวันต่อร้าน จะได้ HTTP 429 reason: too_many_failed (กันการส่ง QR มั่ว)
- เกินกำหนดจะได้ HTTP 429 พร้อม
reason: rate_limitedและ headerRetry-After(วินาที) — ทุกคำตอบ 429 และ 503 มี header นี้ รวมคีย์ทดสอบ รอตามนั้นแล้วค่อยส่งใหม่ (ไม่หักสลิป) - ถ้าต้องตรวจจำนวนมาก ให้ทำคิวฝั่งระบบของคุณและส่งทีละชุด อย่าวนส่งซ้ำทันทีเมื่อได้ 429
HTTP/1.1 429 Too Many Requests
Retry-After: 37
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 37
Content-Type: application/json
{
"success": false,
"status": "Failed",
"code": "429",
"reason": "rate_limited",
"message": "ส่งคำขอถี่เกินกำหนด กรุณาลองใหม่ภายหลัง",
"requestId": "8a4d2f6c-1e9b-4c7a-b3d5-6f0e8a2c4b19"
}ระบบส่งผลไปยัง Callback URL ของคุณอัตโนมัติตาม event ที่เลือกไว้ในหน้า Callback / Webhook ไม่ต้องเขียนโค้ดวนถามเอง
เหตุการณ์ที่ส่ง
| Event | ส่งเมื่อ |
|---|---|
| verify.success | ธนาคารยืนยันสลิปแล้ว (รวมสลิปซ้ำ — ดู data.duplicate) |
| verify.failed | ธนาคารตรวจแล้วไม่ผ่านเท่านั้น — ไม่ส่งเมื่อคำขอถูกปฏิเสธ (มี reason) หรือระบบขัดข้อง |
| credit.low | สลิป/เครดิตคงเหลือลดลงถึงเกณฑ์ที่ตั้งไว้ (ส่งครั้งเดียวตอนข้ามเกณฑ์) |
| credit.empty | สลิป/เครดิตหมด (ส่งครั้งเดียวตอนหมด) |
รวมสลิปที่ตรวจผ่านบอท LINE ของร้านด้วย
Header ที่แนบมากับคำขอ
POST /webhooks/verifyslips HTTP/1.1
Content-Type: application/json
User-Agent: Slip2Try-Webhook/1.0
X-VerifySlips-Event: verify.success
X-VerifySlips-Delivery: 3e5b7c1d-9a2f-4d6e-8b0c-1f4a7e2d9c63
X-VerifySlips-Signature: sha256=9f86d081884c7d65...
X-VerifySlips-Signature-Previous: sha256=7d1a54127b222502... # รุ่น 1 ด้วย Secret เดิม — มีเฉพาะ 24 ชม. หลังหมุน Secret
X-VerifySlips-Signature-V2: t=1789753097,sha256=4e07408562bedb8b...
X-VerifySlips-Signature-V2-Previous: t=1789753097,sha256=b5c1e9d07a3f2c84... # มีเฉพาะ 24 ชม. หลังหมุน Secret (ลายเซ็นด้วย Secret เดิม)
X-VerifySlips-Timestamp: 1789753097
X-VerifySlips-Test: true # มีเมื่อส่งจากปุ่ม "ส่ง webhook ทดสอบ" หรือเป็นผลจากคีย์ทดสอบX-VerifySlips-Timestamp คือเวลาที่ส่ง (unix วินาที) · X-VerifySlips-Signature-V2 คือลายเซ็นที่รวมเวลาด้วย (HMAC-SHA256 ของ ${timestamp}.${body}) แนะนำให้ใช้ตัวนี้ และปฏิเสธคำขอที่เวลาห่างจากปัจจุบันเกิน 5 นาที เพื่อกันการส่งซ้ำ
ตัวอย่าง Body ของแต่ละ event
Body · verify.success
{
"id": "3e5b7c1d-9a2f-4d6e-8b0c-1f4a7e2d9c63",
"event": "verify.success",
"environment": "live",
"createdAt": "2026-10-14T04:20:11.512Z",
"channel": "api",
"requestId": "3b1f0c9e-6a2d-4f5e-8b7c-1d2e3f4a5b6c",
"transactionRef": "A3828f7b4f3244ff3",
"data": {
"verificationId": "6f1c2a9e-4b7d-4c1e-9a52-0d3f8e7b1c24",
"status": "success",
"message": null,
"amount": 100000,
"currency": "THB",
"duplicate": false,
"firstVerifiedAt": null,
"checks": {
"stale": false,
"slipAgeHours": 0.1,
"receiver": "matched",
"receiverLabel": "บัญชีหลัก",
"amount": "matched",
"expectedAmount": 100000
},
"transfer": { "...": "รายละเอียดการโอนเหมือนใน data.transfer" }
}
}data.duplicate เป็น true เมื่อร้านคุณเคยตรวจสลิปนี้สำเร็จแล้ว ส่วน data.firstVerifiedAt คือเวลาตรวจครั้งแรก (เป็น null เมื่อไม่ซ้ำ)
id คือรหัสการส่ง (ค่าเดิมทุกครั้งที่ลองใหม่และเมื่อกดส่งใหม่เอง ใช้กันประมวลผลซ้ำ) · environment เป็น live หรือ test · verify.* มี channel (api / line / web) และ requestId ของคำขอตรวจ (null ถ้า channel ไม่ใช่ api)
data.unit เป็น slip (แบบแพ็กเกจ) หรือ credit (แบบเครดิต) · transactionRef เป็น null เสมอสำหรับ credit.*
การส่งและการลองใหม่
- ปลายทางต้องตอบ HTTP 2xx ภายใน 10 วินาที จึงนับว่าส่งสำเร็จ
- ตอบไม่ใช่ 2xx, หมดเวลา, เชื่อมต่อไม่ได้ หรือ redirect (3xx) = ล้มเหลว — ระบบไม่ตาม redirect ให้ใส่ URL ปลายทางจริง
- ส่งครั้งแรกทันทีหลังตรวจเสร็จ (หลังตอบคำขอ API แล้ว — webhook ไม่ถึงก่อนคำตอบ API) · ล้มเหลวแล้วลองใหม่ไม่เร็วกว่า 10 วินาที, 1 นาที, 5 นาที, 30 นาที และ 2 ชั่วโมง (รวม 6 ครั้ง) แล้วหยุด — การลองใหม่ทำงานเมื่อมีคำขอเข้าระบบ (จากร้านใดก็ได้) หรือรอบตั้งเวลาวันละครั้ง (07:00 น. เวลาไทย) ช่วงที่ระบบเงียบ แต่ละครั้งจึงอาจช้ากว่ากำหนดได้ถึงราว 24 ชั่วโมง · อย่ารอ webhook เป็นทางเดียว — ใช้ GET /api/v1/verifications ตรวจย้อนหลัง หรือกดส่งใหม่เองในประวัติการส่ง
- การส่งทดสอบจากพอร์ทัลมี
"test": trueใน body · ผลจากคีย์ทดสอบมี"environment": "test"— ทั้งสองแบบมี header X-VerifySlips-Test: true และไม่ลองส่งใหม่ - URL ต้องขึ้นต้นด้วย https:// และเป็นที่อยู่สาธารณะ (ไม่รับ localhost หรือ IP ภายใน)
ตรวจลายเซ็น HMAC-SHA256 ก่อนเชื่อเสมอ
แนะนำแท็บ V2: ตรวจ X-VerifySlips-Signature-V2 (HMAC ของ เวลา.body) และปฏิเสธเวลาที่ห่างจากปัจจุบันเกิน 5 นาที · แท็บ V1 (X-VerifySlips-Signature จาก body อย่างเดียว) ยังใช้ได้สำหรับระบบเดิม แต่กันการส่งซ้ำ (replay) ไม่ได้
verify_signature_v2.js
const express = require('express');
const crypto = require('crypto');
const app = express();
const TOLERANCE_SEC = 300; // ยอมรับลายเซ็นที่อายุไม่เกิน 5 นาที
// Secret ที่ตั้งไว้ในหน้า Callback / Webhook
const SECRETS = [
process.env.VERIFYSLIPS_CALLBACK_SECRET || 'your_webhook_secret',
process.env.VERIFYSLIPS_CALLBACK_SECRET_PREVIOUS, // Secret เดิมระหว่างสลับ (ไม่บังคับ) — ลบออกเมื่อสลับเสร็จ
].filter(Boolean);
// t=<unix>,sha256=<hex>
function validV2(header, rawBody) {
const parts = Object.fromEntries(String(header || '').split(',').map((p) => p.trim().split('=')));
const ts = parts.t || '';
// เวลาเก่าเกินหรืออยู่ในอนาคต → ปฏิเสธ (กันการส่งซ้ำ)
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > TOLERANCE_SEC) return false;
const given = Buffer.from(String(parts.sha256 || ''));
return SECRETS.some((secret) => {
const expected = Buffer.from(crypto.createHmac('sha256', secret).update(`${ts}.`).update(rawBody).digest('hex'));
// เทียบแบบ constant-time เพื่อป้องกัน timing attack
return given.length === expected.length && crypto.timingSafeEqual(given, expected);
});
}
// ต้องอ่าน Raw Body เพื่อคำนวณ HMAC-SHA256 ให้ถูกต้อง
app.post('/webhooks/verifyslips', express.raw({ type: 'application/json' }), (req, res) => {
// ตรวจ V2 ก่อน ไม่ผ่านค่อยตรวจ V2-Previous (ช่วง 24 ชม. หลังหมุน Secret)
if (!validV2(req.headers['x-verifyslips-signature-v2'], req.body) && !validV2(req.headers['x-verifyslips-signature-v2-previous'], req.body)) {
return res.status(401).send('Invalid signature');
}
const payload = JSON.parse(req.body.toString('utf8'));
// id เดิมทุกครั้งที่ลองส่งใหม่ → ข้ามถ้าเคยประมวลผลแล้ว
if (alreadyProcessed(payload.id)) return res.status(200).send('ok');
// ผลทดสอบ (environment: test) ไม่ใช่เงินจริง → ตอบรับแต่ไม่ให้ของ
if (payload.environment !== 'live') return res.status(200).send('ok');
if (payload.event === 'verify.success') {
const { transactionRef, data } = payload;
const checks = data.checks || {};
if (data.duplicate) {
// สลิปเคยใช้แล้ว (ครั้งแรก: data.firstVerifiedAt) → ไม่เติมเงินซ้ำ
} else if (checks.receiver === 'mismatch' || checks.amount === 'mismatch' || checks.stale) {
// เข้าบัญชีอื่น ยอดไม่ตรง หรือสลิปเก่า (เหมือน warnings ในคำตอบ API) → ให้คนตรวจ
} else {
console.log(`สลิปสำเร็จ: ${transactionRef} ยอด: ${data.amount}`);
// TODO: อัปเดตออเดอร์เป็นชำระแล้ว (เก็บ transactionRef เป็นคีย์ไม่ซ้ำ)
}
}
// ตอบ 200 ให้เร็วที่สุด
res.status(200).send('ok');
});
app.listen(3000);หมุน Secret และพักการส่ง
- กดหมุน Secret ที่หน้า Webhook ระบบออก Secret ใหม่ทันที — ใน 24 ชั่วโมงถัดไปทุก webhook มีลายเซ็น 2 ชุด:
X-VerifySlips-Signature-V2ด้วย Secret ใหม่ และX-VerifySlips-Signature-V2-Previousด้วย Secret เดิม (ใช้ t= เดียวกัน) - ฝั่งคุณ: ตรวจ
X-VerifySlips-Signature-V2ก่อน ถ้าไม่ตรงให้ตรวจกับX-VerifySlips-Signature-V2-Previous— อัปเดต Secret ในระบบของคุณให้เสร็จภายใน 24 ชั่วโมง หลังจากนั้นหัวX-VerifySlips-Signature-V2-Previousจะไม่ถูกส่งอีก - ยังตรวจลายเซ็นรุ่น 1 อยู่: ช่วง 24 ชั่วโมงเดียวกันมี
X-VerifySlips-Signature-Previous(sha256= ของ body ด้วย Secret เดิม) คู่กับX-VerifySlips-Signature— ตรวจX-VerifySlips-Signatureก่อน ไม่ตรงค่อยตรวจX-VerifySlips-Signature-Previous - พักการส่ง (pause) ได้จากหน้าเดียวกัน — ระหว่างพัก เหตุการณ์ใหม่จะไม่ถูกส่งและไม่ส่งย้อนหลังเมื่อเปิดอีกครั้ง (ค้นผลย้อนหลังด้วย GET /api/v1/verifications) · ปุ่มส่งทดสอบและรายการที่รอลองใหม่อยู่แล้วยังส่งตามปกติ
1. ใช้ Raw Body
คำนวณ Hash จาก Raw Body ก่อน JSON parse มิฉะนั้นค่าจะไม่ตรง
2. เทียบแบบ constant-time
ใช้ hash_equals / crypto.timingSafeEqual / hmac.compare_digest ห้ามใช้ == หรือ ===
3. กันประมวลผลซ้ำ
webhook อาจมาซ้ำได้ (ลองใหม่) — ใช้
idใน body (ค่าเดียวกับ headerX-VerifySlips-Deliveryและคงเดิมทุกครั้งที่ลองใหม่) เป็นคีย์กันประมวลผลซ้ำ4. ตอบ 200 เร็วที่สุด
งานหนักเช่นตัดสต็อกหรือส่งอีเมล ให้ส่งเข้า Background Queue
ใช้แปลง fromBankCode / toBankCode ในผลตรวจสลิปเป็นชื่อและโลโก้ธนาคาร (รหัส 3 หลักตาม ธปท.)

ธนาคารกรุงเทพ
BBL · Bangkok Bank
002
ธนาคารกสิกรไทย
KBANK · Kasikorn Bank
004
ธนาคารกรุงไทย
KTB · Krungthai Bank
006
ธนาคารทหารไทยธนชาต
TTB · TMBThanachart Bank
011
ธนาคารไทยพาณิชย์
SCB · The Siam Commercial Bank
014
ธนาคารซิตี้แบงก์
CITI · Citibank
017
ธนาคาร CIMB ไทย
CIMB · CIMB Thai Bank
022
ธนาคาร UOB
UOB · United Overseas Bank
024
ธนาคารกรุงศรีอยุธยา
BAY · Krungsri Bank
025
ธนาคารออมสิน
GSB · Government Savings Bank
030
ธนาคารเอชเอสบีซี
HSBC · HSBC
031
ธนาคารอาคารสงเคราะห์
GHB · GH Bank
033
ธนาคาร ธ.ก.ส.
BAAC · Bank for Agriculture and Agricultural Cooperatives
034
ธนาคารอิสลามแห่งประเทศไทย
IBANK · Islamic Bank of Thailand
066
ธนาคารทิสโก้
TISCO · Tisco Bank
067
ธนาคารเกียรตินาคินภัทร
KKP · Kiatnakin Phatra Bank
069
ธนาคารไอซีบีซี (ไทย)
ICBC · ICBC (Thai)
070
ธนาคารไทยเครดิต
TCRB · Thai Credit Bank
071
ธนาคารแลนด์ แอนด์ เฮ้าส์
LHB · LH Bank
073
ธนาคารคลิกซ์
CLICX · CLICX Bank
088
พร้อมเพย์และวอลเล็ต (ไม่มีรหัสธนาคาร)
API ไม่คืนรหัสสำหรับพร้อมเพย์หรือ e-wallet — เมื่อปลายทางเป็นบริการเหล่านี้ toBankCode อาจว่าง ให้ดูจากชื่อธนาคาร/บริการที่ส่งมาแทน (หน้าเว็บและบอท LINE จับคู่โลโก้จากชื่อ)
- จับคู่จากชื่อ

พร้อมเพย์
- จับคู่จากชื่อ

ทรูมันนี่
บริการที่เปิดให้ร้านของคุณ และช่องทางติดต่อเมื่อแก้เองไม่ได้
สิทธิ์ของร้านคุณ
บริการที่ API key ของร้านใช้ได้
- ตรวจสลิป
verify:slipเปิดใช้งาน - ตรวจชื่อบัญชี
verify:nameปิดใช้งาน
ติดต่อฝ่ายเทคนิค
ลองเรียก GET /api/v1/ping และดูหน้าประวัติ / ประวัติการส่ง webhook ก่อน — ถ้ายังแก้ไม่ได้ ทักเราได้เลย
Telegram ฝ่ายเทคนิค
@blacksteps
Discord ฝ่ายเทคนิค
discord.gg/Utr2KgzjqE
เครื่องมือสำหรับนักพัฒนาในพอร์ทัล
ไล่ปัญหาเองได้ก่อนติดต่อเรา — ทุกคำตอบมี requestId ใช้ค้นในประวัติการเรียก API