跟我一步步探尋打款系統的建立過程

1.項目背景
初始階段
業務方訂單審覈通過後,會有離線任務不斷輪訓向支付中心發起調用,支付中心打款處理完成後會返回ifSuccess(是否落庫),state,code,error Message等。如果落庫且code爲打款成功,訂單業務狀態會修改爲打款成功。

發展階段
爲了配合業務發展,會增加各種活動來拉動訂單量,有些活動有打款需求,因爲業務處於快速發展期,實現方案都是在業務訂單向支付中心發起調用後,把與訂單有關的活動打款也向支付發起調用。待打款都成功後,再分別將業務訂單和活動的狀態置爲打款成功。

病痛階段
1.支付中心打款成功需要滿足很多條件比如:爲每個場景分配的財務編碼需要在有效期,財務編碼額度需要足夠,用戶需要有第三方的openId進行打款等。一旦某個打款情況出現問題會導致,其餘相關的業務都會被阻塞(主業務訂單狀態的修改會在最後,因爲離線任務是輪訓的是未打款成功的訂單,即與訂單相關活動的打款依賴於訂單的重試功能)

2.相關代碼耦合越發嚴重,訂單發起打款的代碼裏冗雜了越來越多各個活動的打款代碼,後續開發和維護成本越來越高。

3.打款異常情況越發增多,每天都需要rd分攤很多時間去幫助客服查詢訂單未完結或者沒有收到某個打款的原因。

嘗試階段
1.通過類似spring的擴展點模式,將各個活動的打款代碼抽離出去,從代碼可讀性和易維護開發性上講好了許多,但是依然會有訂單狀態阻塞情況出現,各個打款直接也會相互影響,排查問題也會較爲困難。

2.提供客服查詢工具,讓用戶可以根據訂單id查詢業務訂單和相關活動的打款情況。但是增加新的活動還需要繼續完善客服查詢工具,且當調用支付中心因爲用戶賬號沒有落庫(落庫後支付中心會通過各種機制保障打款成功)時,失敗信息只會記錄在日誌(沒有被收集在hive)中,兩邊都不可查詢。當客服提供一個很久之前的打款問題訂單時,查詢會非常麻煩。

2.方案設計目標
解除耦合
剝離業務訂單打款與活動打款的耦合,保障業務訂單打款一定成功,結合業務情況考慮,不對活動打款進行強一致處理,活動打款失敗的少數情況,由客服處理。

方便查詢
之前打款問題查詢困難,佔用開發時間過多,後續活動打款需要新增加開發時間,有一些問題查詢困難,希望可以給客服更方便的查詢打款方式

功能內聚
新開發的打款方案要做到方便業務調用,內部的邏輯與業務鬆耦合,只需要提供給業務插入打款和查詢打款的能力

監控預警
通過聚攏各個情況下的打款,做到統一監控,配合統計日誌,設置關鍵項目(財務編碼過期,打款金額)的報警及監控,統計錯誤打款郵件發送的功能

3.打款方案
在這裏插入圖片描述
4.迭代過程
版本1
爲了保障系統安全平穩上線,採用小步迭代的方式,先選取一個活動調用打款服務,其餘的打款情況依然調用支付中心。打款服務提供插入接口插入待打款記錄和離線任務輪訓打款記錄發起打款。

爲了保證打款id的全局唯一性(當然也可以使用更好地保障全局唯一的id生成方式),也爲了做到業務的高度聚合,打款id由之前的使用各個打款情況的業務id變爲在插入打款記錄時生成打款id。

插入接口參數:用戶id,業務類型,打款計劃id,金額,業務id,開始打款時間(可以爲空),打款描述(將會展示在用戶的微信收款記錄中),mchid,訂單id

業務類型與業務id會在數據庫中設置唯一索引,來保障唯一性。通過業務類型的方式也避免了不同業務可能存在的業務id衝突的問題,也可以使得打款服務可以支撐未來其它業務的接入。

開始打款時間可以不傳,傳遞時離線任務只會查詢開始打款時間在當前時間之前的。

打款描述,打款計劃id,mchid爲業務方屬性,打款服務不與業務耦合,所以由業務方傳遞。

訂單id可以不傳,傳遞時是爲與某個訂單相關的打款,不傳時視爲單獨的打款記錄。

離線任務輪選打款服務
在這裏插入圖片描述

離線任務應用與打款服務之間採取逐筆調用的方式是爲了避免數據過大引發接口超時。最終job執行成功後,也會在任務平臺可以觀測到job的運行情況,如果任務執行超時也會影響觀測結果。在調用支付中心後不再像之前一樣只根據打款是否成功進行狀態變化,會把支付返回來的所有錯誤code和message記錄在這條打款記錄的數據中。也會記錄相應的統計日誌,供數據平臺抓取。

第一步的設計過程中有一個難點,之前的業務是直接用業務id調用支付中心的,每一筆調用雖然冪等但是不能保證立即打款給用戶,而業務訂單狀態和活動打款狀態都會在支付中心返回確認打款成功之後,才變爲打款成功狀態,否則業務會重試調用支付。因爲現在打款服務的插入接口中會自己生成打款id,如果打款id不同,就有可能造成多次打款。我們考慮了兩種解決方案,第一種是數據遷移,這種方案在遷移過程中需要暫停線上業務的打款,需要人工關注各個業務的數據是否正常,在對用戶的影響和人工消耗方面都是很大的。第二種方案,在打款服務插入接口中,冗餘一部分代碼來處理兼容,每次插入記錄之前都先用業務id去支付中心查詢,如果能查到結果(說明曾經用它作爲打款id打款),則把業務id同同時也作爲打款id記錄。

版本2
在版本1上線穩定運行一段時間後,打款服務已經做好了接入其它打款場景的準備。根據現有業務情況,我們選擇業務訂單的打款同步調用打款服務插入接口,其餘訂單關聯活動在消費訂單狀態變化的mq時調用打款服務插入接口。這樣既實現了業務訂單與訂單關聯活動的解耦,也使得活動的代碼內聚,從而減少了問題排查的成本和後期代碼的維護工作量。

版本3
當打款的事情都完成後,就開始着手提供給客服更好地查詢工具以緩解開發人工介入的成本。之前的查詢工具問題在於,打款失敗問題不明確,有新的活動都需要重新增加查詢代碼。

在打款服務插入接口中,我們允許業務方傳入訂單id,我們會將傳入的訂單id記錄在該條記錄當中,查詢打款記錄時,只需要提供訂單id,就可以將相關的打款一連串的查詢出來,我們只需要將業務類型對應的實際活動告知客服方或者在前端代碼中增加一個枚舉項即可,打款失敗的錯誤信息因爲完善的記錄,我們可以都展示給客服,客服可以根據明確的問題,直接找到問題解決方,避免了業務方開發在中間做中介的人工消耗。

版本四
打款服務上線一段時間後,一個用戶體驗問題暴露了出來,爲了代碼高度解耦,最初的設計中,只要業務訂單調用打款服務插入接口返回成功後,業務訂單就可以將訂單狀態置爲打款成功,由打款服務保障段成功。但是這麼做可能會在一些場合下,用戶看到訂單狀態已經是打款成功了,但是實際上沒有收到錢,會給用戶很不好的用戶體驗。

我們採取在打款服務中提供打款查詢接口,根據業務類型和業務id查詢打款情況,業務訂單在輪訓未打款成功訂單,插入打款服務後,會接着調用查詢打款服務,只有查詢結果爲打款成功後纔將業務訂單狀態修改爲打款成功。

版本五
版本五迎來了最大的變化,原本的打款服務只支持微信打款,而隨着業務的拓展未來可能會有qq打款和支付寶打款的情況。爲了實現qq生態和阿里生態的業務推廣,對應的打款服務也需要具備這樣的能力。

根據業務情況,微信打款和qq打款是獲得用戶的微信或者qq的openid進行打款,可以作爲常規打款渠道,支付寶打款是讓用戶填寫支付寶賬號和姓名進行打款,只能是用戶綁定微信或者綁定qq賬戶失效後的一個備用渠道,且出於安全考慮用戶輸入一次支付寶打款信息,只能將用戶填寫之前的帶打款金額打給用戶,下次需要支付寶打款時,需要用戶重新綁定支付寶。

爲了滿足上面說的業務場景,我們增加了一個用戶支付寶信息記錄表,以及一個支付寶打款離線任務,插入接口新增參數:渠道是否明確,打款方式,打款計劃與渠道的對應map,old打款id,業務打款創建時間。

之前的離線打款任務也做了一些調整,根據業務情況。即使用戶綁定了支付寶,我們也要優先給用戶使用微信或者qq打款。在打款時會先判斷渠道是否明確(微信或qq生態),如果渠道明確則沿用之前的打款邏輯,如果渠道不明確,先嚐試微信打款,查詢沒有打款成功後,再次嘗試qq打款,查詢沒有打款成功後,修改記錄狀態爲待支付寶打款。

在這裏插入圖片描述

支付寶離線任務會查詢啓用狀態的支付寶賬戶記錄,然後根據記錄去找尋在該記錄創建時間之前存在的待打款記錄,修改改記錄的狀態,並且新插入一條打款記錄(渠道明確指定支付寶打款)。後續流程可以沿用之前的設計。

在這裏插入圖片描述

有三個在設計點在設計之時着重考慮了下,從嘗試給未知渠道打款到生成新的打款記錄都用的是同一個打款id,(支付寶離線任務會在添加記錄時把原打款id傳入)而沒有在新生成的時候沿用版本一的方式新生成打款id,就是希望通過打款id的一致確保不會因爲異常情況出現多打款的情況。

插入接口需要傳入打款計劃和業務對應關係map,是因爲不同的業務不同去打的打款計劃id可能會有多個,在嘗試打款的情況下,業務方並不會指明打款方式和傳入唯一的打款計劃id,如果再打款服務內記錄每個活動可能的打款計劃id,會讓打款服務與業務高度耦合。所以通過{「weixin」:123,「qq」,231}類似的方式來獲得。

支付寶信息記錄表設置中間狀態,在中間狀態時不允許用戶綁定新的支付寶賬戶。是擔心用戶在支付寶離線任務運行過程中綁定新的支付寶,或者在job運行期間還有新的訂單。 造成用戶認爲的支付寶打款賬戶和實際不一致,或者訂單漏打的情況。

狀態流轉圖
在這裏插入圖片描述

結語 迭代是沒有盡頭的,寫此文的時候就已經有了不少新改動需要設計了。希望這個系統可以越來越完善,也希望此文可以給大家參考。