移動端支付系統如何設計有效地防重失效機制?



導讀html

「目前在互聯網應用的大部分支付場景中,對接支付寶、微信移動支付產品這樣須要用戶參與支付流程的支付方式已經變得很是廣泛,相似的還有PC端銀行網銀支付;而經過綁定用戶銀行卡、對接銀行卡快捷支付通道直接扣款的支付方式,雖然還在電商、保險、互聯網金融、租房等行業被普遍應用,可是隨着微信錢包、支付寶錢包這類移動互聯網支付方式的興起,用戶規模的迅速增加,再加上用戶銀行卡信息安全、直連銀行通道關閉等因素用戶市場份額正在逐步減小」。前端


實際上,這種須要客戶端參與支付流程的方式相比銀行卡快捷支付直接扣款這類支付方式,在支付系統的流程及訂單結構等設計上是存在較大差別的,其中訂單的防重失效機制的設計更是一個比較棘手的問題。數據庫


參與過支付系統開發或在業務系統中開發支付功能的同窗可能會遇到相似這樣的業務需求:安全


用戶在外賣網站或App上購買了點了一份外賣,並經過微信支付進行付款,系統在收到用戶支付完成的消息後,提示用戶付款成功並派單給餐館?微信


初看這個問題,可能不少同窗都會有疑問,這不是一個很簡單的支付流程嗎?大部分支付場景不都是這樣的麼?網絡


實際上是這樣的,做爲正常的支付流程來說,上述場景並無什麼問題,在整個系統鏈運行穩定的狀況下,可能大部分參與者並不會有什麼感受;可是,做爲一個具有專業精神的小碼農來講,仍是有不少異常場景須要考慮的,否則就可能會由於系統流程上設計的缺陷而給公司和用戶體驗形成比較大的傷害。併發


那麼上述需求中,會有什麼樣的異常場景呢?與支付系統防重失效機制的設計有什麼關聯?異步


咱們能夠先來看一下以上場景在系統流程中的運行狀況(須要放大查看):
微信支付



在上面的流程中,雖然從用戶角度看可能只是幾秒鐘的事情,但實際上整個系統鏈是經歷了一個比較長的調用過程。具體以下:優化

  • 用戶在點外賣的過程當中選擇微信支付後,App會將支付請求發送給外賣後臺系統,若是在整個外賣平臺中,支付系統是一個獨立的系統,則外賣業務後臺服務會在生成業務訂單後將支付請求發送給獨立的「支付系統」進行處理;

  • 此時支付系統做爲獨立的中間系統會處理外賣平臺業務後臺發送過來的支付請求,記錄其業務訂單號並生成對應支付系統自身的支付流水號,並對支付流水進行狀態初始化(這裏涉及一個業務訂單號&支付訂單號如何匹配問題,會在後面的討論中闡述);

  • 支付系統調用微信統一下單接口進行預支付(這裏的操做方式就是類網銀式的支付方式,先進行預支付而後由用戶跳到站外進行支付),並同步獲得微信支付返回的預支付訂單信息,支付系統此時須要更新支付訂單爲pending狀態表示處於預支付狀態;

  • 而後支付系統將預支付信息同步給調用方—外賣業務後臺,外賣業務後臺再同步給外賣App;

  • 外賣App會根據預支付訂單信息經過客戶端支付SDK喚起微信支付客戶端,由用戶操做微信支付客戶端直接向微信支付發起付款動做,須要注意的是,此時調用鏈已經轉移到了站外,實際上此時用戶是否支付或是否支付成功,不管是支付系統仍是外賣系統及App自己都是沒法直接感知到支付結果的,須要逐層回調;

  • 微信支付會經過循環調用的方式主動將支付結果回調給支付系統,再由支付系統回調給外賣業務系統,最後在用戶直接感知前由App主動查詢外賣業務系統訂單支付狀態,同時提示用戶支付成功或支付處理中這樣的信息;


從上面的流程能夠了解到,實際上你們平時在經過App購物時支付的一瞬間是經歷了很複雜的流程。那麼,不知道在支付的過程當中有沒有這樣的體驗?


在點外賣後付款了,微信也提示支付成功了,可是外賣App卻始終不顯示點餐成功?即便選擇從新支付也提示支付中,不容許重複支付?或者選擇從新支付之後外賣App顯示也顯示點餐成功了,可是以前支付的錢卻不見了,只能打客服投訴,各類麻煩?


上述問題,在目前支付流程的設計上是必然會發生的,目前做者所在的公司也有相似的問題,雖然這種問題發生的機率可能不是特別高,可是絕對是破壞用戶體驗以及增長了客服的工做量,處理得是否得當是衡量一套支付系統是否強大的核心指標之一。


那麼怎樣的設計才能很好地解決此類問題呢?


從流程上看用戶選擇微信支付並喚起微信錢包付款後,實際上外賣平臺支付系統已經感知不到系統的狀態了,也就說此時用戶是否完成了支付,平臺是沒法同步感知的,只能依賴於微信的主動通知回調,通常來講目前主流的支付公司都有一套完整的商戶通知邏輯,會在支付完成後實時通知到商戶。


可是不少時候會有多種因素致使這種通知被延遲,比較常見的因素主要有網絡、自身平臺系統服務宕機、第三方渠道通知服務故障等。


也就說會有用戶支付了點外賣的錢,系統卻沒有實時顯示支付成功的問題,也就是咱們常說的短時掉單問題;或者用戶沒有及時支付,從新付款時卻會被提示「支付中請勿重複提交」,也就是支付防重問題


對於掉單問題的處理,能夠根據業務場景進行考慮,但不管是哪一種方案,越快速補償業務越可以有效地提高用戶體驗,減小系統異常處理流程,讓防重機制更加靈敏,在避免重複支付問題的同時提升支付成功率。


另外,是否容許用戶重複支付,如何利用衝正機制有效提升用戶體驗的同時快速保障用戶權益,也是須要在總體方案中進行考慮的方面。


根據業務時效性要求不一樣,大體有兩種方案:


異步補償機制。

具體來講,就是支付流程按照正常的流程走,經過採用旁掛定時的方式掃描系統中必定時間策略範圍內的pengding狀態的訂單,經過微信提供的訂單查詢接口主動輪詢,一旦支付狀態查詢到終態即刻觸發系統回調,完成支付訂單及業務邏輯的補償;

另外一方面,若是pengding狀態訂單經過輪詢方式沒有查詢到最終狀態則須要設置必定的重複輪詢策略,例如5分鐘、10分鐘、20分鐘、1小時、3小時、8小時、24小時這樣,並在超過策略規定的時間及輪詢次數後將支付流水更新爲失效終態,並提供訂單查詢接口供業務平臺完成自身業務訂單邏輯的更新。


系統示意圖以下:

經過旁掛式的方式,支付主流程會變的相對簡單,只須要考慮正常的收單場景,對於不少業務實時性不過高的支付場景,這種方式也夠用。但對於業務實時性要求很是高,而且對用戶體驗有極致要求的場景來講,這種方式顯然也是存在明顯問題的。


咱們仍是拿點外賣這件事來講,外賣後臺在接收到用戶經過App發送的點餐支付請求後生成外賣訂單並將支付請求發送給平臺支付系統,支付系統通常來講會首先進行訂單防重判斷,即已經發起過的成功支付/支付中的請求不被容許發起第二次,支付成功的交易不容許重複發起。


可是等待支付或支付失敗的交易不少公司內部支付系統都會被要求容許發起二次付款,在外賣點餐環節,若是用戶點餐了可是並無馬上進行支付或者支付因爲某種緣由失敗了,是能夠從新發起付款的,在這種狀況下,支付系統就會面臨一個問題,因爲不知道在進行預支付後用戶是否完成了支付,對因而否應該繼續讓用戶發起支付請求,防重邏輯就會變的遲鈍,若是容許用戶支付則可能出現重複扣款的問題,不容許則會影響用戶體驗,爲了讓整個機制變得合理,因此須要依賴於上述系統的補償機制來進行回盤或失效處理。


這裏會遇到如下三種狀況:

一、用戶最終未支付,則系統安裝必定的輪詢機制進行後續的訂單失效處理便可;


二、用戶完成了支付,支付系統遲遲收不到微信的回調,經過逐步輪詢的方式系統也會進行後續的訂單回調補償;但這裏的問題是,若是異步補償系統對訂單的輪詢不夠及時(在支付訂單量比較大的狀況下,經過定時輪詢的方式在時效性上較差),那麼就會致使一個比較尷尬的狀況,用戶付完了錢,可是外賣訂單很長時間顯示未支付,沒法進行派單,在輪詢補償系統完成回調後觸發派單操做,但每每極可能已通過了飯點,而且極可能用戶已經觸發了申訴流程,外賣平臺須要進行退款操做(增長客服工做量);


三、則是用戶當時並未及時支付,在訂單失效前的某個時間,用戶可能會選擇從新付款,由於此時支付系統訂單並未失效,會處於支付中狀態,觸發防重機制,沒法再次發起付款;


對於二、3兩種狀況,若是須要很好的知足業務要求,就要提升支付系統時效性,提升訂單防重失效、快速回盤的處理時間。要達到這樣的效果,每每單純的依賴旁掛式的處理方案是很難達到的,而是須要讓實時支付流程的設計變得更加智能和靈敏。


實時支付流程優化設計

爲解決上面的問題咱們須要在實時支付流程中加入異常優化機制,從整個流程的設計上去解決,讓整個支付系統變得更加智能和靈敏,雖然這種方式看似讓支付主流程變得複雜了不少,但從優化用戶體驗、提升系統靈敏度的角度看,這種複雜度是值得的而且是能夠經過技術細節屏蔽的。


那麼具體應該怎樣去設計這樣的流程?


詳細如圖所示(需點擊放大):




如上圖所示,支付系統接收到前端發起的支付請求,系統首先須要進行防重判斷,這裏爲了有效地防止併發請求,採用Codis鎖的方式,即一筆業務支付訂單請求發送到支付系統後首先獲取Codis全局鎖,若是存在鎖則說明訂單正常被處理/未被正常處理,此時咱們須要進行鎖更新時間判斷,若是鎖的更新時間與系統當前時間差<=10s(可根據業務場景進行動態調整,即10s內同一筆商戶訂單號的支付請求不容許被屢次發起),則頗有可能此時系統正在處理這筆支付請求,應該正常進行防重處理;


相反,若是獲取的Codis鎖的更新時間與系統當前時間差>10s,則此時會存在兩種狀況,一種就是這筆支付訂單沒有被正常支付,是應該被容許從新發起支付的;另外一種可能則是用戶可能支付成功了,只是渠道在支付結果回調的過程當中出了問題致使系統掉單。這兩種狀況混在一塊兒,系統並不能馬上識別出到底屬於何種狀況。


這個問題是一個很是廣泛和典型的問題,幾乎不少公司都會遇到。


此時,支付系統有兩種選擇,一種選擇是執行嚴格的防重策略,即要求全部對接支付平臺的業務系統每次調用支付請求都必須生成不一樣的商戶訂單號,支付系統對於同一個訂單號不管支付成功與否都不容許將此商戶支付單號重複發送給支付平臺,這種方案與第三方支付公司的接口約定一致。


這種防重策略粗暴簡單,本質上是將邏輯的複雜性傳到給了業務系統,也會讓業務變的難受,若是支付平臺在後期經歷太重建,須要推進業務線切換的話,也每每會招致業務系統的反對。


那麼如何讓支付平臺自己來屏蔽這種複雜的細節,讓業務儘量無感知?


兩個訂單號

  • 商戶訂單號:業務系統發起的向支付系統發起支付請求是生成的在商戶系統中惟一標識一筆訂單的標記。

  • 支付訂單號:業務系統向支付系統發起支付請求後,支付平臺自己生成的系統惟一標識一筆支付流水的標記,而且是支付系統與第三方支付渠道交互的惟一流水標識。


爲了達到以上目標須要在支付系統內部採用1:3(舉例)的訂單模型,即1筆業務訂單號能夠對應支付系統3筆支付訂單流水,而且每筆支付流水容許被髮起的條件是上筆支付流水數據庫訂單狀態是未支付成功,而且須要在當前這筆支付流水從新生成後將上筆支付流水放入訂單動態實效隊列,進行快速失效處理。


之因此採用以上方式,緣由在於超過3次時間間隔超過30s(策略能夠根據業務實踐進行動態調整)還未完成支付的狀況,系統基本能夠認定屬於惡意點擊行爲,能夠直接拒絕此筆業務訂單從新發起支付了。


須要動態將上筆支付訂單快速置爲實效的緣由在於,咱們須要在內部設定一個邏輯:「若是支付訂單處於實效狀態並在後面接收到了第三方支付成功的回調,則須要系統自動發起該筆支付訂單的原路退款邏輯,並確保該筆訂單不會被通知到商戶側」。這種現象之因此出現,在於咱們爲了提升系統的實時性容許了少許重複扣款的狀況發生,並進行了自動衝正邏輯。


固然,在細節的處理上咱們是在當前流水發起前對上筆流水已經進行了一輪訂單實時查詢,若是結果爲支付成功,則這次請求會直接返回支付成功(或者,也能夠提示已經支付成功,App主動查詢支付系統的訂單狀態來完成回盤)。


若是當前訂單再次預支付成功,在同步返回預支付結果前須要更新Codis中訂單鎖的時間及發起次數。同時,在接受到第三方正常的支付成功回調後完成訂單狀態更新及商戶通知後消除Codis鎖。


上述策略,爲解決防重&二次支付問題提供了一種方案,固然還有不少細節的代碼邏輯是須要考慮完善的,例如,實時查詢超時的策略、退款的觸發時間、用戶提示等。


此外,若是用戶再也不選擇再次發起付款,系統中的存量訂單也須要經過文中早些時候介紹過的異步補償機制逐步將進行失效處理(具體策略機制可參考圖示及以前的概述),只是若是在異步補償機制過程當中發現掉單的訂單,是否正常回盤或自動給用戶退款,就須要具體狀況具體分析了。


以上就是本文的所有內容了,在整個支付系統的搭建的過程當中還有不少細節邏輯是能夠優化的,須要根據具體業務進行實踐與處理。鑑於經驗和水平有限,不足之處,還請批評指正(能夠直接在公衆號進行評論交流哦)。


若是以爲小哥在認真寫文章,能夠關注下公衆號,支持下哦

本文分享自微信公衆號 - 無敵碼農(jiangqiaodege)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索