Skip to content

Retry · endpoint ล่ม · การส่งซ้ำ ​

ถ้า endpoint ของคุณไม่ตอบ 2xx เราจะส่งซ้ำตามตารางคงที่ข้างล่าง · ตัวเลขในหน้านี้เป็นชุดเดียวในระบบ ไม่มีค่าอื่น

ตารางรอบ retry ​

attemptหน่วงจากครั้งก่อนสะสมจากเวลาที่สร้าง event
1ทันที00:00:00
210 วินาที00:00:10
31 นาที00:01:10
45 นาที00:06:10
530 นาที00:36:10
62 ชั่วโมง02:36:10
76 ชั่วโมง08:36:10
812 ชั่วโมง20:36:10
924 ชั่วโมง44:36:10
1024 ชั่วโมง68:36:10
1124 ชั่วโมง92:36:10
  • รวม 11 ครั้ง · หน้าต่างประมาณ 92 ชั่วโมง 36 นาที (3 วัน 20 ชั่วโมง)
  • มี jitter ±10% ต่อรอบ ⇒ ตารางนี้เป็นค่าเป้าหมาย · เวลาจริงของแต่ละใบดูที่ next_retry_at ในหน้าจัดการ
  • X-PG-Attempt header บอกว่ากำลังอยู่รอบไหน · delivery metadata ไม่อยู่ใน event body

ทำไม inbox ต้องเก็บ 31 วันทั้งที่ retry มีแค่ ~92 ชั่วโมง

manual resend ทำได้ถึง 30 วันหลังสร้าง event ดังนั้น pg_webhook_inbox ต้องเก็บอย่างน้อย 31 วัน เพื่อให้การส่งซ้ำวันสุดท้ายยังชน dedupe row

รอบที่ 9–11 ห่างกันวันละครั้ง

ถ้า endpoint คุณล่มไป 2 ชั่วโมงแล้วกลับมา คุณจะยังไม่ได้ event ที่ค้างทันที — ต้องรอถึงรอบถัดไปตามตาราง

อย่ารอเฉย ๆ · ใช้ replay ย้อนหลัง หรือ reconcile ด้วย GET /v1/pay/events ดึงของที่ค้างมาเลย

อะไรนับว่าสำเร็จ ​

เกณฑ์ค่า
สำเร็จHTTP 200 201 202 204 เท่านั้น
timeout ตอนเชื่อมต่อ5 วินาที
timeout ถึงไบต์แรกของ response10 วินาที
timeout รวมทั้ง request15 วินาที (แนะนำให้ตอบภายใน 3 วินาที)
response body ของคุณเราอ่านสูงสุด 8 KB ไปเก็บ log แล้วปิด connection · เนื้อหาไม่มีผลต่อความสำเร็จ
request body ของเรา≤ 256 KB (รับประกัน)
3xx🔴 ไม่ตาม redirect = failure (กัน SSRF + กันไม่ให้ลายเซ็นไปโผล่โฮสต์อื่น)
410 Gonefailure ถาวร → หยุด retry ทันที + ติดธง endpoint_gone
429failure → retry ตามตาราง · 🔴 เราไม่อ่าน Retry-After ของคุณ ตารางคงที่เพื่อให้คาดเดาได้
4xx/5xx อื่น · TLS/DNS/connection errorfailure → retry ตามตาราง

เลิก retry เมื่อ ได้ 2xx · ครบ 11 attempt · ได้ 410 · คุณลบหรือปิด endpoint (delivery ที่ค้างถูกยกเลิก) · ระบบปิด endpoint อัตโนมัติ

สถานะของ delivery มี 4 ค่าเท่านั้น: pending · delivered · failed · canceled

ตอบ 3 วินาที ไม่ใช่ 15

15 วินาทีคือเส้นตายที่เราตัด ไม่ใช่งบเวลาที่ให้ใช้

handler ที่ใช้ 12 วินาทีจะเริ่ม timeout ทันทีที่ traffic เพิ่มขึ้นนิดเดียว แล้วเข้าวงจร: timeout → retry → ทำงานซ้ำ → ช้าลง → timeout อีก

รูปแบบที่ถูก: verify → transaction ที่ insert/dedupe inbox พร้อม outbox → commit → ตอบ 200 · outbox relay ทำงานต่อภายหลัง

อย่าตอบ 500 เมื่อ verify ลายเซ็นไม่ผ่าน

ตอบ 500 = failure = เรา retry ทั้ง 11 รอบ ทุก event ที่ verify ไม่ผ่าน = คุณถล่มตัวเอง

ลายเซ็นไม่ผ่าน → 401 · timestamp เกินหน้าต่าง → 400 · event ซ้ำ → 🔴 200

endpoint ล่มยาว ​

ล่มติดกัน 6 ชม.    → อีเมล warning + banner ในหน้าจัดการ (ยัง retry ตามปกติ)
ล่มติดกัน 12 ชม.   → อีเมล warning ครั้งที่ 2 + แจ้งผู้ให้บริการที่ดูแลบัญชีคุณ
ล่มติดกัน 24 ชม.   → 🔴 ปิด endpoint อัตโนมัติ

เงื่อนไขการปิดอัตโนมัติ (ต้องครบทั้งสองข้อ): ใน 24 ชั่วโมงล่าสุด success = 0 และ delivery ที่ fail ≥ 50

สิ่งที่เกิดขึ้นเมื่อถูกปิด

  • endpoint.status = 'disabled_by_system'
  • หยุดสร้าง delivery ใหม่ — แต่ event ยังถูกเก็บครบ 30 วัน
  • delivery ที่ค้างอยู่ถูกยกเลิก
  • ยิง webhook.endpoint.disabled ไป endpoint กลุ่มอื่นที่ยังดี + อีเมล + บันทึก audit
  • 🔴 เปิดคืนต้องกดเองจากหน้าจัดการ — มีปุ่ม Replay failed deliveries (72 ชม. ล่าสุด) ให้กดต่อทันที

endpoint ที่ flaky ไม่ถูกปิด

มี success อย่างน้อย 1 ครั้งใน 24 ชั่วโมง = ไม่ถูกปิด ⇒ endpoint ที่ fail 90% ยังอยู่ต่อได้ (แต่จะโดนเตือน)

เกณฑ์ตั้งไว้แบบนี้เพราะการปิด endpoint ของ merchant ที่แค่ช้า อันตรายกว่าการปล่อยให้ retry ต่อ

ถ้าคุณมี endpoint เดียวและมันถูกปิด

event ที่ ปิดรับไม่ได้ (deposit.reversed · withdrawal.fill_reversed · balance.debited · webhook.key.rotating · webhook.endpoint.disabled) จะถูกส่งทาง อีเมล ไปที่ technical contact พร้อม banner สีแดงค้างในหน้าจัดการ

⇒ ตั้ง technical contact เป็นเมลที่มีคนอ่านจริง ไม่ใช่ noreply@ · และควรตั้ง endpoint สำรองไว้อย่างน้อยอีกกลุ่ม

การกดส่งซ้ำเอง ​

ส่งซ้ำใบเดียว ​

bash
curl -X POST \
  "https://api.example-wl.com/v1/bo/webhooks/whep_01K2A9F3TNXR7VB2HD0MKQ5CJW/deliveries/whd_01K3M8QX5DHN2VRT0BKZQ7W4YM/resend" \
  -H "Authorization: Bearer $BO_OIDC_ACCESS_TOKEN" \
  -H "X-PG-Step-Up-Token: $BO_STEP_UP_TOKEN" \
  -H "Idempotency-Key: resend-whd_01K3M8QX-1"

ส่งซ้ำเป็นชุด (replay) ​

bash
curl -X POST \
  "https://api.example-wl.com/v1/bo/webhooks/whep_01K2A9F3TNXR7VB2HD0MKQ5CJW/replay" \
  -H "Authorization: Bearer $BO_OIDC_ACCESS_TOKEN" \
  -H "X-PG-Step-Up-Token: $BO_STEP_UP_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: replay-2026-08-18-to-21" \
  -d '{
    "event_types": ["deposit.settled", "deposit.reversed"],
    "created_after": "2026-08-18T00:00:00.000Z",
    "created_before": "2026-08-21T00:00:00.000Z",
    "only_failed": true
  }'

resend/replay เป็น /v1/bo/* จึงใช้ OIDC access token ของเจ้าหน้าที่พร้อม step-up token ไม่ใช้ sk_live_* หรือ PG HMAC

กติกาของการส่งซ้ำ ​

เรื่องเป็นยังไง
event.id🔴 เหมือนเดิมทุกประการ ⇒ dedupe ฝั่งคุณยังทำงาน — กด replay ทั้งสัปดาห์ก็ไม่เครดิตซ้ำ ถ้าคุณ dedupe ถูก
ไบต์ของ bodyเหมือนเดิมทุกไบต์ — เราเก็บ payload ไว้ตั้งแต่ตอนสร้าง ไม่ render ใหม่ ⇒ api_version ที่เปลี่ยนกลางทางไม่ทำให้ payload เพี้ยน
ลายเซ็นคำนวณใหม่ (t ต้องใหม่) และเซ็นด้วยกุญแจที่ active ตอนนี้
delivery metadataอยู่ใน header เท่านั้น: X-PG-Delivery-Id ใหม่, X-PG-Attempt: 1, X-PG-Resend: manual · event body ไม่เปลี่ยน
retryได้ครบ 11 รอบใหม่
bulk10,000 event ต่อคำสั่ง ปล่อยที่ 50 ใบ/วินาที · มี audit ทุกครั้ง
อายุส่งซ้ำได้ 30 วัน นับจาก created_at ของ event
rate limitbucket webhook_resend — 10 คำสั่ง/นาที ต่อ endpoint (นับคำสั่ง ไม่ใช่จำนวน delivery) ⇒ คำสั่ง replay ที่มี 10,000 ใบ นับเป็น 1

replay 10,000 ใบ = 50 ใบ/วินาที ยิงเข้า endpoint คุณ

ก่อนกด ให้แน่ใจว่า handler ตอบเร็ว (< 3 วิ) และ dedupe ทำงาน · ไม่งั้นคุณจะสร้าง incident เองจากการกู้ incident

เมื่อ event หายจริง ๆ ​

หมดรอบ 11 ครั้งแล้วยังไม่สำเร็จ → webhook_deliveries.status = 'failed' · event ยังอยู่ในระบบ 30 วัน และส่งซ้ำได้

แต่ทางที่ถูกต้องกว่าเมื่อกลับมาจาก downtime ยาวคือ ดึงเอง ไม่ใช่รอ:

bash
# ดึงทุก event ตั้งแต่ sequence ล่าสุดที่คุณเห็น
curl -s "https://api.example-wl.com/v1/pay/events?since_sequence=88214&limit=100" \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "PG-Signature: ..." -H "PG-Timestamp: ..." -H "PG-Nonce: ..." -H "PG-Version: 2026-08-01"
  • ย้อนหลังได้ 30 วัน
  • since_sequence เป็น cursor ตัวเดียว ของ endpoint นี้ (ไม่ใช้ cursor แบบ endpoint อื่น)
  • ป้อนเข้า handler เดิมได้เลย — dedupe และ version guard จะจัดการซ้ำให้เอง

🔴 webhook ไม่ใช่ source of truth — ledger คือความจริง webhook คือการแจ้งเตือน · reconcile job รายวันเป็นเงื่อนไขบังคับก่อน go-live ไม่ใช่ทางเลือก

เฝ้าอะไรบ้าง ​

ตั้ง alert เมื่อเพราะ
verify ลายเซ็นไม่ผ่าน > 0อาจกำลังหมุนกุญแจ หรือมีคนยิงของปลอม
handler ตอบ non-2xx เกิน 1% ใน 5 นาทีก่อนถึงเกณฑ์ปิด endpoint อัตโนมัติ 24 ชม.
X-PG-Attempt > 3 บ่อยผิดปกติendpoint ช้าหรือ timeout เป็นระบบ
sequence มี gap ค้างเกิน 4 วันหายจริง (gap ชั่วคราวเป็นเรื่องปกติ — ดู ordering)

log ที่ต้องมีทุกใบ: event.id + X-PG-Delivery-Id + X-PG-Attempt — delivery headers ใช้เพื่อ observability เท่านั้น ห้ามขับ business logic

ขั้นต่อไป ​

อ่านต่อเมื่อ
ลำดับไม่การันตี — version guard🔴 retry 92 ชม. แปลว่า event เก่ามาถึงทีหลังได้เสมอ — อ่านหน้านี้
ตรวจลายเซ็น Ed25519ยัง verify ไม่ผ่านและกำลังโดน retry ถล่ม
ทดสอบก่อนขึ้น productionอยากลองทำให้ endpoint ล่มแล้วดูผล
ภาพรวม webhookต้องการเกณฑ์ตั้ง endpoint และโครงซองอีกครั้ง
เช็กลิสต์ก่อน go-liveใกล้ขึ้น production

เอกสารชุดนี้อิงสัญญา API เวอร์ชัน 2026-08-01