如題今天要描述一個問題是:程序在確認訂單時拉起第三方支付,支付失敗了,引起的問題。
為了能清楚的描述問題,我把場景復現一下,大家肯定都有過APP購物的體會,大家一定知道有一個按鈕叫“確認”或者“結算”之類的功能按鈕,
點擊一下彈出一個框讓進行微信支付或支付寶支付或銀聯支付或其他什么支付的。
那么這個“確認”或者“結算”功能按鈕在背后到底做了哪些事情,成功或失敗是怎么處理的,需要怎么處理,這些都是值得討論的問題。
我們在做商城時就遇到了這樣的問題,我們的問題是我們第一次點擊“確認”按鈕時程序報錯說支付失敗了,第一次失敗沒問題呀,可能就是支付有問題吧,
那么我們再次支付唄,OK,那我們再點,這個時候又報錯了,說購物車為空,我們測試的妹妹就暈了。
明明頁面上購物車里有商品啊,誰說的為空,你自己看嘛,你自己看嘛,誰說的為空。
這就是我們的問題。
那么遇到這樣問題怎么辦,我們把問題細化,細化,在細化,我們一步一步看每一個步驟。
然后我們會看到在訂單提交,支付付款時,這里邊的過程需要細化的地方就是,
先要創建訂單把用戶購物車里所有的商品、優惠券、積分等所有東西計算好之后把的結果落庫,把購物車清空。然后在向微信、支付寶等發起支付。
這里細化出來了兩步:
1、創建訂單,清空購物車
2、向第三方支付平台支付。
既然分出來了兩步那么每一步就都會有出錯的可能,每一步都可能出錯。出錯並不可怕,可怕的是出錯不認,知錯不改,明知故犯。
上邊的兩步必須是順序的執行,也就是說如果第一步出錯,那么第二部不用執行了,而如果第一只執行成功,第二步失敗,這個問題就比較麻煩。
這就是我們的測試妹妹看到的問題,
第一次是真的支付失敗了,但是創建訂單成功了,所以購物車在后台數據庫清空了,而頁面沒有刷新,第二次呢購物車真的是清空了,在也無法支付了,因為后台會重新檢測購物車。
我們先來這樣討論問題
首先我們已經把上邊說步驟分成了兩步,那么我們就按照兩步的方式來思考問題,
每一步在一個單獨的事務里。為什么要這么看,好像APP的開發人員說支付這一步驟是APP直接調用微信或支付寶不通知服務器端的。
那么只有第一步是服務器完全可以控制並且在自己的一個事務里,那么可以給APP一個約定,成功怎么處理,失敗怎么處理。
如果失敗還好,直接說你不用走第二步,當然失敗不是我們期望的,我們也不希望失敗,所以第一步成功了。
那么到第二步怎么辦呢,這里按之前所說的這一步是APP直接拉起微信或支付寶的,服務器端根本不知道,
那么這里的問題就來了,如果支付成功,當然是我們期望的一個結果,直接寫會數據庫,支付成功回寫,這沒有問題,是我們期望的結果。
(這里沒有討論,在支付成功后回寫失敗怎么辦,這也是一個大坑,雖然概率不大,但是不能假設,特別是量大的時候,一旦出了問題,用戶可是真的付錢了,非常特別的麻煩。你拿用戶錢,告訴用戶沒有支付,不給用戶東西,你搶錢嘛)
那么支付失敗怎么辦,我們的焦點問題就是如果支付失敗了,APP又不刷新,仍然能夠看到購物車里有商品,
而實際上因為第一步的調用成功,購物車已經被清空,購物車里的商品已經落庫成為了訂單。
這時不能在對購物車進行支付,而實際上是訂單。訂單的狀態是未支付或者支付未成功。
討論到這里,我們已經非常明確的看到以下內容,首先在顯示上混淆了支付的是訂單還是購物車,
因為在APP上顯示的是操作一次,按了一個按鈕發起兩個調用,讓用戶感覺到或者給你迷惑的感覺的是我這次是對購物車進行的支付,
買的是購物車里的商品嘛,但實際上是由於APP在進行了第一次創建訂單調用后,緊接着直接拉起支付,沒有通知服務器。
這是兩個步驟在一起有沒有很好的異常處理或回滾機制,看似友好減少用戶操作的行為卻是給用戶造成了極大的麻煩。
首先這里一定要明確的是支付的一定是訂單,不是購物車。
當然用戶是可以不用感知到支付的是購物車或者是訂單的,也可以不用感受到有任何變化的,這個時候如果兩次調用全部成功或一起失敗,都是沒有問題的,
但我們的問題就是用戶沒有任何感知的情況下,第一個調用成功了,第二個失敗了,APP還沒有任何變化,或者沒有刷新的意願,
那么第一個數據想回滾,或者有回滾的意願,或者想有其他的處理,問題就來了。
對於這樣的一個分析,上邊解決問題是思路相對也比較明確,
第一種,那就分成兩步,每一步單獨處理,從業務流程上給出一個解決方案,
就是可以一起提交,但是如果是第一步成功,第二步失敗了,那么在這個時候,APP上可以給給提示,比如說:你的訂單已經提交但是支付失敗請盡快完成支付,等。
或者干脆就是直接分成兩步,第一步訂單提交,並給出提示。第二,訂單支付並給出提示。
第二種,我們假設這個支付可以有服務器端發起,把創建訂單落庫和支付的業務放進一個事務里,同樣是一次調用,那么由於事物的支持,可以一起失敗,一起成功,這想來是比較棒的解決方案。
第三種可能,是不是可以有APP來發起事務,然后第二調用失敗了,讓第一次操作取消,那這里就涉及到兩種方案,
第一種,使用事務,讓第一次的提交在第二支付成功時,才真正落庫。
第二種,可以把第一次是數據庫狀態變成提交前的狀態,相當於回滾,但實際上更加類似又重新了一份數據,或者更改原來的為落庫前的狀態。
或者第三種,讓用戶先支付成功,然后在寫訂單,(這種感覺是騷主意吧,還涉及到庫存,那一超賣,在那個點上賣完了怎么辦)
第四種可能,APP同時把自己拉起支付調用的支付結果在告訴服務器,讓服務器來返回APP的這一次行為是最終該怎么辦。
其他解決方案,歡迎大家一起吐槽,討論~
回來散步時又想到一種辦法,
第五種辦法,在用戶點“確認”或“結算”按鈕時,在服務器端校驗除了校驗購物車是不是為空的話,
再校驗最近的沒有支付或者支付失敗的訂單的,對比一下點擊按鈕的時間和訂單的時間,在某個可允許的范圍內,可以假設就是支付的這個訂單。
(想來這個也是一種不算好的辦法,但理論上也可以解決這樣情況的大部分問題)
本來我想是非常簡單的問題,除了第三種然APP來做事務相對困難,其他的都是業務流程稍微變化一下就能很好的解決問題,
但是我反應了幾次這樣的問題,也是沒人理我,感覺世界好悲哀。
有人認定了創建訂單有問題,有人說支付有問題,支付不成功嘛,我給人解釋,但是感覺...就是那種感覺“他人笑我太瘋癲,我笑他人看不穿”。
但是就像我上邊說的出錯不認,知錯不改,產品不改進,用戶體驗差,拼命的讓程序員改一些明明流程問題引起的看似是BUG的問題,可憐那些悲催的程序員吧。