付款流程分為三個階段,本文聚焦 Phase 1:CreateTradesOrderProcessor

flowchart TD
    P1["⭐ Phase 1\nCreateTradesOrderProcessor\n使用者按下結帳\n→ 訂單寫入 DB,初始狀態決定往後走哪條路"]
    P2["Phase 2\nProcessPayment\nmweb 在同一 Request 內\n同步呼叫 PaymentMiddleware"]
    P3["Phase 3\nFinishPayment\n非同步 Callback 或 ReCheck Job\n付款最終結果回來時處理"]

    P1 -->|"Group A:建單即有憑證,立刻進 ProcessPayment"| P2
    P1 -->|"Group B:WaitingToPay,等使用者外部付款"| P3
    P2 -->|"Redirect / Pending → 導頁後等"| P3
    P2 -->|"Success → success flow"| ERP
    P2 -->|"Fail → 取消"| CANCEL
    P3 -->|Success| ERP["WaitingToTrans\n→ NMQ 轉單 ERP\n→ WaitingToSalesOrder"]
    P3 -->|"Failed / Expired"| CANCEL["TransToCancel\n→ 退資源 + NMQ 轉單"]

    style P1 fill:#4A7CB5,color:#fff
    style P2 fill:#888,color:#fff
    style P3 fill:#888,color:#fff
    style ERP fill:#5BA85B,color:#fff
    style CANCEL fill:#D9534F,color:#fff

Phase 1 在整個流程中扮演什麼角色?

Phase 1 是整個付款狀態機的起點 — 它不只是「把訂單存進去」,而是在建單的同一個動作裡,根據金流類型決定訂單接下來走哪條路

階段 入口 觸發者 時機
⭐ Phase 1 CreateTradesOrderProcessor.cs 使用者按下結帳 同步,在同一 Request Pipeline 內
Phase 2 TradesOrderPaymentService.ProcessPayment() mweb 前台 緊接在建單 Pipeline 之後
Phase 3 TradesOrderPaymentService.FinishPayment() PayChannelReturn 或 ReCheck Job 使用者在外部完成操作後

Phase 1 決定的初始狀態,直接決定 Phase 2 還是 Phase 3 是真正付款確認的入口:

  • 初始 WaitingToTrans / WaitingToAuth / WaitingTo3DAuth → Phase 2 是主要付款入口
  • 初始 WaitingToPay → Phase 2 只是「登記等待」,Phase 3 才是付款確認入口

f

建立訂單當下,系統會依照金流類型,為 OrderSlaveFlow 設定一個「起始狀態」,寫入 DB 後由付款結果進一步驅動流轉

起始狀態是告訴系統接下來由誰負責推進這筆訂單 — 是系統主動呼叫付款 API,還是等使用者去外部付款後 Callback 回來?

最直覺的設計是一個欄位記錄訂單狀態,但實際上系統、使用者、倉管三方對「同一筆訂單」需要看到完全不同的資訊:

graph TD
    ORDER[同一筆訂單]
    ORDER -->|StatusDef| SYS["🖥️ 系統\n我現在要跑什麼 Processor?\n下一個動作是什麼?"]
    ORDER -->|StatusForUser| USER["👤 使用者\n我的訂單在哪個階段?\n還需要我做什麼事?"]
    ORDER -->|StatusForSCM| SCM["🏭 倉管系統\n這張單需要準備出貨嗎?\n現在能動嗎?"]

    style SYS fill:#4A7CB5,color:#fff
    style USER fill:#E8A838,color:#fff
    style SCM fill:#7DC67E,color:#fff

三個維度各自獨立演進:一張剛建立的 ATM 訂單,對系統而言是 WaitingToTrans(準備觸發付款流程),對使用者顯示的卻是 WaitingToPay(請去 ATM 轉帳)。如果只有一個狀態,要嘛系統看到的內部術語暴露給使用者,要嘛使用者的顯示邏輯污染了驅動後端 Processor 的核心狀態。

維度 欄位 對象 回答的核心問題
StatusDef OrderSlaveFlow_StatusDef 系統核心 後端 Processor 下一步要做什麼?
StatusForUser OrderSlaveFlow_StatusForUserDef 前台使用者 使用者訂單頁要顯示什麼狀態?
StatusForSCM OrderSlaveFlow_StatusForSCMDef 倉庫 SCM 倉管系統現在能對這張單進行什麼操作?

f

WaitingToTrans = 待轉單 是把 WebStore 的 TradesOrder 轉寫進 ERP 成為 SalesOrder — 這是前台電商系統與後台倉儲系統的交接點。

CreateTradesOrderProcessor 在進入 switch 之前,先把所有訂單的 StatusDef 設為 WaitingToTrans。這個選擇暗藏了一個設計決策:

讓錯誤暴露,而非靜默等待

如果預設值是 WaitingToPay(等 Callback),當一個新金流被加進系統但忘了在 switch 中配置,訂單就會靜靜地停在那裡等一個永遠不會來的 Callback — 問題可能數小時後才被發現。而 WaitingToTrans 意味著系統看到這個狀態就會試圖繼續推進,失敗就失敗,錯誤立刻浮現

flowchart TD
    A[CreateTradesOrderProcessor 建立訂單] --> B["所有金流先套用 Fail-Fast 預設值\nStatusDef = WaitingToTrans(待轉單)"]
    B --> C{switch PayProfileType}
    C -->|命中 case| D[依金流特性覆蓋需要的維度]
    C -->|CommonThirdPartyPayType Flag| E["新金流安全網\n→ WaitingToPay"]
    C -->|完全未命中| F["立即進入轉單流程\n→ 錯誤立即浮現"]
    D --> G[寫入 DB]
    E --> G
    F --> G

    style B fill:#E8A838,color:#fff
    style E fill:#5BA85B,color:#fff
    style F fill:#D9534F,color:#fff
    style G fill:#4A7CB5,color:#fff

注意 CommonThirdPartyPayType Flag 作為 switch 的最後一道 default 安全網,給「已知是第三方非同步金流但還沒加 case」的新金流提供保護,讓它們安全落入 WaitingToPay。

常見起始狀態一覽

起始狀態 語意 代表金流
WaitingToTrans 待轉單 — 付款已確認(或即將同步確認),等 NMQ 將訂單寫入 ERP Stripe、ATM、CustomOfflinePayment
WaitingToPay 等使用者在外部完成付款後 Callback LinePay、Razer 系列、AsiaPay 系列
WaitingToAuth 等信用卡授權回傳 一般信用卡(CreditCardOnce / Installment)
WaitingTo3DAuth 等 3D Secure 驗證完成 ApplePay(非 HK)、PayPage 信用卡
WaitingToThirdPartyTrans 建單後立即轉單至第三方 JKOPay(街口)

f

各金流在 CreateTradesOrderProcessor 建立訂單時的 OrderSlaveFlow 起始狀態,是訂單寫入 DB 當下的起點

完整對照表

金流 / 群組 StatusDef StatusForUser StatusForSCM 備註
【預設值(未命中 switch)】 WaitingToTrans — — Stripe 系列、CreditCardOnce_Stripe、CreditCardOnce_CheckoutDotCom
CreditCardOnce / Installment(一般信用卡) WaitingToAuth — — 等待刷卡授權
CreditCardOnce / Installment(PayPage 代客下單) WaitingTo3DAuth WaitingToPay — PayPage 流程特殊處理
ATM WaitingToTrans WaitingToPay — StatusDef 不覆蓋,只改 ForUser
JKOPay(街口) WaitingToThirdPartyTrans WaitingToPay — 建單後直接轉單
ApplePay(非 HK) WaitingTo3DAuth WaitingToPay — 失敗可由 Recheck NMQ Redo
ApplePay(HK,Stripe) WaitingToTrans — — HK 走跨國 Stripe 原流程
GlobalPay WaitingToPay WaitingToPay — CanCancel = false(付款完成前停用取消)
LinePay / CathayPay / PXPay / Aftee / icashPay / EasyWallet / PoyaPay / Atome / PXPayPlus / PlusPay / Wallet WaitingToPay WaitingToPay — 第三方非同步,建單後等 Callback
EWallet_PayMe / AliPayHK_EftPay / WechatPayHK_EftPay WaitingToPay WaitingToPay — 跨國 EftPay 系列
Razer 系列 / SwiftPass / EftPay / AsiaPay 系列 WaitingToPay WaitingToPay — 跨國金流,非同步 Callback
CreditCardOnce_Razer / CreditCardOnce_AsiaPay / CreditCardOnce_Cybersource / TwoCTwoP / QFPay WaitingToPay(一般) WaitingToPay — 定期購自動建單時例外:保持預設 WaitingToTrans
CustomOfflinePayment(其他轉帳) WaitingToTrans WaitingToPay WaitingToPay 唯一同時設定 SCM 狀態的金流
CommonThirdPartyPayType Flag(default) WaitingToPay WaitingToPay — 未在 switch 中列舉的新金流

狀態分布視覺化

graph TD
    subgraph WTT["StatusDef = WaitingToTrans(系統主動打 API)"]
        T1[Stripe 系列\nCheckoutDotCom]
        T2[ATM]
        T3[CustomOfflinePayment]
        T4[ApplePay HK]
    end

    subgraph WTP["StatusDef = WaitingToPay(等 Callback)"]
        P1[LinePay / CathayPay\nPXPay / Aftee...]
        P2[Razer 系列\nAsiaPay 系列]
        P3[EftPay 跨國系列]
        P4[GlobalPay]
        P5[TwoCTwoP / QFPay]
    end

    subgraph SPECIAL["StatusDef = 其他(特殊授權流程)"]
        S1[信用卡一般\n→ WaitingToAuth]
        S2[ApplePay 非 HK\nPayPage 信用卡\n→ WaitingTo3DAuth]
        S3[JKOPay\n→ WaitingToThirdPartyTrans]
    end

    WTT ~~~ WTP
    WTP ~~~ SPECIAL

    style WTT fill:#4A7CB5,color:#fff
    style WTP fill:#7DC67E,color:#fff
    style SPECIAL fill:#E8A838,color:#fff

所有金流依照建單後的付款時序,分為兩大群組。這個分組的本質,不是金流商的技術規格差異,而是「付款憑證在建單當下是否已在系統手中」

為什麼會有兩種截然不同的路徑?

建立訂單時,系統面對一個根本問題:我現在有能力完成付款嗎?

graph TD
    Q{建單當下\n系統有付款憑證嗎?}
    Q -->|✅ 有\n卡號Token / ApplePay Token\n已在本次 Session| A
    Q -->|❌ 沒有\n使用者需去 LINE App\n銀行頁面完成驗證| B

    subgraph A["Group A:同步完成型"]
        A1[系統主動呼叫付款 API\n不需要使用者再做任何事]
    end

    subgraph B["Group B:非同步導頁型"]
        B1[訂單先停在 WaitingToPay\n等使用者操作完後 Callback]
    end

    style A fill:#4A7CB5,color:#fff
    style B fill:#7DC67E,color:#fff

Group A 的金流(信用卡、ApplePay)在使用者送出結帳的當下,付款憑證就已交給系統了;Group B(LinePay、街口)的真正付款動作發生在外部 App,系統拿不到憑證,只能等對方告知結果

f

Group A:同步付款型

建單後在同一個 Request Pipeline 內完成付款,不需等使用者跳頁操作:

graph LR
    A[建單完成\n付款憑證已在 Session] --> B{金流授權方式}
    B -->|信用卡一般| C[WaitingToAuth\n呼叫刷卡授權 API]
    B -->|ApplePay 非HK\nPayPage 信用卡| D[WaitingTo3DAuth\n等 3D Secure 驗證]
    B -->|JKOPay| E[WaitingToThirdPartyTrans\n直接轉單至街口]
    B -->|Stripe / CheckoutDotCom\nApplePay HK / ATM\nCustomOfflinePayment| F["WaitingToTrans(待轉單)\n同步付款完成後立即排 ERP 轉單"]

    style A fill:#4A7CB5,color:#fff
    style C fill:#E8A838,color:#fff
    style D fill:#E8A838,color:#fff
    style E fill:#E8A838,color:#fff
    style F fill:#5BA85B,color:#fff
金流 起始 StatusDef 說明
CreditCardOnce / CreditCardInstallment WaitingToAuth 授權成功後 → WaitingToTrans → 排 ERP 轉單
JKOPay WaitingToThirdPartyTrans 街口轉單完成後 → WaitingToTrans → 排 ERP 轉單
ApplePay(非 HK)、PayPage 信用卡 WaitingTo3DAuth 3DS 通過後 → WaitingToTrans → 排 ERP 轉單
Stripe / CheckoutDotCom、ApplePay HK WaitingToTrans 直接以待轉單起始,同步付款 + ERP 轉單一氣呵成
ATM、CustomOfflinePayment WaitingToTrans 入帳通知到來 → 已在待轉單狀態,直接排 ERP 轉單

Stripe 系列在建單當下就設為 WaitingToTrans,代表「付款將在本次 Pipeline 同步完成,完成後無需再改狀態,直接觸發 ERP 轉單」。WaitingToTrans 是這條路徑的終點站,不是中間站

f

Group B:非同步導頁型

系統沒有付款憑證,訂單必須「停在那裡等」:

這裡有一個設計上的取捨:系統可以不建單,等使用者付款成功後才建。但這樣如果建單失敗,使用者已付錢了怎麼辦?先建單再付款,能確保無論付款結果如何,訂單記錄都已存在,後續補償邏輯有依據可操作。

graph LR
    A["建單完成\nStatusDef = WaitingToPay\n(訂單已存在 DB,等待付款)"] --> B[使用者導頁至\n外部金流頁面]
    B --> C{使用者在外部的操作結果}
    C -->|✅ 付款成功 Callback| D[FinishPayment\n→ WaitingToTrans\n進入後續流程]
    C -->|⏰ 逾期未付| E[TransToCancel\n自動取消]
    C -->|❌ 付款失敗 Callback| F[TransToCancel\n取消]

    style A fill:#4A7CB5,color:#fff
    style D fill:#5BA85B,color:#fff
    style E fill:#D9534F,color:#fff
    style F fill:#D9534F,color:#fff

涵蓋金流:LinePay / CathayPay / PXPay / Aftee / icashPay / EasyWallet / PoyaPay / Atome / PXPayPlus / PlusPay / Wallet / GlobalPay / EWallet_PayMe / AliPayHK_EftPay / WechatPayHK_EftPay / Razer 系列 / SwiftPass / EftPay 系列 / AsiaPay 系列 / TwoCTwoP / QFPay

f

兩組的核心差異

Group A(同步付款型) Group B(非同步導頁型)
建單當下有付款憑證? ✅ 有 ❌ 沒有
起始 StatusDef WaitingToAuth / WaitingTo3DAuth / WaitingToTrans / WaitingToThirdPartyTrans WaitingToPay
何時進入 WaitingToTrans? 同步付款成功後(或直接以 WaitingToTrans 起始) 外部金流 Callback 確認付款後
ERP 轉單觸發時機 同一 Request Pipeline 結束時 FinishPayment Callback 處理完成時
若付款失敗 同步失敗 → 立即 TransToCancel Callback 失敗或逾期 → TransToCancel

f

CreditCardOnce_Razer、CreditCardOnce_AsiaPay、CreditCardOnce_Cybersource、TwoCTwoP、QFPay 這五種金流,同一個 PayProfileType 存在兩種完全不同的起始狀態:

1
2
3
4
5
6
7
8
9
10
case PayProfileTypeDefEnum.CreditCardOnce_Razer:
case PayProfileTypeDefEnum.TwoCTwoP:
case PayProfileTypeDefEnum.QFPay:
if (context.IsRegularOrderAutoGen == false)
{
orderSlaveFlow.OrderSlaveFlow_StatusDef = WaitingToPay;
orderSlaveFlow.OrderSlaveFlow_StatusForUserDef = WaitingToPay;
}
// IsRegularOrderAutoGen = true → 維持預設 WaitingToTrans
break;
場景 StatusDef 為什麼?
一般結帳 WaitingToPay 使用者需要被導到金流頁面完成授權
定期購自動建單 WaitingToTrans 系統已持有 Token,直接扣款,沒有使用者可以導頁

定期購第一次結帳時,金流商回傳了一個可重複使用的 Token(類似授權書)。往後每期自動扣款,系統持有這個 Token 直接呼叫金流 API,整個流程中根本不存在「使用者在操作」這件事。

這是同一個付款方式在兩種情境下的本質差異:一個是使用者主動發起、需要導頁完成授權;另一個是系統代為執行、Token 已存在、不需要任何人工干預。用 IsRegularOrderAutoGen 區分,讓同一個 PayProfileType 在不同情境下走對的路。

f

所有金流建單時通常只設定 StatusDef 和 StatusForUser,而 CustomOfflinePayment(其他轉帳)是唯一在建單當下就設定 StatusForSCM 的金流:

1
2
3
// StatusDef 維持預設 WaitingToTrans(不覆蓋)
orderSlaveFlow.OrderSlaveFlow_StatusForUserDef = WaitingToPay;
orderSlaveFlow.OrderSlaveFlow_StatusForSCMDef = WaitingToPay; // ← 其他金流不設 SCM
一般數位金流 CustomOfflinePayment
StatusForSCM 何時設定? 付款成功後才設定 建單當下就設定
為什麼? 未付款的訂單倉管不需要知道 入帳可能在數天後,人工比對需要早期可見性

信用卡刷卡、LinePay 付款,通常秒級完成,倉管等付款確認後再看到訂單完全來得及。但銀行轉帳可能要 1–3 個工作天,這段期間倉管可能需要提前備貨、協調出貨時間、或主動聯絡客戶確認付款進度。讓 SCM 在建單就看到這筆訂單,是為了業務操作流暢,而非技術上的必要

f