Cách Stripe gửi webhook

Không công bố timeout; thường dùng 20 giây. Thử lại 3 ngày rồi Stripe vô hiệu hóa endpoint. Định dạng chữ ký đã kiểm chứng, kèm code xác minh Node.

Hành vi gửi

Lấy từ tài liệu của chính hãng, kiểm tra lần cuối 2026-08-06. Hãng không nêu con số nào thì bảng này cũng không ghi.

Thời hạn phản hồihãng không công bố

Stripe không công bố con số nào. Tài liệu chỉ nói phải trả về 2xx trước mọi logic chậm. 20 giây là con số cộng đồng thường dùng

Thử lạiExponential backoff trong tối đa 3 ngày ở live mode. Sandbox: 3 lần gửi trong vài giờ
Ngừng gửiEndpoint bị vô hiệu hóa nếu tiếp tục thất bại, có email báo trước. Sự kiện vẫn nằm trong Events API trong 30 ngày
Nguồnhttps://docs.stripe.com/webhooks

Chữ ký

Kiểm chứng bằng một bộ xác minh chạy thật, có bộ test chứng minh, chứ không đọc từ tài liệu. Kiểm chứng lần cuối 2026-08-20.

Thuật toánHMAC-SHA256
Payload được ký{timestamp}.{body}
Encodinghex
Headerstripe-signature = t=X,v1=hex
Dung sai5 phút

Xác minh

Dùng raw body của request, đúng từng byte, trước mọi bước parse JSON. Mọi cơ chế trên trang này đều hỏng ngay khi framework serialize lại payload.

const crypto = require("node:crypto");

// stripe-signature: "t=1700000000,v1=abc..." — HMAC-SHA256 over "{t}.{body}"
const pairs = Object.fromEntries(
  req.headers["stripe-signature"].split(",").map((p) => p.split("=")),
);
const expected = crypto
  .createHmac("sha256", endpointSecret) // whsec_... from the endpoint's page
  .update(`${pairs.t}.${rawBody}`)
  .digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(pairs.t)) < 300;
const received = Buffer.from(pairs.v1 ?? "");
const wanted = Buffer.from(expected);
// timingSafeEqual throws on a length mismatch, so check that first.
const valid =
  fresh && received.length === wanted.length && crypto.timingSafeEqual(received, wanted);

Hoặc để SDK làm: stripe.webhooks.constructEvent(rawBody, sig, secret). Hàm này cần raw body, nên hãy gắn express.raw() vào route webhook trước mọi JSON parser.

Những chỗ dễ vấp

Stripe không bao giờ công bố thời hạn phản hồi. Tài liệu chỉ nói phải trả về 2xx trước mọi logic chậm; 20 giây là con số cộng đồng bám theo, và những handler ghi đồng bộ vào một database chậm thì sự kiện nào cũng sát ngưỡng đó.

Sau 3 ngày thất bại, sự kiện lặng lẽ rời khỏi hàng đợi thử lại. Nếu tiếp tục thất bại, endpoint bị vô hiệu hóa, có email báo trước, và các sự kiện vẫn lấy về được qua Events API trong 30 ngày.

Đọc thêm

AnyHook đứng trước các endpoint nhận webhook từ Stripe: trả lời sau khoảng 1 giây, tự thử lại theo lịch riêng khi server của bạn ngừng hoạt động, và giữ mọi sự kiện để bạn phát lại được. Đổi một URL, giữ nguyên code; để nhận được các lần thử lại và phát lại, bạn xác minh AnyHook-Signature, vì chữ ký riêng của Stripe hết hạn sau 5 phút.

Cách hoạt động →