(此處只講pay)javascript
微信商戶平臺,公衆號的後臺管理工具,包含公衆號的商戶信息,公衆號支付,掃碼支付,刷卡支付css
1.商戶信息包含商戶號,和此公衆平臺關聯的商戶號,需登陸商戶平臺設置商戶祕鑰keyhtml
2.公衆號支付包含支付受權目錄,測試白名單(做用於微信Web開發者工具測試),掃碼支付回調URL,刷卡支付java
3.公衆平臺基本配置中查看本公衆號的AppId,支付時使用 jquery
微信商戶平臺,微信支付的後臺管理工具,包含流水,訂單,數據中心,帳戶中心web
1.每一個公衆號對應一個商戶平臺,商戶平臺有本身祕鑰key,支付中使用json
2.APP支付的微信開放平臺也對應一個商戶平臺,APP支付的key需使用此商戶平臺的微信
微信開放平臺,移動端使用的平臺,APP支付需建立此平臺,建立移動應用app
1.建立此平臺,審覈移動應用,經過後會發郵件,其中包含商戶帳戶信息,APP支付需使用微信公衆平臺
2.開放平臺使用本身的AppId,和對應商戶的key,切記
3.App支付和公衆號的支付使用的AppId和key都不相同
JsApi支付
1.調用支付接口,判斷訂單號是否正確
/** * 獲取微信支付第三方受權URL * * @param orderNo * 訂單編號,受權以前要拿到本身服務端的訂單號 * @param request * @param response * @throws IOException */ @RequestMapping(value = "/generateAuthurlWithOuttradeno") public String generateAuthurlWithOuttradeno( String orderNo, ModelMap modelMap, HttpServletRequest request, HttpServletResponse response) throws IOException { // 重定向URL,用戶受權後會自動重定向到這裏,就能夠獲取用戶受權信息 String basePath = Constant.domain+"wechat/wechatPay/doWeChatAuth.do"; if (orderNo != null && orderNo.length() > 0) { // 根據重定向URL構建受權URL,具體方法見下面的方法:buildAuthUrl String authUrl = WechatUrl.buildAuthUrl(Constant.APPID, basePath, orderNo); modelMap.put("stateCode", 200); modelMap.put("msg", "操做成功"); modelMap.put("data", authUrl); } else { modelMap.put("stateCode", 500); modelMap.put("msg","錯誤的訂單號:" + orderNo ); modelMap.put("data", ""); } logger.info("進來了----------"+modelMap.get("data")); return "gcar/weChat/weChatUserInfo"; } /** * AUTH2.0 網頁受權處理,這個不須要本身調用,微信會重定向到這裏 * * @param request * @param response * @throws IOException */ @RequestMapping("doWeChatAuth") public void doWeiChatAuth( @RequestParam("state") String outTradeNo,// 訂單編號,來自受權url ModelAndView modelAndView, HttpServletRequest request, HttpServletResponse response) throws IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); // 獲取受權後的code String code = request.getParameter("code"); logger.info("微信回調,獲取用戶受權code"+code); if ("authdeny".equals(code) == false) { // 獲取用戶受權後的信息 String authMsg = WechatUrl.fetchAuthReturnMsg(Constant.APPID, Constant.APPSECRET, code, "POST"); logger.info("微信回調,獲取用戶受權信息"+authMsg); // JSONObject爲阿里的fastjson提供 AuthJsonObject authJsonObject = JSONObject.parseObject(authMsg, AuthJsonObject.class); // 刷新AccessToken,默認獲取的有效期過短 String refreshTokenobject = WechatUrl.refreshAccessToken( Constant.APPID, authJsonObject.getRefresh_token(), "POST"); AuthJsonObject refreshObject = JSONObject.parseObject( refreshTokenobject, AuthJsonObject.class); // 根據AccessToken和用戶的openId獲取用戶信息 String userInfo = WechatUrl.getUserinfo( refreshObject.getAccess_token(), refreshObject.getOpenid(), "POST"); WeChatUserInfo weChatUserInfo = JSONObject.parseObject(userInfo, WeChatUserInfo.class); if (weChatUserInfo != null) { // 請求轉發處處理微信統一下單的接口,獲取JS SDK調用的配置參數和支付用的參數 response.sendRedirect(Constant.domain+"wechat/wechatPay/redirectToPayDetail.do?no=" + outTradeNo + "¶m_0=" + weChatUserInfo.getOpenid()); } } else { response.getOutputStream().write(new String("用戶拒絕受權").getBytes()); } } /** * 微信公衆號 支付 * 獲取微信JS SDK初始化配置參數,調用微信統一下單接口獲取微信prepay_id,獲取JS支付用的參數 * * @param op_d * 用戶openId * @param no * 訂單號 * @param modelAndView * @param request * @param response * @return * @throws IOException * @throws IllegalAccessException */ @RequestMapping("redirectToPayDetail") public String redirectToPayDetail( @RequestParam("param_0") String op_d, @RequestParam("no") String no, ModelMap modelMap, HttpServletRequest request, HttpServletResponse response) throws IOException, IllegalAccessException { logger.info("進去系通通一下單接口"); request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); // JS SDK簽名URL,是當前頁面的location,這裏就是第三步重定向的URL,用來參與計算JS SDK初始化的簽名 String signUrl = Constant.domain+"wechat/wechatPay/redirectToPayDetail.do?no=" + no + "¶m_0=" + op_d; // 簽名隨機字符串,因爲微信裏面的坑太多,因此咱們全部的簽名字符串和時間戳最好公用,否則真的會蒙圈的 String weiPaySignStr = WechatUrl.getRandomStringByLength(20); // 簽名用時間戳 單位秒 String timestamp = (System.currentTimeMillis()/1000) + ""; // 獲取JS API 初始化配置參數 Map<String, String> initConfigParams = WechatUrl.FetchConfigParams(signUrl, weiPaySignStr, timestamp); // 根據訂單號驗證訂單是否存在,根據本身業務寫 查詢訂單 //Userorderlist order = userorderlistMapper.selectByOrderNo(no); // if (order != null && order.getOrderno().length() > 0) {// 訂單存在的時候就進行支付前一系列準備工做 // 統一下單簽名參數哈希表 // 統一下單的body,就是商品詳情,要是傳中文記得newString(body.getBytes("ISO8859-1")),否則JS簽名失敗, // 可是即便這樣,支付成功後微信給你發的支付憑證中商品詳情仍是亂碼顯示,因此傳英文吧,這坑懶得去填 String body = "jiche WeChat Order Pay"; Map<String, Object> prepaySignParam = new HashMap<String, Object>(); prepaySignParam.put("appid", Constant.APPID); prepaySignParam.put("mch_id", Constant.MCHID); prepaySignParam.put("body", body); prepaySignParam.put("nonce_str", weiPaySignStr); // 微信異步通知地址,在這裏處理後續訂單邏輯 prepaySignParam.put("notify_url", Constant.domain+"wechat/wechatPay/weipayCallBack.do"); prepaySignParam.put("out_trade_no", no); prepaySignParam.put("spbill_create_ip", request.getRemoteAddr());// 用戶IP地址 // 單位分 //prepaySignParam.put("total_fee",new BigDecimal(Double.parseDouble(order.getTotalprice()) * 100).intValue()); prepaySignParam.put("total_fee",1); prepaySignParam.put("trade_type", "JSAPI"); prepaySignParam.put("openid", op_d); // 構造微信預支付訂單 WeiChatPreOrder weiChatPreOrder = new WeiChatPreOrder(); weiChatPreOrder.setAppid(Constant.APPID); weiChatPreOrder.setMch_id(Constant.MCHID); weiChatPreOrder.setBody(body); weiChatPreOrder.setNonce_str(weiPaySignStr); weiChatPreOrder .setNotify_url(Constant.domain+"wechat/wechatPay/weipayCallBack.do"); weiChatPreOrder.setOut_trade_no(no);// 系統訂單號 weiChatPreOrder.setSpbill_create_ip(request.getRemoteAddr()); //weiChatPreOrder.setTotal_fee(new BigDecimal(Double // .parseDouble(order.getTotalprice()) * 100).intValue());// 交易金額 weiChatPreOrder.setTotal_fee(1); weiChatPreOrder.setTrade_type("JSAPI"); weiChatPreOrder.setOpenid(op_d); // 計算統一下單簽名參數計算預支付訂單的MD5簽名 weiChatPreOrder.setSign(Signature.getSign(prepaySignParam)); // 生成XML訂單 String xmlOrder = weiChatPreOrder.toXml(); logger.info("統一下單接口的xml"+xmlOrder); // 經過微信下單接口獲取prepay_id String prepayId = WechatUrl.getPrepayId(xmlOrder); logger.info("統一下單接口的prepayId"+prepayId); // JS支付參數 SortedMap<String, String> paySignparams = new TreeMap<String, String>(); //切記此處的參數必定要和微信文檔同樣,最好粘貼過來,別本身寫 paySignparams.put("appId", Constant.APPID); paySignparams.put("timeStamp", timestamp); paySignparams.put("nonceStr", weiPaySignStr); paySignparams.put("signType", "MD5"); // 計算JS SDK支付調用簽名字符串【appId, nonceStr,package,signType,timeStamp】 StringBuffer signBuff = new StringBuffer(); signBuff.append("appId=").append(Constant.APPID + "&nonceStr=") .append(weiPaySignStr + "&package=prepay_id=") .append(prepayId + "&signType=MD5&timeStamp=") .append(timestamp + "&key=").append(Constant.WECHAT_KEY); // 計算MD5簽名 String paySign = MD5Util.MD5Encode(signBuff.toString()); paySignparams.put("paySign", paySign); modelMap.put("configParam", initConfigParams); modelMap.put("payParams", paySignparams); modelMap.put("msg", "ok"); modelMap.put("payId", prepayId); //modelMap.put("order", order);// 訂單信息 //request.setAttribute("payId", prepayId); /*} else { modelAndView.addObject("msg", "找不到訂單/無效的訂單編號"); }*/ // 返回支付頁面 return "gcar/weChat/weChatPay"; } /** * 微信支付異步通知處理接口 * @param request * @param response * @return * @throws IOException */ @RequestMapping("weipayCallBack") public void weipayCallBack(HttpServletRequest request,HttpServletResponse response) throws IOException{ logger.info("**************************微信支付異步回調通知開始***********************"); //System.out.println("收到微信異步通知。。。"); InputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } outSteam.close(); inStream.close(); String resultStr = new String(outSteam.toByteArray(),"utf-8"); Map<String, Object> resultMap = new HashMap<String, Object>(); try { resultMap = XMLParser.getMapFromXML(resultStr); String out_trade_no = (String) resultMap.get("out_trade_no"); String return_code = (String) resultMap.get("return_code"); String total_fee = (String) resultMap.get("total_fee"); String bank = (String) resultMap.get("bank_type"); String transaction_id = (String) resultMap.get("transaction_id"); Integer fee = Integer.decode(total_fee)/100; //簽名驗證 boolean valid = Signature.checkIsSignValidFromResponseString(resultStr); ChargeOrderLog chargeOrderLog = chargeOrderLogService.findChargeOrderLogByOrderNo(out_trade_no); ChargeOrderLog log = new ChargeOrderLog(); if(chargeOrderLog == null){ this.logger.info(out_trade_no + ",訂單不存在....."); }else{ //日誌 log.setOrderNo(chargeOrderLog.getOrderNo()); log.setBank(fee.toString()); log.setWechatJson(resultMap.toString()); log.setLogType(0); log.setCompletionTime(new Date()); log.setBank(bank); log.setPayWaterNo(transaction_id); } if(return_code.equals("SUCCESS") && valid){ try { chargeOrderLogService.updateChargeOrderLogComplete(log); } catch (Exception e) { logger.info("************微信支付異步回調查詢訂單異常"+out_trade_no); } //處理訂單後續業務 logger.info("************微信支付異步回調通知支付成功"+resultMap); }else{ try { chargeOrderLogService.updateChargeOrderLogFail(log); } catch (Exception e) { logger.info("************微信支付異步回調插入日誌異常"+e.getMessage()); } logger.info("************微信支付異步回調通知支付失敗"+resultMap); } } catch (ParserConfigurationException e) { logger.info("************微信支付異步回調異常"+e.getMessage()+"微信返回"+resultStr); e.printStackTrace(); } catch (SAXException e) { logger.info("************微信支付異步回調異常"+e.getMessage()+"微信返回"+resultStr); e.printStackTrace(); } //通知微信.異步確認成功.必寫.否則會一直通知後臺.八次以後就認爲交易失敗了.[必定別手賤傳return_msg回去,他們×××會繼續回調的] String success = "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>"; response.getOutputStream().write(new String(success).getBytes()); logger.info("**************************微信支付異步回調通知結束***********************"); } //jsp頁面,發起jsAPI <%@ page language="java" pageEncoding="UTF-8"%> <%@ include file="/WEB-INF/common/init-taglib.jsp"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=320, user-scalable=0, initial-scale=1,maximum-scale=1"> <meta content="yes" name="apple-mobile-web-app-capable"/> <meta content="yes" name="apple-touch-fullscreen"/> <meta content="telephone=no" name="format-detection"/> <meta content="black" name="apple-mobile-web-app-status-bar-style"> <title>微信支付訂單詳情確認頁面</title> <link href="${ctx }/static/gcar_html/css/service.css" rel="stylesheet" type="text/css" /> <link href="${ctx}/static/gcar_html/css/css.css" rel="stylesheet"> <link href="${ctx}/static/gcar_html/css/popup.css" rel="stylesheet"> <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script> <script src="${ctx}/static/gcar_html/js/popup.js"></script> <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> </head> <body> <div class="body"> <div class="head"> <%-- <div class="logo"><img src="<%=basePath%>jsp/weixin/img/logo1.png"/></div> --%> <div><span>極車公社科技有限公司</span></div> </div> <c:if test="${msg eq 'ok' }"> <script type="text/JavaScript"> console.log('${payParams.appId}'); console.log('${configParam.timestamp}'); console.log('${configParam.signStr}'); console.log('${configParam.sign}'); console.log('${payParams.timeStamp}'); console.log('${payParams.nonceStr}'); console.log('${payParams.signType}'); console.log('${payParams.paySign}'); console.log('${payId}'); function payWeixin(){ window.wx.config({ debug:false,//關閉了JS調試,開發階段可爲true appId: '${payParams.appId}', timestamp: '${configParam.timestamp}', nonceStr: '${configParam.signStr}', signature: '${configParam.sign}', jsApiList: ['checkJsApi', 'chooseWXPay'] }); wx.ready(function() { wx.checkJsApi({ jsApiList: ['chooseWXPay'], success: function(res) { console.log(JSON.stringify(res)); } }); wx.chooseWXPay({ timestamp: '${payParams.timeStamp}', nonceStr: '${payParams.nonceStr}', package: "prepay_id=${payId}", signType: '${payParams.signType}', paySign: '${payParams.paySign}', success: function (res) { // 支付成功後的回調函數 alert("支付成功");//後續動做本身定 console.log('支付成功'); } }); }) } </script> <div class="oderDetail"> <!-- 支付訂單詳情顯示,本身弄--> <h1>我要支付</h1> </div> <div class="ok"> <button onClick="payWeixin()" class="button button-raised button-caution" type="button">肯定支付</button> </div> </c:if> <c:if test="${msg ne 'ok' }"> <div style="margin-top: 25% auto;"> <h3> ${msg } </h3> </div> </c:if> </div> </body> </html>
App支付就簡單多了,只須要統一下單接口,傳給APP端,發起調用就ok
/** * 構造微信統一下單接口 返回數據 * @Description:TODO * @author:JIAZHIPENG * @time:2016-10-19 下午7:17:19 * @return String */ private SortedMap<String, Object> makeWechatAppPay(ChargeOrder chargeOrder,HttpServletRequest request){ logger.info("進去系通通一下單接口"); SortedMap<String, Object> paySignparams = new TreeMap<String, Object>(); SortedMap<String, Object> newPaySignparams = new TreeMap<String, Object>(); try { // 簽名隨機字符串,因爲微信裏面的坑太多,因此咱們全部的簽名字符串和時間戳最好公用,否則真的會蒙圈的 String weiPaySignStr = WechatUrl.getRandomStringByLength(20); // 簽名用時間戳 秒 String timestamp = (System.currentTimeMillis()/1000) + ""; // 統一下單簽名參數哈希表 // 統一下單的body,就是商品詳情,要是傳中文記得newString(body.getBytes("ISO8859-1")),否則JS簽名失敗, // 可是即便這樣,支付成功後微信給你發的支付憑證中商品詳情仍是亂碼顯示,因此傳英文吧,這坑懶得去填 String body = "jiche WeChat Order Pay"; Map<String, Object> prepaySignParam = new HashMap<String, Object>(); prepaySignParam.put("appid", Constant.APPID); prepaySignParam.put("mch_id", Constant.MCHID); prepaySignParam.put("body", body); prepaySignParam.put("nonce_str", weiPaySignStr); // 微信異步通知地址,在這裏處理後續訂單邏輯 prepaySignParam.put("notify_url", Constant.domain+"wechat/wechatPay/weipayCallBack.do"); prepaySignParam.put("out_trade_no", chargeOrder.getChargeOrderNo()); //獲取系統訂單編號 prepaySignParam.put("spbill_create_ip", request.getRemoteAddr());// 用戶IP地址 //測試先改成0.01 // 單位分 prepaySignParam.put("total_fee",1); //正式 //prepaySignParam.put("total_fee",chargeOrder.getTotal().multiply(new BigDecimal(100)).intValue()); prepaySignParam.put("trade_type", "APP"); // 構造微信預支付訂單 WeiChatPreAppOrder weiChatPreOrder = new WeiChatPreAppOrder(); weiChatPreOrder.setAppid(Constant.APPID); weiChatPreOrder.setMch_id(Constant.MCHID); weiChatPreOrder.setBody(body); weiChatPreOrder.setNonce_str(weiPaySignStr); weiChatPreOrder .setNotify_url(Constant.domain+"wechat/wechatPay/weipayCallBack.do"); weiChatPreOrder.setOut_trade_no(chargeOrder.getChargeOrderNo());// 系統訂單號 weiChatPreOrder.setSpbill_create_ip(request.getRemoteAddr()); //測試先改成0.01 weiChatPreOrder.setTotal_fee(1); //正式 //weiChatPreOrder.setTotal_fee(chargeOrder.getTotal().multiply(new BigDecimal(100)).intValue()); weiChatPreOrder.setTrade_type("APP"); // 計算統一下單簽名參數計算預支付訂單的MD5簽名 logger.info("微信的key"+Constant.WECHAT_KEY); weiChatPreOrder.setSign(Signature.getSign(prepaySignParam)); // 生成XML訂單 String xmlOrder = weiChatPreOrder.toXml(); logger.info("統一下單接口的xml"+xmlOrder); // 經過微信下單接口獲取prepay_id String prepayId = WechatUrl.getPrepayId(xmlOrder); logger.info("統一下單接口的prepayId"+prepayId); // 支付參數 切記全小寫,對照APP支付微信文檔 粘貼過來 paySignparams.put("appid", Constant.APPID); paySignparams.put("partnerid", Constant.MCHID); paySignparams.put("prepayid", prepayId); paySignparams.put("package", "Sign=WXPay"); paySignparams.put("noncestr", weiPaySignStr); paySignparams.put("timestamp", timestamp); logger.info("傳給app的參數 appId"+Constant.APPID+"partnerId"+Constant.MCHID +"prepayId"+prepayId+"noncestr"+weiPaySignStr+"timeStamp"+timestamp+"key"+Constant.WECHAT_KEY); // 計算MD5簽名 APP發起支付參與 [參與簽名的字段名爲appId,partnerId,prepayId,nonceStr,timeStamp,package] String paySign = Signature.getSign(paySignparams); logger.info(paySign); //String paySign = MD5Util.MD5Encode(signBuff.toString()); paySignparams.put("sign", paySign); newPaySignparams.put("appId", Constant.APPID); newPaySignparams.put("partnerId", Constant.MCHID); newPaySignparams.put("prepayId", prepayId); newPaySignparams.put("packageValue", "Sign=WXPay"); newPaySignparams.put("nonceStr", weiPaySignStr); newPaySignparams.put("timeStamp", timestamp); newPaySignparams.put("sign", paySign); } catch (Exception e) { logger.info("app獲取支付所需參數錯誤"+e.getMessage()); } // 返回支付頁面 return newPaySignparams; }
App回調支付和JsApi的同樣,不作詮釋
後面附件會上傳支付須要的工具類,須要的小夥伴能夠下載,記得好評!
author:賈小仙
time:2016/10/19