Webhooks

รับ event tip.created ตรวจสอบ timestamp และลายเซ็น HMAC-SHA256 จาก raw request body

รับ event

แอปต้องมี HTTPS Webhook URL และ grant ต้องมี scope events.subscribe เมื่อครีเอเตอร์ได้รับทิปสำเร็จ Furipay จะ POST payload นี้:

{
  "event": "tip.created",
  "name": "Ploy",
  "amount": 50,
  "message": "สู้ ๆ นะ!",
  "createdAt": "2026-08-14T09:12:00.000Z"
}

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

ทุก request มี header X-Furipay-Signature รูปแบบ t=UNIX_SECONDS,v1=HEX_SIGNATURE โดยลายเซ็นคือ HMAC-SHA256 ของ t.rawBody ด้วย webhook secret

เก็บ raw body เดิมก่อน parse JSON อย่า stringify object กลับเพื่อยืนยันลายเซ็น:

import { createHmac, timingSafeEqual } from "node:crypto";

function verifyFuripayWebhook(
  rawBody: Buffer,
  header: string,
  webhookSecret: string,
): boolean {
  const fields = Object.fromEntries(
    header.split(",").map((part) => part.split("=", 2)),
  );
  const timestamp = Number(fields.t);
  const provided = Buffer.from(fields.v1 ?? "", "hex");

  if (
    !Number.isInteger(timestamp) ||
    Math.abs(Date.now() / 1000 - timestamp) > 300 ||
    provided.length !== 32
  ) {
    return false;
  }

  const expected = createHmac("sha256", webhookSecret)
    .update(String(timestamp) + ".")
    .update(rawBody)
    .digest();

  return timingSafeEqual(expected, provided);
}

การจำกัด timestamp ไม่เกิน 5 นาทีช่วยลดความเสี่ยงจาก replay จากนั้นตอบ HTTP 2xx ให้เร็วและค่อยทำงานหนักใน queue

การส่งซ้ำและการทดสอบ

  • Furipay ให้เวลาแต่ละ request 10 วินาที
  • เมื่อเกิด network error, timeout หรือ non-2xx ระบบพยายามส่งสูงสุด 4 ครั้ง โดยรอ 0, 5, 60 และ 600 วินาทีก่อนแต่ละครั้ง
  • ปุ่มส่ง event ทดสอบใน Developer Portal ส่งเพียงครั้งเดียวและบันทึกผลใน Delivery log
  • Endpoint ต้องรองรับ event ซ้ำได้ เพราะการ retry อาจทำให้ request ที่ประมวลผลสำเร็จแล้วถูกส่งอีกครั้ง
Webhooks | Furipay Docs