一句話總結:Webhook 就是「事件發生時,系統主動把消息推給你指定的 URL」,拍賣場景下它負責串接金流、物流、第三方通知等所有需要即時反應的外部系統,設計重點在簽章驗證、冪等處理和指數退避重試。
你有沒有遇過這種狀況——買家已經付完款了,但賣家後台遲遲沒更新狀態,客服電話接到手軟?或者第三方物流系統明明已經收到包裹,你的拍賣平台還顯示「待出貨」?
這些問題八成出在系統間的事件通知沒接好。今天我們來聊聊拍賣網站最關鍵的黏合劑——Webhook 通知架構該怎麼設計,才能讓系統跟系統之間自動把事情辦妥。
Webhook 到底是什麼?跟輪詢 API 差在哪?
Webhook 是一種事件驅動的 HTTP 回呼機制——當特定事件發生時,伺服器主動對你預先登記的 URL 發送一個 HTTP POST 請求,把事件資料推過去。 跟傳統的輪詢(Polling)相比,差異很明顯:
| 比較項目 | Polling(輪詢) | Webhook(推送) |
|---|---|---|
| 發起方 | 客戶端定時來問 | 伺服器主動推 |
| 即時性 | 取決於輪詢間隔(通常 30 秒~5 分鐘) | 事件發生後幾秒內 |
| 伺服器負擔 | 高(大量無意義的請求) | 低(有事才發) |
| 實作難度 | 低 | 中(需處理簽章、重試、冪等) |
根據 Zapier 在 2025 年的開發者調查,超過 83% 的 SaaS 平台已經支援 Webhook 作為主要的事件通知方式,而純 Polling 的使用率則持續下降。在拍賣場景下,出價更新、得標確認、付款回調這些事件動輒需要秒級反應,Polling 的延遲根本不能接受。
舉個最直接的例子:買家透過綠界 ECPay 付款成功後,綠界會對你的 Webhook URL 發一個 POST,你的系統收到後立刻更新訂單狀態、發送得標確認通知給賣家。這整個流程不需要任何人工介入,也不需要你的系統每隔 30 秒去問綠界「這筆付了沒」。
拍賣平台需要串接哪些 Webhook 事件?
拍賣平台的 Webhook 大致分兩個方向:「接收外部 Webhook」和「對外發送 Webhook」。 兩邊都要顧到。
接收端:外部系統推給你的事件
- 金流回調 — 綠界、藍新等支付閘道在付款成功/失敗時推送交易結果。這是最關鍵的一條,漏接就等於錢收到了但系統不知道
- 物流狀態更新 — 超商取貨、宅配到貨等物流節點變動
- 社群登入回調 — LINE Login、Google OAuth 等第三方登入狀態通知
- 簡訊/Email 送達回報 — Twilio、SendGrid 等服務回報訊息投遞結果
發送端:你的平台推給第三方的事件
- 拍品狀態變更 — 上架、結標、流標等事件通知給賣家的 ERP 系統
- 訂單建立/更新 — 通知物流系統準備出貨
- 會員行為事件 — 推送到 CRM 或行銷自動化平台(如 HubSpot)做再行銷
一個中型拍賣平台,光是金流 + 物流 + 通知這三塊,每天就可能處理 5,000~20,000 筆 Webhook 事件。量一大,架構不穩就會出事。
Webhook 的簽章驗證怎麼做才安全?
簽章驗證是 Webhook 安全性的第一道防線——沒做好這關,任何人只要知道你的 Webhook URL 就能偽造事件。 常見的做法是 HMAC-SHA256 簽章。
流程大致是這樣:
- 你跟對方約定一組 Secret Key(不會透過網路傳輸)
- 發送方用這個 Secret 對 Payload 做 HMAC-SHA256 雜湊,把結果放在 HTTP Header(通常是
X-Signature-256) - 你收到 Webhook 後,用同一個 Secret 對 Raw Body 做同樣的雜湊
- 比對兩個值是否一致——一致就是合法的,不一致就直接拒絕
// Laravel 中驗證 Webhook 簽章的範例
public function handleWebhook(Request $request)
{
$secret = config('services.payment.webhook_secret');
$signature = $request->header('X-Signature-256');
$payload = $request->getContent();
$expected = hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expected, $signature)) {
// 簽章不符,拒絕處理
Log::warning('Webhook 簽章驗證失敗', [
'ip' => $request->ip(),
]);
return response('Unauthorized', 401);
}
// 簽章驗證通過,繼續處理事件
$event = json_decode($payload, true);
// ...
}
有幾個容易踩的坑:
- 一定要用
hash_equals()做比對,不要用==或===。後者會被 timing attack 攻擊,透過回應時間差推敲出正確的簽章 - 用 Raw Body 而不是解析後的 JSON 來算簽章。PHP 的
json_decode再json_encode回去可能會改變格式(像是 Unicode 編碼或浮點精度),導致簽章對不上 - Secret Key 要定期輪換。支援多組 Secret 同時有效(舊的給一個過渡期),避免換 Key 的瞬間漏接事件
Stripe 的 Webhook 安全設計是業界標竿,他們甚至在簽章中加入了 timestamp 防止 replay attack。根據他們的工程部落格,約 0.3% 的 Webhook 請求是惡意偽造的,比你想像的多。
重試機制該怎麼設計才不會雪崩?
Webhook 重試是一把雙面刃——不重試會漏事件,重試太兇會壓垮對方。 最佳實務是指數退避(Exponential Backoff)加上最大重試次數。
我們平台的重試策略是這樣設計的:
| 重試次數 | 等待時間 | 累計時間 |
|---|---|---|
| 第 1 次 | 10 秒 | 10 秒 |
| 第 2 次 | 30 秒 | 40 秒 |
| 第 3 次 | 2 分鐘 | 2 分 40 秒 |
| 第 4 次 | 10 分鐘 | 12 分 40 秒 |
| 第 5 次 | 1 小時 | 1 小時 12 分 40 秒 |
| 第 6 次(最終) | 4 小時 | 5 小時 12 分 40 秒 |
6 次都失敗就丟進 Dead Letter Queue(DLQ),由人工介入排查。同時發 Slack 告警給 on-call 工程師。
這裡有個實際踩坑的案例。去年某拍賣平台的金流 Webhook 處理邏輯有 Bug,每次收到都回 500。金流端的重試機制很激進(每 5 秒重試一次,連續 100 次),結果 短短 8 分鐘內打了超過 100 次相同的 Webhook,而且平台那邊每收到一次就寫一筆錯誤 Log,Log 服務也跟著被打掛。後來他們加上了以下防護:
- 接收端對同一個
event_id做冪等處理(重複的直接回 200 不再處理) - 發送端改用指數退避,並設上限 6 次
- 兩端都加了 Circuit Breaker,連續失敗超過閾值就暫停,等冷卻後再試
冪等處理怎麼做?
「冪等」聽起來很學術,白話講就是:同一個事件推給你十次,結果跟推一次一模一樣。做法很直覺——每個 Webhook 事件帶一個唯一的 event_id,你收到後先查資料庫有沒有處理過這個 ID,有就直接回 200,沒有才處理。
// 冪等處理的簡化範例
public function processWebhook(array $event)
{
$eventId = $event['event_id'];
// 用資料庫或 Redis 記錄已處理的事件
if (Cache::has("webhook_processed:{$eventId}")) {
return response('Already processed', 200);
}
// 處理事件邏輯...
$this->handleEvent($event);
// 標記為已處理,保留 72 小時
Cache::put("webhook_processed:{$eventId}", true, now()->addHours(72));
return response('OK', 200);
}
這個 72 小時的 TTL 不是隨便設的。大多數 Webhook 系統的重試窗口不會超過 48 小時,留 72 小時是額外的安全邊際。
拍賣場景下 Webhook 的效能瓶頸在哪?
最常見的瓶頸不是 Webhook 本身,而是 Webhook 觸發的下游邏輯太重。 比如一個「出價成功」事件觸發的 Webhook 可能要做這些事:
- 更新拍品目前最高價
- 通知前一位最高出價者「你被超價了」
- 檢查是否觸發防狙擊延時規則
- 同步到搜尋引擎索引
- 推送到數據分析平台
如果這五件事全部在 Webhook 處理的同一個 HTTP Request 裡做完,回應時間可能拉到 3-5 秒。對發送端來說,你遲遲不回 200 他就會以為你掛了,然後啟動重試——惡性循環就開始了。
解法是 快速回應 + 非同步處理:
- 收到 Webhook 後,先做最基本的簽章驗證和冪等檢查
- 把事件丟進 Queue(Laravel 用
dispatch()丟 Job) - 立刻回 200
- 背後的 Worker 慢慢處理那五件事
這種做法的好處是你的 Webhook endpoint 回應時間可以壓在 100ms 以內,發送端不會覺得你有問題。而且 Queue 天然有重試和失敗處理的機制,比你自己在 HTTP 層搞重試穩定得多。
如果你的拍賣平台同時有即時出價的 WebSocket 架構,Webhook 和 WebSocket 是互補的——WebSocket 負責前端即時更新,Webhook 負責後端系統間的事件串接。兩者別搞混。
監控和除錯怎麼做?
沒有監控的 Webhook 系統就像沒有儀表板的車——出事了你根本不知道哪裡壞的。 至少要做到以下幾件事:
Webhook 日誌
每一筆收到和發出的 Webhook 都要記錄:
- Timestamp
- Event type 和 Event ID
- HTTP 狀態碼(發送端看對方回什麼,接收端記自己回什麼)
- Request/Response Body(要注意脫敏,別把信用卡號或密碼存明文)
- 處理耗時
- 重試次數
這些 Log 最好獨立存放,不要混在一般的 Application Log 裡。量大的話可以考慮推到 Elasticsearch 做查詢和視覺化。
即時告警
- 某個 Webhook endpoint 連續 N 次回非 200 → 告警
- DLQ 堆積超過閾值 → 告警
- 某個事件類型的平均處理時間突然飆升 → 告警
管理後台
如果你的平台有管理儀表板,建議加一個 Webhook 管理頁面,讓技術人員可以:
- 看到每個 Webhook subscription 的狀態(啟用/停用/異常)
- 手動重送失敗的 Webhook
- 查看最近 N 筆事件的詳細記錄
這功能看起來不性感,但出問題的時候能救命。
對外提供 Webhook 時要注意什麼?
如果你的拍賣平台計畫對外開放 API,Webhook 訂閱功能幾乎是必備的。 參考 Stripe、Shopify 等成熟平台的做法,有幾個設計要點:
- 讓開發者自己選要訂閱哪些事件。不要一口氣把所有事件都推過去,浪費對方的處理資源
- 提供 Test Webhook 功能。開發者在串接階段能手動觸發測試事件,確認自己的 endpoint 正常運作
- 文件要寫清楚每個事件的 Payload 結構。最好附上範例 JSON。這部分可以參考拍賣 API 設計的規劃
- Rate Limiting。限制每個訂閱者在單位時間內能收到的 Webhook 數量,避免你的系統因為一個失控的 subscriber 而被拖慢
Shopify 的統計顯示,提供完善 Webhook 支援的平台,第三方開發者的整合速度平均快了 60%,開發者滿意度也明顯較高。如果你的平台有生態系擴展的規劃,Webhook 就是基礎建設。
FAQ
Q:Webhook 跟 WebSocket 有什麼不同?該用哪個? 簡單講,Webhook 是 Server-to-Server 的事件通知,走一般的 HTTP 請求;WebSocket 是 Server-to-Client 的即時通訊,維持一條持久連線。在拍賣系統裡,前端即時更新用 WebSocket(如 Reverb 即時出價架構),後端系統間串接用 Webhook。兩者搭配使用,不是二選一。
Q:Webhook 接收端一定要是 HTTPS 嗎? 生產環境下,是的。沒有 HTTPS 的 Webhook URL 意味著 Payload 在傳輸過程中是明文的,任何中間節點都能看到(包括可能含有個資的訂單資料)。多數成熟的 Webhook 發送端(如 Stripe、綠界)也會強制要求 HTTPS。
Q:Webhook 處理失敗了,怎麼通知相關人員? 最穩的做法是「DLQ + 告警通知」雙管齊下。失敗事件進 Dead Letter Queue 暫存,同時透過 Slack、Email 或站內通知系統告知 on-call 人員。管理後台也要有手動重送的功能,方便排查後一鍵補發。
Q:自建 Webhook 系統還是用第三方服務? 看規模。每天幾百筆以下,自建就好,Laravel 的 Queue + Event 機制很夠用。如果量大到每天數萬筆、需要跨多個地區節點,可以考慮 Svix、Hookdeck 這類 Webhook 基礎設施服務,他們幫你處理重試、簽章、監控這些苦工。
Webhook 這東西,設計得好的時候你完全感覺不到它的存在,出問題的時候才知道它有多重要。就像拍賣平台背後的水管工程——買家點下付款那一刻,從金流確認、訂單更新、賣家通知到物流啟動,背後全是 Webhook 在默默搬磚。
如果你正在規劃拍賣網站的技術架構,Webhook 通知系統絕對值得在早期就好好設計。搭配完善的通知系統和穩定的 API 生態,才能讓整個平台的各個環節真正串起來,自動化地跑。想要從頭開始打造這樣的系統,可以參考專業的電商網站建置服務,少走很多彎路。