作者: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)如果重复会导致支付失败。
  二、测试用户要加入白名单。

  微信的流程图有一点点儿不太直观,我画了一个简单的,应该一看就会明白。
[align=center]

[img]http://dl2.iteye.com/upload/attachment/0123/3788/17b646bb-f76f-3306-bd7b-770674a27932.png[/img]
[/align]

  程序的主体代码如下。

一、几个基本的辅助类
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>

公众号微信支付开发手记相关推荐

  1. 微信服务号 微信支付开发

    微信支付,须要通过微信支付验证 眼下.支付仅限服务号, 做微信支付开发,主要看开发文档 统一下单. 订单查询 退款等 1.发起支付.都是通过h5发起的,首先获取prepay_id 发起支付,须要统一下 ...

  2. vue开发项目微信公众号授权支付开发

    一.注册微信公众号服务号并填写企业信息(个人订阅号没有开发微信支付的权限) 链接: https://mp.weixin.qq.com/ 二.在微信公众号内进行微信认证(3-5个工作日) 三.在微信公众 ...

  3. weixin-java-pay实现公众号微信支付与退款

    内容来自:https://www.jianshu.com/p/0a0ccc15cb80 pom.xml 文件 需要在 pom.xml 加入以下依赖! <dependency><gro ...

  4. 微信sdk 公众号 微信支付 NFC 坑笔记

    微信sdk--微信支付--NFC项目 坑&笔记 微信公众号开发的环境配置 准备工作 & 微信公众平台配置 & 环境配置 微信的登录流程(即授权机制)遵循OAuth2.0标准,实 ...

  5. uniapp公众号微信支付

    1.安装jssdk npm install jweixin-module --save 2.调用 <template><viewstyle="width: 100%;hei ...

  6. Vue公众号微信支付

    需要获取的支付信息: openid.attach(备注).body(支付标题).out_trade_no(订单号).total_fee(订单价格,单位分,100是一元).goods_tag(订单类型) ...

  7. 公众号微信支付ios和android,uni-app微信公众号支付和分享,特别是ios下的配置,完美解决...

    一.支付 由于在ios中uni-app发布的应用是单应用,不管访问哪个页面,始终记录的是首次进来的那个页面. 这样的话,在微信支付签名时会报签名不对的错误.怎么解决? 老王的解决方案是在main.js ...

  8. php 公众号微信支付流程,微信公众号支付完整流程案例

    简介 微信公众号支付,顾名思义就是必须在微信中实现支付,并且需要公众号配合. 教程 由于我们使用的是第三方封装好的接口,这里省去了我们自己配置公众号.为什么用第三方?因为个人没有申请权限. 交互细节: ...

  9. 公众号微信支付ios和android,【微信支付】

    跳转到还款小程序 var extraData = { appid:wxcbda96de0b165486, sub_appid:wxcbda96de0b165482, mch_id:1900009231 ...

最新文章

  1. 计算机图形学——三角形网格
  2. node学习笔记--模块加载
  3. 回归与梯度下降法及实现原理
  4. linux下Vim和Terminal配色
  5. eclipse中Android模拟器常见的问题解决方法汇总
  6. python/Django(增、删、改、查)操作
  7. Flask最强攻略 - 跟DragonFire学Flask - 第四篇 Flask 中的模板语言 Jinja2 及 render_template 的深度用法
  8. Adsense加入黑名单的预防办法
  9. CSS解决无空格太长的字母,数字不会自动换行的问题
  10. 重点推荐:HP大中华区总裁孙振耀退休感言
  11. 大学生数学建模赛题解析及优秀论文-2021电工杯A题高铁牵引供电系统运行数据分析及等值建模(附Python代码)
  12. ccccccccccc
  13. curl encode
  14. Android事件传递
  15. [HAOI2007]理想的正方形(单调队列)
  16. Win10安装Apache和PHP
  17. 湖南对口升学计算机专科学院,湖南省计算机对口升学的大学有哪些
  18. 技术团队里什么样的人会被清除?抢老板的工作干合适吗?
  19. 知道2020年计算机二级考试成绩,2020年9月计算机二级考试成绩可以查了 多少分及格...
  20. Nuxt中全局路由守卫的写法

热门文章

  1. 手机电池的正确保养方法
  2. webBrowser中打不开http://subway.simba.taobao.com页面解决办法
  3. Mysql数据库---学习总结(一)
  4. 直播卖货系统如何保证连麦音质?来看看回声消除
  5. python绘制蜡烛图_一步一步教你用Python画出专业的K线图
  6. 微信小程序:rich-text 富文本中图片自适应
  7. 网站添加百度影音的方法
  8. 深圳地区信用报告现场查询温馨提示
  9. ABAP ALV LVC三种选择框设置方式
  10. AntDesign前端表格排序