Cách PayPal gửi webhook

PayPal ký bằng RSA-SHA256 trên CRC32 của body, nối chuỗi tới certificate được host. Vì sao verify API là lựa chọn hợp lý, kèm cấu trúc khi tự xác minh.

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ố
Thử lạihãng không công bố
Ngừng gửihãng không công bố

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ánRSA-SHA256
Payload được ký{txId}|{txTime}|{webhookId}|{crc32(body)}
Encodingbase64
Headerpaypal-transmission-sig + 4 more
Dung saikhông quy định

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.

// Manual verification means: fetch the cert from paypal-cert-url, extract
// its public key, then RSA-SHA256-verify paypal-transmission-sig against
//   "{transmission-id}|{transmission-time}|{webhook-id}|{crc32(rawBody)}"
// Most integrations skip that and call the verify endpoint instead:
const res = await fetch(
  "https://api-m.paypal.com/v1/notifications/verify-webhook-signature",
  {
    method: "POST",
    headers: { authorization: `Bearer ${accessToken}`, "content-type": "application/json" },
    body: JSON.stringify({
      transmission_id: req.headers["paypal-transmission-id"],
      transmission_time: req.headers["paypal-transmission-time"],
      cert_url: req.headers["paypal-cert-url"],
      auth_algo: req.headers["paypal-auth-algo"],
      transmission_sig: req.headers["paypal-transmission-sig"],
      webhook_id: webhookId, // yours, from the developer dashboard
      webhook_event: JSON.parse(rawBody),
    }),
  },
);
const valid = (await res.json()).verification_status === "SUCCESS";

Nếu bạn tự xác minh: kiểm tra cert_url trỏ về paypal.com trước khi tải nó, nếu không kẻ tấn công chỉ cần host certificate của riêng họ.

Những chỗ dễ vấp

Đây là cơ chế duy nhất trên trang có thể phải gọi mạng khi xác minh. Năm header chữ ký đi kèm mỗi sự kiện, và payload được ký chứa CRC32 của body chứ không phải chính body.

Đọc thêm

AnyHook đứng trước các endpoint nhận webhook từ PayPal: 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.

Cách hoạt động →