ไอเดีย #73: ระบบ webhook relay และ retry
ระบบเราต้องรับ webhook จาก payment gateway บริการขนส่ง หรือ LINE แต่ถ้าวันนั้นเราปิดเซิร์ฟเวอร์เพื่อ deploy ห้านาที webhook ที่ส่งมาช่วงนั้นก็หายไป ลูกค้าจ่ายเงินแล้วแต่ออเดอร์ไม่อัปเดต ส่วนตอนพัฒนาบนเครื่องตัวเองก็รับ webhook ไม่ได้อีก ไอเดียนี้คือ webhook relay ตัวกลางที่รับทุกอย่างไว้ก่อน เก็บถาวร แล้วส่งต่อไปยังปลายทางพร้อม retry และตรวจลายเซ็นให้
ใครจะใช้
- นักพัฒนา backend — สร้าง endpoint รับ ตั้งปลายทาง ดูประวัติและสั่งส่งซ้ำ
- DevOps — ดูอัตราความสำเร็จ ตั้งการแจ้งเตือนเมื่อส่งไม่ผ่าน
- ผู้ให้บริการต้นทาง — ไม่ต้องรู้ตัวเลย แค่ส่งมาที่ URL ของ relay
ฟีเจอร์ขั้นต่ำ (MVP)
- สร้าง inbound endpoint แบบ URL สุ่มต่อแหล่ง ตอบ 200 ทันทีหลังบันทึกลงฐานข้อมูล
- ตรวจลายเซ็นของต้นทาง (HMAC ตาม header ที่ผู้ให้บริการกำหนด) ถ้าไม่ผ่านให้ทำเครื่องหมายไว้
- ส่งต่อไปยังปลายทางหนึ่งหรือหลายที่ พร้อมเซ็นลายเซ็นใหม่ของ relay เอง
- Retry แบบ exponential backoff เมื่อปลายทางตอบ error หรือ timeout จนถึงจำนวนครั้งสูงสุด
- Dead letter: เก็บรายการที่ล้มเหลวทั้งหมด และกด replay ได้จากหน้าเว็บ
- หน้าดูรายละเอียดทุก request: header, body, ผลการส่งแต่ละครั้ง
- แจ้งเตือนเมื่อปลายทางล้มเหลวต่อเนื่อง
ต่อยอดได้อีก
- CLI สำหรับ forward webhook มาที่ localhost ตอนพัฒนา
- กรองหรือแปลง payload ก่อนส่ง (transform)
- รับประกันลำดับการส่งต่อ key ที่กำหนด
- Fan-out ไปหลายระบบพร้อมกัน
โครงข้อมูลตั้งต้น
source— id, name, slug, verify_type, verify_secret_encdestination— id, source_id, url, signing_secret_enc, max_attempts, timeout_ms, is_activeevent— id, source_id, received_at, headers_json, body, signature_valid, idempotency_keydelivery— id, event_id, destination_id, status, attempt_count, next_attempt_atdelivery_attempt— id, delivery_id, attempted_at, response_status, response_body_snippet, duration_ms
ระดับความยาก และสิ่งที่จะได้เรียนรู้
ปานกลางถึงท้าทาย
- ทำ job queue ที่มี scheduled retry และ backoff พร้อม jitter
- ตรวจและสร้าง HMAC signature อย่างถูกต้อง (เทียบแบบ constant-time)
- ออกแบบให้ idempotent ปลายทางรับซ้ำได้ไม่พัง
- ใช้
SELECT ... FOR UPDATE SKIP LOCKEDทำ worker หลายตัวดึงงานพร้อมกัน
ข้อควรระวัง
- payload ของ webhook การชำระเงินมีข้อมูลลูกค้า ควรเข้ารหัสหรือกำหนดอายุการเก็บ และจำกัดคนเปิดดู
- secret ของแต่ละแหล่งต้องเก็บแบบเข้ารหัส ไม่แสดงเต็มในหน้าเว็บ
- URL ปลายทางที่ผู้ใช้ตั้งเองเสี่ยง SSRF ต้องกันการยิงเข้าเครือข่ายภายใน
คุณเคยทำ webhook หายจนต้องตามแก้ข้อมูลด้วยมือไหมครับ? แล้วระหว่างเก็บ body ไว้ในฐานข้อมูลกับเก็บใน object storage คิดว่าแบบไหนเหมาะกว่าเมื่อปริมาณเยอะขึ้น มาเล่าให้ฟังหน่อย ใครทำแล้วเอามาแชร์ในกระทู้ได้เลย