客戶按下結帳就建單,不管他後來有沒有付錢。
「訂單什麼時候建立?」這題的答案會決定你的系統好不好維護。
有些人選「付款成功才建立」,理由聽起來很合理:沒付錢的不算訂單,資料庫才不會一堆垃圾。
實際上這條路走不通。
因為有些付款方式,本來就需要訂單先存在。 ATM 虛擬帳號要綁一張單才發得出去,客戶可能三天後才去繳款。超商代碼繳費也一樣。你不可能等他付完錢才建單——他要付給誰?
因為付款失敗你也需要知道。 客戶刷卡被拒,你什麼紀錄都沒有。他打電話來問「我剛剛下單失敗了」,你連他買了什麼都查不到。
結帳表單送出、金額算完,第一件事就是建單,狀態一律是:
'status' => Order::STATUS_DRAFT,
'payment_status' => Order::PAYMENT_STATUS_UNPAID,
'fulfillment_status' => Order::FULFILLMENT_STATUS_UNFULFILLED,
不管客戶接下來要刷卡、要 ATM、要超商取貨付款,都先走到這一步。付款方式的差異,從下一步才開始。
建完單之後,訂單只會被幾個方法推進:
$order->markOpen(); // 訂單成立(ATM 這種還沒付錢的也算成立)
$order->markPaid(); // 付款完成
$order->markCanceled(); // 取消
這些方法全部寫在 Order model 裡面。
重點是:狀態字串不准出現在各個金流的流程裡。 不然你有六種付款方式,就有六個地方在寫 $order->status = 'open',哪天規則要改,你得改六個地方,而且一定會漏。
重複通知不會出事。 金流的 notify 會重送,所以 markPaid() 第一行就檢查:已經是 paid 就直接 return。點數結算也以 order_id 去重。外部系統重打十次,結果跟打一次一樣。
通知信只寄一次。 訂單可能先 markOpen() 再 markPaid(),兩次都想寄信。我用一個 confirmation_notified_at 欄位擋住,寄過就不寄了。
draft 是有價值的資料。 那些沒有付款的單,就是「客戶走到最後一步卻放棄」的紀錄。後台看得到、可以打電話追、可以分析哪個付款方式的放棄率特別高。
不要把它當垃圾。 那是你最接近成交的一批客戶。