一:準備工作
申請服務號(企業)開通微信支付開發配置 具體準備工作請參考Spring Boot入門教程(三十九):微信支付整合-申請服務號和微信支付二:支付方式刷卡支付(MICROPAY) :刷卡支付是使用者展示微信錢包內的“刷卡條碼/二維碼”給商戶系統掃描後直接完成支付的模式。主要應用線下面對面收銀的場景。相當於支付寶的條碼支付掃碼支付:掃碼支付是商戶系統按微信支付協議生成支付二維碼,使用者再用微信“掃一掃”完成支付的模式。該模式適用於PC網站支付、實體店單品或訂單支付、媒體廣告支付等場景。相當於支付寶的電腦網站支付H5支付:H5支付是指商戶在微信客戶端外的移動端網頁展示商品或服務,使用者在前述頁面確認使用微信支付時,商戶發起本服務呼起微信客戶端進行支付。主要用於觸屏版的手機瀏覽器請求微信支付的場景。可以方便的從外部瀏覽器喚起微信支付。相當於支付寶的手機網站支付公眾號支付(JSAPI):商戶已有H5商城網站,使用者通過訊息或掃描二維碼在微信內開啟網頁時,可以呼叫微信支付完成下單購買的流程。App支付:APP支付又稱移動端支付,是商戶通過在移動端應用APP中整合開放SDK調起微信支付模組完成支付的模式。小程式支付:在小程式中使用H5支付: 是應用在微信客戶端外,是一種WAP支付。 公眾號支付(JSAPI): 是應用在微信內的一種支付。 兩種方式的應用場景不一樣。
1. 場景介紹步驟1:使用者選擇刷卡支付付款並開啟微信,進入“我”->“錢包”->“收付款”條碼介面;步驟2:收銀員在商戶系統操作生成支付訂單,使用者確認支付金額;步驟3:商戶收銀員用掃碼裝置掃描使用者的條碼/二維碼,商戶收銀系統提交支付;步驟4:微信支付後臺系統收到支付請求,根據驗證密碼規則判斷是否驗證使用者的支付密碼,不需要驗證密碼的交易直接發起扣款,需要驗證密碼的交易會彈出密碼輸入框。支付成功後微信端會彈出成功頁面,支付失敗會彈出錯誤提示。2. 驗證密碼規則:支付金額>1000元的交易需要驗證使用者支付密碼使用者賬號每天最多有5筆交易可以免密,超過後需要驗證密碼微信支付後臺判斷使用者支付行為有異常情況,符合免密規則的交易也會要求驗證密碼3. 免密支付流程:(1)收銀員在商戶收銀臺生成支付訂單,向用戶展示支付金額;
(3)收銀員使用掃碼裝置讀取使用者手機螢幕上的條碼;
(4)掃碼裝置將讀取的資訊上傳給門店收銀臺;
(5)門店收銀臺得到支付資訊後,向商戶收銀後臺發起支付請求。
(7)微信支付系統得到商戶側的支付請求之後會對請求進行驗證,驗證通過之後會對請求資料進行處理,最後將處理後的支付結果返回給商戶收銀後臺。如果支付成功,微信支付系統會將支付結果返回給商戶,同時把支付結果通知給使用者(以簡訊、微信訊息的形式通知)。
(8)商戶收銀後臺對得到的支付結果進行簽名驗證和處理,再將支付結果返回給門店收銀臺。
(9)收銀員看到門店收銀臺的支付結果後給使用者發貨。
4. 驗密支付流程驗密支付流程文件
在商戶呼叫【提交刷卡支付API】發起支付請求之後,微信支付後臺提示使用者輸入密碼確認支付,介面同步返回USERPAYING狀態,商戶系統再輪詢呼叫查詢訂單介面來確認當前使用者是否已經支付成功。
以下時序圖說明驗密支付流程:
(1)商戶門店生成訂單後,收銀臺向後臺系統發起支付請求。
(6)使用者得到輸入密碼提示後,確認支付並輸入密碼。
(9)商戶收銀臺得到USERPAYING狀態後,經過商戶後臺系統呼叫【查詢訂單API】查詢實際支付結果。
(10)如果支付結果仍為USERPAYING,則每隔5秒迴圈呼叫【查詢訂單API】判斷實際支付結果,如果使用者取消支付或累計30秒使用者都未支付,商戶收銀臺退出查詢流程後繼續呼叫【撤銷訂單API】撤銷支付交易。
5. 異常處理使用者遇到支付異常,請按如下說明處理
(1)使用者微信端彈出系統錯誤提示框,使用者可在交易列表檢視交易情況,如果未找到訂單,需要商戶重新發起支付交易;如果訂單顯示成功支付,商戶收銀系統再次呼叫【查詢訂單API】查詢實際支付結果;
(3)當交易超時或支付交易失敗,商戶收銀系統必須呼叫【撤銷訂單API】,撤銷此交易。
(4)由於銀行系統異常、使用者餘額不足、不支援使用者卡種等原因使當前支付交易失敗,商戶收銀系統應該把錯誤提示明確展示給收銀員。
(5)根據返回的錯誤碼,判斷是否需要撤銷交易,具體詳見API返回錯誤碼列表
public Map<String, String> microPayWithPos(Map<String, String> reqData) throws Exception { return this.microPayWithPos(reqData, this.config.getHttpConnectTimeoutMs());}/** * 提交刷卡支付,針對軟POS,儘可能做成功 * 內建重試機制,最多60s * @param reqData * @param connectTimeoutMs * @return * @throws Exception */public Map<String, String> microPayWithPos(Map<String, String> reqData, int connectTimeoutMs) throws Exception { int remainingTimeMs = 60*1000; long startTimestampMs = 0; Map<String, String> lastResult = null; Exception lastException = null; while (true) { startTimestampMs = WXPayUtil.getCurrentTimestampMs(); int readTimeoutMs = remainingTimeMs - connectTimeoutMs; if (readTimeoutMs > 1000) { try { lastResult = this.microPay(reqData, connectTimeoutMs, readTimeoutMs); String returnCode = lastResult.get("return_code"); if (returnCode.equals("SUCCESS")) { String resultCode = lastResult.get("result_code"); String errCode = lastResult.get("err_code"); if (resultCode.equals("SUCCESS")) { break; } else { // 看錯誤碼,若支付結果未知,則重試提交刷卡支付 if (errCode.equals("SYSTEMERROR") || errCode.equals("BANKERROR") || errCode.equals("USERPAYING")) { remainingTimeMs = remainingTimeMs - (int)(WXPayUtil.getCurrentTimestampMs() - startTimestampMs); if (remainingTimeMs <= 100) { break; } else { WXPayUtil.getLogger().info("microPayWithPos: try micropay again"); if (remainingTimeMs > 5*1000) { Thread.sleep(5*1000); } else { Thread.sleep(1*1000); } continue; } } else { break; } } } else { break; } } catch (Exception ex) { lastResult = null; lastException = ex; } } else { break; } } if (lastResult == null) { throw lastException; } else { return lastResult; }}
看Demo中的方法,整個方法也沒有查詢訂單,也沒有撤銷訂單的操作,而是不停的重複呼叫microPay,這和上面文件的(9)、(10)條邏輯不一樣
注意:微信的刷卡支付並沒有支付通知介面,只有退款通知介面;支付寶的條碼支付是有支付通知介面的,兩家是不一樣的。四:開發文件在整合之前,一定要熟悉業務,熟悉文件
刷卡文件,開發前請詳細認證的看完該文件。
刷卡支付流程圖 必須看
刷卡文件中有demo,SDK與DEMO下載,需要下載下來,熟悉一下專案結構,以及READEME.md
五:整合步驟1. 引入SDKwxpay-sdk 專案中的src就是要引入的sdk,可以直接將src的所有7個原始檔拖入到自己專案中wxpay-sdk 的READEME.md中說可以通過maven來引入sdk現在有兩種方式,選擇其中一種,究竟選哪一種?
wxpay-sdk的src下面有7個java檔案,通過maven引入可以看到的sdk中就4個java檔案,兩種方式檔案個數不一致,多出來的3個檔案是demo中用到的檔案,並不是原始sdk中的檔案,通過檢視sdk中的原始碼可以看到,這4個檔案其實就是使用httpclient來呼叫微信支付的支付介面,其中很多重要的邏輯並沒有按照官方文件中說的那樣缺少重要的邏輯實現,這個需要自己去完善一些邏輯
開發中發現WXPay這個類和maven中的類並不完全一樣,多了兩個方法microPayWithPos,其中這兩個方法是demo作者自己基於內部方法microPay的一個封裝,完善了部分邏輯, 但是完善的邏輯和官網文件的描述不一致,有重大邏輯問題
檢視demo中的test發現竟然有一個類叫test,命名不清晰,語義太籠統,而且還是小寫字母開頭,不符合Java的基本命名規範,關於java中的測試的命名規則一般類名以Test作為字尾,而該demo以Test作為字首,一般測試類方法的命名以test作為字首,但是demo中的測試並沒有什麼test,一般測試類都要使用測試框架如Junit等,但是demo中的測試並沒有使用測試框架,而是使用main方法來執行的。
對wxpay-sdk的評價:wxpay-sdk只是使用httpclient來呼叫微信支付的介面,只管呼叫微信支付介面,然後解析一下響應,並不處理支付中的業務邏輯,儘管一些和支付密切相關的重要邏輯也不會處理,甚至解密的工具方法都沒有現成的,需要開發者自己去處理。看demo可以知道,寫的不是一般的爛,沒想到微信那麼大的廠竟然sdk寫的這麼懶,demo寫的這麼low,真是丟人,和支付寶的sdk比一個地下一個天上。
罵完了,還是要整合的,這裡我選擇不相信demo的原始檔,使用README.md中的maven來引入sdk,一個是demo寫的太爛,多出來的檔案也不是不要不行的,多出來的方法有重大邏輯缺陷,萬一哪天微信哪天良心發現完善sdk了,更新了maven版本,自己只需要修改一下maven的版本號就行了。
<dependency> <groupId>com.github.wxpay</groupId> <artifactId>wxpay-sdk</artifactId> <version>0.0.3</version></dependency>2. application.yml配置微信支付引數,其中appID、mchID、key、certPath是必須的,notifyUrl是可選的,sandboxKey和useSandbox是自己配置的,便於正式環境和沙箱環境的切換,關於引數的值去微信的開發配置中檢視
useSandbox:true表示使用沙箱環境,如果為false為正式環境sandboxKey: 沙箱環境API祕鑰,需要通過下面的WXPayClient#getSignKey方法獲取pay: wxpay: appID: xxx mchID: xxx key: xxx sandboxKey: xxx certPath: /var/local/cert/apiclient_cert.p12 notifyUrl: http://65ta5j.natappfree.cc/wxpay/refund/notify useSandbox: false3. MyWXPayConfig配置微信引數,其實就是一種Properties類,需要實現WXPayConfig中的方法 注意:demo中的WXPayConfig是一種抽象類abstract class,可以看到maven與demo的不統一
4. WXPayClientWXPayClient 是對WXPay的一個封裝,增加了microPayWithPOS方法,內部呼叫WXPay#microPay,但是sdk中的microPay並沒有處理當微信支付時微信提示使用者輸入密碼,這時sdk直接返回的錯誤,這裡處理的邏輯就是在指定時間內去輪詢支付結果,然後將輪詢的結果返回出去,而不是就直接返回錯誤了,demo專案也有個microPayWithPOS實現,但是它的邏輯是當用戶輸入密碼的情況會輪詢的去下單去呼叫WXPay#microPay,這種做法是不符合微信的官方文件的。另外增加了獲取沙箱環境API祕鑰和解密退換通知的方法。
吐槽一下:感覺像這種解密方法WXPayUtil中竟然沒有,這是必須的不可少的啊,怎麼會沒有呢,這也太爛了吧,這還稱為sdk啊