Skip to main content

Webhook

Webhook ช่วยให้ระบบของคุณรับการแจ้งเตือนแบบเรียลไทม์อัตโนมัติจาก PayerScan ระบบจะส่งคำขอ POST ไปยัง callback_url ที่ร้านค้าระบุไว้เมื่อสร้างใบแจ้งหนี้

ประเภทเหตุการณ์

PayerScan มี 2 เหตุการณ์หลักที่ส่งกลับในฟิลด์ status:

  1. completed — ชำระเงินสำเร็จ: มีการยืนยันธุรกรรมที่ตรงกับใบแจ้งหนี้แล้ว (blockchain หรือ Binance Pay)
  2. expired — ใบแจ้งหนี้หมดอายุ: ใบแจ้งหนี้เกินเวลาที่กำหนดสำหรับการชำระเงิน

ร้านค้าต้องเปิด URL สาธารณะ (แนะนำ HTTPS) รับคำขอ POST ประมวลผล payload และ ส่งกลับ HTTP 2xx ภายในเวลา timeout ที่กำหนด (เช่น 10 วินาที)


โครงสร้าง Payload

1. Webhook เมื่อชำระเงินสำเร็จ (status: completed)

เมื่อพบธุรกรรมที่ตรงกัน (ที่อยู่ถูกต้อง จำนวนเงินถูกต้อง อยู่ในกรอบเวลา) ระบบจะอัปเดตใบแจ้งหนี้เป็น completed และส่ง POST ไปยัง callback_url พร้อม payload ดังนี้:

Payload (JSON body)

{
"merchant_id": "MERCHANT_001",
"api_key": "YOUR_API_KEY",
"request_id": "order-1234",
"trans_id": "TID-ABC123DEF4567890",
"status": "completed",
"amount": "100",
"token_symbol": "USDT",
"token_price": "1",
"token_amount": "100.0026",
"network_symbol": "BSC",
"from_address": "0x8894e0a0c962cb723c1976a4421c95949be2d4e3",
"to_address": "0xc221460115e2CfCa5bF089A7e647b11cb9631efE",
"transaction_hash": "0xd346b32b83b35376d42a2464598fbf565fffb39e6569200f034a5e8342c532d7"
}
ฟิลด์คำอธิบาย
merchant_idรหัสร้านค้า
api_keyAPI Key (สำหรับตรวจสอบข้ามฝั่งหากต้องการ)
request_idรหัสคำสั่งซื้อของคุณ (สำหรับเปรียบเทียบกับใบแจ้งหนี้ภายในของคุณ)
trans_idรหัสใบแจ้งหนี้ ใช้เพื่ออัปเดตคำสั่งซื้อที่ตรงกัน
statusเป็น "completed" เสมอ
amountจำนวนเงิน USD (string)
token_symbolเหรียญ/โทเค็น (เช่น USDT)
token_priceอัตราแลกเปลี่ยน ณ เวลาทำธุรกรรม (string) อาจ fallback เป็น amount หากไม่มีข้อมูล
token_amountจำนวนคริปโตจริงที่โอน (string) อาจเป็น null ในบางกรณีที่หายาก
network_symbolเครือข่าย (เช่น BSC, TRC20, ETH, SOL, BINANCE_ID, OKX_UID, BYBIT_UID, ...)
from_addressที่อยู่กระเป๋าเงินผู้ส่ง
to_addressที่อยู่กระเป๋าเงินผู้รับ (ของร้านค้า)
transaction_hashแฮชธุรกรรมบน blockchain

ข้อกำหนดสำหรับ Merchant

  1. รับ POST ที่ endpoint ที่ตรงกับ callback_url
  2. แยกวิเคราะห์ JSON body
  3. ตรวจสอบ (ไม่บังคับแต่แนะนำ): เปรียบเทียบ merchant_id หรือ trans_id กับข้อมูลภายในของคุณ
  4. อัปเดตคำสั่งซื้อ: ทำเครื่องหมายคำสั่งซื้อที่ตรงกับ trans_id หรือ request_id ว่าชำระแล้ว; บันทึก transaction_hash หากต้องการ
  5. ส่งกลับ HTTP 2xx (เช่น 200 OK) ภายใน timeout (เช่น 10 วินาที) ไม่จำเป็นต้องมี response body; ระบบต้องการเพียงสถานะ 2xx เพื่อถือว่า webhook สำเร็จ

หากคุณส่งกลับ 4xx/5xx หรือ timeout ระบบจะทำเครื่องหมาย callback เป็น failed และมีกลไก ลองใหม่ (จำนวนครั้งและช่วงเวลาขึ้นอยู่กับการตั้งค่า backend)


2. Webhook เมื่อใบแจ้งหนี้หมดอายุ (status: expired)

เมื่อใบแจ้งหนี้เปลี่ยนเป็นสถานะ expired (เกิน expires_at) ระบบจะส่ง POST ไปยัง callback_url พร้อม payload ที่ง่ายกว่า:

Payload (JSON body)

{
"merchant_id": "MERCHANT_001",
"request_id": "order-1234",
"trans_id": "TID-ABC123DEF4567890",
"status": "expired",
"amount": "100"
}
ฟิลด์คำอธิบาย
merchant_idรหัสร้านค้า
request_idรหัสคำสั่งซื้อของคุณ (หากระบุไว้ตอนสร้างใบแจ้งหนี้)
trans_idรหัสใบแจ้งหนี้ ใช้เพื่ออัปเดตคำสั่งซื้อที่ตรงกัน
statusเป็น "expired" เสมอ
amountจำนวนเงิน USD (string)

คุณสามารถใช้ข้อมูลนี้เพื่ออัปเดตคำสั่งซื้อเป็น "หมดอายุ" ยกเลิกการจอง ฯลฯ คุณยังควรส่งกลับ HTTP 2xx เพื่อไม่ให้ระบบถือว่าเป็นข้อผิดพลาด


3. ตัวอย่างการจัดการ Webhook (Node.js)

app.post('/webhook/payment', express.json(), async (req, res) => {
const { merchant_id, api_key, trans_id, request_id, status, amount, transaction_hash } = req.body;

// ตรวจสอบ merchant_id (ทั้ง completed และ expired)
if (merchant_id !== process.env.PAYERSCAN_MERCHANT_ID) {
return res.status(401).send('Unauthorized');
}

// ตรวจสอบ api_key (เฉพาะ completed — expired ไม่มี api_key)
if (status === 'completed' && api_key !== process.env.PAYERSCAN_API_KEY) {
return res.status(401).send('Unauthorized');
}

// ค้นหาคำสั่งซื้อตาม request_id หรือ trans_id
const order = await orderService.findByRequestId(request_id) || await orderService.findByTransId(trans_id);
if (!order) {
return res.status(404).send('Order not found');
}

if (status === 'completed') {
await orderService.markPaid(order, { transaction_hash, amount });
} else if (status === 'expired') {
await orderService.markExpired(order);
}

res.status(200).send('OK');
});

การตรวจสอบลายเซ็น

เพื่อให้แน่ใจว่า webhook มาจาก PayerScan จริง คุณควรตรวจสอบลายเซ็นหรือตรวจสอบข้อมูลข้ามฝั่ง

  • HTTPS: ใช้ HTTPS สำหรับ callback_url เสมอเพื่อป้องกันการดักฟัง
  • ตรวจสอบ API Key (เฉพาะ completed): Webhook completed จะมี api_key อยู่ใน payload เปรียบเทียบกับ API key ที่คุณเก็บไว้เพื่อยืนยันความถูกต้อง หมายเหตุ: webhook expired ไม่มี api_key

ความเป็น Idempotent

trans_id หนึ่งจะได้รับเหตุการณ์ completed เพียงครั้งเดียว แต่กลไกลองใหม่อาจส่งซ้ำ ตรวจสอบให้แน่ใจว่าตรรกะการประมวลผลของคุณมีความ idempotent (อัปเดตหลายครั้งปลอดภัย ไม่เครดิตซ้ำ)

ลองใหม่เมื่อ Webhook ล้มเหลว

เมื่อเซิร์ฟเวอร์ของร้านค้าส่งกลับข้อผิดพลาด (4xx/5xx) หรือ timeout ระบบจะลองส่ง webhook ใหม่ตามตารางดังนี้:

ลำดับเวลา
ครั้งที่ 1ทันที (เมื่อใบแจ้งหนี้เสร็จสมบูรณ์)
ครั้งที่ 2หลังจาก 10 วินาที
ครั้งที่ 3หลังจาก 30 วินาที
ครั้งที่ 4หลังจาก 30 วินาที
ครั้งที่ 5หลังจาก 60 วินาที

สูงสุด 5 ครั้ง หลังจากล้มเหลว 5 ครั้ง callback_status จะเปลี่ยนเป็น failed และไม่มีการลองใหม่อีก

ร้านค้าควรตรวจสอบให้แน่ใจว่า endpoint มีเสถียรภาพและส่งกลับ 2xx อย่างรวดเร็ว (< 10 วินาที) เพื่อหลีกเลี่ยงการลองใหม่ที่ไม่จำเป็น