跟我一步步探尋打款系統的建立過程
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執行期間還有新的訂單。 造成使用者認為的支付寶打款賬戶和實際不一致,或者訂單漏打的情況。
狀態流轉圖
結語 迭代是沒有盡頭的,寫此文的時候就已經有了不少新改動需要設計了。希望這個系統可以越來越完善,也希望此文可以給大家參考。