下單時只存快照,付款成功才真的動帳本。
點數的時間點很容易搞錯。
下單就扣點、下單就發點,是錯的。 客戶選了 ATM,三天沒去繳款,那張單作廢了——但他的點數已經被扣掉、回饋也已經發出去了。
正確的順序是這樣:
結帳 算出這張單「會」用掉幾點、「會」回饋幾點 → 存進訂單當快照
付款完成 照著快照,真的去動點數帳本
訂單上有兩個欄位:
'points_used' => $cart->pointsRedeemed, // 這張單折抵了幾點
'reward_points' => $cart->pointsEarned, // 這張單會回饋幾點
這兩個數字是計價流程算出來的,下單當下就凍結。之後點數規則改了、會員的點數變了,都不影響這張已經成立的訂單。
public function markPaid(?string $reference = null): void
{
if ($this->payment_status === self::PAYMENT_STATUS_PAID) {
return;
}
// ...更新狀態...
PointLedger::settleOrder($this);
}
settleOrder() 做兩件事:先依 points_used 扣點(寫 point_usages),再依 reward_points 發一張新的點數卡(寫 point_credits)。
計算已經在結帳時做完了,這裡只是照著快照執行。 這一層不做任何數學。
金流的 notify 一定會重送。網路逾時重送、對方系統重試、你回應慢了對方再打一次——這在串接金流時是常態,不是例外。
所以結算必須「執行十次跟執行一次結果一樣」。