weChatPay心路历程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了weChatPay心路历程相关的知识,希望对你有一定的参考价值。
微信公众平台
(此处只讲pay)
微信商户平台,公众号的后台管理工具,包含公众号的商户信息,公众号支付,扫码支付,刷卡支付
1.商户信息包含商户号,和此公众平台关联的商户号,需登录商户平台设置商户秘钥key
2.公众号支付包含支付授权目录,测试白名单(作用于微信Web开发者工具测试),扫码支付回调URL,刷卡支付
3.公众平台基本配置中查看本公众号的AppId,支付时使用
微信商户平台
微信商户平台,微信支付的后台管理工具,包含流水,订单,数据中心,账户中心
1.每个公众号对应一个商户平台,商户平台有自己秘钥key,支付中使用
2.APP支付的微信开放平台也对应一个商户平台,APP支付的key需使用此商户平台的
微信开放平台
微信开放平台,移动端使用的平台,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=\‘#\‘" /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支付就简单多了,只需要统一下单接口,传给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
本文出自 “贾小仙” 博客,请务必保留此出处http://hackerxian.blog.51cto.com/9240575/1863572
以上是关于weChatPay心路历程的主要内容,如果未能解决你的问题,请参考以下文章