跳轉到主要內容

支付狀態詳解:待支付、已支付、失敗、已過期和已退款分別是什麼意思

支付狀態詳解:pending、paid、failed、expired、partially_refunded 和 refunded 各代表什麼,訂單如何在它們之間流轉,以及每一步該做什麼。

發佈於 約 4 分鐘閲讀

手繪插畫:一張從待支付流向已支付、失敗和已過期,再到部分退款和已退款的狀態流程圖,旁邊一個朝它走來的人
本文目錄
  1. 訂單狀態
  2. pending:在等待,而不是失敗
  3. paid:什麼時候履約
  4. failed 和 expired:訂單未付款關閉的兩種方式
  5. 部分退款和已退款
  6. 拒付有單獨的狀態
  7. 每次狀態變化對應的事件
  8. 處理支付狀態的經驗法則
  9. 總結

支付狀態告訴你一筆訂單處在生命週期的哪個階段:還在等買家付款、已經付款、未付款就關閉了,還是已經退款。正確解讀它,決定了你什麼時候發貨、什麼時候釋放庫存、對買家説什麼。而大多數接入問題,都來自把一個還沒結束的狀態當成了最終結果,或者把一個只是在等待的狀態當成了失敗。

這篇文章説明 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 → paidpayment.succeeded
pending → failedpayment.failed
pending → expiredpayment.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;
  • 拒付有自己的狀態;
  • 每次變化都有對應的事件,也要訂閲失敗和過期事件。

在沙箱裏把它跑通

註冊商戶後即可申請測試密鑰,不需要等待資質審核。