ตรวจสลิป
มีสอง endpoint ที่เกี่ยวกับสลิป ใช้ต่างกันดังนี้
| Endpoint | ใช้เมื่อ | ผลต่อรายการ |
|---|---|---|
POST /v1/slips/verify | อยากรู้ว่าสลิปเป็นของจริงหรือไม่ และข้อมูลในสลิปคืออะไร | ไม่เปลี่ยนสถานะรายการใด |
POST /v1/payins/{id}/slip | ผู้จ่ายโอนแล้วแต่ระบบยังไม่จับคู่ ต้องการใช้สลิปยืนยัน pay-in | ถ้าตรง pay-in จะเป็น succeeded และส่ง webhook |
ตรวจสลิป
หัวข้อที่มีชื่อว่า “ตรวจสลิป”POST /v1/slips/verifyส่งได้สองแบบ
- JSON ส่งข้อความจาก QR code บนสลิปใน
payload(สูงสุด 2000 ตัว) และเลือกใส่checkDuplicate: trueเพื่อให้ตรวจว่าสลิปนี้เคยถูกใช้แล้วหรือไม่ - multipart/form-data ส่งรูปสลิป (JPG หรือ PNG ไม่เกิน 5 MB) ใน part
fileเพียง part เดียว ตอนเซ็นให้ hash ไบต์ของไฟล์ ดูการเซ็น request แบบ multipart (แบบนี้ไม่รองรับcheckDuplicate)
import { readFile } from 'node:fs/promises';import { api, signRequest } from './partner-api.mjs';
// 1) QR payloadconst { data: byPayload } = await api('POST', '/v1/slips/verify', { payload: qrText, checkDuplicate: true,});
// 2) Image: sign the file bytes, send only the `file` partconst bytes = await readFile('slip.jpg');const form = new FormData();form.append('file', new Blob([bytes], { type: 'image/jpeg' }), 'slip.jpg');const res = await fetch('https://api.example.com/v1/slips/verify', { method: 'POST', headers: signRequest('POST', '/v1/slips/verify', bytes), body: form,});const { data: byImage } = await res.json();// 1) QR payload$result = partner_api('POST', '/v1/slips/verify', ['payload' => $qrText, 'checkDuplicate' => true])['data'];
// 2) Image: sign the file bytes, send only the `file` part$path = '/v1/slips/verify';$ch = curl_init('https://api.example.com' . $path);curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => partner_sign('POST', $path, file_get_contents('slip.jpg')), CURLOPT_POSTFIELDS => ['file' => new CURLFile('slip.jpg', 'image/jpeg')], CURLOPT_RETURNTRANSFER => true,]);$result = json_decode(curl_exec($ch), true)['data'];curl_close($ch);import requestsfrom partner_api import api, sign_request
# 1) QR payloadresult = api("POST", "/v1/slips/verify", {"payload": qr_text, "checkDuplicate": True})["data"]
# 2) Image: sign the file bytes, send only the `file` partwith open("slip.jpg", "rb") as f: content = f.read()r = requests.post("https://api.example.com/v1/slips/verify", headers=sign_request("POST", "/v1/slips/verify", content), files={"file": ("slip.jpg", content, "image/jpeg")}, timeout=60)result = r.json()["data"]ผลการตรวจ
หัวข้อที่มีชื่อว่า “ผลการตรวจ”ผลตรวจที่ไม่ผ่านยังได้ HTTP 200 ให้ดูที่ verified และ failureCode
{ "data": { "object": "slip_verification", "verified": true, "failureCode": null, "payload": "0041000600000101030040220...", "bankReference": "202601011234567890", "transferredAt": "2026-01-01T00:04:12.000Z", "amount": "150.37", "currency": "THB", "sender": { "bankCode": "SCB", "bankName": "Siam Commercial Bank", "accountNumber": "xxx-x-x4567-x", "accountName": "นาย สมชาย ส", "accountNameEn": "MR. SOMCHAI S" }, "receiver": { "bankCode": "KBANK", "bankName": "Kasikorn Bank", "accountNumber": "xxx-x-x5432-x", "accountName": "บจก. ตัวอย่าง", "accountNameEn": "EXAMPLE CO., LTD." } }, "requestId": "req_..."}failureCode | ความหมาย |
|---|---|
slip_not_found | ไม่พบรายการโอนตามสลิป |
duplicate_slip | สลิปนี้เคยถูกใช้แล้ว |
amount_mismatch | ยอดไม่ตรง |
receiver_mismatch | ผู้รับไม่ตรง |
invalid_image | อ่านรูปไม่ได้หรือไฟล์ไม่ถูกต้อง |
invalid_payload | payload ไม่ถูกต้อง |
verification_unavailable | ตรวจไม่ได้ชั่วคราว ลองใหม่ภายหลัง |
ข้อมูลใน sender และ receiver มาจากธนาคาร บางฟิลด์อาจเป็น null หรือถูกปิดบางส่วน
ทดสอบด้วย test key
หัวข้อที่มีชื่อว่า “ทดสอบด้วย test key”เมื่อเรียกด้วย test key (pk_test_...) ระบบจะไม่ตรวจกับธนาคารจริงและไม่ใช้โควตา แต่ตอบผลจำลองตาม payload
payload ขึ้นต้นด้วย | ผล |
|---|---|
TESTFAIL | verified: false, failureCode: "slip_not_found" |
TESTDUP | verified: false, failureCode: "duplicate_slip" |
| อย่างอื่น (หรือส่งเป็นรูป) | verified: true ยอด 100.00 THB และ bankReference เป็น TEST-REF-0000000000 |
ข้อผิดพลาด
หัวข้อที่มีชื่อว่า “ข้อผิดพลาด”| HTTP | code | ความหมาย |
|---|---|---|
| 400 | parameter_missing | ไม่ได้ส่ง payload (JSON) หรือ file (multipart) |
| 400 | file_invalid | ไม่มีไฟล์ ไม่ใช่ JPG/PNG หรือใหญ่เกิน 5 MB |
| 400 | parameter_unknown | multipart มี part อื่นนอกจาก file |
| 422 | slip_verification_unavailable, slip_verification_failed | บริการตรวจสลิปขัดข้อง ลองใหม่ภายหลัง |
| 429 | quota_exceeded | ใช้โควตาการตรวจสลิปครบแล้ว ติดต่อทีมงาน |
อัปโหลดสลิปให้ pay-in
หัวข้อที่มีชื่อว่า “อัปโหลดสลิปให้ pay-in”POST /v1/payins/{id}/slipใช้เมื่อผู้จ่ายโอนเงินแล้วแต่ pay-in ยังเป็น pending เช่น ผู้จ่ายส่งสลิปมาให้คุณ ส่งแบบ multipart/form-data มี part file เพียง part เดียว (JPG หรือ PNG ไม่เกิน 5 MB) และเซ็นด้วย hash ของไบต์ไฟล์ ดูฟังก์ชัน uploadSlip ในโค้ดตัวอย่าง
{ "data": { "object": "slip_upload", "matched": true, "slipStatus": "matched", "payin": { "object": "payin", "id": "pin_3f2b1c0d9e8f4a7b8c6d5e4f3a2b1c0d", "status": "succeeded", "...": "..." } }, "requestId": "req_..."}matched: trueแปลว่าสลิปตรงกับ pay-in และ pay-in เป็นsucceededแล้ว ระบบจะส่ง webhookpayin.succeededตามปกติmatched: falseแปลว่ายังจับคู่ไม่ได้ ดูslipStatusและสถานะของpayin
| HTTP | code | ความหมาย |
|---|---|---|
| 400 | file_invalid | ไม่มีไฟล์ ไม่ใช่ JPG/PNG หรือใหญ่เกิน 5 MB |
| 404 | resource_not_found | ไม่พบ pay-in |
| 409 | payin_not_pending | pay-in ไม่ได้รอการจ่ายแล้ว (สำเร็จ หมดเวลา หรือถูกยกเลิก) |
| 409 | slip_duplicate | สลิปนี้ถูกใช้ไปแล้ว |
| 422 | slip_unreadable | อ่าน QR code บนสลิปไม่ได้ ขอรูปที่ชัดกว่า |
| 422 | slip_amount_mismatch | ยอดในสลิปไม่ตรงกับ amount ของ pay-in |
| 422 | slip_receiver_mismatch | บัญชีผู้รับในสลิปไม่ตรงกับ payTo |
| 422 | slip_verification_failed | ตรวจกับธนาคารไม่ผ่าน |
| 422 | slip_verification_unavailable | บริการตรวจสลิปปิดอยู่ชั่วคราว |