付款與 Webhook
追蹤 Checkout、Stripe 回呼、交易紀錄和權益授予,定位付款後未開通等問題。
付款排錯應追蹤同一筆業務的 Checkout ID、Stripe Event ID 和內部執行紀錄。不要為了重現問題而反覆實際付款,也不要只根據付款成功頁面,就判斷帳號已取得權限。
一、先核對連線與方案
| 設定 | 作用 | 常見錯誤 |
|---|---|---|
payment.provider | 目前的付款服務供應商 | 設定與實際呼叫不一致 |
STRIPE_CONNECTION_ID | 應用程式內部的 Stripe 連線識別碼 | 已有資料後任意更換,導致找不到原紀錄 |
STRIPE_SECRET_KEY | 呼叫 Stripe API | 測試、正式環境模式或帳戶不一致 |
STRIPE_WEBHOOK_SECRET | 驗證回呼簽章 | 使用另一個 Endpoint 或 CLI 監聽器的 Secret |
| 產品及方案 Key | 查找應用程式方案 | 前端提交的 Key 已重新命名或不存在 |
方案 priceId | 對應 Stripe Price | 仍有預留值,或 Price 屬於其他模式 |
STRIPE_CONNECTION_ID 是專案自行定義的穩定識別碼,不是 Stripe 自動提供的 Price ID。測試與正式環境應分別設定,已有正式環境紀錄後,請勿任意更換。
npm run doctor 和 npm run doctor:remote 能提示缺少的變數,但不會驗證每個 Price 的歸屬和可購買狀態。方案顯示的價格也不會改變 Stripe 的實際收費。
二、無法建立 Checkout
在 Network 中檢查建立 Checkout 的請求,先區分參數驗證、登入要求、重複購買限制和 Stripe API 錯誤。
如果 Price 仍包含 _replace_me,請先在對應的 Stripe 模式中建立價格,再填入實際 ID。一次性方案與訂閱方案,也要使用適合各自付款方式的 Price。
出現 paymentsProductAlreadyPurchased 時,請檢查 allowRepurchase 和使用者既有的有效購買紀錄。不要刪除交易歷史,讓使用者再次付款。若業務需要允許重複購買,應調整方案規則,並測試購買後的權益處理。
重複購買判斷也不是並行鎖定機制。多個分頁可能在第一筆付款完成前建立多個 Checkout Session,測試時只保留一個有效流程。
三、本機收不到 Webhook
本機開發網址不能直接作為 Stripe 從公用網路投遞的目標。可以使用 Stripe CLI,將測試事件轉送至開發伺服器:
stripe login
stripe listen --forward-to http://127.0.0.1:5173/api/webhooks/stripe請將連接埠替換為 npm run dev 實際監聽的連接埠,將監聽器輸出的簽章 Secret 填入本機 .env 的 STRIPE_WEBHOOK_SECRET,再重新啟動開發伺服器。這個值不能與主控台建立的 Endpoint Secret 混用,詳細說明請見 Stripe 本機監聽與回呼說明。
接著從應用程式頁面建立一筆測試 Checkout,再觀察 CLI 投遞、伺服器端記錄和本機資料庫。單獨觸發模擬付款事件,可能缺少應用程式建立的 Checkout、使用者關聯或方案對應關係,不能據此判定整個購買流程失敗。
專案也提供 webhook:stripe:dev 與 webhook:stripe:prod,它們會建立遠端 Webhook 目標,並將 Secret 寫回對應的環境檔案,並不是本機轉送器。使用這些指令碼時,需要提供可公開存取的網址。已有目標時,請先核對,不要反覆建立。
四、回呼簽章、路徑或設定錯誤
目前入口為:
POST {VITE_SITE_URL}/api/webhooks/stripe| 現象 | 檢查順序 |
|---|---|
| 404、405 | 完整網址、HTTP 方法、目標 Worker 是否為目前版本 |
| 簽章驗證失敗 | Endpoint 或 CLI Secret、原始請求本文、Stripe-Signature 請求標頭 |
PAYMENT_NOT_CONFIGURED | 線上 Worker 的連線 ID、API Key、Webhook Secret |
| 5xx | 伺服器端最早的例外、Stripe API、D1 和業務回呼處理 |
WEBHOOK_BUSY | 同一事件是否已有處理租用,查看時間和對應記錄 |
簽章驗證使用原始請求本文。不要先解析 JSON 再重新序列化,也不要為了排錯而關閉簽章驗證,請參閱 Stripe 簽章疑難排解。
修改本機 .env.production 後,仍需要更新部署。檔案已寫好,不代表 Worker 執行時的 Secret 已更新。網域變更時,也要檢查 Stripe 目標網址是否仍指向舊網域。
五、從事件帳本確認處理位置
在正確的業務資料庫 DB 中,以唯讀方式查詢目標 Stripe Event。請將範例識別碼替換成本次事件 ID:
SELECT id, provider, connection_id, event_id, event_type,
status, created_at, updated_at
FROM webhook_events
WHERE provider = 'stripe'
AND event_id = 'evt_replace_me'
ORDER BY id DESC
LIMIT 10;同一事件也需要搭配 connection_id 判斷歸屬。不要將完整的 raw_body 匯出至共用記錄,它包含業務和使用者資訊。
| 結果 | 含義與下一步 |
|---|---|
| 沒有紀錄 | 檢查是否投遞至正確環境、是否通過簽章驗證,以及是否支援該事件類型 |
processing | 查看租用資訊、更新時間和目前執行記錄,不能只憑狀態就判定仍在處理 |
failed | 在受控環境查看 last_error,定位失敗的處理步驟 |
succeeded | 繼續核對交易和權益,不代表所有後續非同步工作都已完成 |
目前 webhook_events 沒有 attempt_count 欄位。投遞次數請查看 Stripe 投遞歷史,Command 嘗試次數則查看 command_execution,兩者並非同一個計數。
不支援的事件會寫入 payment.webhook.ignored 記錄並傳回成功,不會進入這份帳本。已處理成功的重複事件,也會直接傳回成功,因此不能只憑 2xx 就證明新增了一筆交易。
六、已付款,卻沒有取得角色或權益
依以下順序,找出最後一個成功步驟:
Stripe 付款狀態
→ 對應回呼投遞與簽章驗證
→ checkout_sessions / transactions / transaction_items
→ 方案與 Price 對應關係
→ event_execution / command_execution
→ user_role_capability / user_entitlement_capability一次性付款和訂閱使用的業務事件不同,不能要求每筆訂閱都必須找到 PaymentSucceededEvent。訂閱還應檢查 subscriptions、subscription_items,以及訂閱建立、更新和結束事件。
請優先檢查以下情況:
- 回呼中的 Price ID 未對應至目前的產品方案,無法產生預期的授予輸入。
- 自訂後的方案未設定需要授予的角色或權益。
- 付款紀錄已更新,但 Event 建立或 Command 執行失敗。
- 權益已儲存,但頁面檢查的是另一個能力 Key、使用者或來源。
- 使用者完成 Checkout 頁面操作,但付款方式仍處於非同步確認階段。
背景執行紀錄的查詢和狀態說明,請見背景工作與統計。確認根因前,不要手動新增交易或直接授予帳號權限,否則會掩蓋故障,並破壞後續撤銷所需的關聯。
七、重複投遞、忙碌狀態與重播
Stripe 可能重複投遞,事件順序也不應視為業務發生順序,請參閱 Stripe Webhook 投遞行為。
範本依供應商、連線 ID 和 Event ID 辨識事件。成功紀錄會直接傳回成功,有效處理租用仍被占用時,則傳回非 2xx,讓供應商稍後重試。目前 Stripe 處理租用的最長有效時間為三分鐘。到期後,由後續投遞嘗試重新取得處理權,不是背景計時器一到時間就一定會恢復。
重播前,請確認設定已修正,並檢查交易、權益和外部副作用是否已發生。等冪邏輯會略過已成功的事件,重播該事件不會自動修正另一個獨立失敗的非同步 Command。
不要刪除事件帳本、將狀態改為未處理,或偽造新的事件 ID 來強制重新執行。若處理結果與外部狀態不一致,應先完成對帳,再透過對應業務層設計修正方式。
八、帳單入口與退款
帳單入口需要使用者對應的 payment_customers 紀錄,Customer 必須屬於目前連線與模式。接著檢查 Stripe 入口設定和返回網址,不能使用另一環境的 Customer 進行測試。
目前退款處理會儲存退款紀錄,應用程式回呼會記錄 refund.updated,但不會自動撤銷一次性購買授予的角色或權益。退款成功後仍可存取,不一定是回呼失敗,也可能是尚未實作退款後的產品規則。訂閱終止流程則應另外檢查對應的訂閱事件。
修正後的驗證
使用測試模式,從應用程式建立一筆完整購買,確認交易、事件、權限和頁面行為一致。再驗證重複投遞不會重複授予權益,以及未購買的使用者仍無法存取受限功能。正式環境問題的恢復,以原業務紀錄對帳為準,不要求使用者再次付款來證明修正有效。