收不到支付 Webhook 回調怎麼辦?排查原因並補回漏掉的事件
收不到支付 Webhook 回調時的排查清單:先看投遞記錄,再查回調地址、超時、驗簽和事件訂閲,最後通過事件接口補回漏掉的事件。
本文目錄
收不到支付 Webhook 回調是最常見的對接問題之一,而原因幾乎總是那麼幾種:沒有訂閲事件的端點,請求根本沒發出;發到了一個返回錯誤或重定向的地址;處理時間超過了超時限制;或者請求到了,卻被你自己的代碼拒絕了。好消息是,漏收回調不等於漏掉付款:付款無論如何都已記錄,事件也可以重新取回。
這份清單逐步説明如何找出收不到回調的原因、每種原因怎麼修,以及如何補回所有漏掉的事件。
第一步:回調到底發出去了嗎?
在排查你的服務器之前,先看平台這一側。在控制台 開發者 → Webhook 的 Webhook 投遞台賬 裏,可以看到近 30 天每一次投遞的事件、關聯單號、嘗試次數 和 最近結果:
| 投遞記錄顯示 | 含義 | 下一步看哪裏 |
|---|---|---|
| 這個事件完全沒有投遞 | 沒有任何端點訂閲它 | 「有沒有訂閲這個事件?」 |
| 無法連接 | 地址沒有響應:DNS、TLS、防火牆,或服務器宕機 | 「回調地址能從公網訪問嗎?」 |
| 你的接口返回 HTTP 4xx/5xx | 請求到了,但你的服務器拒絕了或處理出錯 | 「你的處理代碼拒絕了它嗎?」 |
| 你的接口返回 HTTP 3xx | 你的服務器做了重定向,按失敗處理 | 「回調地址能從公網訪問嗎?」 |
| 等待重試 | 投遞失敗,已安排下一次重試 | 修好原因,重試時會自動送達 |
| 重試已耗盡 | 6 次嘗試全部失敗 | 修好原因後點擊 立即重投 |
| 已送達 | 你的接口按時返回了 2xx | 問題出在收到之後你自己的處理流程 |
開發者 → 事件 頁面從事件的角度展示同樣的信息:每個事件是 已送達、重試中、有幾個地址投遞失敗,還是因為沒有配置接收地址而 無投遞。
有沒有訂閲這個事件?
- 既沒有端點,也沒有
notify_url。 下單時沒傳notify_url的訂單,會投遞給它所屬應用中訂閲了該事件的 Webhook 端點(以及賬戶級端點)。一個都沒有,就什麼都不會發送。正式訂單不傳notify_url時,若沒有端點訂閲payment.succeeded,創建時就會被拒絕;但退款、拒付等其他事件,仍然需要單獨訂閲。 - 訂閲錯了事件。 每個端點都有自己的事件類型列表。只訂閲
payment.succeeded是常見的坑:這樣你永遠收不到payment.failed、payment.expired、退款和拒付的通知。見 事件類型。 - 環境或應用不對。 沙箱和正式環境的端點相互獨立,端點也屬於某個應用。用測試密鑰創建的訂單永遠不會投遞到正式端點,一個應用的訂單也不會投遞到另一個應用的端點。
- 訂單級
notify_url優先。 如果訂單創建時傳了notify_url,這個訂單的事件會發到該地址,而不是你的端點。排查端點之前,先看一下訂單本身。
回調地址能從公網訪問嗎?
- 必須是公網 HTTPS 地址。
localhost、內網地址或 HTTP 地址在下單時就會被拒絕;本地調試可以用 cloudflared 或 ngrok 暴露一個公網地址。 - 填處理接口,不要填首頁。 平台不跟隨重定向,3xx 按失敗處理,所以處理接口前面的
http → https或www跳轉都會讓投遞失敗。請填寫最終的地址。 - 讓請求能通過。 防火牆、WAF 規則、機器人防護或接口前的 HTTP 基本認證,都會把回調變成一個攔截頁面。
- 改好地址就能救回隊列。 還在排隊的重試和手動重投,都會發到端點當前的地址,所以改正地址就能挽救排隊中的投遞。(訂單級
notify_url保持下單時傳入的值不變。)
你的處理代碼拒絕了它嗎?
- 驗簽失敗。 必須用原始請求體驗簽,在任何 JSON 解析或重新序列化之前;解析後再編碼的請求體就對不上了。密鑰輪換期間,
X-KukoPay-Signature請求頭裏會有多個v1=簽名,只要有一個匹配就是合法請求。用Object.fromEntries之類的寫法解析請求頭只會保留最後一個,導致這 24 小時內的投遞全部驗簽失敗。 - 時間戳校驗太嚴,或者服務器時鐘不準。只拒絕超過 5 分鐘的時間戳,並保持時鐘同步。
- 遇到沒預料到的事件類型就崩潰。 對不處理的事件類型也返回 2xx,不要拋出異常。
- 用驗簽自檢工具。 Webhook 頁面上的 驗簽自檢 可以粘貼收到的原始請求體和簽名頭,告訴你問題出在密鑰、請求體被重新格式化,還是時間戳。
一個正確驗簽並去重的處理方式,見 Webhook 重複通知怎麼辦。
是不是超時了?
只有你的接口在 10 秒內返回 2xx,投遞才算成功。如果處理代碼在響應之前調用了緩慢的發貨接口、發送郵件或生成發票,負載一高就會超時,而且在它還沒處理完的時候就被重投。先響應,後處理:驗簽、記錄事件、返回 2xx,再把耗時的工作交給隊列。
Webhook 是怎樣重試的?
投遞失敗會按以下間隔重試,連同第一次共 6 次:
首次發送 → 30 秒 → 2 分鐘 → 10 分鐘 → 30 分鐘 → 2 小時
- 每次重試的事件
id都不變,請用它去重。 - 每個端點各自重試,一個端點故障不會拖住其他端點。
- 第 6 次失敗後不再自動重試。修好原因後,在投遞記錄裏點擊 立即重投。
如何補回漏掉的 Webhook 事件
即使所有投遞都失敗了,甚至從來沒有配置過端點,事件仍然在。在 KukoPay,事件先記錄、後投遞,所以 事件接口 就是補回的途徑:
# 拉回昨天所有支付成功事件
curl -G "https://api.kukopay.com/v1/events" \
-H "X-Api-Key: $KUKOPAY_API_KEY" \
-d type=payment.succeeded \
-d "created[gte]=2026-11-03T00:00:00Z" \
-d "created[lte]=2026-11-03T23:59:59Z" \
-d limit=100Webhook 的請求體就是事件對象,所以補回的事件直接交給同一套處理代碼即可。因為你的處理代碼按事件 id 去重,重放已經處理過的事件也不會有任何影響。投遞記錄保留 30 天,事件本身不會被清理。
如果只關心一筆訂單,直接查詢訂單:只有 status: "paid" 才表示已經付款。
端到端測試 Webhook
- 發送測試回調(在 開發者 → 沙箱聯調 中)會向一個端點或地址發送正確簽名的請求,並顯示發出的內容和你的服務器返回的結果。它的
id以evt_test_開頭,帶"test": true。 - 對沙箱訂單使用 模擬結果,不打開收銀台就能走完真實流程:事件、投遞和重試。
- 讓接收端第一次返回
500,觀察重試是否帶着相同的事件id到達。
這些和其他沙箱場景一起列在 支付測試卡號大全 裏。
不要讓一次漏收的回調決定訂單結果
排查期間請記住,訂單並沒有丟:對賬任務會持續向通道核對待支付的付款,而買家的回跳無論如何都不是結果,見 return_url 和 notify_url 的區別。不要因為沒收到回調就把訂單標記為失敗;去查詢訂單,或者補回事件。
總結
- 先看投遞記錄:沒有投遞、無法連接、HTTP 錯誤或重試已耗盡,分別指向不同的原因;
- 檢查事件訂閲、環境、應用,以及訂單級
notify_url; - 使用直接響應的公網 HTTPS 地址,中間不要有重定向或防火牆攔截;
- 用原始請求體驗簽,輪換期間接受任意一個
v1=,並在 10 秒內返回 2xx; - 一次失敗的投遞在約 2 小時 45 分鐘內共嘗試 6 次,之後使用 立即重投;
- 漏掉的事件都可以從事件接口補回:即使投遞失敗,事件也已經記錄。
在沙箱裏把它跑通
註冊商戶後即可申請測試密鑰,不需要等待資質審核。