springboot 微信支付

微信支付第三弹--SpringBoot整合微信APP支付

原文链接:https://blog.csdn.net/qq_37345604/article/details/93039953

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_37345604/article/details/93039953

吐槽

 做完APP微信支付,就两个字:心累,并不是这个功能有多难,就是想吐槽一下微信,太TMD的店大欺客了!签名,呵呵,参数顺序都得按照他们的排序。。。。。。。。

吐槽归吐槽,还是做一下知识复盘,下面是做APP微信支付步骤和代码,框架用的是SpringBoot

步骤

必备参数:

 ①:appid:微信开放平台上面的应用appid,和公众号appid不同

②:mch_id:商户ID,微信商户平台上商户信息

③:key:商户key(API秘钥)

        登录微信商户平台--->账户中心--->API安全--->设置秘钥

步骤

    ①、根据账号参数拼接进行签名

    ②、根据参数和签名发起微信统一下单接口

    ③、把微信统一下单返回参数返回移动端

    ④、移动端根据参数拉起微信支付

    ⑤、支付成功后微信进行回调通知

    ⑥、判断微信返回状态,成功处理当前平台业务并返回微信return_code和return_msg两个参数,不然微信会一直进行回调

参数配置

  1.  
    #微信APP支付参数
  2.  
    wxpayconfig:
  3.  
    #商户应用appId
  4.  
    appid: wx383123456fbb7826
  5.  
    #商户ID
  6.  
    mch_id: 1234567011
  7.  
    #设备号
  8.  
    device_info: WEB
  9.  
    #商户key:api秘钥(32位)
  10.  
    key: VfnmAMI111111111EQjhvglWzDDO
  11.  
    #统一下单接口
  12.  
    url: https://api.mch.weixin.qq.com/pay/unifiedorder
  13.  
    #回调接口
  14.  
    notify_url: http://baidu.com/home/wechatnotify
  15.  
    wx_package: Sign=WXPay

微信配置类

  1.  
    /**
  2.  
    * WxpayConfig.java
  3.  
    * com.prereadweb.order.config
  4.  
    * Copyright (c) 2019,
  5.  
    */
  6.  
    package com.prereadweb.order.config;
  7.  
     
  8.  
    import lombok.Data;
  9.  
    import org.springframework.boot.context.properties.ConfigurationProperties;
  10.  
    import org.springframework.stereotype.Component;
  11.  
     
  12.  
    /**
  13.  
    * @Description: 微信配置类
  14.  
    * @author: Administrator
  15.  
    * @date: 2019/6/17 19:35
  16.  
    */
  17.  
    @Data
  18.  
    @Component
  19.  
    @ConfigurationProperties(prefix="wxpayconfig")
  20.  
    public class WxpayConfig {
  21.  
     
  22.  
    private String appid; // 公众账号ID
  23.  
     
  24.  
    private String mch_id; // 商户号
  25.  
     
  26.  
    private String device_info; // 设备号
  27.  
     
  28.  
    private String key; // 商户的key【API密匙】
  29.  
     
  30.  
    private String url; // api请求地址
  31.  
     
  32.  
    private String notify_url; // 服务器异步通知页面路径
  33.  
     
  34.  
    private String return_url; // 服务器同步通知页面路径
  35.  
    private String wx_package;
  36.  
    }

统一下单代码

controller层

  1.  
    /**
  2.  
    * @Function: 去支付
  3.  
    * @author: YangXueFeng
  4.  
    * @Date: 2019/6/14 16:46
  5.  
    */
  6.  
    @RequestMapping("/gowechatpay")
  7.  
    public Object goWeChatPay(@Param("orderId") Long orderId, HttpServletRequest request, HttpServletResponse response) throws Exception {
  8.  
    return weChatService.goWeChatPay(orderId, request);
  9.  
    }

 service层代码

  1.  
    /**
  2.  
    * @Function: 去支付
  3.  
    * @author: YangXueFeng
  4.  
    * @Date: 2019/6/14 16:50
  5.  
    */
  6.  
    @Override
  7.  
    public Map<String, Object> goWeChatPay(Long orderId, HttpServletRequest request) {
  8.  
    Map<String, Object> map = new HashMap<>();
  9.  
    if(Util.isEmpty(orderId)) {
  10.  
    map.put("code", UserStatusEnum.ERROR.intKey());
  11.  
    map.put("msg", UserStatusEnum.ERROR.value());
  12.  
    return map;
  13.  
    }
  14.  
    //获取订单信息
  15.  
    PayParameterForm payParameter = orderMapper.getPayParameter(orderId);
  16.  
    double price = payParameter.getActualPrice();
  17.  
    System.out.println("price:" + price);
  18.  
    // 微信开放平台审核通过的应用APPID
  19.  
    System.out.println("appid是:" + wxpayconfig.getAppid());
  20.  
    System.out.println("mch_id是:" + wxpayconfig.getMch_id());
  21.  
    String nonce_str = Util.getRandomString(30);
  22.  
    System.out.println("随机字符串是:" + nonce_str);
  23.  
    int total_fee = (int) (price * 100);
  24.  
     
  25.  
    String total_price = null;// 订单总金额,单位为分,详见支付金额
  26.  
    String spbill_create_ip = WXSignUtils.getRemortIP(request);// "127.0.0.1";
  27.  
    System.out.println("spbill_create_ip===="+spbill_create_ip);
  28.  
    String notify_url = wxpayconfig.getNotify_url();
  29.  
    System.out.println("notify_url是:" + notify_url);
  30.  
    String trade_type = "APP";
  31.  
     
  32.  
    // 参数:开始生成签名
  33.  
    SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
  34.  
    parameters.put("appid", wxpayconfig.getAppid());
  35.  
    parameters.put("body", payParameter.getTitle());
  36.  
    parameters.put("mch_id", wxpayconfig.getMch_id());
  37.  
    parameters.put("nonce_str", nonce_str);
  38.  
    parameters.put("notify_url", notify_url);
  39.  
    parameters.put("out_trade_no", String.valueOf(payParameter.getOrderId()));
  40.  
    /*
  41.  
    parameters.put("total_fee", total_fee);
  42.  
    */
  43.  
    parameters.put("spbill_create_ip",spbill_create_ip);
  44.  
    parameters.put("total_fee", 1);
  45.  
    parameters.put("trade_type", trade_type);
  46.  
    String sign = WXSignUtils.createSign("UTF-8", parameters);
  47.  
    System.out.println("签名是:" + sign);
  48.  
    Unifiedorder unifiedorder = new Unifiedorder();
  49.  
    unifiedorder.setAppid(wxpayconfig.getAppid());
  50.  
    unifiedorder.setBody(payParameter.getTitle());
  51.  
    unifiedorder.setMch_id(wxpayconfig.getMch_id());
  52.  
    unifiedorder.setNonce_str(nonce_str);
  53.  
    unifiedorder.setNotify_url(notify_url);
  54.  
    unifiedorder.setOut_trade_no(String.valueOf(payParameter.getOrderId()));
  55.  
    unifiedorder.setSpbill_create_ip(spbill_create_ip);
  56.  
    unifiedorder.setTotal_fee(1);
  57.  
    unifiedorder.setTrade_type(trade_type);
  58.  
    unifiedorder.setSign(sign);
  59.  
     
  60.  
    // 构造xml参数
  61.  
    String xmlInfo = HttpXmlUtils.xmlInfo(unifiedorder);
  62.  
    System.out.println("xmlInfo:" + xmlInfo);
  63.  
     
  64.  
    String wxUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
  65.  
    String method = "POST";
  66.  
    String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString();// 请求微信
  67.  
    System.out.println("weixinPost:" + weixinPost);
  68.  
    UnifiedorderResult unifiedorderResult = ParseXMLUtils.jdomParseXml(weixinPost);// 解析微信的反馈
  69.  
    if (unifiedorderResult != null) {
  70.  
    if ("SUCCESS".equals(unifiedorderResult.getReturn_code())) {
  71.  
    if("INVALID_REQUEST".equals(unifiedorderResult.getErr_code())){
  72.  
    map.put("code", UserStatusEnum.ERROR.intKey());
  73.  
    map.put("msg", "参数错误");
  74.  
    return map;
  75.  
    }
  76.  
    // 开始拼接App调起微信的参数
  77.  
    SortedMap<Object, Object> wxAppparameters = new TreeMap<Object, Object>();
  78.  
    wxAppparameters.put("appid", unifiedorderResult.getAppid());
  79.  
    wxAppparameters.put("partnerid", unifiedorderResult.getMch_id());
  80.  
    wxAppparameters.put("prepayid", unifiedorderResult.getPrepay_id());
  81.  
    wxAppparameters.put("package", wxpayconfig.getWx_package());
  82.  
    wxAppparameters.put("noncestr", nonce_str);
  83.  
    wxAppparameters.put("timestamp", String.valueOf(new Date().getTime()).substring(0, 10));
  84.  
    wxAppparameters.put("sign", WXSignUtils.createSign("UTF-8", wxAppparameters));
  85.  
    map.put("code", UserStatusEnum.SUCCESS.intKey());
  86.  
    map.put("msg", UserStatusEnum.SUCCESS.value());
  87.  
    map.put("data", wxAppparameters);
  88.  
    return map;
  89.  
    } else {
  90.  
    System.out.println("错误原因为:" + unifiedorderResult.getReturn_msg());
  91.  
    map.put("code", UserStatusEnum.ERROR.intKey());
  92.  
    map.put("msg", unifiedorderResult.getReturn_msg());
  93.  
    return map;
  94.  
    }
  95.  
    } else {
  96.  
    System.out.println("服务端请求微信的返回值异常。");
  97.  
    map.put("code", UserStatusEnum.ERROR.intKey());
  98.  
    map.put("msg", "服务端请求微信的返回值异常。");
  99.  
    return map;
  100.  
    }
  101.  
    }

此处签名时参数的排序如下↓

谨记!这是微信挖的一个坑,如果不按照这个排序的话,你的统一下单接口很有可能会一直返回:签名失败

返回参数:

微信回调函数

controller层

声明:此处要有返回值,如果你的项目配置了拦截器,记得把这个方法的路径放开,不然微信会访问不到

  1.  
    /**
  2.  
    * @Function: 微信支付回调
  3.  
    * @author: YangXueFeng
  4.  
    * @Date: 2019/6/18 18:50
  5.  
    */
  6.  
    @RequestMapping("/wechatnotify")
  7.  
    public String weChatNotify(HttpServletRequest request) {
  8.  
    String returnXML = null;
  9.  
    try {
  10.  
    returnXML = weChatService.weChatNotify(request);
  11.  
    } catch (Exception e) {
  12.  
    e.printStackTrace();
  13.  
    }
  14.  
    return returnXML;
  15.  
    }

service层

  1.  
    /**
  2.  
    * @Function: 微信回调接口
  3.  
    * @author: YangXueFeng
  4.  
    * @Date: 2019/6/17 11:05
  5.  
    */
  6.  
    @Override
  7.  
    public String weChatNotify(HttpServletRequest request) throws Exception {
  8.  
    Map<String, String> map = new HashMap<>();
  9.  
    System.out.println("----------------微信回调开始啦----------------------");
  10.  
    // 读取参数
  11.  
    InputStream inputStream;
  12.  
    StringBuffer sb = new StringBuffer();
  13.  
    inputStream = request.getInputStream();
  14.  
    String s;
  15.  
    BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
  16.  
    while ((s = in.readLine()) != null) {
  17.  
    sb.append(s);
  18.  
    }
  19.  
    in.close();
  20.  
    inputStream.close();
  21.  
    // 解析xml成map
  22.  
    Map<String, String> m = new HashMap<String, String>();
  23.  
    m = WXSignUtils.doXMLParse(sb.toString());
  24.  
    // 过滤空 设置 TreeMap
  25.  
    SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
  26.  
    Iterator<String> it = m.keySet().iterator();
  27.  
    while (it.hasNext()) {
  28.  
    String parameter = it.next();
  29.  
    String parameterValue = m.get(parameter);
  30.  
     
  31.  
    String v = "";
  32.  
    if (null != parameterValue) {
  33.  
    v = parameterValue.trim();
  34.  
    }
  35.  
    System.out.println("p:" + parameter + ",v:" + v);
  36.  
    packageParams.put(parameter, v);
  37.  
    }
  38.  
    // 微信支付的API密钥
  39.  
    String key = wxpayconfig.getKey();
  40.  
    if(!isTenpaySign("UTF-8", packageParams, key)){
  41.  
    map.put("return_code", "FAIL");
  42.  
    map.put("return_msg", "return_code不正确");
  43.  
    return StringUtil.GetMapToXML(map);
  44.  
    }
  45.  
    //返回状态存入redis中
  46.  
    if(m.get("return_code").equals("SUCCESS")){
  47.  
    RedisUtil.set("wx"+m.get("out_trade_no"),m.get("result_code"),300);
  48.  
    }
  49.  
    if (isTenpaySign("UTF-8", packageParams, key)) {
  50.  
    // 验证通过
  51.  
    if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
  52.  
    String out_trade_no = (String) packageParams.get("out_trade_no");
  53.  
    /* 订单不为空 */
  54.  
    if (!Util.isEmpty(out_trade_no)) {
  55.  
    //支付成功后的业务处理
  56.  
    OrderEntity order = orderMapper.getOrderInfo(Long.valueOf(out_trade_no));
  57.  
    if(!Util.isEmpty(order)){
  58.  
    order.setStatus(CalculatStaticConstant.CHECK_ONE);
  59.  
    order.setCompleteTime(DateUtil.currentDate());
  60.  
    orderMapper.updateOrder(order);
  61.  
    System.out.println("----------------修改订单状态----------------------");
  62.  
    }
  63.  
    /* 添加支付信息 */
  64.  
    OrderPayEntity orderPay = new OrderPayEntity();
  65.  
    orderPay.setId(Long.valueOf(IdUtils.getPrimaryKey()));
  66.  
    orderPay.setOrderId(order.getId());
  67.  
    orderPay.setUserId(order.getUserId());
  68.  
    orderPay.setPayPrice(order.getActualPrice());
  69.  
    orderPay.setPayType(PayTypeEnum.WE_CHAT_PAY.intKey());
  70.  
    orderPay.setStatus(CalculatStaticConstant.CHECK_ONE);
  71.  
    orderPay.setPayTime(DateUtil.currentDate());
  72.  
    orderMapper.saveOrderPay(orderPay);
  73.  
    System.out.println("----------------添加支付信息----------------------");
  74.  
    map.put("return_code", "SUCCESS");
  75.  
    map.put("return_msg", "OK");
  76.  
    return StringUtil.GetMapToXML(map);
  77.  
    }
  78.  
    }
  79.  
    } else {
  80.  
    System.out.println("支付失败");
  81.  
    map.put("return_code", "error");
  82.  
    map.put("return_msg", "支付失败");
  83.  
    return StringUtil.GetMapToXML(map);
  84.  
    }
  85.  
    System.out.println("支付失败");
  86.  
    System.out.println("支付失败");
  87.  
    map.put("return_code", "error");
  88.  
    map.put("return_msg", "支付失败");
  89.  
    return StringUtil.GetMapToXML(map);
  90.  
    }
  1.  
    /**
  2.  
    * @Function: 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
  3.  
    * @author: YangXueFeng
  4.  
    * @Date: 2019/6/17 17:10
  5.  
    */
  6.  
    @SuppressWarnings("rawtypes")
  7.  
    public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams,
  8.  
    String API_KEY) {
  9.  
    StringBuffer sb = new StringBuffer();
  10.  
    Set es = packageParams.entrySet();
  11.  
    Iterator it = es.iterator();
  12.  
    while (it.hasNext()) {
  13.  
    Map.Entry entry = (Map.Entry) it.next();
  14.  
    String k = (String) entry.getKey();
  15.  
    String v = (String) entry.getValue();
  16.  
    if (!"sign".equals(k) && null != v && !"".equals(v)) {
  17.  
    sb.append(k + "=" + v + "&");
  18.  
    }
  19.  
    }
  20.  
    sb.append("key=" + API_KEY);
  21.  
    // 算出摘要
  22.  
    String mysign = MD5Util.MD5Encode(sb.toString()).toLowerCase();
  23.  
    String tenpaySign = ((String) packageParams.get("sign")).toLowerCase();
  24.  
    return tenpaySign.equals(mysign);
  25.  
    }

相关工具类

微信签名工具类

  1.  
    /**
  2.  
    * WXSignUtils.java
  3.  
    * com.prereadweb.order.util
  4.  
    * Copyright (c) 2019,
  5.  
    */
  6.  
    package com.prereadweb.order.util;
  7.  
     
  8.  
    import com.prereadweb.utils.MD5Util;
  9.  
    import org.jdom.Document;
  10.  
    import org.jdom.JDOMException;
  11.  
    import org.jdom.input.SAXBuilder;
  12.  
     
  13.  
    import javax.servlet.http.HttpServletRequest;
  14.  
    import java.io.ByteArrayInputStream;
  15.  
    import java.io.IOException;
  16.  
    import java.io.InputStream;
  17.  
    import java.util.*;
  18.  
     
  19.  
    /**
  20.  
    * @Description: 微信支付签名
  21.  
    * @author: Administrator
  22.  
    * @date: 2019/6/17 15:43
  23.  
    */
  24.  
    public class WXSignUtils {
  25.  
     
  26.  
    /* API秘钥 */
  27.  
    private static String Key ="VfnmAMasdasdasdasWzDDO";
  28.  
     
  29.  
    /**
  30.  
    * 微信支付签名算法sign
  31.  
    * @param characterEncoding
  32.  
    * @param parameters
  33.  
    * @return
  34.  
    */
  35.  
    public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){
  36.  
    StringBuffer sb = new StringBuffer();
  37.  
    Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)
  38.  
    Iterator it = es.iterator();
  39.  
    while(it.hasNext()) {
  40.  
    Map.Entry entry = (Map.Entry)it.next();
  41.  
    String k = (String)entry.getKey();
  42.  
    Object v = entry.getValue();
  43.  
    if(null != v && !"".equals(v)
  44.  
    && !"sign".equals(k) && !"key".equals(k)) {
  45.  
    sb.append(k + "=" + v + "&");
  46.  
    }
  47.  
    }
  48.  
    sb.append("key=" + Key);
  49.  
    System.out.println("字符串拼接后是:"+sb.toString());
  50.  
    String sign = MD5Util.MD5Encode(sb.toString()).toUpperCase();
  51.  
    return sign;
  52.  
    }
  53.  
     
  54.  
    /**
  55.  
    * @Function: 获取IP
  56.  
    * @author: YangXueFeng
  57.  
    * @Date: 2019/6/17 16:44
  58.  
    */
  59.  
    public static String getRemortIP(HttpServletRequest request) {
  60.  
    if (request.getHeader("x-forwarded-for") == null) {
  61.  
    return request.getRemoteAddr();
  62.  
    }
  63.  
    return request.getHeader("x-forwarded-for");
  64.  
    }
  65.  
     
  66.  
    /**
  67.  
    * @Function: 解析XML
  68.  
    * @author: YangXueFeng
  69.  
    * @Date: 2019/6/17 17:07
  70.  
    */
  71.  
    public static Map doXMLParse(String strxml) throws JDOMException,
  72.  
    IOException {
  73.  
    strxml = strxml.replaceFirst("encoding=".*"", "encoding="UTF-8"");
  74.  
     
  75.  
    if (null == strxml || "".equals(strxml)) {
  76.  
    return null;
  77.  
    }
  78.  
     
  79.  
    Map m = new HashMap();
  80.  
     
  81.  
    InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
  82.  
    SAXBuilder builder = new SAXBuilder();
  83.  
    Document doc = builder.build(in);
  84.  
    org.jdom.Element root = doc.getRootElement();
  85.  
    List list = root.getChildren();
  86.  
    Iterator it = list.iterator();
  87.  
    while (it.hasNext()) {
  88.  
    org.jdom.Element e = (org.jdom.Element) it.next();
  89.  
    String k = e.getName();
  90.  
    String v = "";
  91.  
    List children = e.getChildren();
  92.  
    if (children.isEmpty()) {
  93.  
    v = e.getTextNormalize();
  94.  
    } else {
  95.  
    v = getChildrenText(children);
  96.  
    }
  97.  
     
  98.  
    m.put(k, v);
  99.  
    }
  100.  
     
  101.  
    // 关闭流
  102.  
    in.close();
  103.  
     
  104.  
    return m;
  105.  
    }
  106.  
     
  107.  
    public static String getChildrenText(List children) {
  108.  
    StringBuffer sb = new StringBuffer();
  109.  
    if (!children.isEmpty()) {
  110.  
    Iterator it = children.iterator();
  111.  
    while (it.hasNext()) {
  112.  
    org.jdom.Element e = (org.jdom.Element) it.next();
  113.  
    String name = e.getName();
  114.  
    String value = e.getTextNormalize();
  115.  
    List list = e.getChildren();
  116.  
    sb.append("<" + name + ">");
  117.  
    if (!list.isEmpty()) {
  118.  
    sb.append(getChildrenText(list));
  119.  
    }
  120.  
    sb.append(value);
  121.  
    sb.append("</" + name + ">");
  122.  
    }
  123.  
    }
  124.  
     
  125.  
    return sb.toString();
  126.  
    }
  127.  
     
  128.  
    }

统一下单提交微信参数实体类

  1.  
    /**
  2.  
    * UnifiedorderResult.java
  3.  
    * com.prereadweb.order.util
  4.  
    * Copyright (c) 2019,
  5.  
    */
  6.  
    package com.prereadweb.order.util;
  7.  
     
  8.  
    import lombok.Data;
  9.  
     
  10.  
    /**
  11.  
    * @Description: 统一下单提交(微信参数)
  12.  
    * @author: Administrator
  13.  
    * @date: 2019/6/17 16:11
  14.  
    */
  15.  
    @Data
  16.  
    public class UnifiedorderResult {
  17.  
    private String return_code;
  18.  
    private String return_msg;
  19.  
    private String appid;
  20.  
    private String mch_id;
  21.  
    private String device_info;
  22.  
    private String nonce_str;
  23.  
    private String sign;
  24.  
    private String result_code;
  25.  
    private String err_code;
  26.  
    private String err_code_des;
  27.  
    private String trade_type;
  28.  
    private String prepay_id;
  29.  
    }

DOM解析

  1.  
    /**
  2.  
    * ParseXMLUtils.java
  3.  
    * com.prereadweb.order.util
  4.  
    * Copyright (c) 2019, 北京聚智未来科技有限公司版权所有.
  5.  
    */
  6.  
    package com.prereadweb.order.util;
  7.  
     
  8.  
    import com.prereadweb.utils.Util;
  9.  
    import org.dom4j.Document;
  10.  
    import org.dom4j.DocumentException;
  11.  
    import org.dom4j.DocumentHelper;
  12.  
    import org.dom4j.Element;
  13.  
    import org.dom4j.io.SAXReader;
  14.  
    import org.jdom.input.SAXBuilder;
  15.  
    import org.xml.sax.InputSource;
  16.  
     
  17.  
    import java.io.StringReader;
  18.  
    import java.util.Iterator;
  19.  
    import java.util.List;
  20.  
     
  21.  
    /**
  22.  
    * @Description: DOM解析
  23.  
    * @author: Administrator
  24.  
    * @date: 2019/6/17 15:50
  25.  
    */
  26.  
    public class ParseXMLUtils {
  27.  
     
  28.  
    /**
  29.  
    * 1、DOM解析
  30.  
    */
  31.  
    @SuppressWarnings("rawtypes")
  32.  
    public static void beginXMLParse(String xml){
  33.  
    Document doc = null;
  34.  
    try {
  35.  
    doc = DocumentHelper.parseText(xml); // 将字符串转为XML
  36.  
     
  37.  
    Element rootElt = doc.getRootElement(); // 获取根节点smsReport
  38.  
     
  39.  
    System.out.println("根节点是:"+rootElt.getName());
  40.  
     
  41.  
    Iterator iters = rootElt.elementIterator("sendResp"); // 获取根节点下的子节点sms
  42.  
     
  43.  
    while (iters.hasNext()) {
  44.  
    Element recordEle1 = (Element) iters.next();
  45.  
    Iterator iter = recordEle1.elementIterator("sms");
  46.  
     
  47.  
    while (iter.hasNext()) {
  48.  
    Element recordEle = (Element) iter.next();
  49.  
    String phone = recordEle.elementTextTrim("phone"); // 拿到sms节点下的子节点stat值
  50.  
     
  51.  
    String smsID = recordEle.elementTextTrim("smsID"); // 拿到sms节点下的子节点stat值
  52.  
     
  53.  
    System.out.println(phone+":"+smsID);
  54.  
    }
  55.  
    }
  56.  
    } catch (DocumentException e) {
  57.  
    e.printStackTrace();
  58.  
    } catch (Exception e) {
  59.  
    e.printStackTrace();
  60.  
    }
  61.  
    }
  62.  
     
  63.  
    /**
  64.  
    * 2、DOM4j解析XML(支持xpath)
  65.  
    * 解析的时候自动去掉CDMA
  66.  
    * @param xml
  67.  
    */
  68.  
    public static void xpathParseXml(String xml){
  69.  
    try {
  70.  
    StringReader read = new StringReader(xml);
  71.  
    SAXReader saxReader = new SAXReader();
  72.  
    Document doc = saxReader.read(read);
  73.  
    String xpath ="/xml/appid";
  74.  
    System.out.print(doc.selectSingleNode(xpath).getText());
  75.  
    } catch (DocumentException e) {
  76.  
    e.printStackTrace();
  77.  
    }
  78.  
    }
  79.  
     
  80.  
    /**
  81.  
    * 3、JDOM解析XML
  82.  
    * 解析的时候自动去掉CDMA
  83.  
    * @param xml
  84.  
    */
  85.  
    @SuppressWarnings("unchecked")
  86.  
    public static UnifiedorderResult jdomParseXml(String xml){
  87.  
    UnifiedorderResult unifieorderResult = new UnifiedorderResult();
  88.  
    try {
  89.  
    StringReader read = new StringReader(xml);
  90.  
    // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
  91.  
    InputSource source = new InputSource(read);
  92.  
    // 创建一个新的SAXBuilder
  93.  
    SAXBuilder sb = new SAXBuilder();
  94.  
    // 通过输入源构造一个Document
  95.  
    org.jdom.Document doc;
  96.  
    doc = (org.jdom.Document) sb.build(source);
  97.  
     
  98.  
    org.jdom.Element root = doc.getRootElement();// 指向根节点
  99.  
    List<org.jdom.Element> list = root.getChildren();
  100.  
     
  101.  
    if(list != null && list.size() > 0){
  102.  
    boolean flag1 = true;
  103.  
    boolean flag2 = true;
  104.  
    for (org.jdom.Element element : list) {
  105.  
    System.out.println("key是:"+element.getName()+",值是:"+element.getText());
  106.  
     
  107.  
    if("return_code".equals(element.getName())){
  108.  
    if("FAIL".equals(element.getText())){
  109.  
    flag1 = false;
  110.  
    }else{
  111.  
    unifieorderResult.setReturn_code(element.getText());
  112.  
    }
  113.  
    }
  114.  
     
  115.  
    if("return_msg".equals(element.getName())){
  116.  
    if(element.getText() != null && !"OK".equals(element.getText())){//微信支付的第一个坑,这里返回了OK,23333
  117.  
    System.out.println("统一下单参数有误,错误原因为:"+element.getText());
  118.  
    return null;
  119.  
    }
  120.  
    }
  121.  
     
  122.  
    if(flag1){
  123.  
    if("appid".equals(element.getName())){
  124.  
    unifieorderResult.setAppid(element.getText());
  125.  
    }
  126.  
    if("mch_id".equals(element.getName())){
  127.  
    unifieorderResult.setMch_id(element.getText());
  128.  
    }
  129.  
    if("nonce_str".equals(element.getName())){
  130.  
    unifieorderResult.setNonce_str(element.getText());
  131.  
    }
  132.  
    if("sign".equals(element.getName())){
  133.  
    unifieorderResult.setSign(element.getText());
  134.  
    }
  135.  
    if("err_code".equals(element.getName())){
  136.  
    unifieorderResult.setErr_code(element.getText());
  137.  
    }
  138.  
    if("err_code_des".equals(element.getName())){
  139.  
    unifieorderResult.setErr_code_des(element.getText());
  140.  
    }
  141.  
    if("result_code".equals(element.getName())){
  142.  
    if("FAIL".equals(element.getText())){
  143.  
    flag2 = false;
  144.  
    System.out.println("统一下单业务结果有误,无法返回预支付交易会话标识");
  145.  
    }else{
  146.  
    unifieorderResult.setResult_code(element.getText());
  147.  
    }
  148.  
    }
  149.  
    }
  150.  
    if(flag1 && flag2 && flag2 == true){
  151.  
    if("trade_type".equals(element.getName())){
  152.  
    unifieorderResult.setTrade_type(element.getText());
  153.  
    }
  154.  
    if("prepay_id".equals(element.getName())){
  155.  
    System.out.println("统一下单接口成功返回预支付交易会话标识!");
  156.  
    unifieorderResult.setPrepay_id(element.getText());
  157.  
    }
  158.  
    }
  159.  
     
  160.  
    }
  161.  
    return unifieorderResult;
  162.  
    }else{
  163.  
    return null;
  164.  
    }
  165.  
     
  166.  
    } catch (Exception e) {
  167.  
    e.printStackTrace();
  168.  
    return null;
  169.  
    }
  170.  
     
  171.  
    }
  172.  
     
  173.  
    public static boolean parseInt(String key){
  174.  
    if(!Util.isEmpty(key)){
  175.  
    if(key.equals("total_fee")||key.equals("cash_fee")||key.equals("coupon_fee")||key.equals("coupon_count")||key.equals("coupon_fee_0")){
  176.  
    return true;
  177.  
    }
  178.  
    }
  179.  
     
  180.  
    return false;
  181.  
    }
  182.  
     
  183.  
    }

post提交xml格式的参数

  1.  
    /**
  2.  
    * HttpXmlUtils.java
  3.  
    * com.prereadweb.order.util
  4.  
    * Copyright (c) 2019, 北京聚智未来科技有限公司版权所有.
  5.  
    */
  6.  
    package com.prereadweb.order.util;
  7.  
     
  8.  
    import com.prereadweb.order.form.Unifiedorder;
  9.  
     
  10.  
    import javax.net.ssl.HttpsURLConnection;
  11.  
    import java.io.*;
  12.  
    import java.net.HttpURLConnection;
  13.  
    import java.net.MalformedURLException;
  14.  
    import java.net.URL;
  15.  
     
  16.  
    /**
  17.  
    * @Description: post提交xml格式的参数
  18.  
    * @author: Administrator
  19.  
    * @date: 2019/6/17 15:46
  20.  
    */
  21.  
    public class HttpXmlUtils {
  22.  
    /**
  23.  
    * 开始post提交参数到接口
  24.  
    * 并接受返回
  25.  
    * @param url
  26.  
    * @param xml
  27.  
    * @param method
  28.  
    * @param contentType
  29.  
    * @return
  30.  
    */
  31.  
    public static String xmlHttpProxy(String url,String xml,String method,String contentType){
  32.  
    InputStream is = null;
  33.  
    OutputStreamWriter os = null;
  34.  
     
  35.  
    try {
  36.  
    URL _url = new URL(url);
  37.  
    HttpURLConnection conn = (HttpURLConnection) _url.openConnection();
  38.  
    conn.setDoInput(true);
  39.  
    conn.setDoOutput(true);
  40.  
    conn.setRequestProperty("Content-type", "text/xml");
  41.  
    conn.setRequestProperty("Pragma:", "no-cache");
  42.  
    conn.setRequestProperty("Cache-Control", "no-cache");
  43.  
    conn.setRequestMethod("POST");
  44.  
    os = new OutputStreamWriter(conn.getOutputStream());
  45.  
    os.write(new String(xml.getBytes(contentType)));
  46.  
    os.flush();
  47.  
     
  48.  
    //返回值
  49.  
    is = conn.getInputStream();
  50.  
    return getContent(is, "utf-8");
  51.  
    } catch (MalformedURLException e) {
  52.  
    e.printStackTrace();
  53.  
    } catch (IOException e) {
  54.  
    e.printStackTrace();
  55.  
    } finally{
  56.  
    try {
  57.  
    if(os!=null){os.close();}
  58.  
    if(is!=null){is.close();}
  59.  
    } catch (IOException e) {
  60.  
    e.printStackTrace();
  61.  
    }
  62.  
    }
  63.  
    return null;
  64.  
    }
  65.  
     
  66.  
    /**
  67.  
    * 解析返回的值
  68.  
    * @param is
  69.  
    * @param charset
  70.  
    * @return
  71.  
    */
  72.  
    public static String getContent(InputStream is, String charset) {
  73.  
    String pageString = null;
  74.  
    InputStreamReader isr = null;
  75.  
    BufferedReader br = null;
  76.  
    StringBuffer sb = null;
  77.  
    try {
  78.  
    isr = new InputStreamReader(is, charset);
  79.  
    br = new BufferedReader(isr);
  80.  
    sb = new StringBuffer();
  81.  
    String line = null;
  82.  
    while ((line = br.readLine()) != null) {
  83.  
    sb.append(line + " ");
  84.  
    }
  85.  
    pageString = sb.toString();
  86.  
    } catch (Exception e) {
  87.  
    e.printStackTrace();
  88.  
    } finally {
  89.  
    try {
  90.  
    if (is != null){
  91.  
    is.close();
  92.  
    }
  93.  
    if(isr!=null){
  94.  
    isr.close();
  95.  
    }
  96.  
    if(br!=null){
  97.  
    br.close();
  98.  
    }
  99.  
    } catch (IOException e) {
  100.  
    e.printStackTrace();
  101.  
    }
  102.  
    sb = null;
  103.  
    }
  104.  
    return pageString;
  105.  
    }
  106.  
     
  107.  
    /**
  108.  
    * 构造xml参数
  109.  
    * @param xml
  110.  
    * @return
  111.  
    */
  112.  
    public static String xmlInfo(Unifiedorder unifiedorder){
  113.  
    //构造xml参数的时候,至少又是个必传参数
  114.  
    /*
  115.  
    * <xml>
  116.  
    <appid>wx2421b1c4370ec43b</appid>
  117.  
    <attach>支付测试</attach>
  118.  
    <body>JSAPI支付测试</body>
  119.  
    <mch_id>10000100</mch_id>
  120.  
    <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
  121.  
    <notify_url>http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php</notify_url>
  122.  
    <openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
  123.  
    <out_trade_no>1415659990</out_trade_no>
  124.  
    <spbill_create_ip>14.23.150.211</spbill_create_ip>
  125.  
    <total_fee>1</total_fee>
  126.  
    <trade_type>JSAPI</trade_type>
  127.  
    <sign>0CB01533B8C1EF103065174F50BCA001</sign>
  128.  
    </xml>
  129.  
    */
  130.  
     
  131.  
    if(unifiedorder!=null){
  132.  
    StringBuffer bf = new StringBuffer();
  133.  
    bf.append("<xml>");
  134.  
     
  135.  
    bf.append("<appid><![CDATA[");
  136.  
    bf.append(unifiedorder.getAppid());
  137.  
    bf.append("]]></appid>");
  138.  
     
  139.  
    bf.append("<body><![CDATA[");
  140.  
    bf.append(unifiedorder.getBody());
  141.  
    bf.append("]]></body>");
  142.  
     
  143.  
    bf.append("<mch_id><![CDATA[");
  144.  
    bf.append(unifiedorder.getMch_id());
  145.  
    bf.append("]]></mch_id>");
  146.  
     
  147.  
    bf.append("<nonce_str><![CDATA[");
  148.  
    bf.append(unifiedorder.getNonce_str());
  149.  
    bf.append("]]></nonce_str>");
  150.  
     
  151.  
    bf.append("<notify_url><![CDATA[");
  152.  
    bf.append(unifiedorder.getNotify_url());
  153.  
    bf.append("]]></notify_url>");
  154.  
     
  155.  
    bf.append("<out_trade_no><![CDATA[");
  156.  
    bf.append(unifiedorder.getOut_trade_no());
  157.  
    bf.append("]]></out_trade_no>");
  158.  
     
  159.  
    bf.append("<spbill_create_ip><![CDATA[");
  160.  
    bf.append(unifiedorder.getSpbill_create_ip());
  161.  
    bf.append("]]></spbill_create_ip>");
  162.  
     
  163.  
    bf.append("<total_fee><![CDATA[");
  164.  
    bf.append(unifiedorder.getTotal_fee());
  165.  
    bf.append("]]></total_fee>");
  166.  
     
  167.  
    bf.append("<trade_type><![CDATA[");
  168.  
    bf.append(unifiedorder.getTrade_type());
  169.  
    bf.append("]]></trade_type>");
  170.  
     
  171.  
    bf.append("<sign><![CDATA[");
  172.  
    bf.append(unifiedorder.getSign());
  173.  
    bf.append("]]></sign>");
  174.  
     
  175.  
    bf.append("</xml>");
  176.  
    return bf.toString();
  177.  
    }
  178.  
     
  179.  
    return "";
  180.  
    }
  181.  
     
  182.  
     
  183.  
     
  184.  
     
  185.  
    /**
  186.  
    * post请求并得到返回结果
  187.  
    * @param requestUrl
  188.  
    * @param requestMethod
  189.  
    * @param output
  190.  
    * @return
  191.  
    */
  192.  
    public static String httpsRequest(String requestUrl, String requestMethod, String output) {
  193.  
    try{
  194.  
    URL url = new URL(requestUrl);
  195.  
    HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
  196.  
    connection.setDoOutput(true);
  197.  
    connection.setDoInput(true);
  198.  
    connection.setUseCaches(false);
  199.  
    connection.setRequestMethod(requestMethod);
  200.  
    if (null != output) {
  201.  
    OutputStream outputStream = connection.getOutputStream();
  202.  
    outputStream.write(output.getBytes("UTF-8"));
  203.  
    outputStream.close();
  204.  
    }
  205.  
    // 从输入流读取返回内容
  206.  
    InputStream inputStream = connection.getInputStream();
  207.  
    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
  208.  
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
  209.  
    String str = null;
  210.  
    StringBuffer buffer = new StringBuffer();
  211.  
    while ((str = bufferedReader.readLine()) != null) {
  212.  
    buffer.append(str);
  213.  
    }
  214.  
    bufferedReader.close();
  215.  
    inputStreamReader.close();
  216.  
    inputStream.close();
  217.  
    inputStream = null;
  218.  
    connection.disconnect();
  219.  
    return buffer.toString();
  220.  
    }catch(Exception ex){
  221.  
    ex.printStackTrace();
  222.  
    }
  223.  
     
  224.  
    return "";
  225.  
    }
  226.  
    }

将map转换为xml类

  1.  
    /**
  2.  
    * StringUtil.java
  3.  
    * com.prereadweb.utils
  4.  
    * Copyright (c) 2019, 北京聚智未来科技有限公司版权所有.
  5.  
    */
  6.  
    package com.prereadweb.utils;
  7.  
     
  8.  
    import java.util.Map;
  9.  
     
  10.  
    /**
  11.  
    * @Description: String工具类
  12.  
    * @author: Administrator
  13.  
    * @date: 2019/6/19 11:31
  14.  
    */
  15.  
    public class StringUtil {
  16.  
     
  17.  
    /**
  18.  
    * @Function: 将map转换为xml
  19.  
    * @author: YangXueFeng
  20.  
    * @Date: 2019/6/19 11:32
  21.  
    */
  22.  
    public static String GetMapToXML(Map<String,String> param){
  23.  
    StringBuffer sb = new StringBuffer();
  24.  
    sb.append("<xml>");
  25.  
    for (Map.Entry<String,String> entry : param.entrySet()) {
  26.  
    sb.append("<"+ entry.getKey() +">");
  27.  
    sb.append(entry.getValue());
  28.  
    sb.append("</"+ entry.getKey() +">");
  29.  
    }
  30.  
    sb.append("</xml>");
  31.  
    return sb.toString();
  32.  
    }
  33.  
    }

至此微信APP支付完成,功能其实不难,就是微信官方的API有点CD,大部分做微信支付遇到错误基本都是【签名错误】这个东西下面是遇到这个错误的排查方法

【签名错误排查】

1、xml拼接时候排序一定要按照下面这张图,每次说到这里就想吐槽微信*****

2、使用微信签名工具检查签名和程序生成的是否一致,选择MD5、XML,然后把请求的参数xml(xmlInfo)放进去,看看是否校验通过:签名校验

3、如果签名工具校验通过,说明程序没有问题,如果到了这一步,大部分可以锁定是你的账号问题或者是API秘钥的问题了

首先检查账户有没有开通APP支付功能,还有一点,你开通后,微信会给你账户打一笔验证款,你要点击确认和输入金额,这不操作是一定要有的具体操作请参照微信开放平台

4、API秘钥这里有两点需要声明,API秘钥有时候会出现第一次设置的不能使用,需要多设置几次,还有就是微信官方说的,API秘钥设置成功15分钟后才会生效

5、调用微信统一下单时候传的订单金额total_fee参数是int类型,这也是一个坑坑坑

6、调用统一下单接口时传参数名称要和微信上面的一样,千万别出现大写,不然绝对签名错误:统一下单接口传参

7、如果使用签名工具校验没通过,大部分错误是出在编码格式上面,可以把所有编码格式改成UTF-8,如果有参数的值是中文,可以暂时改成英文,如果英文签名成功,基本锁定就是编码格式问题了

大部分错误都是在这几点上,耐下心思慢慢排,可以多百度一下,总有能解决你的问题的博客

原文地址:https://www.cnblogs.com/linwenbin/p/11355925.html