- 浏览: 5202197 次
- 性别:
- 来自: 北京
-
文章分类
最新评论
-
silence19841230:
先拿走看看
SpringBoot2.0开发WebSocket应用完整示例 -
wallimn:
masuweng 写道发下源码下载地址吧!三个相关文件打了个包 ...
SpringBoot2.0开发WebSocket应用完整示例 -
masuweng:
发下源码下载地址吧!
SpringBoot2.0开发WebSocket应用完整示例 -
masuweng:
SpringBoot2.0开发WebSocket应用完整示例 -
wallimn:
水淼火 写道你好,我使用以后,图标不显示,应该怎么引用呢,谢谢 ...
前端框架iviewui使用示例之菜单+多Tab页布局
作者:wallimn 时间:2017-02-27
本人原创,欢迎转载,转载请保留本文链接。本文地址:http://wallimn.iteye.com/blog/2359379
花了两天时间,琢磨了一下微信支付。感觉坑并不像传言中那么多,主要有两个:
一、app的key与商户的key是两个不一样的key。我用了错误的key,花了很长时间解决支付时签名错误。
二、最后页面调起支付时,一直报错:get_brand_wcpay_request:fail。花了很长时间排除。
由于页面在手机上,很难调试。后来想到一个办法,在页面上建一个div元素,设置其ID为actionResult,然后使用jQuery提供的方法显示调用结果:
$("#actionResult").text(JSON.stringify(res))
可以很容易地看到出错原因。
常见的原因有三个:
1、参数数量不足或名称错误。查看开发文档,提供足够的参数,名称、大小写要正确,注意timeStamp的单位为秒。
2、签名不正确。注意查看签名的要求。
3、地址没有授权、或填写错误。“微信支付”->“开发配置”中,设置授权目录。授权要较长时间才能生效。如果地址为:http://wallimn.iteye.com/weixin/paytest,那么微信平台中的授权地址应为:http://wallimn.iteye.com/weixin/paytest
还有两个要注意的小问题:
一、定单号(out_trade_no)如果重复会导致支付失败。
二、测试用户要加入白名单。
微信的流程图有一点点儿不太直观,我画了一个简单的,应该一看就会明白。
程序的主体代码如下。
一、几个基本的辅助类
1.统一定单类
2.统一定单结果类
3.与微信相关的常量类
二、支付工具类
三、HttpClient辅助类
四、Controller中相关的代码
五、公众号页面
本人原创,欢迎转载,转载请保留本文链接。本文地址:http://wallimn.iteye.com/blog/2359379
花了两天时间,琢磨了一下微信支付。感觉坑并不像传言中那么多,主要有两个:
一、app的key与商户的key是两个不一样的key。我用了错误的key,花了很长时间解决支付时签名错误。
二、最后页面调起支付时,一直报错:get_brand_wcpay_request:fail。花了很长时间排除。
由于页面在手机上,很难调试。后来想到一个办法,在页面上建一个div元素,设置其ID为actionResult,然后使用jQuery提供的方法显示调用结果:
$("#actionResult").text(JSON.stringify(res))
可以很容易地看到出错原因。
常见的原因有三个:
1、参数数量不足或名称错误。查看开发文档,提供足够的参数,名称、大小写要正确,注意timeStamp的单位为秒。
2、签名不正确。注意查看签名的要求。
3、地址没有授权、或填写错误。“微信支付”->“开发配置”中,设置授权目录。授权要较长时间才能生效。如果地址为:http://wallimn.iteye.com/weixin/paytest,那么微信平台中的授权地址应为:http://wallimn.iteye.com/weixin/paytest
还有两个要注意的小问题:
一、定单号(out_trade_no)如果重复会导致支付失败。
二、测试用户要加入白名单。
微信的流程图有一点点儿不太直观,我画了一个简单的,应该一看就会明白。

程序的主体代码如下。
一、几个基本的辅助类
1.统一定单类
package com.wallimn.weixin; /** * 统一定单,用于向微信提供信息,进行支付 * @author wallimn * */ public class UnifiedOrder { private String appid; private String mch_id; private String nonce_str; private String sign_type;//="MD5" private String body;//商品描述 private String device_info="WEB";//设备号 private String detail; private String out_trade_no;//商户订单号 private String fee_type;//,非必需="CNY" private String total_fee;//分为单位 private String spbill_create_ip; private String notify_url; private String trade_type;//JSAPI、NATIVE、APP三种 private String product_id="something";//商品ID,非必需 private String openid;//JSAPI时,必传 public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } 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 getBody() { return body; } public void setBody(String body) { this.body = body; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } 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 getFee_type() { return fee_type; } public void setFee_type(String fee_type) { this.fee_type = fee_type; } 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 String getNotify_url() { return notify_url; } public void setNotify_url(String notify_url) { this.notify_url = notify_url; } public String getTrade_type() { return trade_type; } public void setTrade_type(String trade_type) { this.trade_type = trade_type; } public String getProduct_id() { return product_id; } public void setProduct_id(String product_id) { this.product_id = product_id; } public String getOpenid() { return openid; } public void setOpenid(String openid) { this.openid = openid; } public String getDevice_info() { return device_info; } public void setDevice_info(String device_info) { this.device_info = device_info; } public String getTotal_fee() { return total_fee; } public void setTotal_fee(String total_fee) { this.total_fee = total_fee; } public String getSign_type() { return sign_type; } public void setSign_type(String sign_type) { this.sign_type = sign_type; } }
2.统一定单结果类
package com.wallimn.weixin; /** * 微信定单返回信息 * @author wallimn * */ public class UnifiedOrderResult { private String return_code; private String return_msg; private String appid; private String mch_id; private String nonce_str; private String sign; private String result_code; private String prepay_id; private String trade_type; public String getReturn_msg() { return return_msg; } public void setReturn_msg(String return_msg) { this.return_msg = return_msg; } public String getReturn_code() { return return_code; } public void setReturn_code(String return_code) { this.return_code = return_code; } public String getNonce_str() { return nonce_str; } public void setNonce_str(String nonce_str) { this.nonce_str = nonce_str; } public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } public String getMch_id() { return mch_id; } public void setMch_id(String mch_id) { this.mch_id = mch_id; } public String getSign() { return sign; } public void setSign(String sign) { this.sign = sign; } public String getResult_code() { return result_code; } public void setResult_code(String result_code) { this.result_code = result_code; } public String getPrepay_id() { return prepay_id; } public void setPrepay_id(String prepay_id) { this.prepay_id = prepay_id; } public String getTrade_type() { return trade_type; } public void setTrade_type(String trade_type) { this.trade_type = trade_type; } }
3.与微信相关的常量类
package com.wallimn.weixin; import java.net.URLEncoder; /** * 与微信相关的常量 * @author Administrator * */ public class Config { public static final String app_id=""; public static final String app_sercet=""; /** * 微信登录授权时使用的state参数 */ public static final String state="wallimn"; /** * 微信登录授权时使用的scope参数 */ public static final String scope="snsapi_userinfo"; /** * 获取code的Servlet地址,微信平台提供 */ public static final String code_url = "http://www.day2up.com/bzslql/sso/weixin"; /** * 默认的登录后跳转地址 */ public static final String default_back_url = "/index.jsp"; /** * 微信支付的商户号,微信商户注册时获得 */ public static final String mch_id=""; /** * 商户的api的key,在微信的商户平台中设置。与app的key不一样.这个是个坑! */ public static final String mch_secret=""; /** * 微信统一下单接口 */ public static final String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; /** * 用户支付成功后,微信平台的通知地址 */ public static final String pay_notify_url=""; /** * 返回微信的获取code的地址。将backurl编码到地址中。授权成功后,自动返回backurl对应的地址。 * <br> * 时间:2017年2月27日,作者:http://wallimn.iteye.com * @param backUrl * @return */ public static String getCodeUrl(String backUrl){ try { String url = code_url; //这样可以灵活返回到指定的地址,不必在Servlet中硬编码 if(backUrl!=null){ url += "?backurl="+backUrl; } String ru = URLEncoder.encode(url,"utf-8"); return "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+app_id+"&redirect_uri="+ru+"&response_type=code&scope="+scope+"&state="+state+"#wechat_redirect"; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 获取access_token的链接 * <br> * 时间:2017年2月27日,作者:http://wallimn.iteye.com * @param code * @return */ public static String getTokenUrl(String code){ return "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+app_id+"&secret="+app_sercet+"&code="+code+"&grant_type=authorization_code"; } /** * 刷新access_token的链接 * @param token * @return * 作者:wallimn<br/> * 时间:2017年1月22日<br/> * 联系:54871876@qq.com<br/> */ public static String getFreshTokenUrl(String token){ return "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid="+app_id+"&grant_type=refresh_token&refresh_token="+token; } /** * 使用access_token获取用户信息的链接 * @param token * @param openid * @return * 作者:wallimn<br/> * 时间:2017年1月22日<br/> * 联系:54871876@qq.com<br/> */ public static String getUserInfoUrl(String token,String openid){ return "https://api.weixin.qq.com/sns/userinfo?access_token="+token+"&openid="+openid+"&lang=zh_CN"; } }
二、支付工具类
package com.wallimn.weixin; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.security.MessageDigest; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.io.SAXReader; import com.fasterxml.jackson.databind.ObjectMapper; /** * 微信支付处理辅助类 * @author http://wallimn.iteye.com * */ public class PayUtils { private static final Logger log = LogManager.getLogger(PayUtils.class); /** * 解析统一定单结果数据。 * * @param xml * @return * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ public static UnifiedOrderResult parseUnifiedOrderResult(String xml) { UnifiedOrderResult result = new UnifiedOrderResult(); //AXReader reader = new SAXReader(); Document doc=null; try { //doc = reader.read(xml); doc = DocumentHelper.parseText(xml); result.setReturn_code(((Element) doc.selectSingleNode("/xml/return_code")).getText()); result.setReturn_msg(((Element) doc.selectSingleNode("/xml/return_msg")).getText()); if("SUCCESS".equals(result.getReturn_code())){ result.setAppid(((Element) doc.selectSingleNode("/xml/appid")).getText()); result.setMch_id(((Element) doc.selectSingleNode("/xml/mch_id")).getText()); result.setNonce_str(((Element) doc.selectSingleNode("/xml/nonce_str")).getText()); //result.setOpenid(((Element) doc.selectSingleNode("/xml/openid")).getText()); result.setSign(((Element) doc.selectSingleNode("/xml/sign")).getText()); result.setResult_code(((Element) doc.selectSingleNode("/xml/result_code")).getText()); result.setPrepay_id(((Element) doc.selectSingleNode("/xml/prepay_id")).getText()); result.setTrade_type(((Element) doc.selectSingleNode("/xml/trade_type")).getText()); } doc = null; //reader = null; } catch (DocumentException e) { log.error("解析统一定单返回结果错误:{}",e.getMessage()); e.printStackTrace(); } return result; } /** * 使用必须用户提供的参数,生成统计定单对象 * * @param body * @param detail * @param openid * @param out_trade_no * @param spbill_create_ip * @param total_fee * @param trade_type * @return * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ public static UnifiedOrder getUnifiedOrder(String body, String detail, String openid, String out_trade_no, String spbill_create_ip, String total_fee, String trade_type) { UnifiedOrder order = new UnifiedOrder(); /* * String wx_order = Config.order_url;// 获取统一下单接口地址 String mchappid = * Config.appid;// 商户appid String mchid = Config.mch_id;// 商户ID String * wx_callback = Config.paycallback_url;// 获取微信支付回调接口 String wx_key = * Config.appsercet;// 微信商户后台设置的key String app_mchid = Config.appid;// * APP调起微信支付的商户ID String app_mchappid = Config.mch_id;// APP调起微信的APPID */ order.setAppid(Config.app_id); order.setBody(body); order.setMch_id(Config.mch_id); order.setNonce_str(UUID.randomUUID().toString().replace("-", "")); order.setNotify_url(Config.pay_notify_url); order.setOpenid(openid); order.setOut_trade_no(out_trade_no); order.setSpbill_create_ip(spbill_create_ip); order.setTotal_fee(total_fee); order.setTrade_type(trade_type); order.setDetail(detail); return order; } /** * 将bean转化为Map,方便下一步签名、转化xml使用 * * @param bean * @return * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ public static Map<String, String> BeanToMap(Object bean) { Map<String, String> map = null; try { map = BeanUtils.describe(bean); map.remove("class"); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } return map; } /** * 将Map数据转化为统一定单要求的xml数据。 * * @param map * @return * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ public static String MapToXML(Map<String, String> map) { if (map == null) return null; StringBuilder sb = new StringBuilder(); sb.append("<xml>"); String value = null; Set<String> keySet = map.keySet(); String [] keyArray = keySet.toArray(new String[0]); Arrays.sort(keyArray); for (String key :keyArray) { value = map.get(key); if (value == null) { value = ""; } else if (value.length() > 60) { // 简单处理一下,如果过长,就加上XML的转义 value = "<![CDATA[" + value + "]]"; } sb.append("<").append(key).append(">").append(value).append("</").append(key) .append(">"); } sb.append("</xml>"); return sb.toString(); } /** * 读取流中的xml数据,转化为Map<String,String> * * @param is * @return * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ public static Map<String,String> XmlToMap(InputStream is){ HashMap<String, String> map = new HashMap<String, String>(); log.info("------------微信回调函数----------------"); // 1、读取传入信息并转换为map SAXReader reader = new SAXReader(); Document document = null; try { document = reader.read(is); log.info("支付返回结果:/n/r"+document.asXML()); Element root = document.getRootElement(); List<Element> list = root.elements(); for (Element e : list) { map.put(e.getName().trim(), e.getText().trim()); } document = null; reader = null; } catch(Exception e){ log.error("XML转化为bean错误,原因:{}",e.getMessage()); e.printStackTrace(); } return map; } /** * 提交统一定单,仅对应于JSAPI类型 * * @param orderXml * @return 返回结果为适合公众号页面上使用的JSON数据。格式与微信要求一致。 * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ public static String postUnifiedOrder(String orderXml) { String response = ""; try {// 注意,此处的httputil一定发送请求的时候一定要注意中文乱码问题,中文乱码问题会导致在客户端加密是正确的,可是微信端返回的是加密错误 log.info("统一定单发送:"+Config.unifiedorder_url); response = HttpUtils.post(Config.unifiedorder_url, orderXml); log.info("统一定单结果:"+response); UnifiedOrderResult orderResult = PayUtils.parseUnifiedOrderResult(response); // 6、处理请求结果 log.info("return_code={}, result_code={}",orderResult.getReturn_code(),orderResult.getResult_code()); if ("SUCCESS".equals(orderResult.getReturn_code()) && "SUCCESS".equals(orderResult.getResult_code())) { log.error("微信支付统一下单请求成功:" + orderResult.getPrepay_id()); } else { log.error("微信支付统一下单请求错误:{}" + orderResult.getReturn_msg()); return null; } HashMap<String, String> back = new HashMap<String, String>(); ObjectMapper mapper = new ObjectMapper(); // 生成客户端调时需要的信息对象 // 网页调起的时候 //时戳,要求转化为以秒为单位 String time = Long.toString(System.currentTimeMillis() / 1000); back.put("appId", Config.app_id); back.put("timeStamp", time); String uuid = UUID.randomUUID().toString().replace("-",""); back.put("nonceStr", uuid); back.put("package", "prepay_id=" + orderResult.getPrepay_id()); back.put("signType", "MD5"); //根据以上参数,计算签名。 String sign = signature(back, Config.mch_secret); back.put("paySign", sign); String json = mapper.writeValueAsString(back); //log.info("返回也面的JSON:/n/r{}"+json); return json; } catch (Exception e) { log.error("微信支付统一下单失败,http请求失败:{}", e.getMessage()); e.printStackTrace(); } return null; } /** * 查检定单数据 * * @param order * @return * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ public static boolean checkUnifiedOrder(UnifiedOrder order) { // if (StringUtils.isBlank(order.getDetail()) ) { // log.error("微信支付统一下单请求错误:detail为空!"); // return false; // } if ( StringUtils.isBlank(order.getBody())) { log.error("微信支付统一下单请求错误:body为空!"); return false; } if ( StringUtils.isBlank(order.getSpbill_create_ip())) { log.error("微信支付统一下单请求错误:spbill_create_ip为空!"); return false; } if (StringUtils.isBlank(order.getOut_trade_no()) ) { log.error("微信支付统一下单请求错误:out_trade_no为空!"); return false; } if (StringUtils.isBlank(order.getTotal_fee())) { log.error("微信支付统一下单请求错误:total_fee为空!"); return false; } if (StringUtils.isBlank(order.getTrade_type())) { log.error("微信支付统一下单请求错误:trade_type为空!"); return false; } try {// 进行格式转换异常获取,保证数目正确 int fee = Integer.parseInt(order.getTotal_fee()); if (fee == 0 || !order.getTotal_fee().equals(String.valueOf(fee))) {// 微信支付的支付金额必须为大于0的int类型,单位为分 log.error("微信支付统一下单请求错误:请求金额不能为0,不能为小数"); return false; } } catch (Exception e) { log.error("微信支付统一下单请求错误:请求金额格式错误,值:{}", order.getTotal_fee()); return false; } if (!("JSAPI".equalsIgnoreCase(order.getTrade_type()) || "NATIVE".equalsIgnoreCase(order.getTrade_type()) || "APP".equalsIgnoreCase(order.getTrade_type()))) { log.error("微信支付统一下单请求错误:支付类型不正确,{}", order.getTrade_type()); return false; } /* 公众号调起微信支付的时候,必须要有openID */ if ("JSAPI".equalsIgnoreCase(order.getTrade_type()) && StringUtils.isBlank(order.getOpenid())) { log.error("微信支付统一下单请求错误:请求参数不足,当类型为JSAPI时,必须提供openid"); return false; } if (StringUtils.isBlank(order.getAppid()) || StringUtils.isBlank(order.getMch_id()) || StringUtils.isBlank(order.getNotify_url())) { log.error("微信支付统一下单请求错误:系统配置信息缺失,appid, mch_id, notify_url"); return false; } return true; } /** * 签名生成的通用步骤如下: * 第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 * 特别注意以下重要规则: ◆ 参数名ASCII码从小到大排序(字典序); ◆ 如果参数的值为空不参与签名; ◆ 参数名区分大小写; ◆ * 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。 ◆ * 微信接口可能增加字段,验证签名时必须支持增加的扩展字段 * 第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。 * key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 * */ /** * 为微信支付发送数据进行签名的类。 * * @param map * @param app_secret * @return * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ public static String signature(Map<String, String> map, String app_secret) { List<String> keyList = new LinkedList<String>(); // 如果参数不为空,加入签名的字符串 for (Entry<String, String> entry : map.entrySet()) { if (StringUtils.isNotEmpty(entry.getValue())) { keyList.add(entry.getKey()); } } String[] keyArray = keyList.toArray(new String[0]); Arrays.sort(keyArray); StringBuilder sbInfo = new StringBuilder(); // 进行字典排序 for (String str : keyArray) { sbInfo.append(str).append("=").append(map.get(str)).append("&"); } if (StringUtils.isNotEmpty(app_secret)) { sbInfo.append("key=" + app_secret); } String tosend = sbInfo.toString(); MessageDigest md = null; byte[] bytes = null; try { md = MessageDigest.getInstance("MD5"); bytes = md.digest(tosend.getBytes("utf-8")); } catch (Exception e) { e.printStackTrace(); } String sign = byteToStr(bytes); System.out.println("签名字符串:"+tosend); sign = sign.toUpperCase(); System.out.println("签名字结果:"+sign); return sign; } /** * 字节数组转换为字符串 * * @param byteArray * @return */ private static String byteToStr(byte[] byteArray) { String strDigest = ""; for (int i = 0; i < byteArray.length; i++) { strDigest += byteToHexStr(byteArray[i]); } return strDigest; } /** * 字节转换为字符串 * * @param mByte * @return */ private static String byteToHexStr(byte mByte) { char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char[] tempArr = new char[2]; tempArr[0] = Digit[(mByte >>> 4) & 0X0F]; tempArr[1] = Digit[mByte & 0X0F]; String s = new String(tempArr); return s; } /** * SHA-1签名,这个在实际中并未使用 * <br> * 时间:2017年2月27日,作者:http://wallimn.iteye.com * @param map * @return */ public static String signatureSHA1(Map<String, String> map) { Set<String> keySet = map.keySet(); String[] str = new String[map.size()]; StringBuilder tmp = new StringBuilder(); // 进行字典排序 str = keySet.toArray(str); Arrays.sort(str); for (int i = 0; i < str.length; i++) { String t = str[i] + "=" + map.get(str[i]) + "&"; tmp.append(t); } String tosend = tmp.toString().substring(0, tmp.length() - 1); MessageDigest md = null; byte[] bytes = null; try { md = MessageDigest.getInstance("SHA-1"); bytes = md.digest(tosend.getBytes("utf-8")); } catch (Exception e) { e.printStackTrace(); } String singe = byteToStr(bytes); return singe.toLowerCase(); } }
三、HttpClient辅助类
package com.wallimn.weixin; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; public class HttpUtils { private static final String Charset = "utf-8"; public static HttpClient client; static { client = HttpClientBuilder.create().build(); } /** * 发送请求,如果失败,会返回null * @param url * @param map * @return */ public static String post(String url, Map<String, String> map) { // 处理请求地址 try { URI uri = new URI(url); HttpPost post = new HttpPost(uri); // 添加参数 List<NameValuePair> params = new ArrayList<NameValuePair>(); for (String str : map.keySet()) { params.add(new BasicNameValuePair(str, map.get(str))); } post.setEntity(new UrlEncodedFormEntity(params, Charset)); // 执行请求 HttpResponse response = client.execute(post); if (response.getStatusLine().getStatusCode() == 200) { // 处理请求结果 StringBuffer buffer = new StringBuffer(); InputStream in = null; try { in = response.getEntity().getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(in,Charset)); String line = null; while ((line = reader.readLine()) != null) { buffer.append(line); } } catch (UnsupportedOperationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { // 关闭流 if (in != null) try { in.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return buffer.toString(); } else { return null; } } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (ClientProtocolException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } return null; } /** * 发送请求,如果失败会返回null * @param url * @param str * @return */ public static String post(String url, String str) { // 处理请求地址 try { URI uri = new URI(url); HttpPost post = new HttpPost(uri); post.setEntity(new StringEntity(str, Charset)); // 执行请求 HttpResponse response = client.execute(post); if (response.getStatusLine().getStatusCode() == 200) { // 处理请求结果 StringBuffer buffer = new StringBuffer(); InputStream in = null; try { in = response.getEntity().getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(in,"utf-8")); String line = null; while ((line = reader.readLine()) != null) { buffer.append(line); } } finally { // 关闭流 if (in != null) in.close(); } return buffer.toString(); } else { return null; } } catch (URISyntaxException | IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } /** * 发送GET方式的请求,并返回结果字符串。 * <br> * 时间:2017年2月27日,作者:http://wallimn.iteye.com * @param url * @return 如果失败,返回为null */ public static String get(String url) { try { URI uri = new URI(url); HttpGet get = new HttpGet(uri); HttpResponse response = client.execute(get); if (response.getStatusLine().getStatusCode() == 200) { StringBuffer buffer = new StringBuffer(); InputStream in = null; try { in = response.getEntity().getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(in,Charset)); String line = null; while ((line = reader.readLine()) != null) { buffer.append(line); } } finally { if (in != null) in.close(); } return buffer.toString(); } else { return null; } } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClientProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }
四、Controller中相关的代码
package com.wallimn.bzslql.controller; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.h2.util.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.wallimn.weixin.Config; import com.wallimn.weixin.HttpUtils; import com.wallimn.weixin.PayUtils; import com.wallimn.weixin.UnifiedOrder; import com.wallimn.weixin.User; /** * 与微信相关的处理Controller * * @author wallimn * */ @Controller @RequestMapping("/weixin") public class WeixinController extends CommonController { private static Logger log = LogManager.getLogger(WeixinController.class); @Autowired private HttpServletRequest request; @Autowired protected HttpServletResponse response; /** * 接受用户提供的信息,生成标准的统一订单,提交到微信平台进行处理,提交成功后,返回JSON数据, * 公众号网页依据此JSON内容,调用微信平台的接口、输入密码,完成支付 * * @param body * @param detail * @param out_trade_no * @param total_fee * @return * @throws Exception * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ @RequestMapping("/unifiedorder") @ResponseBody public String unifiedorder(String body, String detail, String out_trade_no, String total_fee) throws Exception { String spbill_create_ip = request.getRemoteAddr(); String openid = this.getLoginUserId(); log.info("创建定单:body:{}, detail:{},out_trade_no:{},total_fee:{}", body, detail, out_trade_no, total_fee); UnifiedOrder order = PayUtils.getUnifiedOrder(body, "", openid, out_trade_no, spbill_create_ip, total_fee, "JSAPI"); if (PayUtils.checkUnifiedOrder(order) == true) { Map<String, String> map = PayUtils.BeanToMap(order); String sign = PayUtils.signature(map, Config.mch_secret); log.info("签名:" + sign); map.put("sign", sign);//加入签名 String orderXml = PayUtils.MapToXML(map);//生成xml定单数据 log.info("统一定单:" + orderXml); String result = PayUtils.postUnifiedOrder(orderXml); log.info("统一定单结果:" + result); // String result = WechatOrderUtils.createOrder(detail, body, // openid, ip, goodSn, orderSn, total_fee, "JSAPI"); return result; } else { return this.getFailJSON("FAIL", "定单参数错误!"); } } /** * 微信支付回调函数。 当用户支付成功后,通过此地址通知操作结果 * 该地址在向微信发送统一定单时,告知微信平台。 * * @param request * @param response * @throws IOException * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ @RequestMapping(value = "/paycallback", method = RequestMethod.POST) public void payCallBack(HttpServletRequest request, HttpServletResponse response) throws IOException { InputStream is = request.getInputStream(); Map<String, String> map = PayUtils.XmlToMap(is); String result_code = map.get("result_code"); // 支付成功 if ("SUCCESS".equals(result_code)) { String sign = map.get("sign"); // System.out.println(map.toString()); // 2、克隆传入的信息并进行验签 map.remove("sign"); String sign2 = PayUtils.signature(map, Config.mch_secret); if (StringUtils.equals(sign, sign2)) { log.info("微信支付回调函数,验答正确。处理业务逻辑。"); // TODO:添加业务处理逻辑 } else { log.error("微信支付回调函数:验签错误!"); } } else if ("FAIL".equals(result_code)) { log.error("微信支付回调函数:失败!失败代码为:{}", map.get("return_code")); } // 返回信息,防止微信重复发送报文 // 由于微信后台会同时回调多次,所以需要做防止重复提交操作的判断 // 此处放防止重复提交操作 String result = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml>"; PrintWriter out = new PrintWriter(response.getOutputStream()); out.print(result); out.flush(); out.close(); } /** * 支付操作准备界面(测试用)。 * 注意映射地址为:/weinxin/paytest/170228,那么在微信设置界面中,填写的授权地址应为:/weinxin/paytest/ * 这是一个坑 * * @return * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ @RequestMapping("/paytest/170228") public String paytest() { return "paytest"; } /** * 支付操作准备界面。 * 注意映射地址为:/weinxin/paytest/170228,那么在微信设置界面中,填写的授权地址应为:/weinxin/payreal/ * * @return * <br> * 时间:2017年2月27日<br> * 作者:http://wallimn.iteye.com<br> * 联系:54871876@qq.com */ @RequestMapping("/payreal/170228") public String payreal() { return "payreal"; } }
五、公众号页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <!DOCTYPE html> <!-- 作者: wallimn, http://wallimn.iteye.com 时间:2017年1月26日 功能: --> <html lang="zh-cn"> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>JSAPI方式支付调起页面</title> <c:set var="ctx" value="${pageContext.request.contextPath}"></c:set> <script type="text/javascript" src="${ctx }/js/jquery-1.11.1.min.js"></script> <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> <script type="text/javascript"> function submit(){ $("#actionResult").text(""); $.ajax({ type: 'POST', url: '${ctx }/weixin/unifiedorder?dt='+(new Date()).getTime(), data: {'detail':'测试detail','body':'商户名-商品名','out_trade_no':(new Date()).getTime().toString(16),'total_fee':$("input[name=total_fee]").val()}, success: function(json){ if(!json || !json.appId){ $("#actionResult").text("参数错误!"); return ; } wxpay(json); }, dataType: "json"}); } function wxpay(json){ function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId" :json.appId, //公众号名称,由商户传入 "timeStamp":json.timeStamp, //时间戳,自1970年以来的秒数 "nonceStr" :json.nonceStr, //随机串 "package" : json.package, "signType" :json.signType, //微信签名方式: "paySign" :json.paySign //微信签名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) { $("#actionResult").text("支付成功!"); } // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 else{ $("#actionResult").text("支付失败:"+JSON.stringify(res)); } } ); } 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(){ $("#sub").click(function(){ submit(); }); $("#actionResult").text("WeixinJSBridge"); }); </script> </head> <body> <div style="text-align:center"> <div id="actionResult">注意单位为(分)</div> <input type="text" placeholder="请输入金额(分)" name="total_fee" value="1"><br> <input type="button" id="sub" value="支付"> </div> </body> </html>
发表评论
-
gradle编译错误:Could not find method compile() for arguments
2020-09-19 10:50 18788编译(IDEA+Gradle)一个别人的工程,出现一个 ... -
netty心跳检查之UDP篇
2019-09-15 08:50 2590部分UDP通信场景中,需要客户端定期发送心跳信息,以获取终 ... -
解决tomcat部署两个SpringBoot应用提示InstanceAlreadyExistsException
2019-06-30 11:49 3608两个SpringBoot应用部署在一个Tomcat中,单独 ... -
Eclipse配置MyBatis代码自动化功能
2019-06-29 10:16 19221.安装插件 Eclipse中,Help->Ecli ... -
vue.js中使用qrcode生成二维码
2019-05-20 00:00 7732一、安装包 npm install qrcodejs2 --s ... -
MySQL插入数据报错: Incorrect string value: '\xFD\xDE'
2019-03-31 23:19 1317我MySQL数据库用的uft-8字符集,插入数据一直很正常 ... -
vue自定义组件并双向绑定属性
2019-03-08 22:46 3305做了两个子组件,原理基本一样,一个是使用原生的select ... -
vue-router简单示例
2019-03-05 00:32 1211写个基本完整、稍有借鉴意义的示例,防止自己忘记。 &l ... -
“联通充值系统繁忙”轻松应对
2019-02-06 11:03 4052大过年的,联通充个值一直报“充值系统繁忙”。昨天晚上试了几 ... -
electron.js数据库应用---导航菜单(element-ui+mysql)
2019-02-05 21:33 2446一、环境搭建 略, ... -
electron.js数据库应用---入门(mysql+element-ui)
2019-01-27 23:19 7623我的机器:Windows10,64 ... -
SpringMVC 在controller层中注入成员变量request,是否线程安全
2018-12-17 21:17 2824@RestController public class ... -
VueJS 组件参数名命名与组件属性转化
2018-12-03 00:00 2138转自:https://www.cnblogs.com/meiy ... -
vue-resource拦截器实现token发送及检验自动化
2018-11-16 22:38 3116用了很长时间vue-resource,最近思考$http发 ... -
element-ui试用手记
2018-10-29 20:25 1798element-ui、iviewui都以vue.js为基础 ... -
iviewui中表格控件中render的使用示例
2018-07-07 16:46 9843示例了如何在表格中显示按钮,如何将代码转化为文字。 i ... -
Tomcat错误“Alias name tomcat does not identify a key entry”解决
2018-07-05 21:39 6778申请到了阿里云的证书后,下载、按照说明生成jks格式证书、 ... -
阿里云免费证书“fileauth.txt内容配置错误”解决
2018-07-05 20:43 5398最近研究微信小程序开发,上阿里云申请了个证书,使用文件验证 ... -
springboot2.0跨域配置
2018-07-04 22:11 5327springboot2.0跨域配置: 一、代码 ... -
微信小程序使用code换openid的方法(JAVA、SpringBoot)
2018-07-01 21:52 10501微信小程序序的代码中提示,使用code换取openid,但 ...
相关推荐
asp.net微信公众号微信支付接口实例源码
PHP微信公众号微信支付完整示例-Demo,只需修改配置几个参数就可以正常运行。里面附含有说明
微信公众号开发相关方法微信公众号开发相关方法微信公众号开发相关方法微信公众号开发相关方法微信公众号开发相关方法微信公众号开发相关方法微信公众号开发相关方法微信公众号开发相关方法微信公众号开发相关方法...
本人从taobao上发300 元购买回来的 程序虽然不是最新的,但是绝对可以用 里面有详细的配置说明,按照配置对应填上就行了。
微信支付必须微信支付代码集合 前后端代码都有 可以结合我发的博客内容来读 有问题私聊我
微信公众号的开发文档微信公众号的开发文档微信公众号的开发文档微信公众号的开发文档微信公众号的开发文档微信公众号的开发文档微信公众号的开发文档微信公众号的开发文档微信公众号的开发文档微信公众号的开发文档...
PHP 淘宝客+京东+拼多多三合一导购返佣系统源码 带公众号微信端+H5 端,可封装 APP,带搭建教程
微信公众号支付需要到的工具类和我个人支付的代码都在里面。 博客:http://blog.csdn.net/aofavx/article/details/52220394
微信公众平台管理系统 微信公众号 微信开发 微信商城 dtcms框架,包含微网站、微商城、客户管理、用户管理、菜单管理等
微信公众号支付是基于微信公众号而开发的支付接口,在已有的公众号里可以添加 ASP 的公众号支付,微信中生成订单后,直接调出微信钱包支付,非常方便,同样支持自动更新订单状态。 H5 支付是手机浏览器使用的接口,...
基于PHP开发一款淘宝客商城系统源码,是一款淘宝客京东拼多多三合一公众号系统,包含了公众号微信端、H5端和封装APP 。本程序完整,包括常用素材文件 常用图片文件,总之非常全面。 淘宝客公众号京东淘宝拼多多...
2019年微信官方的demo文件又改动了,这是基于最新的微信官方代码封装成的微信支付类,只需要填写配置,即可直接调用,适用于微信公众号开发时的支付业务。一个单文件,不但包含了微信支付的类,还包含的类调用,js...
公众号调起微信支付最新最全jar包
完整的微信公众号支付代码,可以参考博客:https://mp.csdn.net/postedit/80789991
微信支付类-含公众号支付和刷卡支付实例微信支付类-含公众号支付和刷卡支付实例微信支付类-含公众号支付和刷卡支付实例微信支付类-含公众号支付和刷卡支付实例微信支付类-含公众号支付和刷卡支付实例微信支付类-含...
微信公众号支付Java实现Demo
PHP微信公众号管理系统 PHP微信公众号管理系统 PHP微信公众号管理系统 PHP微信公众号管理系统 PHP微信公众号管理系统 PHP微信公众号管理系统 PHP微信公众号管理系统 PHP微信公众号管理系统 PHP微信公众号...
微信HOOK、微信机器人 wxhook,数据库解密 微信公众号采集 微信公众号爬虫,企业微信HOOK.zip微信HOOK、微信机器人 wxhook,数据库解密 微信公众号采集 微信公众号爬虫,企业微信HOOK.zip微信HOOK、微信机器人 ...
微信外浏览器h5支付,微信公众号支付,别人已经封装好的jar还不错,经过测试可以使用,需要注意的地方看我博客
微信小程序的优势在于它方便快捷、轻量级、跨平台、丰富的推广方式、丰富的功能接口、数据分析与优化、结合微信支付、支持多场景应用、社交功能以及多端同步等。这些优点使得小程序能够满足用户的多种需求,提供更好...