前言:最近要完成一个关于微信h5支付的需求,因此就花了一段时间来研究微信关于微信关于h5方面的相关文档,官方提供的文档内容至关复杂,并且相对内容比较分散。并且在整个支付流程中的问题也是情况百出,不少东西都没有给出详细的说明,我是在查询了大量的博客才将这个流程走通。一句话就是,这里的坑很是多,下面我将详细的介绍相应的流程,以及应该避免那些坑,本人也是初学者望各位大神多提意见。php
一:token服务器验证(仅支持80端口):html
咱们须要在微信公众号的的管理界面设置一个路径和token值,微信服务器会访问该路径而且传递一个CheckModel对象过来,咱们要作的是将该对象里面的echostr返回过去,微信仅支持访问80端口,因此若是使用tomcat的容器须要去修改server.xml文件(详情百度tomcat默认80端口访问server.xml配置)。java
完成服务器token验证代码:(WeixinPayConstants类中有微信服务号相关的信息,在最后我会将相关代码给出来)web
@Controller @RequestMapping("/token") public class TokenController { @Autowired private TokenService tokenService; /** * 开发者模式token校验 * @param tokenModel * @throws ParseException * @throws IOException */ @RequestMapping(value = "/check", method = RequestMethod.GET, produces = "text/plain") public @ResponseBody String validate(CheckModel tokenModel, HttpServletRequest httpServletRequest) throws ParseException, IOException { String wxToken = WeixinPayConstants.token; tokenModel.setSignature(httpServletRequest.getParameter("signature")); tokenModel.setTimestamp(Long.valueOf(httpServletRequest.getParameter("timestamp"))); tokenModel.setNonce(Long.valueOf(httpServletRequest.getParameter("nonce"))); tokenModel.setEchostr(httpServletRequest.getParameter("echostr")); tokenModel.setEchostr(httpServletRequest.getParameter("echostr")); return tokenService.validate(wxToken, tokenModel); }
Validate方法:ajax
@Service public class TokenService { /** * 微信开发者验证 * @param wxToken * @param tokenModel * @return */ @Transactional public String validate(String wxToken, CheckModel tokenModel){ String signature = tokenModel.getSignature(); Long timestamp = tokenModel.getTimestamp(); Long nonce =tokenModel.getNonce(); String echostr = tokenModel.getEchostr(); if(signature!=null&×tamp!=null&nonce!=null) { String[] str = {wxToken, timestamp+"", nonce+""}; Arrays.sort(str); // 字典序排序 String bigStr = str[0] + str[1] + str[2]; // SHA1加密 String digest = EncoderHandler.encode("SHA1", bigStr).toLowerCase(); // 确认请求来至微信 if (digest.equals(signature)) { //最好此处将echostr存起来,之后每次校验消息来源都须要用到 return echostr; } } return "error"; }
CheckModel实体:算法
public class CheckModel extends ErrorCodeModel{ String signature; Long timestamp; Long nonce; String echostr; public String getSignature() { return signature; } public void setSignature(String signature) { this.signature = signature; } public Long getTimestamp() { return timestamp; } public void setTimestamp(Long timestamp) { this.timestamp = timestamp; } public Long getNonce() { return nonce; } public void setNonce(Long nonce) { this.nonce = nonce; } public String getEchostr() { return echostr; } public void setEchostr(String echostr) { this.echostr = echostr; } }
EncoderHandler类:数据库
public class EncoderHandler { private static final String ALGORITHM = "MD5"; private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * encode string * * @param algorithm * @param str * @return String */ public static String encode(String algorithm, String str) { if (str == null) { return null; } try { MessageDigest messageDigest = MessageDigest.getInstance(algorithm); messageDigest.update(str.getBytes()); return getFormattedText(messageDigest.digest()); } catch (Exception e) { throw new RuntimeException(e); } } /** * encode By MD5 * * @param str * @return String */ public static String encodeByMD5(String str) { if (str == null) { return null; } try { MessageDigest messageDigest = MessageDigest.getInstance(ALGORITHM); messageDigest.update(str.getBytes()); return getFormattedText(messageDigest.digest()); } catch (Exception e) { throw new RuntimeException(e); } } /** * Takes the raw bytes from the digest and formats them correct. * * @param bytes * the raw bytes from the digest. * @return the formatted bytes. */ private static String getFormattedText(byte[] bytes) { int len = bytes.length; StringBuilder buf = new StringBuilder(len * 2); // 把密文转换成十六进制的字符串形式 for (int j = 0; j < len; j++) { buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]); buf.append(HEX_DIGITS[bytes[j] & 0x0f]); } return buf.toString(); }
当你写好这个功能的时候,在微信公众号后台管理:开发——》基本配置——》输入token验证地址,点击提交就会有相应的提示。json
2、微信网页调起js sdk网页支付流程:后端
对页面受权—》发起支付—》回调—》根据相应返回参数判断支付结果进行页面跳转api
1. 咱们在任何一个页面要调起微信的支付接口都要先对这个页面进行受权,其次就是要在服务号的管理平台上对支付路径进行注册以下图。(在相应的页面须要调用微信的js文件 )<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
完对受权路径的添加以后咱们就能够进入代码的编写了:
1.1 在进入要调起支付页面的时候,页面经过ajax请求后端接口,而且获得相关信息:
后端方法实现:
/** * 获得获取微信权限所需的相关参数 * * @param request * @return */ //返回js权限相关的信息给前台 @ResponseBody @RequestMapping(value = "/getWeiXinConfig", method = RequestMethod.POST) public String getWeiXinConfig(HttpServletRequest request) { // 处理JsAPI_ticket 问题(存储数据库) String jsapi_ticket = getJsapiTicket(); logger.info("获取到的信息:------------:" + JSON.toJSON(jsapi_ticket)); String url = request.getParameter("url"); long timastamp = WeiXinDateUtil.getNowTimeStamp(); WeiXinConfig weiXinConfig = new WeiXinConfig(); weiXinConfig.setApp_id(WeixinPayConstants.appid); weiXinConfig.setTimestamp(String.valueOf(timastamp)); weiXinConfig.setNonceStr(RandomUtil.generateString(20)); //设置config模块的签名 SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); parameters.put("jsapi_ticket", jsapi_ticket); parameters.put("timestamp", timastamp); parameters.put("noncestr", RandomUtil.generateString(20)); parameters.put("url", url); String sign = WeiXinCrateSign.createSign("utf-8", parameters); weiXinConfig.setSignature(sign); String jsonString = JSON.toJSONString(weiXinConfig); return jsonString; }
获取jsapi_ticket:
/** * 从数据库中查询出当前的JsAPI_ticket或者从网络中获取 * * @return */ private String getJsapiTicket() { WeiXinJurisdictionConfig weiXinJurisdictionConfig = weiXinJurisdictionConfigService.selectByPrimaryKey(1); logger.info("数据库获取到的微信JsAPITicket信息:------------:" + JSON.toJSON(weiXinJurisdictionConfig)); if (weiXinJurisdictionConfig.getJsapiTicket() == "0") { String jsapi = GetJsAPITicket.getJsAPITicket(); logger.info("微信服务器获取到的微信JsAPITicket信息:------------:" + jsapi); weiXinJurisdictionConfig.setJsapiTicket(jsapi); logger.info("修改了JsAPITicket以后的实体:------------:" + weiXinJurisdictionConfig); weiXinJurisdictionConfigService.updateByPrimaryKeySelective(weiXinJurisdictionConfig); return jsapi; } else { return weiXinJurisdictionConfig.getJsapiTicket(); } }
WeiXinJurisdictionConfig类:
public class WeiXinJurisdictionConfig { private Integer id; private String jsapiTicket; private String accessToken; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getJsapiTicket() { return jsapiTicket; } public void setJsapiTicket(String jsapiTicket) { this.jsapiTicket = jsapiTicket == null ? null : jsapiTicket.trim(); } public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken == null ? null : accessToken.trim(); } }
从网络获取jsapi_ticket:
public class GetJsAPITicket { /** * 获取JsAPIticket * @return */ public static String getJsAPITicket(){ String access_token = GetAccessToken.getAccess_token(); String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ access_token +"&type=jsapi"; String jsAPITicketString = GetAccessToken.getJsonString(url); JsAPITicket jsAPITicket = JSON.parseObject(jsAPITicketString,JsAPITicket.class); System.out.println("jsapitiket: "+jsAPITicket.getTicket()); return jsAPITicket.getTicket(); } }
JsAPITicket类:
public class JsAPITicket { private String ticket; private int expiresIn; public String getTicket() { return ticket; } public void setTicket(String ticket) { this.ticket = ticket; } public int getExpiresIn() { return expiresIn; } public void setExpiresIn(int expiresIn) { this.expiresIn = expiresIn; } }
根据以上代码可得要想获取jsapi_ticket首先要获取AccessToken:
/** * Created by Administrator on 2016/8/19. */ public class GetAccessToken { /** * 获取Accesstoken * @return access_token */ public static String getAccess_token() { AccessToken accessToken = null; String jsonString = null; String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + WeixinPayConstants.appid+ "&secret=" + WeixinPayConstants.appsecret; jsonString = getJsonString(url); accessToken = JSON.parseObject(jsonString,AccessToken.class); System.out.println("access_token:---------------- "+accessToken.getAccess_token()); return accessToken.getAccess_token(); } /** * 获取json字符串经过get方式 * @param url 地址 * @return */ public static String getJsonString(String url){ String message = null; try { URL urlGet = new URL(url); HttpURLConnection http = (HttpURLConnection) urlGet .openConnection(); http.setRequestMethod("GET"); // 必须是get方式请求 http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); http.setDoOutput(true); http.setDoInput(true); System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 链接超时30秒 System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒 http.connect(); InputStream is = http.getInputStream(); int size = is.available(); byte[] jsonBytes = new byte[size]; is.read(jsonBytes); message = new String(jsonBytes, "UTF-8"); is.close(); } catch (Exception e) { e.printStackTrace(); } return message; } }
AccessToken类:
/** * Created by Administrator on 2016/8/19. */ public class AccessToken { private String access_token; private String expires_in; public String getAccess_token() { return access_token; } public void setAccess_token(String access_token) { this.access_token = access_token; } public String getExpires_in() { return expires_in; } public void setExpires_in(String expires_in) { this.expires_in = expires_in; } }
当以上步骤都走完的时候这是前台的ajax 的success中就会,调起wx.config()(注意里面的参数的名的大小写,以及有那些参数,还有须要调起的接口须要在jsApiList中列出来,详情参考下面的代码或微信官方文档);对该页面进行受权,当受权成功的时候就会执行wx.ready();
2. // 注意:全部的JS接口只能在公众号绑定的域名下调用,公众号开发者须要先登陆微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。 // 若是发如今 Android 不能分享自定义内容,请到官网下载最新的包覆盖安装,Android 自定义分享接口需升级至 6.0.2.58 版本及以上。 // 完整 JS-SDK 文档地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html $(function () { $.ajax({ url: path+"/weiXin/weiXinPay/getWeiXinConfig", type: "POST", data: {url: window.location.href}, success: function (data) { var weixinConfig = JSON.parse(data); wx.config({ appId: weixinConfig.app_id, timestamp: weixinConfig.timestamp, nonceStr: weixinConfig.nonceStr, signature: weixinConfig.signature, jsApiList: [// 全部要调用的 API 都要加到这个列表中 "chooseWXPay" ] }); wx.ready(function () { $.ajax({ url: path+"/weiXin/weiXinPay/getOrder", type: "POST", data: {}, success: function (data) { if (data != null) { var weixinPay = JSON.parse(data); wx.chooseWXPay({ timestamp: weixinPay.timeStamp, // 支付签名时间戳,注意微信jssdk中的全部使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 nonceStr: weixinPay.nonceStr, // 支付签名随机串,不长于 32 位 package: weixinPay.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***) signType: weixinPay.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5' paySign: weixinPay.paySign, // 支付签名 success: function (res) { // 支付成功后的回调函数 根据相应的返回参数判断支付结果,而且进行相应的页面跳转 }, error: function (res) { alert(res + "失败!") } }); }else{ alert("出错了!"); } }, error: function (data) { alert(data + "REEOR") } }) }) }, error: function (data) { alert(data + "REEOR") } }) })
当执行wx.ready() 时整个流程是:生成一个订单—》调用微信的获取点单id的接口—》获取到订单id,而后将一系列的相关签名和其余参数传递到页面—》调起微信的相关控件,输入密码完成支付。
2.1 根据上面的ajax请求访问后端获取订单信息的相关接口:
后端处理代码:
/** * 获取微信支付所需的相关参数 * * @param httpServletRequest * @param session * @return * @throws Exception */ @ResponseBody @RequestMapping(value = "/getOrder", method = RequestMethod.POST) public String getOrderInfo(HttpServletRequest httpServletRequest, HttpSession session) throws Exception { String orderXml;//微信下单xml String orderInfo;//订单的简介 String nonce_str;//32位之内随机字符串 String out_trade_no;//商户订单号 int total_fee; String jsonString = null; getWeiXinOpenIdReturn(session);//获取用户的openid存放到session中 logger.info("session获取出来的订单信息:------------>>>>>>>saleProduct:" + JSON.toJSON(session)); // 下单所用到真实的信息,从系统session获取 WeiXinOrderNeedInfo weiXinOrderNeedInfo = (WeiXinOrderNeedInfo) session.getAttribute("weiXinOrderNeedInfo"); if (weiXinOrderNeedInfo != null) { logger.info("session获取出来的订单信息:------------>>>>>>>saleProduct:" + JSON.toJSON(weiXinOrderNeedInfo)); orderInfo = "菜小乐支付订单";// 订单的简介 nonce_str = weiXinOrderNeedInfo.getNonce_str(); out_trade_no = weiXinOrderNeedInfo.getOut_trade_no();// 商户订单号(商户本身系统内生成的订单号32位之内) total_fee = weiXinOrderNeedInfo.getTotal_fee();// 总金额,单位默认为分 String openid = String.valueOf(session.getAttribute("openId"));// 用户的openid WeiXinGetOrder weiXinGetOrder = new WeiXinGetOrder(); weiXinGetOrder.setAppid(WeixinPayConstants.appid); weiXinGetOrder.setBody(orderInfo); weiXinGetOrder.setMch_id(WeixinPayConstants.mch_id); weiXinGetOrder.setNonce_str(nonce_str); weiXinGetOrder.setNotify_url(WeixinPayConstants.notify_url);// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。 weiXinGetOrder.setOpenid(openid); weiXinGetOrder.setOut_trade_no(out_trade_no); weiXinGetOrder.setSpbill_create_ip(GetClientIp.getIpAddr(httpServletRequest)); //用户页面的ip weiXinGetOrder.setTrade_type("JSAPI"); weiXinGetOrder.setTotal_fee(total_fee); //设置并获得签名 SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); parameters.put("appid", WeixinPayConstants.appid); parameters.put("body", orderInfo); parameters.put("mch_id", WeixinPayConstants.mch_id); parameters.put("nonce_str", nonce_str); parameters.put("notify_url", WeixinPayConstants.notify_url);// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。 parameters.put("openid", openid); parameters.put("out_trade_no", out_trade_no); parameters.put("spbill_create_ip", GetClientIp.getIpAddr(httpServletRequest));//用户页面的ip parameters.put("trade_type", "JSAPI"); parameters.put("total_fee", total_fee); String sign = WeiXinCrateSign.createSign("utf-8", parameters); System.out.println("签名------------------" + sign); weiXinGetOrder.setSign(sign); orderXml = getOrderXml(weiXinGetOrder); System.out.println("直接字符串输出" + orderXml); //获取相关的订单信息,注意获得的是xml格式的信息,须要在下面对其解析 String stringObject = CommonUtil.httpsRequest(WeixinPayConstants.createOrderURL, "POST", orderXml).toString(); System.out.println("返回的xmlstring" + stringObject); //将map对象转换成xml格式数据 Map map = ObjectAndXmlUtil.doXMLParse(stringObject); String prepay_id = String.valueOf(map.get("prepay_id")); logger.info("-----统一下单接口的订单id:" + JSON.toJSON(map.get("prepay_id"))); WeiXinPay weiXinPay = new WeiXinPay(); String nonceStr = RandomUtil.generateString(15); String timaStamp = String.valueOf(WeiXinDateUtil.getNowTimeStamp()); weiXinPay.setPackage("prepay_id=" + prepay_id); weiXinPay.setTimeStamp(timaStamp);// weiXinPay.setNonceStr(nonceStr); weiXinPay.setAppId(WeixinPayConstants.appid); weiXinPay.setSignType("MD5"); SortedMap<Object, Object> parameter = new TreeMap<Object, Object>(); parameter.put("timeStamp", timaStamp); parameter.put("appId", WeixinPayConstants.appid); parameter.put("nonceStr", nonceStr); parameter.put("signType", "MD5"); parameter.put("package", weiXinPay.getPackage()); weiXinPay.setPaySign(WeiXinCrateSign.createSign("utf-8", parameter)); jsonString = JSON.toJSONString(weiXinPay); } return jsonString; }
每个订单都对应一个微信用户,因此在此以前咱们要获取用户的openid:
/** * 获取微信用户的openid * * @param session */ public void getWeiXinOpenIdReturn(HttpSession session) { String code = String.valueOf(session.getAttribute("returnCode")); System.out.println("code值----------" + code); String oauth_url = WeixinPayConstants.oauth_url.replace("APPID", WeixinPayConstants.appid).replace("SECRET", WeixinPayConstants.appsecret).replace("CODE", code); logger.info("oauth_url:" + oauth_url); JSONObject jsonObject = CommonUtil.httpsRequestToJsonObject(oauth_url, "POST", null); logger.info("jsonObject:" + jsonObject); Object errorCode = jsonObject.get("errcode"); if (errorCode != null) { logger.info("code不合法"); } else { String openId = jsonObject.getString("openid"); logger.info("openId:" + openId); logger.info("openId:" + openId); session.setAttribute("openId", openId); System.out.println("openid-----------" + openId); } }
具体获取openid的相关代码:
public class CommonUtil { /** * 获取用户openid的网络实现 * @param requestUrl * @param requestMethod * @param outputStr * @return */ public static JSONObject httpsRequestToJsonObject(String requestUrl, String requestMethod, String outputStr) { JSONObject jsonObject = null; try { StringBuffer buffer = httpsRequest(requestUrl, requestMethod, outputStr); jsonObject = JSONObject.fromObject(buffer.toString()); } catch (ConnectException ce) { System.out.println("链接超时:" + ce.getMessage()); } catch (Exception e) { System.out.println("https请求异常:" + e.getMessage()); } return jsonObject; } /** * /获取openid的post方法 * @param requestUrl * @param requestMethod * @param output * @return * @throws NoSuchAlgorithmException * @throws KeyManagementException * @throws IOException */ public static StringBuffer httpsRequest(String requestUrl, String requestMethod, String output) throws NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException, IOException{ URL url = new URL(requestUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod(requestMethod); if (null != output) { OutputStream outputStream = connection.getOutputStream(); outputStream.write(output.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; connection.disconnect(); return buffer; }
将要下单的信息转换为xml格式的数据(要在每条数据中加上<![CDATA]>,腾讯官方文档是没有加的,可是接口又须要加上,当初我在这里也卡了很长一段时间):
/** * 将要下单的订单信息转化成xml * * @param weiXinGetOrder * @return */ private String getOrderXml(WeiXinGetOrder weiXinGetOrder) { String data = "<xml>" + "<appid>" + "<![CDATA[" + weiXinGetOrder.getAppid() + "]]>" + "</appid>" + "<body>" + "<![CDATA[" + weiXinGetOrder.getBody() + "]]>" + "</body>" + "<mch_id>" + "<![CDATA[" + weiXinGetOrder.getMch_id() + "]]>" + "</mch_id>" + "<nonce_str>" + "<![CDATA[" + weiXinGetOrder.getNonce_str() + "]]>" + "</nonce_str>" + "<notify_url>" + "<![CDATA[" + weiXinGetOrder.getNotify_url() + "]]>" + "</notify_url>" + "<openid>" + "<![CDATA[" + weiXinGetOrder.getOpenid() + "]]>" + "</openid>" + "<out_trade_no>" + "<![CDATA[" + weiXinGetOrder.getOut_trade_no() + "]]>" + "</out_trade_no>" + "<spbill_create_ip>" + "<![CDATA[" + weiXinGetOrder.getSpbill_create_ip() + "]]>" + "</spbill_create_ip>" + "<total_fee>" + "<![CDATA[" + weiXinGetOrder.getTotal_fee() + "]]>" + "</total_fee>" + "<trade_type>" + "<![CDATA[" + weiXinGetOrder.getTrade_type() + "]]>" + "</trade_type>" + "<sign>" + "<![CDATA[" + weiXinGetOrder.getSign() + "]]>" + "</sign>" + "</xml>";//将实体转化成xml(string) return data; }
ObjectAndXmlUtil类(进行实体与xml数据之间的相互转化):
public class ObjectAndXmlUtil { /** * 将微信xml转化成实体 * * @param xmlStr * * @return */ public static WeiXinGetOrder XMLStringToBeanWeiXinGetOrder(String xmlStr) { XStream xstream = new XStream(); WeiXinGetOrder weiXinGetOrder = (WeiXinGetOrder) xstream.fromXML(xmlStr); return weiXinGetOrder; } public static InputStream String2Inputstream(String str) { return new ByteArrayInputStream(str.getBytes()); } /** * 获取子结点的xml * @param children * @return String */ public static String getChildrenText(List children) { StringBuffer sb = new StringBuffer(); if (!children.isEmpty()) { Iterator it = children.iterator(); while (it.hasNext()) { Element e = (Element) it.next(); String name = e.getName(); String value = e.getTextNormalize(); List list = e.getChildren(); sb.append("<" + name + ">"); if (!list.isEmpty()) { sb.append(getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } /** * 解析xml,返回第一级元素键值对。若是第一级元素有子节点,则此节点的值是子节点的xml数据。 * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map doXMLParse(String strxml) throws Exception { if (null == strxml || "".equals(strxml)) { return null; } Map map = new HashMap(); InputStream in = String2Inputstream(strxml); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(in); Element root = doc.getRootElement(); List list = root.getChildren(); Iterator it = list.iterator(); while (it.hasNext()) { Element e = (Element) it.next(); String k = e.getName(); String v = ""; List children = e.getChildren(); if (children.isEmpty()) { v = e.getTextNormalize(); } else { v = getChildrenText(children); } map.put(k, v); } //关闭流 in.close(); return map;
} }
WeiXinGetOrder类,相关参数在代码中有相关说明:
/** * Created by Administrator on 2016/8/18. */ @XmlRootElement public class WeiXinGetOrder implements Serializable { private String appid;//公众号id private String mch_id;//商户号 private String nonce_str;//很多于32位随机字符串 private String sign;//签名 private String body;//商品的描述(浏览器打开的移动网页的主页title名-商品概述) private String out_trade_no;//商户订单号(商户本身系统内生成的订单号32位之内) private int total_fee;//总金额,单位默认为分 private String spbill_create_ip;//提交用户终端的ip private String notify_url;//接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。 private String trade_type;//交易类型 取值以下:JSAPI private String openid;//交易类型 取值以下:JSAPI public String getOpenid() { return openid; } public void setOpenid(String openid) { this.openid = openid; } public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public String getMch_id() { return mch_id; } public void setMch_id(String mch_id) { this.mch_id = mch_id; } public String getNonce_str() { return nonce_str; } public void setNonce_str(String nonce_str) { this.nonce_str = nonce_str; } public String getNotify_url() { return notify_url; } public void setNotify_url(String notify_url) { this.notify_url = notify_url; } public String getOut_trade_no() { return out_trade_no; } public void setOut_trade_no(String out_trade_no) { this.out_trade_no = out_trade_no; } public String getSign() { return sign; } public void setSign(String sign) { this.sign = sign; } public String getSpbill_create_ip() { return spbill_create_ip; } public void setSpbill_create_ip(String spbill_create_ip) { this.spbill_create_ip = spbill_create_ip; } public int getTotal_fee() { return total_fee; } public void setTotal_fee(int total_fee) { this.total_fee = total_fee; } public String getTrade_type() { return trade_type; } public void setTrade_type(String trade_type) { this.trade_type = trade_type; } }
在上面的实体类中有一个属性是sign:我在这里也给出微信的签名算法(官方的签名规定:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3):
@SuppressWarnings("unchecked") //api 密钥 private static String Key = WeixinPayConstants.partnerkey; /** * 微信签名算法 * @param characterEncoding 编码格式 * @param parameters 签名所涉及到数据 * @return sign 签名 */ public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();//全部参与传参的参数按照accsii排序(升序) Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); Object v = entry.getValue(); if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + Key); String sign = WeiXinMD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; }
后端的相关步骤执行完以后,程序会执行到上面给出来的ajax的success方法中,经过判断返回值就能够判断是否支付成功,并进行先关的页面跳转:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
结束:因为本人也是新手,这些代码很low仅仅是简单的实现了支付的功能而已,后期有时间在来重构这些代码了。