Webhooks 詳解:簽章負載與重試機制

Backend2026-10-08TryQuickToolBox

你剛剛使用 webhooks 整合了一個支付閘道。在測試環境中一切正常,但在生產環境中,事件有時會遺失,或者你會收到重複的通知,導致重複扣款。根本原因通常在於你如何處理簽章負載與重試。本文將解釋 webhooks 的運作機制,並提供實用步驟來建立穩健且安全的整合。

什麼是 Webhooks?

Webhooks 是使用者定義的 HTTP 回呼。當來源系統中發生事件時(例如,一筆付款被處理),它會向你指定的 URL 發送一個包含事件資料的 HTTP POST 請求。與輪詢不同,webhooks 以近乎即時的方式推送資料,可降低延遲與伺服器負載。

然而,webhooks 也帶來挑戰:驗證真實性、處理失敗,以及確保僅處理一次。讓我們逐一解決。

簽章負載:驗證真實性

由於 webhook 端點是公開可存取的,任何人都可以發送偽造請求。為了防止這種情況,提供者會使用密鑰對負載進行簽章。你驗證簽章以確保請求是真實的。

HMAC 簽章的運作方式

大多數提供者使用 HMAC(雜湊訊息鑑別碼)搭配 SHA-256。提供者使用請求主體與共享密鑰計算簽章,然後將其包含在標頭中(例如 X-Signature)。你在自己這邊重新計算簽章並進行比對。

Python 範例:

import hmac
import hashlib

def verify_signature(payload, secret, received_signature):
    expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received_signature)

重要: 使用原始請求主體,而非解析後的 JSON,因為解析可能會改變空白字元並破壞簽章。

常見陷阱

重試:優雅地處理失敗

你的端點可能暫時無法使用,或者網路問題可能導致失敗。可靠的 webhook 系統會以指數退避重試失敗的傳遞。

重試策略

提供者通常會在非 2xx 回應或逾時時重試。常見模式:

策略說明範例
固定間隔每 N 秒重試一次每 30 秒,最多 5 次
指數退避每次嘗試將延遲加倍1 秒、2 秒、4 秒、8 秒...
指數退避加抖動加入隨機性以避免驚群效應1 秒 ± 0.5 秒、2 秒 ± 1 秒...

作為接收方,你無法控制發送方的重試策略,但你可以設計端點使其具備韌性。

接收方的最佳實踐

  1. 快速回應: 在幾秒內回傳 2xx。將處理卸載至背景工作。
  2. 保持冪等: 使用負載中的冪等鍵以避免重複處理。
  3. 記錄所有內容: 儲存傳入的 webhooks 以供除錯與重放。
  4. 監控失敗: 為重複失敗設定警報。

實作冪等性

重複會發生:發送方可能因為你的回應緩慢而重試,或者你可能意外處理同一個事件兩次。冪等性確保多次處理一個事件與處理一次具有相同的效果。

使用唯一的事件 ID(通常在負載或標頭中提供),並將其儲存在具有唯一約束的資料庫中。在處理之前,檢查該 ID 是否存在;如果存在,則跳過。

def process_event(event_id, data):
    if EventLog.exists(event_id):
        return  # already processed
    EventLog.create(event_id)
    # process data...

對於高吞吐量的系統,使用分散式鎖或資料庫交易以避免競爭條件。

安全性考量

除了簽章驗證之外,還需考慮:

在本機測試 Webhooks

在開發期間,你需要一個公開 URL 來接收 webhooks。像 ngrok 或 localtunnel 這類工具可以將你的本機伺服器暴露出去。或者,許多提供者提供 CLI 將事件轉發到你的 localhost。

模擬失敗以測試重試處理:故意回傳 500 錯誤並觀察提供者的重試模式。

常見問題

如何驗證 webhook 簽章?

使用 HMAC 搭配共享密鑰。計算原始請求主體的雜湊,並使用恆定時間比較函式將其與簽章標頭進行比對。

如果收到重複的 webhooks 該怎麼辦?

透過儲存事件 ID 來實作冪等性。在執行業務邏輯之前,檢查該事件是否已經處理過。

我可以僅依賴 webhook 重試來確保可靠性嗎?

不行。重試有幫助,但無法保證傳遞。對於關鍵事件,將 webhooks 與輪詢備援或持久化事件的訊息佇列結合使用。

在除錯 webhook 負載時,你可能需要檢查 JSON 資料。使用我們的 JSON Formatter 來快速美化列印並驗證負載。