pay api 1 - 設計現狀
定期購到了建單時間,mweb 如何完成這一期訂單?
排程送來定期購資料後,mweb 會先確認這次請求是否已完成,再組出可供結帳的資料。接著依付款方式執行建單流程,並記錄本次結果。
SubscriptionOrderAutoGen檢查來源、重複請求與 TracingGetRegularOrder PipelinePayProcessContext執行 GetPayProcess,補收件人與發票資料CreditCardProcess建單與信用卡授權流程CashOnDeliveryProcess建立貨到付款訂單PayProcessFlowType 處理。GameBall 與 Nine1V2。程式內的舊 Nine1 分支不執行這項比對。ShoppingCartV2Service.GetShoppingCartContextForRegularOrder() 先放入請求資料與商店設定,再執行 GetRegularOrder Pipeline。軌道由上往下呈現主要處理階段。
GetRegularOrderProcessorRegularOrderGiftPrepareProcessorGetPayTypeIsAvaliableProcessorCheckShippingProfileTypeIsAvaliableV2Processor CalculateShoppingCartPayShippingV2Processor CalculateShoppingCartFeeV2Processor PickupShoppingCartCheckoutTypeV2Processor CalculateShoppingCartShippingDateV2Processor ShoppingCartClientEntity,交給付款 Context 建立流程GetRegularOrder 的註冊順序分組,列出代表性 Processor。其餘檢查與設定仍在同一條 Pipeline 中;context.IsFinish 成立時會提前停止。TradesOrderService 將虛擬購物車交給 PayProcessService。時序圖把 Context 建立、暫存、GetPayProcess Pipeline,以及外層補資料分開呈現。
GetPayProcess 的 Processor 依序處理付款資料、Token、配送、驗證與遮罩;收件人及發票的定期購專用補值發生在 Pipeline 回傳後。PayProcessService.CreateTradesOrder() 依 PayProcessFlowType 選擇 Processor Pipeline。雙泳道以相同階段對照信用卡與貨到付款,節點沿各自的垂直線往下執行。
CreateTradesOrder() → ExecuteProcess(PayProcessFlowType)CreditCardProcess信用卡CheckCreditCardProcessorMapToTradesOrderProcessorCreateTradesOrderProcessorThreeDSecureProcessor → AuthProcessorCashOnDeliveryProcess貨到付款ValidateProcessDataProcessorMapToTradesOrderProcessorCreateTradesOrderProcessorTransferOrderProcessorCreateTradesOrderProcessor 之後才到 ThreeDSecureProcessor 與 AuthProcessor;貨到付款沒有這兩個信用卡授權節點。context.IsFinish 成立時,後續 Processor 不再執行。Provider 檢查是做什麼的?
mweb 會確認「商店目前設定的定期購 Provider」與「這次 API 請求宣告的 Provider」一致。兩者不同時,這次請求不能繼續把定期購資料組成付款用的購物車。
商店設定代表目前由哪套定期購流程負責;請求值代表這次呼叫宣告的來源。mweb 建立購物車 context 時,分別把它們放進以下欄位。
context.Data.RegularOrderProvider由 GetShopDefaultValue 讀取 RegularOrder / RegularOrderProvidercontext.RegularOrderData.Data.Provider 來自 request 的 RegularOrderProviderGetRegularOrderProcessor 的新定期購檢查RegularOrderException("Other"),不會進入後續付款流程。Nine1 時,不執行這段 Provider 相等比對。例如商店設定已是 Nine1V2,這次請求卻宣告 GameBall,mweb 會在主單檢查階段拒絕這次建單。這表示請求來源與商店設定不符,不代表信用卡授權失敗。
GetRegularOrderProcessor 先依商店設定的 Provider 選擇資料取得方式,取得主單後才做欄位驗證與上述比對。GameBall 與 Nine1V2 在這裡走同一條路。
GameBall / Nine1V2GetCheckOutInfo 取得結帳資料。ShoppingCartNodeMappingToRegularOrderEntity 組成主單 Entity,再執行 Provider 相等比對。Nine1RegularOrderService.Get 讀取既有主單。Nine1 時,略過新定期購的 Provider 相等比對。定期購資料如何變成虛擬購物車?
這一期訂單沒有經過會員手動加購物車。mweb 會把定期購請求、商店設定與商品頁資料組成購物車結帳所需的結構,再交給既有付款流程。
SubscriptionOrderAutoGen TradesOrderServiceShoppingCartV2Service GetRegularOrderShoppingCartClientEntity,交給付款 Context 建立流程ShoppingCartV2Service 的定期購入口先建立 ShoppingCartV2Context,再依 GetRegularOrder 的註冊順序執行 Processor。下圖追蹤資料形成與交接。
ShopDefault 讀取目前的 RegularOrderProvider。RegularOrderData,連同商店 Provider、來源追蹤與購物時間建立 ShoppingCartV2Context。GetRegularOrderProcessor 讀取並驗證主單,取得商品頁資料,再把主單價格、數量、交期與金物流條件寫入仿購物車結構。GetRegularOrder Processor 準備贈品,檢查金物流、計算運費與限制,並預選結帳方式及計算配送日期。shoppingCartContext.Data:ShoppingCartClientEntity組車成功後,TradesOrderService 將它交給 PayProcessService 建立付款 Context。GetRegularOrderProcessor 在中途會依商店 Provider 選擇主單來源,並核對請求 Provider;詳細條件見前一個 tab。GetRegularOrderProcessor 實際做了什麼?它是把定期購主單轉成購物車商品資料的核心節點。下方依 Process 的執行順序展開;後續運費計算與結帳預選由同一條 Pipeline 的其他 Processor 接手。
RegularOrderEntity。GameBall / Nine1V2GetCheckOutInfo,再以請求欄位組主單。RegularOrderService.Get 讀取既有主單。Nine1 時,若為門市自取先檢查門市可用,並比對 Provider。接著執行 ValidateShop、ValidateSalePage。ShoppingCartClientEntity,並複製贈品需求。Nine1 依主單指定類型篩選。檢查選項、依溫層分組,補上識別碼、Provider 與結帳資訊,最後設為 context.Data。RegularOrderData 不存在或主單 ID 為 0,這個 Processor 直接返回;商品資料缺失、售完或金物流不可用時的結果見下方「組車階段停下來」一節。商品頁提供可販售商品與原本金物流選項;定期購主單則保留本期的購買條件。轉換時兩邊資料會合併,不能只把 API request 原封不動交給付款流程。
SalePageGroupList。PayProfileTypeDef、ShippingProfileTypeDefNine1 再依主單指定類型篩選,後續 Processor 計算可結帳組合。SubscriptionGiftItems、RegularOrderProvider;GameBall / Nine1V2 路徑另帶入 RegularOrderCheckoutInfo。AssingShoppingCartInfo 仍把購物車商品價格設回主單價格。context.IsFinish = true查不到仿購物車商品頁資料,或映射後全部商品已售完,後面的 GetRegularOrder Processor 不再執行。GetRegularOrderProcessor 會中止轉換。虛擬購物車如何變成付款 Context?
虛擬購物車完成後,mweb 把它放進付款用的 Context,從已選的結帳方式判定後續流程,並補上本期定期購的收件與發票資料。
TradesOrderService 將購物車交給 PayProcessService.GetProcessContextFor。這段先建立付款資料,執行付款前置 Pipeline,回到外層後再覆寫定期購專用欄位。
ShoppingCartClientEntity 與會員 IDSalePageGroupList 為空時,拋出「購物車不得為空」例外。PayProcessContextEntity放入購物車與會員、產生 Context 識別碼,設定 V2 版本、拆單設定及定期購專用資料。GetPayProcess先以 UserDataHelper.Save 暫存 Context,再依序執行付款資料、Token、配送、驗證與遮罩的 Processor。TradesOrderService 補定期購資料以主單資料覆寫收件人;處理符合條件的馬來西亞發票資訊;請求有統編時,補上會員發票欄位。PayProcessContextEntity帶著購物車、付款流程類型及定期購資料,交給後續建立訂單流程。CreateTradesOrder 流程處理。ShoppingCartV2 放入虛擬購物車,ShoppingCartVersion 設為 V2,並設定 MemberId。IsOrdersSplitByQty 由商店設定取得。UniqueKey 與 ContextId。這組識別碼用於暫存與後續載入。RegularOrderProvider 與 RegularOrderCheckoutInfo。IsRegularOrderAutoGen 設為 true。GetPayProcessDataProcessor 從暫存資料還原購物車,讀取 SelectedCheckoutPayTypeGroup.StatisticsTypeDef,先設定 PayProfileType,再決定 PayProcessFlowType。以下列出主要對應。
CreditCardOnceCreditCardInstallmentCreditCardProcessCashOnDeliveryCashOnDeliveryCashOnDeliveryProcessATMATMAtmProcessStorePayShopProcessStorePay 會依已選的 Family、SevenEleven、HiLife 或 OKmart 配送類型設定付款類型。其他付款方式依各自的 PayProfileType 分支決定流程;此處的分類還不是扣款。GetPayProcess Pipeline 做哪些準備?PayProcessContextEntityGetPayProcessDataProcessorGetSalePageMemoSettingsProcessor GetPayProcessTokenProcessor GetLastOrderInfoProcessor PayTypeExpressProcessorOverseaShippingProcessorCheckStorePayProcessorCheckLocationPickupProcessorValidateProcessDataProcessorMaskProcessDataProcessorPayProcessProcessorModule 的註冊順序排列,共列出 10 個 Processor。內部來源在 GetLastOrderInfoProcessor 不沿用一般結帳的上一筆訂單收件資訊;外層以本期定期購主單覆寫收件人。建立訂單後,這一期會怎麼付款?
PayProcessContext 已選好付款方式。CreateTradesOrder() 依 PayProcessFlowType 執行對應 Pipeline:一般信用卡、Stripe/Razer 信用卡與貨到付款,實際付款的節點並不相同。
PayProcessService.CreateTradesOrder(context)以 context.PayProcessFlowType.ToString() 選出已註冊的 Processor PipelineCreditCardProcessCreditCardOnce/CreditCardInstallmentCreateTradesOrderProcessor→ThreeDSecureProcessor→AuthProcessor→CreateRegularOrderProcessorThirdPartyProcessCreditCardOnce_Stripe/CreditCardOnce_RazerITradesOrderPaymentService.ProcessPayment(),再由對應通道組織支付資料。CreateTradesOrderProcessor→ThirdPartyPayApiProcessor→TransferOrderProcessor→CreateRegularOrderProcessorCashOnDeliveryProcessCashOnDeliveryCreateTradesOrderProcessor→TransferOrderProcessor→CreateRegularOrderProcessorIsRegularOrderAutoGen 表示這次是定期購自動建單。它不直接決定走哪條 Pipeline,卻會影響支付通道組付款資料的方式。三種信用卡分支使用的保存資料也不同。
CreditCardOnce以下是 Nine1Payment 分支RegularOrderCheckoutInfo.credit_card_token 等已保存欄位↓ArrangeDataProcessor 寫入 PaymentInfo.CreditCardInfo↓AuthProcessor 依 CreditCardGatewayType 執行授權CreditCardOnce_StripeRegularOrderCheckoutInfo 中的 CustomerId、PaymentMethod↓StripePayChannelService 組出 customer_id、payment_method,並設 off_session = true↓送往第三方付款服務,標示為會員未在線上的付款CreditCardOnce_RazerRegularOrderCheckoutInfo 中的 CreditCardToken 與持卡人聯絡資料↓RazerPayChannelService 組出 card_token、card_user_email 等欄位↓送往第三方付款服務;不重新組本次輸入的卡號與 CVVNine1Payment 分支;其他 Gateway 不能直接套用同一組欄位。Stripe 與 Razer 的保存資料格式亦各自不同。AuthProcessor 對授權失敗有取消訂單處理;ThirdPartyPayApiProcessor 遇到付款服務回報 Fail 會拋出流程狀態例外。因此,圖中的 TransferOrderProcessor、CreateRegularOrderProcessor 與 AfterOrderProcessor 是註冊的後續節點,不代表付款失敗後仍會執行。off_session 是請求模式。mweb 會在定期購自動建單且有保存結帳資料時送出這個旗標;實際付款結果仍取決於支付服務回應,不能把它視為每次都授權成功或永遠不需驗證。PayProcessService.cs、GetPayProcessDataProcessor.cs、PayProcessProcessorModule.cs、ArrangeDataProcessor.cs、ThirdPartyPayApiProcessor.cs、StripePayChannelService.cs、RazerPayChannelService.cs。信用卡定期購,下一期沿用的是什麼資料?
本期自動建單會取回首次訂閱留下的結帳資訊,再交給對應金流組付款請求。保存格式依市場與支付通道而異;以下用馬來西亞 Razer 信用卡說明完整鏈路。
CreateRegularOrderProcessor 在一般結帳建立定期購設定。對 GameBall/Nine1V2,它把 GetCheckOutInfo() 組出的資料寫入 RegularOrder_CheckoutInfo。馬來西亞 Razer 信用卡分支保存卡片 Token 與持卡人聯絡資料。GetRegularOrderProcessor 透過 RegularOrderService.GetCheckOutInfo() 讀取保存內容,放進虛擬購物車的 RegularOrderCheckoutInfo。GetProcessContextForRegularOrderPay() 把購物車中的結帳資訊複製到 PayProcessContext,並設定 IsRegularOrderAutoGen = true。CreditCardOnce_Razer 選到 ThirdPartyProcess。Pipeline 先執行 CreateTradesOrderProcessor,再由 ThirdPartyPayApiProcessor 呼叫付款服務;RazerPayChannelService 使用保存資料組出 card_token 等欄位。CreateRegularOrderProcessor 遇到內部來源會直接返回,不會為本期再建立一份定期購設定。在 IsRegularOrderAutoGen 且為 Razer 信用卡通道時,RazerPayChannelService.GetPayExtendInfo() 讀取保存的 RegularOrderCheckoutInfo。它把 Token 與持卡人資料映射到付款請求,不採用一般結帳的卡號、效期與 CVV 欄位組法。
credit_card_token映射為 card_token,交給支付通道辨識已保存的卡片。credit_card_user_email映射為 card_user_email。credit_card_user_fullname映射為 card_user_fullname。credit_card_user_phone映射為 card_user_phone。CreditCardProcess 依 Gateway 處理;香港 Stripe 保存 customer_id 與 payment_method。因此「本期使用 Razer card_token」只適用於此處的 Razer 分支。Worker 呼叫 SubscriptionOrderAutoGen 後,先判斷 API 是否成功,再看回傳的 OrderSlaveFlowStatus。這裡的 Pending 是 Worker 對本期明細的分類,不是另一個信用卡 Token 狀態。
SubscriptionOrderAutoGen 的回應先看 API ReturnCode 與 Data.Status,成功才讀 OrderSlaveFlowStatusReturnCode 非 API0001、資料缺失,或 Data.Status 非 Success。Worker 記錄本期建單失敗與錯誤原因。OrderSlaveFlowStatus = WaitingToPay。Worker 保存訂單代碼,將本期明細標為 Pending,暫不當作付款完成。WaitingToPay。Worker 記錄本期成功,推進出貨週期並更新訂單對照。RegularCheckPendingPaymentJob 查詢本期 Pending 明細,再向 SCM 查詢對應 TSCode 的 SalesOrder 狀態。狀態是 WaitingToShipping、ConfirmedToShipping 或 Finish 時解除 Pending 並記為成功;其他結果走 CreditCardFail 處理。這個 Job 查的是 SCM 訂單狀態,不是直接向 Razer 查 Token 扣款結果。Pending 可使訂單成為 WaitingToPay。Worker 實際分流依據是 API 回傳的 OrderSlaveFlowStatus;兩層狀態不可直接畫成同一個值。CreateRegularOrderProcessor.cs、GetRegularOrderProcessor.cs、PayProcessService.cs、RazerPayChannelService.cs,以及 Worker 的 RegularCreateOrderJob.cs、RegularCheckPendingPaymentJob.cs。貨到付款的這一期,mweb 會怎麼建單?
排程仍會建立本期交易訂單,但付款不在建單當下向信用卡通道請款。這條路徑的重點是建立訂單、排入轉單工作,以及把結果回報給定期購 Worker。
CashOnDeliveryGetPayProcessDataProcessor 將 PayProcessFlowType 設為 CashOnDeliveryProcess,CreateTradesOrder() 依此執行已註冊的 Processor。CreateTradesOrderProcessor 寫入本期交易訂單。訂單流程狀態起始預設為 WaitingToTrans。TransferOrderProcessor 建立轉單 Task。依設定排入 TransferOrderToERP 或 TransToERPTmp;這一步表示工作已排入,不表示 ERP 已完成轉單。CreateRegularOrderProcessor 看到內部來源會直接返回,不會再次建立訂閱主設定。Pipeline 接著執行後續訂單處理,API 回傳本期訂單識別資訊。WaitingToTrans。TransferOrderProcessor 本身只負責排入工作。CashOnDeliveryProcess 的註冊清單沒有 AuthProcessor 或 ThirdPartyPayApiProcessor。因此這次建單不會向 Razer 送 card_token,也沒有線上信用卡扣款步驟。WaitingToPay,與 CreateTradesOrderProcessor 的預設 WaitingToTrans 不符。若後續系統顯示待付款,應另看該階段的狀態欄位與更新來源。GetPayProcessDataProcessor.cs、PayProcessProcessorModule.cs、CreateTradesOrderProcessor.cs、TransferOrderProcessor.cs、CreateRegularOrderProcessor.cs。同樣是本期建單,哪些地方真的不同?
付款方式先決定執行哪條 Pipeline;建立交易訂單時的初始狀態也各有規則。之後的授權或第三方付款結果可能再次更新訂單狀態,因此需要分清「剛寫入訂單」與「API 回傳時」。
CreditCardOnceCreditCardProcess初始流程狀態:WaitingToAuth付款節點:AuthProcessor 依信用卡 Gateway 進行授權。CreditCardOnce_StripeThirdPartyProcess初始流程狀態:沿用 WaitingToTrans 預設值。付款節點:ThirdPartyPayApiProcessor;付款服務可依結果更新狀態。CreditCardOnce_RazerThirdPartyProcess初始流程狀態:定期購自動建單沿用 WaitingToTrans;一般結帳改設 WaitingToPay。付款節點:ThirdPartyPayApiProcessor;付款服務可依結果更新狀態。CashOnDeliveryCashOnDeliveryProcess初始流程狀態:沿用 WaitingToTrans 預設值。後續節點:TransferOrderProcessor 排入轉單 Task,沒有線上授權 Processor。CreateTradesOrderProcessor 建立 OrderSlaveFlow 時的 StatusDef。它不是整個付款流程的最終狀態,也不等於消費者畫面顯示的狀態。CreateTradesOrderProcessor 先寫入 OrderSlaveFlow.StatusDef。Pending 時會寫入 WaitingToPay。SubscriptionOrderAutoGen 從訂單流程查出 OrderSlaveFlowStatus,與交易訂單識別資訊一起回傳。RegularCreateOrderJob 只在 API 成功且回傳 WaitingToPay 時,把本期明細分類為 Pending。OrderSlaveFlow.StatusDef、面向會員的 StatusForUserDef,以及 Worker 的本期明細狀態是不同資料。只看到 WaitingToPay 這個字,不能直接推定它來自哪個欄位或表示哪個付款階段。GetPayProcessDataProcessor.cs、CreateTradesOrderProcessor.cs、TradesOrderPaymentService.cs、TradesOrderService.cs;Worker 的 RegularCreateOrderJob.cs。同一期請求重送,mweb 怎麼找回已建的訂單?
RegularOrderTracing 用呼叫端提供的 TraceId 記錄一次自動建單嘗試。它保存處理狀態與建立出的交易訂單識別資訊,讓已成功的同一請求可以直接回傳原結果。
SubscriptionOrderAutoGen 收到 TraceId先執行 BlockDuplicateRequest,再查詢 RegularOrderTracingStatusDef = New,記下 RegularOrderId、TraceId、執行日期與預計出貨日,再組購物車、付款 Context 並建單。SuccessOrderSlaveFlowStatus。New 或 Fail 不能視為「已被永久擋下」。BlockDuplicateRequest 處理;持久的結果重用只見於 Tracing 已是 Success 的分支。兩者作用時點不同。New;ExecutedDate 是當天,ExpectShippingDate 來自請求的 NextExpectShippingDate。CreateTradesOrder() 返回後,更新為 Success,並寫入建立出的 TG/TS 代碼與 ID。Fail,回傳失敗結果。Success 代表這次 CreateTradesOrder() 已返回且訂單識別資訊已記錄,不等於第三方付款一定完成。本期交易訂單仍可能回報 WaitingToPay,由 Worker 另外分類為 Pending。NoMoreElmahException 的 catch 直接回傳錯誤,沒有在該分支更新 Tracing;原稿把所有例外都畫成 Fail 並不精確。TraceId、RegularOrderId。Worker 建立 API 請求時,以本期明細的 RodTxid 作為 TraceId。ExecutedDate、ExpectShippingDate,用來追查這次何時執行、預計何時出貨。StatusDef 記錄 New、Success 或 Fail。它描述自動建單嘗試,不是信用卡交易狀態。TradesOrderGroupId/Code、TradesOrderSlaveId/Code,供成功重送時找回原訂單。RegularOrder→長期訂閱設定,保存商品、週期、收件與結帳資訊。RegularOrderTracing→本次自動建單的狀態與交易訂單對照。TradesOrderGroup/Slave→本期實際建立的交易訂單與明細。Success,僅憑此處的 Success 回放分支無法證明「任何時點重送都絕不重複建單」。排查時仍需對照交易訂單與 Tracing。TradesOrderV2Controller.SubscriptionOrderAutoGen()、RegularOrderService.cs、TradesOrderService.GetAutoGenResponseEntity()、Worker 的 RegularCreateOrderJob.cs。


