เริ่มต้นใช้งาน
ข้อมูลหลัก
อื่นๆ

ผลการยืนยันผ่าน Webhooks

SEEK Pass สามารถส่งสถานะการยืนยันผ่านคำขอ POST ไปยังปลายทาง webhook ที่พาร์ทเนอร์ลงทะเบียนไว้ พาร์ทเนอร์สามารถจัดการ webhook ได้โดยการสร้างและอัปเดต รวมถึงการหมุนเวียน webhook secret:
POST /api/partner/v2/webhooks
GET /api/partner/v2/webhooks
GET /api/partner/v2/webhooks/:id
PUT /api/partner/v2/webhooks
PUT /api/partner/v2/webhooks/:id/secret
DELETE /api/partner/v2/webhooks/:id

การลงทะเบียน webhook

ตัวอย่างคำขอทรัพยากรของ webhook:
# POST /api/partner/v2/webhooks

{
  "name": "SEEK Pass Webhook",          // Optional
  "description": "SEEK Pass Webhook",   // Optional
  "url": "https://example.com/webhook"  // Required
}
ตัวอย่างการตอบกลับทรัพยากรของ webhook:
{
  "id": "03a7c560-93cd-4579-a26a-a3b98efa8a49",
  "name": "SEEK Pass Webhook",
  "description": "SEEK Pass Webhook",
  "url": "https://example.com/webhook",
  "secret": "23a8db7b1b368dd0e30eff95b048b994e892000e69a4fbb3ca78ff92e13ad1f7"
}
ดู Open API specification สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการอัปเดต endpoint ของ webhook และรูปแบบคำขอและการตอบกลับ

เหตุการณ์ webhook

SEEK Pass สามารถส่งการอัปเดตสำหรับเหตุการณ์ต่าง ๆ ในขั้นตอนต่าง ๆ ของเวิร์กโฟลว์การยืนยันตัวตน ขณะนี้เรารองรับเหตุการณ์สถานะการยืนยันตัวตนต่อไปนี้ที่ส่งไปยัง webhook ที่ลงทะเบียนแล้ว:
  • canceled
  • verified
Caution
เหตุการณ์ webhook มีเพียงข้อมูลระดับสูงบางส่วน เช่น สถานะ พาร์ทเนอร์ควรเรียก verification status API พร้อม request ID เพื่อดึงผลการยืนยันที่สมบูรณ์ เช่น ชื่อเต็มและวันเกิด
ตัวอย่างเหตุการณ์ webhook:
{
  "event_id": "5c4ac58b-5cf9-40a0-b60a-28c0137663ed",
  "request_id": "7cbc7e51-82f5-43ec-8d63-b7cdf1170425",
  "type": "vwsp_request.verified",
  "created_at": "2025-04-15T02:14:01.307391Z"
}

การตรวจสอบลายเซ็น webhook (HMAC-SHA256)

แต่ละ webhook เชื่อมกับ secret ที่สร้างอัตโนมัติซึ่งเป็นคีย์ร่วมระหว่างพาร์ทเนอร์กับ SEEK Pass เพื่อการสื่อสารที่ปลอดภัย SEEK Pass ใช้ secret นี้ลงนาม payload ทุกครั้งที่ส่งออกด้วย HMAC-SHA256 คุณต้องตรวจสอบลายเซ็นนี้ในทุกคำขอขาเข้าเพื่อยืนยันว่ามาจาก SEEK Pass จริงและไม่ถูกแก้ไขSEEK Pass แนบสอง header ในทุกการส่ง webhook:
Headerคำอธิบาย
X-SignatureHex digest ของ HMAC-SHA256 ของข้อความที่ลงนาม
X-TimestampUnix timestamp เมื่อส่งเหตุการณ์
HMAC ถูกคำนวณจากข้อความที่รวมทั้ง timestamp และ เนื้อหาคำขอดิบ โดยปกติต่อกันเป็น <timestamp>.<raw_body> การรวม timestamp เป็นการออกแบบเพื่อป้องกัน การโจมตีแบบ replay ซึ่งคำขอที่ถูกต้องถูกดักและส่งซ้ำในภายหลังเพื่อตรวจสอบ webhook ขาเข้า endpoint ของคุณควร:
  • 1. ดึงค่า X-Signature และ X-Timestamp จาก header ของคำขอ
  • 2. ประกอบข้อความที่ลงนาม โดยต่อท้าย timestamp กับเนื้อหาดิบเป็น <timestamp>.<raw_body>
  • 3. คำนวณ HMAC-SHA256 ของข้อความนั้นโดยใช้ webhook secret ของคุณเป็นกุญแจ
  • 4. เปรียบเทียบ HMAC ที่คำนวณได้กับ header X-Signature โดยใช้ การเปรียบเทียบแบบคงที่เวลา เพื่อป้องกันการโจมตีแบบ timing
  • 5. ตรวจสอบ timestamp เพื่อปฏิเสธคำขอที่เก่ากว่าช่วงความคลาดเคลื่อนที่สมเหตุสมผล (เช่น ±15 นาที) เพื่อลดการโจมตีแบบ replay
Caution
ใช้เนื้อหาคำขอดิบที่ยังไม่ถูก parse เสมอเมื่อคำนวณ HMAC การ parse JSON แล้ว serialize ใหม่อาจเปลี่ยนลำดับไบต์และทำให้การตรวจสอบลายเซ็นล้มเหลว
ตัวอย่าง Ruby สำหรับการตรวจสอบ HMAC อย่างปลอดภัย
require "openssl"
require "active_support/security_utils"

TOLERANCE_SECONDS = 900

def verify_webhook(raw_body, secret, given_hmac, timestamp, tolerance_seconds = TOLERANCE_SECONDS)
  # Step 1: Reject stale requests to prevent replay attacks
  age = Time.now.to_i - timestamp.to_i
  return false if age.abs > tolerance_seconds

  # Step 2: Reconstruct the signed message
  signed_message = "#{timestamp}.#{raw_body}"

  # Step 3: Recompute the HMAC-SHA256
  calculated_hmac = OpenSSL::HMAC.hexdigest("SHA256", secret, signed_message)

  # Step 4: Constant-time comparison to prevent timing attacks
  return false unless calculated_hmac.bytesize == given_hmac.bytesize
  ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, given_hmac)
end

def handle_event(raw_body, secret, given_hmac, timestamp)
  return if !verify_webhook(raw_body, secret, given_hmac, timestamp)

  event = JSON.parse(raw_body)

  # Step 5: Idempotency check — discard duplicate events within the tolerance window
  return if event_already_processed?(event["event_id"])
  mark_event_as_processed(event["event_id"])

  # ... handle event
end
ตัวอย่าง Node.js สำหรับการตรวจสอบ HMAC อย่างปลอดภัย
const crypto = require("crypto");

const TOLERANCE_SECONDS = 900;

function verifyWebhook(rawBody, secret, givenHmac, timestamp, toleranceSeconds = TOLERANCE_SECONDS) {
  // Step 1: Reject stale requests to prevent replay attacks
  const age = Math.abs(Math.floor(Date.now() / 1000) - parseInt(timestamp, 10));
  if (age > toleranceSeconds) return false;

  // Step 2: Reconstruct the signed message
  const signedMessage = `${timestamp}.${rawBody}`;

  // Step 3: Recompute the HMAC-SHA256
  const calculatedHmac = crypto
    .createHmac("sha256", secret)
    .update(signedMessage, "utf8")
    .digest(); // Returns a Buffer

  const givenHmacBuffer = Buffer.from(givenHmac, "hex");

  // Step 4: Constant-time comparison to prevent timing attacks
  if (calculatedHmac.length !== givenHmacBuffer.length) return false;
  return crypto.timingSafeEqual(calculatedHmac, givenHmacBuffer);
}

async function handleEvent(rawBody, secret, givenHmac, timestamp) {
  if (!verifyWebhook(rawBody, secret, givenHmac, timestamp)) return;

  const event = JSON.parse(rawBody);

  // Step 5: Idempotency check — discard duplicate events within the tolerance window
  if (await isEventAlreadyProcessed(event.event_id)) return;
  await markEventAsProcessed(event.event_id);

  // ... handle event
}

การหมุนเวียน webhook secret

พาร์ทเนอร์สามารถหมุนเวียน webhook secret ได้ด้วย endpoint นี้: PUT /api/partner/v2/webhooks//secretในช่วงเปลี่ยนผ่าน เหตุการณ์ webhook อาจถูกลงนามด้วย secret เดิม endpoint ของพาร์ทเนอร์ควรตรวจสอบเหตุการณ์ที่ลงนามได้ทั้งด้วย secret เก่าและใหม่ แนวทางที่ปลอดภัยคือลอง secret ใหม่ก่อน แล้วจึงสำรองด้วย secret เดิมตัวอย่างการตอบกลับสำหรับการหมุนเวียน secret:
{
  "id": "03a7c560-93cd-4579-a26a-a3b98efa8a49",
  "name": "SEEK Pass Webhook",
  "description": "SEEK Pass Webhook",
  "url": "https://example.com/webhook",
  "secret": "NEW_SECRET"
}

หมายเหตุ

Caution
เหตุการณ์ webhook อาจถูกส่งมาไม่เรียงลำดับเนื่องจากปัจจัยต่าง ๆ เช่น ความหน่วงของเครือข่าย เพื่อช่วยให้คุณระบุลำดับที่ถูกต้อง เพย์โหลดของแต่ละเหตุการณ์จะมีเวลา created_at เราแนะนำให้ทำการประมวลผลเหตุการณ์แบบ idempotent เนื่องจากอาจเกิดเหตุการณ์ซ้ำได้ แต่ละเหตุการณ์จะมี event_id ซึ่งสามารถใช้ระบุและข้ามเหตุการณ์ที่ซ้ำได้
Info
หากปลายทาง webhook ของคุณไม่ตอบกลับสำเร็จ (HTTP 200) ภายใน 5 วินาที SEEK Pass จะลองส่งอีเวนต์อีกครั้งได้สูงสุด 5 ครั้ง
Info
SEEK Pass ส่งเหตุการณ์การยืนยันตัวตนจากที่อยู่ IP ที่กำหนดซึ่งเผยแพร่ที่นี่ ทำให้พาร์ทเนอร์สามารถเพิ่ม IP เหล่านี้ใน allowlist เพื่อการเชื่อมต่อที่ปลอดภัย

ที่อยู่ IP ของ SEEK Pass

ที่อยู่ IP ต่อไปนี้ใช้โดย SEEK Pass ในการส่งเหตุการณ์ webhook พาร์ทเนอร์ควรเพิ่ม IP เหล่านี้ใน allowlist เพื่อให้การส่งการแจ้งเตือน webhook เชื่อถือได้
Caution
ต้องดำเนินการ: จะมีการเพิ่มช่วง IP ใหม่ (18.98.198.160/28) ในอนาคตอันใกล้ กรุณาตรวจสอบให้แน่ใจว่าได้เพิ่มช่วงนี้ลงในรายการที่อนุญาตของคุณล่วงหน้า เพื่อหลีกเลี่ยงการหยุดชะงักในการส่ง webhook
สภาพแวดล้อมที่อยู่ IP
Staging
  • 13.210.155.65
  • 52.64.188.102
  • 54.79.229.97
  • 18.98.198.160/28
Production
  • 13.210.238.239
  • 52.63.199.197
  • 54.252.19.190
  • 18.98.198.160/28