接入微信 支付寶條碼支付的正確姿勢

2021-08-28 02:04:17 字數 4596 閱讀 4717

本文與文章同步

場景

條碼支付

下面來舉例幾種做法,一一來說明各種問題

假設wechat.scanpay() 一次就能確定支付結果,不考慮返回使用者正在支付中的場景,後面會提供方案

做法1

同步請求

public order scanpay()throws payexception

catch(exception e)

return order

}

問題解析

改進後的做法2

同步請求

public order scanpay()throws payexception

catch(exception e)

try.......

}catch(exception e)

trycatch(exception e)

return order

}

問題解析

如果是同步的請求方式,這個問題也能解決,但是解決的方式不太漂亮會影響使用者體驗。最好的方式就是採用訊息佇列進行非同步的處理

改造支付為非同步請求

請求分為多個階段,其中mq表示的是訊息佇列。

我們對訊息佇列的要求是不能丟失訊息,保證至少傳送訊息一次。只要不刪除訊息,訊息就會被再次傳送。

mq的可靠性不再本文範圍之內,請自行google 可靠訊息佇列

//建立訂單,然後返回給呼叫者

public order createorder()

//呼叫者發起支付請求,由於非同步操作,不會返回任何結果

public void reqeustscanpay(orderid,amount,code)

//呼叫者輪詢支付結果

public void checkorder(orderid,firstquerytime) throws payexception

order = getorderbyid(orderid)

if (order == null)

if (order.ispaying)

else if (order.ispaysuccess)

else if (order.ispayfail)

}

傳送的支付訊息開始非同步消費

訊息會處理以下幾個方法:

//當接收到請求掃碼支付時呼叫。掃碼返回結果可能有多種,需要一一判斷,根據不同條件來決定繼續查詢或者是撤銷支付或支付成功、失敗

public void processscanpay(message)

else if (result == error)

else if (result == duplicate pay)

else if (result == paysuccess)

//其他情況不舉例了,根據實際情況來傳送查詢或者撤銷訊息

}catch(exception e)}

public void processquerypay(message)

result = wechat.query(orderid)

if (result == userpaying)

else if (result == paysuccess)

else if (result == payfail)

else if (result == unknown)

else if (result == ordernotexist)

//其他情況不舉例了

}//接收到支付結果時呼叫

public void processpay(paysuccess)

else

delete message}

//撤銷支付,直到撤銷成功才刪除訊息,否則有可能出現支付單邊

public void processcancel()

方案說明

在建立訂單階段,如果訂單建立失敗,前端直接會返回錯誤給使用者,提示支付失敗,不影響資金變動

在建立訂單階段,如果請求超時,同樣可以當做失敗來處理。但是這個超時的請求會帶來2種 可能性:第一種:請求收到了,但無法在指定時間內響應,訂單會建立成功或者建立失敗。第二種,請求沒有收到,訂單也沒有建立成功。

在請求支付階段,請求失敗,表示傳送訊息失敗。不影響資金變動

在請求支付階段,請求成功,表示傳送訊息成功。訊息傳送成功,只要你**沒有bug,訊息一定會正常收到,然後進行支付處理

在請求支付階段,如果請求超時,也會有2種可能。第一種:請求收到了,無法在指定時間相應,訊息傳送成功。第二種:請求收到了,傳送訊息失敗。都沒有關係,客戶端只要繼續輪詢支付結果就可以。

輪詢支付結果階段,任何的超時,都忽略,只要重複輪詢就可以,除非獲得明確的響應結果。當然也不可能無限制的輪詢,可以在指定之間範圍之內,通常我們是在1-2分鐘之內。如果超過這個時間還沒有獲得準確的支付結果,那就可以直接請求撤銷本次支付

輪詢支付結果階段,還會出現的一種狀況就是客戶端crash崩潰退出,無法繼續剛才的輪詢。因此,支付的任何情況都不能把可靠**給客戶端來保證。需要服務端來保證支付的最終結果

服務端其實也存在乙個輪詢,但是這個輪詢不是foreach。試想一下,如果是foreach等待結果,那一次請求的時間就不可控了,當支付併發量大了之後,伺服器就無法接收新的請求了。因此通過訊息佇列來實現輪詢,這樣每次都是非常短時間的資料處理,然後傳送乙個continue的訊息,接收後再次進行後面的處理。這樣的好處就是每次請求都是短時間,不會阻塞伺服器的其他請求

任何網路請求都有可能超時,超時後必須查詢才可能獲得結果,但是也不一定會立刻獲得結果,必須進行多次查詢,因此還是通過訊息佇列來實現foreach的迴圈呼叫

為什麼要延遲3s接收訊息,當使用者需要輸入密碼時,間隔3s,基本時間就足夠了,如果間隔太小,查詢過多也沒有意義,此時間可以根據實際情況調整

會出現的問題

先看乙個錯誤的設計

public void cancelorder(orderid)throw cancelexception

order.makecancel

commit

}

當掃碼支付的請求沒有到達時,如果使用上面的撤銷我們得到的結果只能是訂單不存在,並沒有把訂單進行撤銷。但是支付請求有可能是在撤銷請求之後到達,這時支付就會成功,這與我們期待的結果是不一樣,我們希望使用者的支付是無效的。

正確的設計

//根據訂單號進行支付撤銷,不管訂單是否存在,只要不是關閉或者失敗狀態,都可以進行撤銷

public void cancelorder(orderid)throws cancelexception

else if (order.isclosed)

.......

order.makecancel//把錢退回

}makecancelbyorderid(orderid)

commit

}

那麼 makecancelbyorderid(orderid) 裡面做了什麼呢,這裡大致的表結構如下

table:tb_order_cancel

//記錄了被撤銷的訂單號

public void makecancelbyorderid(orderid)catch(duplicatekeyexception ignore)}

支付請求到達時,會先判斷是否訂單號已經被撤銷,如果被撤銷,就不再進行支付

public order scanpay(orderid,amount) throws payexception

do pay ......

}

以上的方法基本能覆蓋了條碼支付的所有問題。這個只是其中的一部分,還有退款、撤銷業務其實只要按照以上的思路來做,就沒什麼問題了。

開發完成後,需要模擬各種網路情況和返回結果進行測試。這個測試條件比較複雜,只能乙個個模擬。

針對於支付這裡,我開發了一套基於aop的動態模擬功能,提供給測試人員。測試人員通過介面輸入各種條件,就能模擬對應的問題進行測試。目前這個**還在改進,暫時不會開放出來。

總結

mq是基礎的保障設施,如果沒有它,啥也幹不了。當然我們可以用定時任務來替代,不過這個方案在資料和併發少的時候還行,一旦大了,那就不好辦了。

經過網路的請求,timeout必須要考慮。timeout會產生2個結果,乙個是對方收到了,乙個是對方沒有收到

如果要做到無單邊,就一定要配合查詢、撤銷操作。你也可以等定時自動對賬來找出差錯,這個沒問,但是如果能及時退錢,就避免了使用者和商戶的糾紛。以前我們的系統就是沒有及時退回,使用者經常投訴,無奈只能及時人工解決。自從方案換了之後,基本上一旦遇到問題1分鐘之內,都會把錢及時退回,不用人工操作。

支付中的訂單允許再次發起支付

安卓接入微信支付,支付寶支付

2 通過後台給我們的介面,拿到一些支付必須的引數 toastutils.showtoast wxpayentryactivity.this,支付失敗 break case 2 toastutils.showtoast wxpayentryactivity.this,支付取消 break 二 支付寶支...

支付寶條碼支付

最近在做第三方支付功能,其中支付寶用了條碼支付這種高大上的新支付方式,話說還有比較常見的是支付寶掃碼支付,這兩種有什麼區別呢,很簡單,就是掃碼支付是消費者用手機主動掃碼再支付,條碼支付是商家用掃碼槍主動掃碼再支付。那麼為何選擇條碼支付呢,因為這個速度快,從掃消費者手機上的條碼或 到支付完成,那是瞬間...

支付寶接入

三 將上面的資料夾拖入工程,並新增相應的依賴庫 進行編譯,會出現以下問題 1 unknown type name nsstring 或者 unknown type name nsdata 等不識別常見類的問題。這是因為缺少foundation類庫和uikit類庫,支付寶demo中之所以沒有出現此錯誤...