1.支付頁面代碼,我是用把OPENID存入SESSION中的方式,因此有個wxOauth2Urljavascript
<input type="hidden" id="wxOpenId" value="${wxOpenId}"> <input type="hidden" id="wxOauth2Url" value="${wxOauth2Url}"> <a class="item" id="weixin"> <img src="/static/app/member/images/pay_w.png" class="pay_img" alt="" />
2.支付頁面JS調用html
var wxH5 = isweixin(); if (wxH5) { //alert("當前爲微信訪問"); var appId = ""; var timeStamp = ""; var nonceStr = ""; var prepay = ""; var signType = ""; var paySign = ""; var wxOpenId = $("#wxOpenId").val(); var wxOauth2Url = $("#wxOauth2Url").val(); //如何沒有獲取到OPENID if (wxOpenId == '') { window.location.href = wxOauth2Url; //若沒有OPENID 調用CODE獲取OPENID //wxOauth2Url中有個參數state,我把這個參數設定爲訂單ID以便獲取到OPENID後返回到訂單支付頁 //alert("開始獲取用戶OPENID"); } else { //alert("開始提交wxh5pay.do"); $.ajax({ url: '/appApi/pay/wxh5pay.do', type: 'POST', data: { state: state }, cache: false, async: false, dataType: 'JSON', timeout: 5000, error: function(textStatus) { alert('系統錯誤~'); }, success: function(data) { if (data != null) { alert(data.wxh5Data.prepay); appId = data.wxh5Data.appId; timeStamp = data.wxh5Data.timeStamp; nonceStr = data.wxh5Data.nonceStr; prepay = data.wxh5Data.prepay; signType = data.wxh5Data.signType; paySign = data.wxh5Data.paySign; } } }); // 喚起微信支付 if (appId != '') { pay(); } // 喚起微信支付 function pay() { //alert("pay JS 啓動"); if (typeof WeixinJSBridge == "undefined") { if (document.addEventListener) { document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); } else if (document.attachEvent) { document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } } else { onBridgeReady(); } } // 開始支付 function onBridgeReady() { //alert("onBridgeReady JS 啓動"); WeixinJSBridge.invoke('getBrandWCPayRequest', { "appId": appId, // 公衆號名稱,由商戶傳入 "timeStamp": timeStamp + "", // 時間戳,自1970年以來的秒數 "nonceStr": nonceStr, // 隨機串 "package": "prepay_id=" + prepay, "signType": signType, // 微信簽名方式: "paySign": paySign // 微信簽名 }, function(res) { if (res.err_msg == "get_brand_wcpay_request:ok") { alert("支付成功"); // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功後返回 // ok,但並不保證它絕對可靠。 // 回到用戶訂單列表 window.location.href = "/m/member/dingdan_list.html"; } else if (res.err_msg == "get_brand_wcpay_request:cancel") { alert("支付過程當中用戶取消"); } else { // 支付失敗 alert(res.err_msg) } }); } } } /* 判斷是否是微信瀏覽器 */ function isweixin() { var ua = navigator.userAgent.toLowerCase(); if (ua.match(/MicroMessenger/i) == "micromessenger") { return true; } else { return false; } }
3.訂單支付頁控制器,這裏體驗可能不要很好,沒有獲取到OPENID時可能要點2次,後期獲取SESSION能夠考慮在其餘地方作。前端
// 判斷是否爲微信瀏覽器 Boolean validation = false; String ua = request.getHeader("user-agent").toLowerCase(); if (ua.indexOf("micromessenger") > 0) {// 是微信瀏覽器 validation = true; // 讀取用戶的OPENID到SESSION中 String wxOpenId = (String) userHelper.getOpenId(); request.setAttribute("wxOpenId", wxOpenId); if (wxOpenId == null) { String redirect_uri = WxPayUtil.urlEnodeUTF8(WxPayConfig.REDIRECT_URI); // 從新寫稿到SESSION中 String wxOauth2Url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WxPayConfig.APPID + "&redirect_uri=" + redirect_uri + "&response_type=code&scope=snsapi_base&state="+number+"#wechat_redirect"; //redirect_uri 此參數要結合公衆號平臺設置,number這裏是訂單號 request.setAttribute("wxOauth2Url", wxOauth2Url); System.out.println("wxOauth2Url:"+wxOauth2Url); } } request.setAttribute("validation", validation);
4.調用wxOauth2Url 獲取CODE的返回接收URL,在這裏作了寫入OPENID到SEEION和從新返回到支付頁面。java
package com.szqws.nanhaibiz.controller.app; import javax.annotation.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.szqws.nanhaibiz.bean.base.UserHelper; import com.szqws.nanhaibiz.constants.WxPayConfig; import com.szqws.nanhaibiz.controller.system.BaseController; import com.szqws.nanhaibiz.util.system.WxPayUtil; @Controller @RequestMapping(value = "/mp") public class AppWXCodeController extends BaseController { private static final Logger logger = LoggerFactory.getLogger(AppWXCodeController.class); @Resource UserHelper userHelper; /** * 獲取OPENID 且寫入SESSION * * @param code * @return */ @RequestMapping(value = "/get_code.do", method = { RequestMethod.GET }) public String WXPayCode(String code, String state) { try { String openid = WxPayUtil.getOpenId(WxPayConfig.APPID, WxPayConfig.APPSECRET, code); userHelper.putOpenId(openid); System.out.println("openid:" + openid); } catch (Exception e) { logger.debug("獲取OPENID--異常", e); } return "redirect:/m/member/pay/detail_" + state + ".html"; } }
5.完成統一下訂單API,node
// 統一下單支付(Wap支付) public static String appPayH5(WxPaySendData data, String key) { String returnXml = null; try { // 生成sign簽名 SortedMap<String, Object> parameters = new TreeMap<String, Object>(); parameters.put("appid", data.getAppid()); parameters.put("attach", data.getAttach()); parameters.put("body", data.getBody()); parameters.put("mch_id", data.getMch_id()); parameters.put("nonce_str", data.getNonce_str()); parameters.put("notify_url", data.getNotify_url()); parameters.put("out_trade_no", data.getOut_trade_no()); parameters.put("fee_type", data.getFee_type()); parameters.put("total_fee", data.getTotal_fee()); parameters.put("trade_type", data.getTrade_type()); parameters.put("spbill_create_ip", data.getSpbill_create_ip()); parameters.put("device_info", data.getDevice_info()); parameters.put("openid", data.getOpenid()); //這裏是個坑必定要OPENID,掃描支付是不要的 data.setSign(createSign(parameters, key)); XStream xs = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_"))); xs.alias("xml", WxPaySendData.class); String xml = xs.toXML(data); logger.info("統一下單xml爲:\n" + xml); returnXml = HttpClientHelper.sendPostRequestXml("https://api.mch.weixin.qq.com/pay/unifiedorder", xml); logger.info("返回結果:" + returnXml); } catch (Exception e) { logger.debug("微信支付--統一下單--異常:{}", e); } return returnXml; }
正確返回數據應該如圖web
6.WXH5Pay APIajax
/** * 支付執行 * * @param state * @return */ @RequestMapping(value = "/wxh5pay.do", method = { RequestMethod.GET, RequestMethod.POST }) @ResponseBody public Object WXH5pay(String state) { String msg = ""; try { // 分離出訂單號 JSONObject json = JSON.parseObject(state); String rechargeId = json.getString("order_number"); double payPrice = 0.00; String number = null; // 獲取用戶和訂單 User iUser = (User) userHelper.get(); if (iUser == null) { logger.debug("未登錄用戶!"); return null; } Order recharge = orderService.getByNumber(rechargeId); if (recharge == null) { return null; } else { payPrice = recharge.getAmount(); // payPrice = 0.01; number = MyUIDD.getAtomicCounter(); // 更改訂單狀態 Order tmpOrder = new Order(); tmpOrder.setStatus("NOTPAY"); tmpOrder.setId(recharge.getId()); tmpOrder.setNumber(number); orderService.update(tmpOrder); // 第一步提交微信統一下單接口 // 1.準備數據 WxPaySendData data = new WxPaySendData(); String nonceStr = new WxPayUtil().getNonceStr(); data.setAppid(WxPayConfig.APPID); data.setAttach("訂單支付"); data.setBody("訂單金額" + payPrice + "元"); data.setMch_id(WxPayConfig.MCHID); data.setNonce_str(nonceStr); data.setNotify_url(WxPayConfig.NOTIFY_URL); data.setOut_trade_no(number); data.setTotal_fee((int) (payPrice * 100)); data.setFee_type("CNY"); data.setSpbill_create_ip("113.110.254.178"); data.setTrade_type("JSAPI"); data.setDevice_info("WXH5"); data.setOpenid((String)userHelper.getOpenId()); // 2.提交請求 msg = new WxPayUtil().appPayH5(data, WxPayConfig.KEY); // 3.分析返回數據 WxPayReturnData reData = new WxPayReturnData(); XStream xs1 = new XStream(new DomDriver()); xs1.alias("xml", WxPayReturnData.class); reData = (WxPayReturnData) xs1.fromXML(msg); // 第二步 準備JSP數據 String timestamp = String.valueOf(new Date().getTime() / 1000); Map<String, Object> wxh5Data = new HashMap<String, Object>(); wxh5Data.put("appId", WxPayConfig.APPID); wxh5Data.put("timeStamp", timestamp); wxh5Data.put("signType", "MD5"); wxh5Data.put("nonceStr", nonceStr); wxh5Data.put("package", "prepay_id="+reData.getPrepay_id()); //這個prepay_id必須是統一下單返回的又是一大坑,不少人可能用訂單號來生成。 // 參數開始生成簽名 SortedMap<String, Object> parameters = new TreeMap<String, Object>(); parameters.put("appId", WxPayConfig.APPID); parameters.put("timeStamp", timestamp); parameters.put("signType", "MD5"); parameters.put("nonceStr", nonceStr); parameters.put("package", "prepay_id="+reData.getPrepay_id()); String paySign = WxPayUtil.createSign(parameters, WxPayConfig.KEY); // 簽名結束 wxh5Data.put("paySign", paySign); wxh5Data.put("prepay", reData.getPrepay_id()); // 返回數據 Map<String, Object> map = new HashMap<String, Object>(); map.put("wxh5Data", wxh5Data); map.put("reData", reData); map.put("state", state); map.put("orderId", number); return map; } } catch (Exception e) { logger.debug("付款操做異常:AppPayAPI.WXH5pay()", e); return null; } }
注意點:spring
// 獲取code重定向路徑
public static final String REDIRECT_URI = "http://www.XXXX.com/mp/get_code.do";json
此項值須要在公衆號平臺客戶設置 網頁http://www.XXXX.com/mp/api
這裏有個問題 MP_verify_moWSco2lnJUaf56s.txt把這個文件放在mp目錄下後,SpringMVC URL就不生效了,只能這裏提交後把MP目錄名稱改掉。