訂單狀態初始化
付款流程分為三個階段,本文聚焦 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 才是付款確認入口

建立訂單當下,系統會依照金流類型,為 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 | 倉管系統現在能對這張單進行什麼操作? |

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(街口) |

各金流在 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,系統拿不到憑證,只能等對方告知結果

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是這條路徑的終點站,不是中間站

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

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

CreditCardOnce_Razer、CreditCardOnce_AsiaPay、CreditCardOnce_Cybersource、TwoCTwoP、QFPay 這五種金流,同一個 PayProfileType 存在兩種完全不同的起始狀態:
1 | case PayProfileTypeDefEnum.CreditCardOnce_Razer: |
| 場景 | StatusDef | 為什麼? |
|---|---|---|
| 一般結帳 | WaitingToPay |
使用者需要被導到金流頁面完成授權 |
| 定期購自動建單 | WaitingToTrans |
系統已持有 Token,直接扣款,沒有使用者可以導頁 |
定期購第一次結帳時,金流商回傳了一個可重複使用的 Token(類似授權書)。往後每期自動扣款,系統持有這個 Token 直接呼叫金流 API,整個流程中根本不存在「使用者在操作」這件事。
這是同一個付款方式在兩種情境下的本質差異:一個是使用者主動發起、需要導頁完成授權;另一個是系統代為執行、Token 已存在、不需要任何人工干預。用 IsRegularOrderAutoGen 區分,讓同一個 PayProfileType 在不同情境下走對的路。

所有金流建單時通常只設定 StatusDef 和 StatusForUser,而 CustomOfflinePayment(其他轉帳)是唯一在建單當下就設定 StatusForSCM 的金流:
1 | // StatusDef 維持預設 WaitingToTrans(不覆蓋) |
| 一般數位金流 | CustomOfflinePayment | |
|---|---|---|
| StatusForSCM 何時設定? | 付款成功後才設定 | 建單當下就設定 |
| 為什麼? | 未付款的訂單倉管不需要知道 | 入帳可能在數天後,人工比對需要早期可見性 |
信用卡刷卡、LinePay 付款,通常秒級完成,倉管等付款確認後再看到訂單完全來得及。但銀行轉帳可能要 1–3 個工作天,這段期間倉管可能需要提前備貨、協調出貨時間、或主動聯絡客戶確認付款進度。讓 SCM 在建單就看到這筆訂單,是為了業務操作流暢,而非技術上的必要






