支付狀態詳解:待支付、已支付、失敗、已過期和已退款分別是什麼意思
支付狀態詳解:pending、paid、failed、expired、partially_refunded 和 refunded 各代表什麼,訂單如何在它們之間流轉,以及每一步該做什麼。
本文目錄
支付狀態告訴你一筆訂單處在生命週期的哪個階段:還在等買家付款、已經付款、未付款就關閉了,還是已經退款。正確解讀它,決定了你什麼時候發貨、什麼時候釋放庫存、對買家説什麼。而大多數接入問題,都來自把一個還沒結束的狀態當成了最終結果,或者把一個只是在等待的狀態當成了失敗。
這篇文章説明 KukoPay 上每一種支付狀態的含義、訂單如何在它們之間流轉、每次變化會發出哪個事件,以及你的系統在每一步該做什麼。
訂單狀態
| 狀態 | 含義 | 是否終態 |
|---|---|---|
pending | 已創建,等待買家付款 | 否 |
paid | 支付成功,淨額已計入你的餘額 | 否,之後仍可能退款 |
partially_refunded | 部分金額已退款 | 否 |
refunded | 全部金額已退款 | 是 |
failed | 通道判定這筆支付終態失敗或已取消 | 是 |
expired | 收銀台關閉時仍未付款 | 是 |
pending ──► paid ──► partially_refunded ──► refunded
│ └─────────────────────────────▲
├──► failed
└──► expired只有 paid(以及之後的 partially_refunded / refunded)表示真的收到了錢。
pending:在等待,而不是失敗
pending 涵蓋了付款完成之前的一切:買家還沒打開收銀台、正在輸入卡號、正在錢包裏確認,或者正在做 3D Secure 驗證。
有兩點很容易搞錯:
- 銀行卡被拒不會讓訂單失敗。 訂單保持
pending,收銀台會提示原因,買家可以在同一個鏈接上換卡或換方式重試,見 銀行卡支付被拒怎麼辦。 - 買家離開頁面也不會讓訂單失敗。 他可能會回來,也可能已經在銀行 App 裏完成了確認。
所以訂單處於 pending 時,不要取消它、也不要把它標記為失敗。等待下一個狀態。
paid:什麼時候履約
只有在平台向通道回讀這筆付款、核對金額和幣種一致之後,訂單才會變為 paid。這一刻會計算手續費、把淨額計入你的餘額,並發送 payment.succeeded。
以驗簽後的 payment.succeeded Webhook,或者服務端查詢返回 paid 為準履約,永遠不要以買家到達你的 return_url 為準,回跳可能發生在付款之前,也可能根本不發生,見 return_url 和 notify_url 的區別。
failed 和 expired:訂單未付款關閉的兩種方式
failed:支付通道判定這筆支付終態失敗,不會再成功。事件:payment.failed。expired:收銀台關閉前沒有人完成付款:截止時間到了(默認 24 小時,或你設置的expires_at)、你提前關閉了訂單,或者它所屬的收款鏈接已達到收款筆數上限。事件:payment.expired。
兩者都是終態:釋放庫存;如果買家還想再試,創建一筆新訂單。如何設置截止時間、安全地關閉訂單,見 如何自動取消未付款訂單。
部分退款和已退款
退款確認後,paid 訂單會變為 partially_refunded(退了一部分)或 refunded(全部退完)。退款有自己的狀態:
| 退款狀態 | 含義 | 事件 |
|---|---|---|
pending | 已受理,通道處理期間金額被凍結 | — |
succeeded | 已確認並扣賬 | refund.succeeded |
failed | 被通道拒絕,凍結金額已退回 | refund.failed |
只有在 succeeded 時才告訴買家錢已經退回,見 部分退款怎麼操作。
拒付有單獨的狀態
拒付不會替換訂單的狀態,它是一個獨立的對象,狀態有 needs_response、under_review、won、lost 和 closed,事件從 dispute.created 到 dispute.won / dispute.lost / dispute.closed。拒付未結案期間,訂單不能退款。各個狀態的含義見 信用卡拒付是什麼。
每次狀態變化對應的事件
| 變化 | 事件 |
|---|---|
pending → paid | payment.succeeded |
pending → failed | payment.failed |
pending → expired | payment.expired |
| 退款確認 / 被拒 | refund.succeeded / refund.failed |
| 訂單關閉後到達的付款已退回給買家 | payment.duplicate_refunded |
除了成功事件,也要訂閲失敗和過期事件,否則那些永遠不會付款的訂單會一直在你的系統裏處於未結束狀態。如何訂閲見 Webhook 端點怎麼配置。
處理支付狀態的經驗法則
- 只把
paid當作成功,每筆訂單只履約一次,見 Webhook 重複通知怎麼辦。 - 永遠不要讓訂單狀態倒退。 一個遲到或重放的事件,不能撤銷更晚的狀態;先和你已有的狀態比較,只向前推進。
- 用
trade_no/out_trade_no跟蹤訂單,而不是payment_id。 買家更換支付方式時,一筆訂單可能經歷多次支付嘗試,所以payment_id會變。 - 優雅地處理未知的值。 遇到沒預料到的狀態時,記錄日誌並保持訂單原樣,而不是崩潰。
- 拿不準時就查詢。
GET /v1/orders/{trade_no}會返回當前狀態。
總結
pending表示在等待:卡被拒或買家離開頁面,都不會讓訂單失敗;paid是唯一的成功狀態,並已和通道確認;以 Webhook 或查詢結果履約;failed(通道放棄)和expired(收銀台未付款關閉)都是終態;- 退款讓已支付訂單變為
partially_refunded和refunded,退款本身也有pending/succeeded/failed; - 拒付有自己的狀態;
- 每次變化都有對應的事件,也要訂閲失敗和過期事件。
在沙箱裏把它跑通
註冊商戶後即可申請測試密鑰,不需要等待資質審核。