微信小程序支付流程詳解
最近在工作中接入了一下微信小程序支付的功能,雖然說(shuō)官方文檔已經(jīng)比較詳細(xì)了,但在使用過(guò)程中還是踩了不少的坑,整理了一下大體的流程和代碼分享出來(lái)。在開(kāi)始使用小程序支付功能前,需要做好以下的準(zhǔn)備工作:
申請(qǐng)微信小程序,配置小程序id及秘鑰
申請(qǐng)用于支付的微信商戶平臺(tái)賬號(hào),配置商戶號(hào)id及商戶平臺(tái)秘鑰,并綁定小程序與該商戶號(hào)
后端服務(wù)在正式環(huán)境下需要https域名,調(diào)試模式可以不需要
先引用一張小程序支付官方說(shuō)明的流程圖,可以看出小程序支付的主要邏輯集中在后端,前端只需要攜帶參數(shù)請(qǐng)求后端接口,然后根據(jù)后端接口返回的數(shù)據(jù)在前端喚起微信支付即可。
按照上面流程圖中商戶業(yè)務(wù)系統(tǒng)和微信支付系統(tǒng)主要交互步驟,對(duì)流程進(jìn)行拆解說(shuō)明。
1、獲取用戶openId
小程序前端調(diào)用wx.login()獲取登錄憑證code,后端調(diào)用接口獲取用戶的openid和session_key。注意這里在發(fā)起請(qǐng)求的時(shí)候需要攜帶小程序的appId和appSecret。
public OpenIdInfo code2Openid(String code){ String url = "https://api.weixin.qq.com/sns/jscode2session"; String param = "appid=" + mpCommonProperty.getAppid() + "&secret=" + mpCommonProperty.getAppsecret() + "&js_code=" + code + "&grant_type=authorization_code"; String rs = HttpUtils.sendGet(url, param); JSONObject json = JSONObject.parseObject(rs); if (json.get("errcode") == null) { String openid = json.getString("openid"); String sessionKey = json.getString("session_key"); OpenIdInfo openIdInfo = OpenIdInfo.builder() .openId(openid).sessionKey(sessionKey).build(); return openIdInfo; }else { log.error("get openid error"); return null; } }
需要注意每次調(diào)用接口都會(huì)刷新session_key的值,使之前的session_key失效,其他操作諸如解析用戶手機(jī)號(hào)時(shí)會(huì)用到這個(gè)秘鑰,為了避免該情況可以將用戶的openid存儲(chǔ)在業(yè)務(wù)系統(tǒng)的用戶體系中。
2、調(diào)用支付統(tǒng)一下單
微信統(tǒng)一下單接口要求傳遞參數(shù)的形式為xml報(bào)文,因此需要先對(duì)參數(shù)進(jìn)行拼接,這里僅列出了能夠喚起小程序支付所需要的最小參數(shù)范圍,更多的參數(shù)列表可以查看官方文檔。
public String generateUniPayXml(UnifiedParam unifiedParam){ int money = (int) Math.ceil(unifiedParam.getTotalMoney() * 100); //轉(zhuǎn)換為分,向上取整 Map
對(duì)其中幾個(gè)參數(shù)進(jìn)行說(shuō)明:
out_trade_no:商戶訂單號(hào),在我們的后臺(tái)使用某種規(guī)則生成,不能重復(fù)
total_fee:訂單總金額,需要注意單位為分,需要轉(zhuǎn)
body:商品描述
notify_url:支付結(jié)果的回調(diào)接口地址,使用會(huì)在后面介紹
sign:簽名,需要按照微信的規(guī)則生成,算法規(guī)則為去除值為空的元素,參數(shù)名ASCII字典序排序進(jìn)行拼接,拼接API密鑰,使用Md5進(jìn)行加密:
簽名方法如下:
public String signCommon(Map
以上步驟完成后,對(duì)外暴露的統(tǒng)一下單接口如下:
public Map
在調(diào)用后,會(huì)收到同步返回結(jié)果為一段xml報(bào)文,將其解析成Map后可供下一階段使用,同步接口的返回值及錯(cuò)誤碼可以參考官方文檔。
3、二次簽名
在調(diào)用統(tǒng)一下單接口并收到微信的同步返回結(jié)果后,需要對(duì)其進(jìn)行二次簽名,需要進(jìn)行簽名的參數(shù)包括appId、timeStamp、nonceStr、package、signType。
public PrepayInfo secondSign(Map
二次簽名完成后,將timeStamp、nonceStr、package、signType、paySign返回給前端,這里為了方便封裝了一個(gè)對(duì)象用于返回,前端在收到參數(shù)后喚起微信支付。
4、接收支付通知
在前面介紹的統(tǒng)一下單的參數(shù)中,傳入了商戶后端的回調(diào)地址,在支付完成后,微信會(huì)向這個(gè)調(diào)用這個(gè)回調(diào)接口,通知支付結(jié)果。
@PostMapping("fallback") public void fallback(HttpServletRequest request,HttpServletResponse response) throws IOException { StringBuilder sb = new StringBuilder(); BufferedReader reader = null; try (InputStream InputStream = request.getInputStream()) { reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { log.error("getBodyString錯(cuò)誤"); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { log.error(ExceptionUtils.getMessage(e)); } } } String notifyXml=sb.toString(); Map
在接收到返回的報(bào)文后,需要用之前同樣的簽名算法,驗(yàn)證返回報(bào)文的真實(shí)性,并在驗(yàn)證真實(shí)性后再執(zhí)行之后的業(yè)務(wù)邏輯,防止數(shù)據(jù)泄漏導(dǎo)致出現(xiàn)的虛假通知,造成資金損失。
微信在調(diào)用回調(diào)接口時(shí),如果收到我們業(yè)務(wù)系統(tǒng)的應(yīng)答不符合規(guī)范或超時(shí),會(huì)判定本次通知失敗,重新發(fā)送多次通知。在通知一直不成功的情況下,按照官方文檔的說(shuō)明,總計(jì)在24h4m內(nèi)會(huì)調(diào)用15次回調(diào)接口。因此一定要按照規(guī)定返回成功接收的報(bào)文,從一定程度上也能降低系統(tǒng)的負(fù)載。
在測(cè)試中發(fā)現(xiàn),不能使用直接返回String字符串的方式進(jìn)行結(jié)果的返回,仍然會(huì)一直發(fā)起回調(diào),必須使用HttpServletResponse寫(xiě)入返回。即使這么做了,還是建議大家在回調(diào)接口內(nèi)部處理業(yè)務(wù)前再做一下冪等性的處理,防止多次執(zhí)行回調(diào)邏輯造成業(yè)務(wù)系統(tǒng)的數(shù)據(jù)混亂。
小程序
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。