微信支付之微信H5支付
這裡講的是 微信h5支付, 是微信以外的手機瀏覽器呼叫微信h5支付
h5支付:
H5支付是指商戶在微信客戶端外的移動端網頁展示商品或服務,使用者在前述頁面確認使用微信支付時,商戶發起本服務呼起微信客戶端進行支付。
主要用於觸屏版的手機瀏覽器請求微信支付的場景。可以方便的從外部瀏覽器喚起微信支付。
一、基本資訊和配置
在基本配置的之初請參考【公眾號支付】的前面部分,
不同的是H5支付需要申請通過才能使用,申請的地方是商戶平臺 產品中心>我的產品,申請通過如下:
在開發配置裡配上H5支付的域名:
基本資訊配置到此,下面開始敲程式碼
二、開發支付
建議參考微信H5開發文件
(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1
微信H5支付開發也是兩步走:
1. 通過統一下單介面發起請求,獲得mweb_url(支付跳轉url),這個是你接下來進入微信提供的支付頁面的地址;
2. 完成支付流程後,實現後續邏輯。
一)發起統一下單介面
此處請參考【微信支付之公眾號支付】部落格裡的流程,這裡有兩個地方需要注意:
1)本次請求中的引數:交易型別(trade_type)的值是“MWEB”
2)本次返回結果需要得到的是“mweb_url”的值
參考方法(由於上一篇公眾號支付寫的比較詳細,只列出上文沒有細說的方法,值得注意的是這個UnifiedOrder物件裡是沒有openid這個欄位,api說明裡也提到過公眾號支付必填openid,H5支付是不用填寫的,主要是沒有,O(∩_∩)O哈哈~)
/** * * @Description: 微信支付 -統一訂單型別獲得mweb_url * @author tianpengw * @param uo * @return */ public static String getUnifiedOrderMWebUrl(UnifiedOrder uo){ String sign = createUnifiedOrderSign(uo); uo.setSign(sign); String xml = XMLBeanUtils.objectToXMLStr(uo); log.info("H5支付統一訂單請求引數:"+xml); String res = HttpHelper.httpsRequest(unifiedUrl,"POST",xml); log.info("H5支付統一訂單返回結果:"+res); Map<String, String> responseMap = XMLBeanUtils.readStringXmlOut(res); return responseMap.get("mweb_url"); }
相關的工具類不在贅述,參考上文公眾號支付部落格
通過此方法得到MWebUrl結果後,送到前端,通過window.location.href跳轉即可完成H5支付流程,值得注意的是,H5支付掃尾比公眾號支付要麻煩:
【官方文件】 一、回撥頁面 正常流程使用者支付完成後會返回至發起支付的頁面,如需返回至指定頁面,則可以在MWEB_URL後拼接上redirect_url引數,來指定回撥頁面。 如,您希望使用者支付完成後跳轉至https://www.wechatpay.com.cn,則可以做如下處理: 假設您通過統一下單介面獲到的MWEB_URL= https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096 則拼接後的地址為MWEB_URL= https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096&redirect_url=https%3A%2F%2Fwww.wechatpay.com.cn 注意: 1.需對redirect_url進行urlencode處理 2.由於設定redirect_url後,回跳指定頁面的操作可能發生在:1,微信支付中間頁調起微信收銀臺後超過5秒 2,使用者點選“取消支付“或支付完成後點“完成”按鈕。因此無法保證頁面回跳時,支付流程已結束,所以商戶設定的redirect_url地址不能自動執行查單操作,應讓使用者去點選按鈕觸發查單操作。回跳頁面展示效果可參考下圖
官方文件提到兩個關鍵地方,第一點是使用者可通過拼接redirect_url來指引微信處理後的跳轉動向,這個地址需要urlencode處理,第二點是不管什麼結果最終結束後微信都會呼叫這個redirect_url地址,我這裡處理的方式是將重定向地址設定到訂單提交頁增加蒙層和對話方塊,讓使用者選擇結果,然後在過程中查詢訂單結果然後根據結果跳轉不同的頁面
前端部分程式碼如下:
//對瀏覽器的UserAgent進行正則匹配,不含有微信獨有標識的則為其他瀏覽器
var useragent = navigator.userAgent;
if (useragent.match(/MicroMessenger/i) == 'MicroMessenger') {
//此處是公眾號支付的邏輯不在列舉
}else{
$.ajax({
type : "POST",
url : "gotoJSPayH5.json",
dataType : "json",
contentType : 'application/json;charset=UTF-8',
data : JSON.stringify({orderId:orderId}),
success : function(res){
if("success" == res.errCode){
var url = res.data;
window.location.href = url;
}else{
jQAlert("支付操作異常,請稍後再試!");
}
},
error : function(XMLHttpRequest, textStatus,
errorThrown) {
jQAlert('支付異常,請聯絡客服!');
},
complete : function(XMLHttpRequest, textStatus) {
}
});
}
最後貼出服務端方法:
/** * H5支付 * @param req * @param ids * @param addrId * @param orderRemark * @return * @throws UnsupportedEncodingException */ @RequestMapping(value = "gotoJSPayH5.json") @ResponseBody public Response gotoJSPayH5(HttpServletRequest req, @RequestBody PhoneBean pb) throws UnsupportedEncodingException{ log.info("進入H5支付:" + pb.getOrderId()); Response resp = new Response(); if(!MyStringUtils.isEmpty(pb.getOrderId())){ Order order = orderService.findOrderForPayH5(pb.getOrderId()); if(null != order){ Date endDate = MyDateUtil.parseDate(MyDateUtil.get30Minute(order.getOrderCreateTime()),MyDateUtil.DATETIME); if(MyConstants.order_need_pay == order.getOrderStatus() && new Date().before(endDate)){ UnifiedOrder uo = new UnifiedOrder(); uo.setSpbill_create_ip(HttpHelper.getClientIP(req)); uo.setTrade_type(WechatPayUtil.tradeTypeH5); uo.setBody(MyConstants.project_title+"-商品支付"); uo.setOut_trade_no(order.getOrderId()); //測試環境 if("test".equals(getDicValueByAlias("environment"))){ uo.setTotal_fee(1);//測試使用單位分 }else{ uo.setTotal_fee(order.getOrderTotalPrice().multiply(new BigDecimal(100)).intValue());//單位分 } String mwebUrl = WechatPayUtil.getUnifiedOrderMWebUrl(uo); if(!MyStringUtils.isEmpty(mwebUrl)){ //增加支付記錄 WechatPay wp = new WechatPay(); wp.setPayId(RandomUtils.g()); wp.setPayOrderId(order.getOrderId()); //測試環境 if("test".equals(getDicValueByAlias("environment"))){ wp.setPayAmount(new BigDecimal(0.01));//測試使用 }else{ wp.setPayAmount(order.getOrderTotalPrice()); } wp.setPayReturnUrl(mwebUrl); wp.setPayType(WechatPayUtil.tradeTypeH5); wp.setPayStatus(MyConstants.pay_status_ing); wp.setPayNonceStr(uo.getNonce_str()); wpService.insertWechatPay(wp); //拼接實現地址:跳轉支付完成頁面 resp.setData(mwebUrl+ "&redirect_url=" +URLEncoder.encode(MyConstants.product_address + "/phone/payComplete.do?orderId=" + order.getOrderId() + "&tradeType=" + WechatPayUtil.tradeTypeH5,"utf-8")); resp.setErrCode(ErrorCode.err_code_success); } }else if(MyConstants.order_has_pay == order.getOrderStatus()){ resp.setErrCode(ErrorCode.err_code_fail); resp.setErrMsg("訂單已完成支付,請勿重複支付!"); }else{ resp.setErrCode(ErrorCode.err_code_fail); resp.setErrMsg("訂單已失效,請重新選購商品!"); } } }else{ resp.setErrCode(ErrorCode.err_code_fail); resp.setErrMsg("訂單無效,支付異常!"); } return resp; }
這裡有一些常用的方法就不再貼出了,簡單描述下:
MyStringUtils:字串處理工具類isEmpty() 非空判斷方法;
MyDateUtil:日期解析工具類 parseDate()字串轉日期方法;
至此H5支付的主要流程就結束了,我的程式碼是兩種支付的結合,有單獨需求的可以分開,請繼續關注【微信開發之自定義分享】,下文中我將把我自己總結jar包和原始碼都分享出來,供大家參考學習