微信小程序支付(后台java)简单详细教学

微信小程序支付(后台java)简单详细教学,新手入门


前言


本文适用于有一定基础的开发者,简单易通。后台用的是java,我用的是springMVC,其他框架基本同理,前端就一个简单的demo。微信提供了v2和v3两种方式,本文基于v2版支付开发。


一、准备工作

准备支付需要的参数材料如下:

/**
 * 支付配置类
 * @author jsy
 * @version 2020/12/24
 **/
public class WechatConfig {
    //小程序appid(开发者在微信公众平台查询)
    public static final String appid ="";
     //小程序appkey(开发者在微信公众平台查询)
    public static final String APP_SECRET=""
    //微信支付的商户id(开发者在微信商户平台查询)
    public static final String mch_id = "";
    //微信支付的商户密钥(开发者在微信商户平台查询)
    public static final String key = "";
    //支付成功后的服务器回调url(后台随便的一个接口能接收到就行)
    public static final String notify_url = "";
    //签名方式,固定值
    public static final String SIGNTYPE = "MD5";
    //交易类型,小程序支付的固定值为JSAPI
    public static final String TRADETYPE = "JSAPI";
    //微信统一下单接口地址
    public static final String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    }

二、正式开始

1.前端demo制作

在这里插入图片描述

index.wxml如下:

<view class="container">
  <view class="container">
    <input type="text" bindinput="getOrderCode" style="border:1px solid #ccc;"  />
    <button type="primary" bindtap="pay">微信支付</button>
</view>
</view>

index.js如下:

const app = getApp()

Page({
  data: {
    txtOrderCode: ''
  },
  pay: function () {
    var ordercode = this.data.txtOrderCode;
    wx.login({
      success: function (res) {
        if (res.code) {
          wx.request({
            url: 'http://192.168.3.33:8080/dangjian/app/wechatPay/wxPay',
            data: {
              code: res.code,//要去换取openid的登录凭证
              money: ordercode,//支付金额(主要是code和money其他都是业务参数可去掉)
              name:"",  
              idCard:"",
              post:"",
              phone:"",
              receiverInfo:"",
              duesDate:"",
              partyOrg:"",
              agyCode:"",
              agyName:"",
              orderId:""
            },
            method: 'GET',
            success: function (res) {
              console.log(res.data)
              //通过接口返回的data,调用此方法唤起支付页面
              wx.requestPayment({
              	//这里的参数和值基本都是固定,不用更改
                timeStamp: res.data.timeStamp,
                nonceStr: res.data.nonceStr,
                package: res.data.package,
                signType: 'MD5',
                paySign: res.data.paySign,
                success: function (res) {
                  console.log("支付成功")
                  console.log(res);
                },
                fail: function (res) {
                  wx.showToast({
                    title: '支付失败',
                    icon:'none',
                    duration:2000
                  })
                },
                complete: function (res) {
                  console.log('支付完成');
                  var url = that.data.url;
                  console.log('get url', url)
                  if (res.errMsg == 'requestPayment:ok') {
                      wx.showModal({
                          title: '提示',
                          content: '支付成功'
                      });
                    }
                  }
              })
            }
          })
        } else {
          console.log('获取用户登录态失败!' + res.errMsg)
        }
      }
    });
},
getOrderCode: function (event) {
  console.log(event)
    this.setData({
      txtOrderCode: event.detail.value
    });
},
  onLoad: function () {
    if (app.globalData.userInfo) {
      this.setData({
        userInfo: app.globalData.userInfo,
        hasUserInfo: true
      })
    } else if (this.data.canIUse){
      // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
      // 所以此处加入 callback 以防止这种情况
      app.userInfoReadyCallback = res => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })
      }
    } else {
      // 在没有 open-type=getUserInfo 版本的兼容处理
      wx.getUserInfo({
        success: res => {
          app.globalData.userInfo = res.userInfo
          this.setData({
            userInfo: res.userInfo,
            hasUserInfo: true
          })
        }
      })
    }
  },
})

2.java代码编写

WxPayDto类:

package com.jeeplus.modules.app.pay.dto;

import java.io.Serializable;

/**
 * @author jsy
 * @description: 缴费entity
 * @date 2020/12/24 15:47
 */
public class WxPayDto implements Serializable {

    private static final long serialVersionUID = 1L;
    private String partyOrg; 
    private String duesDate; 
    private String money;   //缴费金额
    private String name;   
    private String idCard;
    private String post;
    private String phone;
    private String orderId;
    private String receiverInfo;
    private String code;//获取openid临时凭证
    private String agyCode;
    private String agyName;

    public String getAgyCode() {
        return agyCode;
    }

    public void setAgyCode(String agyCode) {
        this.agyCode = agyCode;
    }

    public String getAgyName() {
        return agyName;
    }

    public void setAgyName(String agyName) {
        this.agyName = agyName;
    }

    public String getPartyOrg() {
        return partyOrg;
    }

    public void setPartyOrg(String partyOrg) {
        this.partyOrg = partyOrg;
    }

    public String getDuesDate() {
        return duesDate;
    }

    public void setDuesDate(String duesDate) {
        this.duesDate = duesDate;
    }

    public String getMoney() {
        return money;
    }

    public void setMoney(String money) {
        this.money = money;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getIdCard() {
        return idCard;
    }

    public void setIdCard(String idCard) {
        this.idCard = idCard;
    }

    public String getPost() {
        return post;
    }

    public void setPost(String post) {
        this.post = post;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public String getReceiverInfo() {
        return receiverInfo;
    }

    public void setReceiverInfo(String receiverInfo) {
        this.receiverInfo = receiverInfo;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

业务代码如下:

	@ApiOperation(value = "统一订单", notes = "统一订单")
    @ResponseBody
    @RequestMapping("wxPay")
    public String wxPay(WxPayDto dto, HttpServletRequest request) {
        Object result = new Object();
        try {
            //获取客户端的ip地址
            String spbill_create_ip = getIpAddr(request);
            //获取openid
            String openid = WeAppUtil.getOpenId(dto.getCode());
            //订单号  uuid
            String outTradeNo = IdGen.uuid();
            //支付业务
            result = wxPay(spbill_create_ip, openid, outTradeNo, dto);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return PayUtil.toJson(result);
    }
    //获取IP
    private String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }
    /**
     * 缴纳费用
     *
     * @author jsy
     * @version 2020/12/24
     **/
    public Map wxPay(String spbill_create_ip, String openId, String orderNumber, WxPayDto dto) {
        Map<String, String> payMap = new HashMap<String, String>();//返回给小程序端需要的参数
        try {
            logger.info("【小程序支付】 统一下单开始, 订单编号=" + orderNumber);
            //商品名称
            String body = dto.getAgyName() + "-" + dto.getName() + "-测试费用";
            //获取客户端的ip地址
            BigDecimal ¥ = new BigDecimal(dto.getMoney());
            //组装参数,用户生成统一下单接口的签名
            logger.info("----------下单接口签名-------");
            Map<String, String> packageParams = new HashMap<>();
            //微信分配的小程序ID
            packageParams.put("appid", WechatConfig.appid);
            //微信支付分配的商户号
            packageParams.put("mch_id", WechatConfig.mch_id);
            //随机字符串
            packageParams.put("nonce_str", System.currentTimeMillis() / 1000 + "");
            //签名类型
            packageParams.put("sign_type", "MD5");
            //充值订单 商品描述
            packageParams.put("body", body);
            //商户订单号
            packageParams.put("out_trade_no", orderNumber);
            //订单总金额,单位为分
            packageParams.put("total_fee",.multiply(BigDecimal.valueOf(100)).intValue() + "");
            //终端IP
            packageParams.put("spbill_create_ip", spbill_create_ip);
            //通知回调地址
            packageParams.put("notify_url", WechatConfig.notify_url);
            //交易类型
            packageParams.put("trade_type", WechatConfig.TRADETYPE);
            //用户标识
            packageParams.put("openid", openId);
            //第一次签名
            String sign = WXPayUtil.generateSignature(packageParams, WechatConfig.key);
            packageParams.put("sign", sign);
            //调用支付定义下单API,返回预付单信息 prepay_id
            String result = HttpKit.post(WechatConfig.pay_url, PaymentKit.toXml(packageParams));
            logger.info("调试模式_统一下单接口 返回XML数据:" + result);
            // 将解析结果存储在HashMap中
            Map<String, String> map = PaymentKit.xmlToMap(result);
            String return_code = map.get("return_code");//返回状态码
            String result_code = map.get("result_code");//返回状态码
            if (return_code.equals("SUCCESS") || return_code.equals(result_code)) {
                //返回的预付单信息
                String prepay_id = map.get("prepay_id");
                payMap.put("appId", WechatConfig.appid);
                payMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
                payMap.put("nonceStr", System.currentTimeMillis() + "");
                payMap.put("package", "prepay_id=" + prepay_id);
                payMap.put("signType", "MD5");
                //再次签名,这个签名用于小程序端调用wx.requesetPayment方法
                String paySign = WXPayUtil.generateSignature(payMap, WechatConfig.key);
                logger.info("=======================第二次签名:", paySign + "============ ======");
                payMap.put("paySign", paySign);
                payMap.put("status", "success");
                //更新订单信息
            } else {
                logger.info("----------统一下单失败-------");
                payMap.put("status", "error");
                return payMap;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return payMap;
    }
    
   /**
     *  小程序获取openid
     * @return
     */
    public static String  getOpenId(String code) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        RestTemplate restTemplate = new RestTemplate(factory());
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+APP_KEY+"&secret="+APP_SECRET+"&js_code="+code+"&grant_type=authorization_code";
        ResponseEntity<String> responseEntity =
                restTemplate.getForEntity(url, String.class);
        String body = responseEntity.getBody();
        JSONObject object = JSON.parseObject(body);
        String openId = object.getString("openid");// 获取openId
        System.out.println("该用户的openid——>"+openId);
        return openId;
    }

回调类

    /**
     * 功能描述: <小程序回调>
     * @return:
     * @auther: jsy
     * @date: 2020/12/25
     **/
    @RequestMapping("/wxProPayNotify")
    public void wxProPayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.info("进入微信小程序支付回调");
        String xmlMsg = HttpKit.readData(request);
        logger.info("微信小程序通知信息"+xmlMsg);
        Map<String, String> resultMap = PaymentKit.xmlToMap(xmlMsg);
        if(resultMap.get(RETURN_CODE).equals(SUCCESS)){
            String orderNo = resultMap.get("out_trade_no");
            logger.info("微信小程序支付成功,订单号{}",orderNo);
            //通过订单号 修改数据库中的记录,业务操作  
        }
        String result =  "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        try {
            response.getWriter().write(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3.工具类(粘贴使用)

  • http,https-request类
package com.jeeplus.modules.app.pay.util;


import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.Map;

/**
 * http,https-request类
 * @author jsy
 * @version 2020/12/24
 **/
public class HttpKit {
    private static String CHARSET = "UTF-8";
    private static final SSLSocketFactory sslSocketFactory = initSSLSocketFactory();
    private static final TrustAnyHostnameVerifier trustAnyHostnameVerifier = new HttpKit().new TrustAnyHostnameVerifier();

    private HttpKit() {
    }

    private static SSLSocketFactory initSSLSocketFactory() {
        try {
            TrustManager[] e = new TrustManager[]{new HttpKit().new TrustAnyTrustManager()};
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init((KeyManager[])null, e, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception var2) {
            throw new RuntimeException(var2);
        }
    }

    public static void setCharSet(String charSet) {
        if(charSet!=null && !charSet.equals("")) {
            throw new IllegalArgumentException("charSet can not be blank.");
        } else {
            CHARSET = charSet;
        }
    }

    private static HttpURLConnection getHttpConnection(String url, String method, Map<String, String> headers) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException {
        URL _url = new URL(url);
        HttpURLConnection conn = (HttpURLConnection)_url.openConnection();
        if(conn instanceof HttpsURLConnection) {
            ((HttpsURLConnection)conn).setSSLSocketFactory(sslSocketFactory);
            ((HttpsURLConnection)conn).setHostnameVerifier(trustAnyHostnameVerifier);
        }

        conn.setRequestMethod(method);
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setConnectTimeout(19000);
        conn.setReadTimeout(19000);
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.setRequestProperty("AuthUser-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
        if(headers != null && !headers.isEmpty()) {
            Iterator i$ = headers.entrySet().iterator();

            while(i$.hasNext()) {
                Map.Entry entry = (Map.Entry)i$.next();
                conn.setRequestProperty((String)entry.getKey(), (String)entry.getValue());
            }
        }

        return conn;
    }

    public static String get(String url, Map<String, String> queryParas, Map<String, String> headers) {
        HttpURLConnection conn = null;
        String e;
        try {
            conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), "GET", headers);
            conn.connect();
            e = readResponseString(conn);
        } catch (Exception var8) {
            throw new RuntimeException(var8);
        } finally {
            if(conn != null) {
                conn.disconnect();
            }
        }
        return e;
    }

    public static String get(String url, Map<String, String> queryParas) {
        return get(url, queryParas, (Map)null);
    }

    public static String get(String url) {
        return get(url, (Map)null, (Map)null);
    }

    public static String post(String url, Map<String, String> queryParas, String data, Map<String, String> headers) {
        HttpURLConnection conn = null;

        String var6;
        try {
            conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), "POST", headers);
            conn.connect();
            OutputStream e = conn.getOutputStream();
            e.write(data.getBytes(CHARSET));
            e.flush();
            e.close();
            var6 = readResponseString(conn);
        } catch (Exception var10) {
            throw new RuntimeException(var10);
        } finally {
            if(conn != null) {
                conn.disconnect();
            }
        }
        return var6;
    }

    public static String post(String url, Map<String, String> queryParas, String data) {
        return post(url, queryParas, data, (Map)null);
    }

    public static String post(String url, String data, Map<String, String> headers) {
        return post(url, (Map)null, data, headers);
    }

    public static String post(String url, String data) {
        return post(url, (Map)null, data, (Map)null);
    }

    private static String readResponseString(HttpURLConnection conn) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;

        try {
            inputStream = conn.getInputStream();
            BufferedReader e = new BufferedReader(new InputStreamReader(inputStream, CHARSET));
            String line = null;

            while((line = e.readLine()) != null) {
                sb.append(line).append("\n");
            }

            String var5 = sb.toString();
            return var5;
        } catch (Exception var14) {
            throw new RuntimeException(var14);
        } finally {
            if(inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException var13) {
                }
            }

        }
    }

    private static String buildUrlWithQueryString(String url, Map<String, String> queryParas) {
        if(queryParas != null && !queryParas.isEmpty()) {
            StringBuilder sb = new StringBuilder(url);
            boolean isFirst;
            if(url.indexOf("?") == -1) {
                isFirst = true;
                sb.append("?");
            } else {
                isFirst = false;
            }

            String key;
            String value;
            for(Iterator i$ = queryParas.entrySet().iterator(); i$.hasNext(); sb.append(key).append("=").append(value)) {
                Map.Entry entry = (Map.Entry)i$.next();
                if(isFirst) {
                    isFirst = false;
                } else {
                    sb.append("&");
                }

                key = (String)entry.getKey();
                value = (String)entry.getValue();
                if(value!=null && !value.equals("")) {
                    try {
                        value = URLEncoder.encode(value, CHARSET);
                    } catch (UnsupportedEncodingException var9) {
                        throw new RuntimeException(var9);
                    }
                }
            }

            return sb.toString();
        } else {
            return url;
        }
    }

    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;

        try {
            StringBuilder e = new StringBuilder();
            br = request.getReader();
            String line = null;

            while((line = br.readLine()) != null) {
                e.append(line).append("\n");
            }

            line = e.toString();
            return line;
        } catch (IOException var12) {
            throw new RuntimeException(var12);
        } finally {
            if(br != null) {
                try {
                    br.close();
                } catch (IOException var11) {
                }
            }

        }
    }

    /** @deprecated */
    @Deprecated
    public static String readIncommingRequestData(HttpServletRequest request) {
        return readData(request);
    }


    private class TrustAnyTrustManager implements X509TrustManager {
        private TrustAnyTrustManager() {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }
    }

    private class TrustAnyHostnameVerifier implements HostnameVerifier {
        private TrustAnyHostnameVerifier() {
        }

        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }


}
  • 微信支付api
package com.jeeplus.modules.app.pay.util;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;


/**
 * 微信支付api
 * @author jsy
 * @version 2020/12/25
 **/
public class PaymentApi {

   private PaymentApi() {}

   // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
   private static String unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";

   /**
    * 交易类型枚举
    * WAP的文档:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_1
    * @author L.cm
    * <pre>
    * email: 596392912@qq.com
    * site: http://www.dreamlu.net
    * date: 2015年10月27日 下午9:46:27
    * </pre>
    */
   public enum TradeType {
       JSAPI, NATIVE, APP, WAP, MWEB
   }

   /**
    * 统一下单
    * @param params 参数map
    * @return String
    */
   public static String pushOrder(Map<String, String> params) {
       return HttpKit.post(unifiedOrderUrl, PaymentKit.toXml(params));
   }

   private static Map<String, String> request(String url, Map<String, String> params, String paternerKey) {
       params.put("nonce_str", System.currentTimeMillis() + "");
       String sign = PaymentKit.createSign(params, paternerKey);
       params.put("sign", sign);
       String xmlStr = HttpKit.post(url, PaymentKit.toXml(params));
       return PaymentKit.xmlToMap(xmlStr);
   }

   /**
    * 文档说明:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_4
    * <pre>
    * @param appId 公众账号ID         是    String(32)    wx8888888888888888    微信分配的公众账号ID
    * 随机字符串         noncestr    是    String(32)    5K8264ILTKCH16CQ2502SI8ZNMTM67VS    随机字符串,不长于32位。推荐随机数生成算法
    * 订单详情扩展字符串    package        是    String(32)    WAP    扩展字段,固定填写WAP
    * @param prepayId 预支付交易会话标识    是    String(64)    wx201410272009395522657a690389285100    微信统一下单接口返回的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
    * 签名                 sign        是    String(32)    C380BEC2BFD727A4B6845133519F3AD6    签名,详见签名生成算法
    * 时间戳            timestamp    是    String(32)    1414561699    当前的时间,其他详见时间戳规则
    * @param paternerKey 签名密匙
    * </pre>
    * @return {String}
    */
   public static String getDeepLink(String appId, String prepayId, String paternerKey) {
       Map<String, String> params = new HashMap<String, String>();
       params.put("appid", appId);
       params.put("noncestr", System.currentTimeMillis() + "");
       params.put("package", "WAP");
       params.put("prepayid", prepayId);
       params.put("timestamp", System.currentTimeMillis() / 1000 + "");
       String sign = PaymentKit.createSign(params, paternerKey);
       params.put("sign", sign);

       String string1 = PaymentKit.packageSign(params, true);

       String string2 = "";
       try { string2 = PaymentKit.urlEncode(string1); } catch (UnsupportedEncodingException e) {}

       return "weixin://wap/pay?" + string2;
   }

   // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
   private static String orderQueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery";

   /**
    * 根据商户订单号查询信息
    * @param appid 公众账号ID
    * @param mch_id 商户号
    * @param paternerKey 商户密钥
    * @param transaction_id 微信订单号
    * @return 回调信息
    */
   public static Map<String, String> queryByTransactionId(String appid, String mch_id, String paternerKey, String transaction_id) {
       Map<String, String> params = new HashMap<String, String>();
       params.put("appid", appid);
       params.put("mch_id", mch_id);
       params.put("transaction_id", transaction_id);
       return request(orderQueryUrl, params, paternerKey);
   }

   /**
    * 根据商户订单号查询信息
    * @param appid 公众账号ID
    * @param mch_id 商户号
    * @param paternerKey 商户密钥
    * @param out_trade_no 商户订单号
    * @return 回调信息
    */
   public static Map<String, String> queryByOutTradeNo(String appid, String mch_id, String paternerKey, String out_trade_no) {
       Map<String, String> params = new HashMap<String, String>();
       params.put("appid", appid);
       params.put("mch_id", mch_id);
       params.put("out_trade_no", out_trade_no);
       return request(orderQueryUrl, params, paternerKey);
   }

   // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3
   private static String closeOrderUrl = "https://api.mch.weixin.qq.com/pay/closeorder";

   /**
    * 关闭订单
    * @param appid 公众账号ID
    * @param mch_id 商户号
    * @param paternerKey 商户密钥
    * @param out_trade_no 商户订单号
    * @return 回调信息
    */
   public static Map<String, String> closeOrder(String appid, String mch_id, String paternerKey, String out_trade_no) {
       Map<String, String> params = new HashMap<String, String>();
       params.put("appid", appid);
       params.put("mch_id", mch_id);
       params.put("out_trade_no", out_trade_no);
       return request(closeOrderUrl, params, paternerKey);
   }

   // 申请退款文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
   public static String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";

//    /**
//     * 申请退款,内部添加了随机字符串nonce_str和签名sign
//     * @param params 参数map,内部添加了随机字符串nonce_str和签名sign
//     * @param paternerKey 商户密钥
//     * @param certPath 证书文件目录
//     * @return  map
//     */
//    public static Map<String, String> refund(Map<String, String> params, String paternerKey, String certPath) {
//        params.put("nonce_str", System.currentTimeMillis() + "");
//        String sign = PaymentKit.createSign(params, paternerKey);
//        params.put("sign", sign);
//        String partner = params.get("mch_id");
//        String xmlStr = HttpKit.delegate.postSSL(refundUrl, PaymentKit.toXml(params), certPath, partner);
//        return PaymentKit.xmlToMap(xmlStr);
//    }

   // 查询退款文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5
   private static String refundQueryUrl = "https://api.mch.weixin.qq.com/pay/refundquery";

   private static Map<String, String> baseRefundQuery(Map<String, String> params, String appid, String mch_id, String paternerKey) {
       params.put("appid", appid);
       params.put("mch_id", mch_id);
       return request(refundQueryUrl, params, paternerKey);
   }

   /**
    * 根据微信订单号查询退款
    * @param appid 公众账号ID
    * @param mch_id 商户号
    * @param paternerKey 商户密钥
    * @param transaction_id 微信订单号
    * @return map
    */
   public static Map<String, String> refundQueryByTransactionId(String appid, String mch_id, String paternerKey, String transaction_id) {
       Map<String, String> params = new HashMap<String, String>();
       params.put("transaction_id", transaction_id);
       return baseRefundQuery(params, appid, mch_id, paternerKey);
   }

   /**
    * 根据微信订单号查询退款
    * @param appid 公众账号ID
    * @param mch_id 商户号
    * @param paternerKey 商户密钥
    * @param out_trade_no 商户订单号
    * @return map
    */
   public static Map<String, String> refundQueryByOutTradeNo(String appid, String mch_id, String paternerKey, String out_trade_no) {
       Map<String, String> params = new HashMap<String, String>();
       params.put("out_trade_no", out_trade_no);
       return baseRefundQuery(params, appid, mch_id, paternerKey);
   }

   /**
    * 根据微信订单号查询退款
    * @param appid 公众账号ID
    * @param mch_id 商户号
    * @param paternerKey 商户密钥
    * @param out_refund_no 商户退款单号
    * @return map
    */
   public static Map<String, String> refundQueryByOutRefundNo(String appid, String mch_id, String paternerKey, String out_refund_no) {
       Map<String, String> params = new HashMap<String, String>();
       params.put("out_refund_no", out_refund_no);
       return baseRefundQuery(params, appid, mch_id, paternerKey);
   }

   /**
    * 根据微信订单号查询退款
    * @param appid 公众账号ID
    * @param mch_id 商户号
    * @param paternerKey 商户密钥
    * @param refund_id 微信退款单号
    * @return map
    */
   public static Map<String, String> refundQueryByRefundId(String appid, String mch_id, String paternerKey, String refund_id) {
       Map<String, String> params = new HashMap<String, String>();
       params.put("refund_id", refund_id);
       return baseRefundQuery(params, appid, mch_id, paternerKey);
   }

   private static String downloadBillUrl = "https://api.mch.weixin.qq.com/pay/downloadbill";

   /**
    * <pre>
    * ALL,返回当日所有订单信息,默认值
    * SUCCESS,返回当日成功支付的订单
    * REFUND,返回当日退款订单
    * REVOKED,已撤销的订单
    * </pre>
    */
   public static enum BillType {
       ALL, SUCCESS, REFUND, REVOKED
   }

   /**
    * 下载对账单
    * <pre>
    * 公众账号ID    appid        是    String(32)    wx8888888888888888    微信分配的公众账号ID(企业号corpid即为此appId)
    * 商户号        mch_id        是    String(32)    1900000109    微信支付分配的商户号
    * 设备号        device_info    否    String(32)    013467007045764    微信支付分配的终端设备号
    * 随机字符串    nonce_str    是    String(32)    5K8264ILTKCH16CQ2502SI8ZNMTM67VS    随机字符串,不长于32位。推荐随机数生成算法
    * 签名        sign        是    String(32)    C380BEC2BFD727A4B6845133519F3AD6    签名,详见签名生成算法
    * 对账单日期    bill_date    是    String(8)    20140603    下载对账单的日期,格式:20140603
    * 账单类型        bill_type    否    String(8)
    * </pre>
    * @param appid 公众账号ID
    * @param mch_id 商户号
    * @param paternerKey 签名密匙
    * @param billDate 对账单日期
    * @return String
    */
   public static Map<String,String> downloadBill(String appid, String mch_id, String paternerKey, String billDate) {
       return downloadBill(appid, mch_id, paternerKey, billDate, null);
   }

   /**
    * 下载对账单
    * <pre>
    * 公众账号ID    appid        是    String(32)    wx8888888888888888    微信分配的公众账号ID(企业号corpid即为此appId)
    * 商户号        mch_id        是    String(32)    1900000109    微信支付分配的商户号
    * 设备号        device_info    否    String(32)    013467007045764    微信支付分配的终端设备号
    * 随机字符串    nonce_str    是    String(32)    5K8264ILTKCH16CQ2502SI8ZNMTM67VS    随机字符串,不长于32位。推荐随机数生成算法
    * 签名        sign        是    String(32)    C380BEC2BFD727A4B6845133519F3AD6    签名,详见签名生成算法
    * 对账单日期    bill_date    是    String(8)    20140603    下载对账单的日期,格式:20140603
    * 账单类型        bill_type    否    String(8)
    * </pre>
    * @param appid 公众账号ID
    * @param mch_id 商户号
    * @param paternerKey 签名密匙
    * @param billDate 对账单日期
    * @param billType 账单类型
    * @return String
    */
   public static Map<String,String> downloadBill(String appid, String mch_id, String paternerKey, String billDate, BillType billType) {
       Map<String, String> params = new HashMap<String, String>();
       params.put("appid", appid);
       params.put("mch_id", mch_id);
       params.put("nonce_str", System.currentTimeMillis() + "");
       params.put("bill_date", billDate);
       if (null != billType) {
           params.put("bill_type", billType.name());
       } else {
           params.put("bill_type", BillType.ALL.name());
       }
       String sign = PaymentKit.createSign(params, paternerKey);
       params.put("sign", sign);
       String str = HttpKit.post(downloadBillUrl, PaymentKit.toXml(params));
       return PaymentKit.xmlToMap(str);

   }

}

  • 微信支付的统一下单工具类
package com.jeeplus.modules.app.pay.util;


import org.apache.commons.io.Charsets;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;



/**
 * 微信支付的统一下单工具类
 * @author jsy
 * @version 2020/12/24
 **/
public class PaymentKit {

   /**
    * 组装签名的字段
    * @param params 参数
    * @param urlEncoder 是否urlEncoder
    * @return String
    */
   public static String packageSign(Map<String, String> params, boolean urlEncoder) {
       // 先将参数以其参数名的字典序升序进行排序
       TreeMap<String, String> sortedParams = new TreeMap<String, String>(params);
       // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
       StringBuilder sb = new StringBuilder();
       boolean first = true;
       for (Entry<String, String> param : sortedParams.entrySet()) {
           String value = param.getValue();
           if (Tools.isEmpty(value)) {
               continue;
           }
           if (first) {
               first = false;
           } else {
               sb.append("&");
           }
           sb.append(param.getKey()).append("=");
           if (urlEncoder) {
               try { value = urlEncode(value); } catch (UnsupportedEncodingException e) {}
           }
           sb.append(value);
       }
       return sb.toString();
   }

   /**
    * urlEncode
    * @param src 微信参数
    * @return String
    * @throws UnsupportedEncodingException 编码错误
    */
   public static String urlEncode(String src) throws UnsupportedEncodingException {
       return URLEncoder.encode(src, Charsets.UTF_8.name()).replace("+", "%20");
   }

   /**
    * 生成签名
    * @param params 参数
    * @param paternerKey 支付密钥
    * @return sign
    */
   public static String createSign(Map<String, String> params, String paternerKey) {
       // 生成签名前先去除sign
       params.remove("sign");
       String stringA = packageSign(params, false);
       String stringSignTemp = stringA + "&key=" + paternerKey;
       return Tools.md5(stringSignTemp).toUpperCase();
   }

   /**
    * 支付异步通知时校验sign
    * @param params 参数
    * @param paternerKey 支付密钥
    * @return {boolean}
    */
   public static boolean verifyNotify(Map<String, String> params, String paternerKey){
       String sign = params.get("sign");
       String localSign = PaymentKit.createSign(params, paternerKey);
       return sign.equals(localSign);
   }

   /**
    * 微信下单,map to xml
    * @param params 参数
    * @return String
    */
   public static String toXml(Map<String, String> params) {
       StringBuilder xml = new StringBuilder();
       xml.append("<xml>");
       for (Entry<String, String> entry : params.entrySet()) {
           String key   = entry.getKey();
           String value = entry.getValue();
           // 略过空值
           if (Tools.isEmpty(value)) continue;
           xml.append("<").append(key).append(">");
               xml.append(entry.getValue());
           xml.append("</").append(key).append(">");
       }
       xml.append("</xml>");
       return xml.toString();
   }

   /**
    * 针对支付的xml,没有嵌套节点的简单处理
    * @param xmlStr xml字符串
    * @return map集合
    */
   public static Map<String, String> xmlToMap(String xmlStr) {
       XmlHelper xmlHelper = XmlHelper.of(xmlStr);
       return xmlHelper.toMap();
   }

}

  • 支付签名常用类
package com.jeeplus.modules.app.pay.util;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.commons.codec.digest.DigestUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

/**
 * 支付签名常用类
 * @author jsy
 * @version 2020/12/24
 **/
public class PayUtil {
    /**
     * 签名字符串
     *
     * @param text          需要签名的字符串
     * @param key           密钥
     * @param input_charset 编码格式
     * @return 签名结果
     */
    public static String sign(String text, String key, String input_charset) {
        text = text + "&key=" + key;
        return DigestUtils.md5Hex(getContentBytes(text, input_charset));
    }

    /**
     * 签名字符串
     *
     * @param text          需要签名的字符串
     * @param sign          签名结果
     * @param key           密钥
     * @param input_charset 编码格式
     * @return 签名结果
     */
    public static boolean verify(String text, String sign, String key, String input_charset) {
        text = text +"&key=" + key;
        String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset)).toUpperCase();
        if (mysign.equals(sign)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @param content
     * @param charset
     * @return
     * @throws java.security.SignatureException
     * @throws UnsupportedEncodingException
     */
    public static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }

    private static boolean isValidChar(char ch) {
        if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
            return true;
        if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))
            return true;// 简体中文汉字编码
        return false;
    }

    /**
     * 除去数组中的空值和签名参数
     *
     * @param sArray 签名参数组
     * @return 去掉空值与签名参数后的新签名参数组
     */
    public static Map<String, String> paraFilter(Map<String, String> sArray) {
        Map<String, String> result = new HashMap<String, String>();
        if (sArray == null || sArray.size() <= 0) {
            return result;
        }
        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
                    || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /**
     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
     *
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, String> params) {
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }

    /**
     * @param requestUrl    请求地址
     * @param requestMethod 请求方法
     * @param outputStr     参数
     */
    public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
        // 创建SSLContext
        StringBuffer buffer = null;
        try {
            URL url = new URL(requestUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod(requestMethod);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.connect();
            //往服务器端写内容
            if (null != outputStr) {
                OutputStream os = conn.getOutputStream();
                os.write(outputStr.getBytes("utf-8"));
                os.close();
            }
            // 读取服务器端返回的内容
            InputStream is = conn.getInputStream();
            InputStreamReader isr = new InputStreamReader(is, "utf-8");
            BufferedReader br = new BufferedReader(isr);
            buffer = new StringBuffer();
            String line = null;
            while ((line = br.readLine()) != null) {
                buffer.append(line);
            }
            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return buffer.toString();
    }

    public static String urlEncodeUTF8(String source) {
        String result = source;
        try {
            result = java.net.URLEncoder.encode(source, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
     *
     * @param strxml
     * @return
     * @throws org.jdom2.JDOMException
     * @throws IOException
     */
    public static Map doXMLParse(String strxml) throws Exception {
        if (null == strxml || "".equals(strxml)) {
            return null;
        }

        Map m = new HashMap();
        InputStream in = String2Inputstream(strxml);
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }

            m.put(k, v);
        }

        //关闭流
        in.close();

        return m;
    }

    /**
     * 获取子结点的xml
     *
     * @param children
     * @return String
     */
    public static String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if (!children.isEmpty()) {
            Iterator it = children.iterator();
            while (it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if (!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }

        return sb.toString();
    }

    public static InputStream String2Inputstream(String str) {
        return new ByteArrayInputStream(str.getBytes());
    }

    /**
     * java对象转字符串
     * @param <T> 泛型占位符
     * @param obj T
     * @return String
     */
    public static <T> String toJson(T obj) {
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").enableComplexMapKeySerialization().create();
        return gson.toJson(obj);
    }
}

  • 支付常量
package com.jeeplus.modules.app.pay.util;


/**
 * 支付常量
 *
 * @author jsy
 * @version 2020/12/24
 **/
public class WXPayConstants {

    public enum SignType {
        MD5, HMACSHA256
    }

    public static final String DOMAIN_API = "api.mch.weixin.qq.com";
    public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com";
    public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com";
    public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com";


    public static final String FAIL = "FAIL";
    /**
     * 成功的标识
     */
    public static final String SUCCESS = "SUCCESS";
    /**
     * 返回状态码的变量名
     */
    public static final String RETURN_CODE = "return_code";
    public static final String HMACSHA256 = "HMAC-SHA256";
    public static final String MD5 = "MD5";

    public static final String FIELD_SIGN = "sign";
    public static final String FIELD_SIGN_TYPE = "sign_type";

    public static final String MICROPAY_URL_SUFFIX = "/pay/micropay";
    public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
    public static final String ORDERQUERY_URL_SUFFIX = "/pay/orderquery";
    public static final String REVERSE_URL_SUFFIX = "/secapi/pay/reverse";
    public static final String CLOSEORDER_URL_SUFFIX = "/pay/closeorder";
    public static final String REFUNDQUERY_URL_SUFFIX = "/pay/refundquery";
    public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";

    // sandbox
    public static final String SANDBOX_MICROPAY_URL_SUFFIX = "/sandboxnew/pay/micropay";
    public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
    public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery";
    public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse";
    public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder";
    public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/secapi/pay/refund";
    public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery";
    public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
    public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report";
    public static final String SANDBOX_SHORTURL_URL_SUFFIX = "/sandboxnew/tools/shorturl";
    public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
}


  • xpath解析xml
package com.jeeplus.modules.app.pay.util;

import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;


/**
 * xpath解析xml
 * <pre>
 *     文档地址:
 *     http://www.w3school.com.cn/xpath/index.asp
 * </pre>
 * @author jsy
 * @version 2020/12/24
 **/
public class XmlHelper {
    private final XPath path;
    private final Document doc;

    private XmlHelper(InputSource inputSource) throws ParserConfigurationException, SAXException, IOException {
        DocumentBuilderFactory dbf = getDocumentBuilderFactory();
        DocumentBuilder db = dbf.newDocumentBuilder();
        doc = db.parse(inputSource);
        path = getXPathFactory().newXPath();
    }

    private static XmlHelper create(InputSource inputSource) {
        try {
            return new XmlHelper(inputSource);
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static XmlHelper of(InputStream is) {
        InputSource inputSource = new InputSource(is);
        return create(inputSource);
    }

    public static XmlHelper of(String xmlStr) {
        StringReader sr = new StringReader(xmlStr.trim());
        InputSource inputSource = new InputSource(sr);
        XmlHelper xmlHelper = create(inputSource);
        IOUtils.closeQuietly(sr);
        return xmlHelper;
    }

    private Object evalXPath(String expression, Object item, QName returnType) {
        item = null == item ? doc : item;
        try {
            return path.evaluate(expression, item, returnType);
        } catch (XPathExpressionException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取String
     * @param expression 路径
     * @return String
     */
    public String getString(String expression) {
        return (String) evalXPath(expression, null, XPathConstants.STRING);
    }

    /**
     * 获取Boolean
     * @param expression 路径
     * @return String
     */
    public Boolean getBoolean(String expression) {
        return (Boolean) evalXPath(expression, null, XPathConstants.BOOLEAN);
    }

    /**
     * 获取Number
     * @param expression 路径
     * @return {Number}
     */
    public Number getNumber(String expression) {
        return (Number) evalXPath(expression, null, XPathConstants.NUMBER);
    }

    /**
     * 获取某个节点
     * @param expression 路径
     * @return {Node}
     */
    public Node getNode(String expression) {
        return (Node) evalXPath(expression, null, XPathConstants.NODE);
    }

    /**
     * 获取子节点
     * @param expression 路径
     * @return NodeList
     */
    public NodeList getNodeList(String expression) {
        return (NodeList) evalXPath(expression, null, XPathConstants.NODESET);
    }


    /**
     * 获取String
     * @param node 节点
     * @param expression 相对于node的路径
     * @return String
     */
    public String getString(Object node, String expression) {
        return (String) evalXPath(expression, node, XPathConstants.STRING);
    }

    /**
     * 获取
     * @param node 节点
     * @param expression 相对于node的路径
     * @return String
     */
    public Boolean getBoolean(Object node, String expression) {
        return (Boolean) evalXPath(expression, node, XPathConstants.BOOLEAN);
    }

    /**
     * 获取
     * @param node 节点
     * @param expression 相对于node的路径
     * @return {Number}
     */
    public Number getNumber(Object node, String expression) {
        return (Number) evalXPath(expression, node, XPathConstants.NUMBER);
    }

    /**
     * 获取某个节点
     * @param node 节点
     * @param expression 路径
     * @return {Node}
     */
    public Node getNode(Object node, String expression) {
        return (Node) evalXPath(expression, node, XPathConstants.NODE);
    }

    /**
     * 获取子节点
     * @param node 节点
     * @param expression 相对于node的路径
     * @return NodeList
     */
    public NodeList getNodeList(Object node, String expression) {
        return (NodeList) evalXPath(expression, node, XPathConstants.NODESET);
    }

    /**
     * 针对没有嵌套节点的简单处理
     * @return map集合
     */
    public Map<String, String> toMap() {
        Element root = doc.getDocumentElement();
        Map<String, String> params = new HashMap<String, String>();

        // 将节点封装成map形式
        NodeList list = root.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node node = list.item(i);
            if (node instanceof Element) {
                params.put(node.getNodeName(), node.getTextContent());
            }
        }
        return params;
    }

    private static DocumentBuilderFactory getDocumentBuilderFactory(){
        return XmlHelperHolder.documentBuilderFactory;
    }

    private static XPathFactory getXPathFactory() {
        return  XmlHelperHolder.xPathFactory;
    }

    /**
     * 内部类单例
     */
    private static class XmlHelperHolder {
        private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        private static XPathFactory xPathFactory = XPathFactory.newInstance();
    }

}

  • 常用工具类
package com.jeeplus.modules.app.pay.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.util.*;


/**
 * 常用工具类
 * @author jsy
 * @version 2020/12/24
 **/
public class WXPayUtil {

   /**
    * XML格式字符串转换为Map
    *
    * @param strXML XML字符串
    * @return XML数据转换后的Map
    * @throws Exception
    */
   public static Map<String, String> xmlToMap(String strXML) throws Exception {
       try {
           Map<String, String> data = new HashMap<String, String>();
           DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
           DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
           InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
           org.w3c.dom.Document doc = documentBuilder.parse(stream);
           doc.getDocumentElement().normalize();
           NodeList nodeList = doc.getDocumentElement().getChildNodes();
           for (int idx = 0; idx < nodeList.getLength(); ++idx) {
               Node node = nodeList.item(idx);
               if (node.getNodeType() == Node.ELEMENT_NODE) {
                   org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                   data.put(element.getNodeName(), element.getTextContent());
               }
           }
           try {
               stream.close();
           } catch (Exception ex) {
               // do nothing
           }
           return data;
       } catch (Exception ex) {
           WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
           throw ex;
       }

   }

   /**
    * 将Map转换为XML格式的字符串
    *
    * @param data Map类型数据
    * @return XML格式的字符串
    * @throws Exception
    */
   public static String mapToXml(Map<String, String> data) throws Exception {
       DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
       DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
       org.w3c.dom.Document document = documentBuilder.newDocument();
       org.w3c.dom.Element root = document.createElement("xml");
       document.appendChild(root);
       for (String key: data.keySet()) {
           String value = data.get(key);
           if (value == null) {
               value = "";
           }
           value = value.trim();
           org.w3c.dom.Element filed = document.createElement(key);
           filed.appendChild(document.createTextNode(value));
           root.appendChild(filed);
       }
       TransformerFactory tf = TransformerFactory.newInstance();
       Transformer transformer = tf.newTransformer();
       DOMSource source = new DOMSource(document);
       transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
       transformer.setOutputProperty(OutputKeys.INDENT, "yes");
       StringWriter writer = new StringWriter();
       StreamResult result = new StreamResult(writer);
       transformer.transform(source, result);
       String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
       try {
           writer.close();
       }
       catch (Exception ex) {
       }
       return output;
   }


   /**
    * 生成带有 sign 的 XML 格式字符串
    *
    * @param data Map类型数据
    * @param key API密钥
    * @return 含有sign字段的XML
    */
   public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
       return generateSignedXml(data, key, WXPayConstants.SignType.MD5);
   }

   /**
    * 生成带有 sign 的 XML 格式字符串
    *
    * @param data Map类型数据
    * @param key API密钥
    * @param signType 签名类型
    * @return 含有sign字段的XML
    */
   public static String generateSignedXml(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
       String sign = generateSignature(data, key, signType);
       data.put(WXPayConstants.FIELD_SIGN, sign);
       return mapToXml(data);
   }


   /**
    * 判断签名是否正确
    *
    * @param xmlStr XML格式数据
    * @param key API密钥
    * @return 签名是否正确
    * @throws Exception
    */
   public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
       Map<String, String> data = xmlToMap(xmlStr);
       if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
           return false;
       }
       String sign = data.get(WXPayConstants.FIELD_SIGN);
       return generateSignature(data, key).equals(sign);
   }

   /**
    * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
    *
    * @param data Map类型数据
    * @param key API密钥
    * @return 签名是否正确
    * @throws Exception
    */
   public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
       return isSignatureValid(data, key, WXPayConstants.SignType.MD5);
   }

   /**
    * 判断签名是否正确,必须包含sign字段,否则返回false。
    *
    * @param data Map类型数据
    * @param key API密钥
    * @param signType 签名方式
    * @return 签名是否正确
    * @throws Exception
    */
   public static boolean isSignatureValid(Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
       if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
           return false;
       }
       String sign = data.get(WXPayConstants.FIELD_SIGN);
       return generateSignature(data, key, signType).equals(sign);
   }

   /**
    * 生成签名
    *
    * @param data 待签名数据
    * @param key API密钥
    * @return 签名
    */
   public static String generateSignature(final Map<String, String> data, String key) throws Exception {
       return generateSignature(data, key, WXPayConstants.SignType.MD5);
   }

   /**
    * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
    *
    * @param data 待签名数据
    * @param key API密钥
    * @param signType 签名方式
    * @return 签名
    */
   public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
       Set<String> keySet = data.keySet();
       String[] keyArray = keySet.toArray(new String[keySet.size()]);
       Arrays.sort(keyArray);
       StringBuilder sb = new StringBuilder();
       for (String k : keyArray) {
           if (k.equals(WXPayConstants.FIELD_SIGN)) {
               continue;
           }
           if(data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
               sb.append(k).append("=").append(data.get(k).trim()).append("&");
       }
       sb.append("key=").append(key);
       if (WXPayConstants.SignType.MD5.equals(signType)) {
           return MD5(sb.toString()).toUpperCase();
       }
       else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
           return HMACSHA256(sb.toString(), key);
       }
       else {
           throw new Exception(String.format("Invalid sign_type: %s", signType));
       }
   }


   /**
    * 获取随机字符串 Nonce Str
    *
    * @return String 随机字符串
    */
   public static String generateNonceStr() {
       return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
   }


   /**
    * 生成 MD5
    *
    * @param data 待处理数据
    * @return MD5结果
    */
   public static String MD5(String data) throws Exception {
       MessageDigest md = MessageDigest.getInstance("MD5");
       byte[] array = md.digest(data.getBytes("UTF-8"));
       StringBuilder sb = new StringBuilder();
       for (byte item : array) {
           sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
       }
       return sb.toString().toUpperCase();
   }

   /**
    * 生成 HMACSHA256
    * @param data 待处理数据
    * @param key 密钥
    * @return 加密结果
    * @throws Exception
    */
   public static String HMACSHA256(String data, String key) throws Exception {
       Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
       SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
       sha256_HMAC.init(secret_key);
       byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
       StringBuilder sb = new StringBuilder();
       for (byte item : array) {
           sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
       }
       return sb.toString().toUpperCase();
   }

   /**
    * 日志
    * @return
    */
   public static Logger getLogger() {
       Logger logger = LoggerFactory.getLogger("wxpay java sdk");
       return logger;
   }

   /**
    * 获取当前时间戳,单位秒
    * @return
    */
   public static long getCurrentTimestamp() {
       return System.currentTimeMillis()/1000;
   }

   /**
    * 获取当前时间戳,单位毫秒
    * @return
    */
   public static long getCurrentTimestampMs() {
       return System.currentTimeMillis();
   }

   /**
    * 生成 uuid, 即用来标识一笔单,也用做 nonce_str
    * @return
    */
   public static String generateUUID() {
       return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
   }

}

  • Tools工具类
package com.jeeplus.modules.app.pay.util;

import java.security.MessageDigest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * 常用工具
 * @author jsy
 * @version 2020/12/24
 **/
public class Tools {

   /**
    * 随机生成六位数验证码
    * @return
    */
   public static int getRandomNum(){
        Random r = new Random();
        return r.nextInt(900000)+100000;//(Math.random()*(999999-100000)+100000)
   }

   /**
    * 检测字符串是否不为空(null,"","null")
    * @param s
    * @return 不为空则返回true,否则返回false
    */
   public static boolean notEmpty(String s){
       return s!=null && !"".equals(s) && !"null".equals(s);
   }

   /**
    * 检测字符串是否为空(null,"","null")
    * @param s
    * @return 为空则返回true,不否则返回false
    */
   public static boolean isEmpty(String s){
       return s==null || "".equals(s) || "null".equals(s);
   }

   /**
    * 字符串转换为字符串数组
    * @param str 字符串
    * @param splitRegex 分隔符
    * @return
    */
   public static String[] str2StrArray(String str,String splitRegex){
       if(isEmpty(str)){
           return null;
       }
       return str.split(splitRegex);
   }

   /**
    * 用默认的分隔符(,)将字符串转换为字符串数组
    * @param str	字符串
    * @return
    */
   public static String[] str2StrArray(String str){
       return str2StrArray(str,",\\s*");
   }

   /**
    * 按照yyyy-MM-dd HH:mm:ss的格式,日期转字符串
    * @param date
    * @return yyyy-MM-dd HH:mm:ss
    */
   public static String date2Str(Date date){
       return date2Str(date,"yyyy-MM-dd HH:mm:ss");
   }

   /**
    * 按照yyyy-MM-dd HH:mm:ss的格式,字符串转日期
    * @param date
    * @return
    */
   public static Date str2Date(String date){
       if(notEmpty(date)){
           SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
           try {
               return sdf.parse(date);
           } catch (ParseException e) {
               e.printStackTrace();
           }
           return new Date();
       }else{
           return null;
       }
   }

   /**
    * 按照参数format的格式,日期转字符串
    * @param date
    * @param format
    * @return
    */
   public static String date2Str(Date date,String format){
       if(date!=null){
           SimpleDateFormat sdf = new SimpleDateFormat(format);
           return sdf.format(date);
       }else{
           return "";
       }
   }

   /**
    * 把时间根据时、分、秒转换为时间段
    * @param StrDate
    */
   public static String getTimes(String StrDate){
       String resultTimes = "";

       SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
       Date now;

       try {
           now = new Date();
           Date date=df.parse(StrDate);
           long times = now.getTime()-date.getTime();
           long day  =  times/(24*60*60*1000);
           long hour = (times/(60*60*1000)-day*24);
           long min  = ((times/(60*1000))-day*24*60-hour*60);
           long sec  = (times/1000-day*24*60*60-hour*60*60-min*60);

           StringBuffer sb = new StringBuffer();
           //sb.append("发表于:");
           if(hour>0 ){
               sb.append(hour+"小时前");
           } else if(min>0){
               sb.append(min+"分钟前");
           } else{
               sb.append(sec+"秒前");
           }

           resultTimes = sb.toString();
       } catch (ParseException e) {
           e.printStackTrace();
       }

       return resultTimes;
   }

   /**
     * 验证邮箱
     * @param email
     * @return
     */
    public static boolean checkEmail(String email){
     boolean flag = false;
     try{
       String check = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
       Pattern regex = Pattern.compile(check);
       Matcher matcher = regex.matcher(email);
       flag = matcher.matches();
      }catch(Exception e){
       flag = false;
      }
     return flag;
    }

    /**
     * 验证手机号码
     * @return
     */
    public static boolean checkMobileNumber(String mobileNumber){
        Pattern p = null;
        Matcher m = null;
        boolean b = false;
        p = Pattern.compile("^[1][3,4,5,7,8][0-9]{9}$"); // 验证手机号
        m = p.matcher(mobileNumber);
        b = m.matches();
        return b;
    }

   /**
    * 将驼峰转下划线
    * @param param
    * @return
    */
   public static String camelToUnderline(String param){
       if (param==null||"".equals(param.trim())){
           return "";
       }
       int len=param.length();
       StringBuilder sb=new StringBuilder(len);
       for (int i = 0; i < len; i++) {
           char c=param.charAt(i);
           if (Character.isUpperCase(c)){
               sb.append("_");
               sb.append(Character.toLowerCase(c));
           }else{
               sb.append(c);
           }
       }
       return sb.toString();
   }

   /**
    * 去掉下划线并将下划线后的首字母转为大写
    * @param str
    * @return
    */
   public static String transformStr(String str){
       //去掉数据库字段的下划线
       if(str.contains("_")) {
           String[] names = str.split("_");
           String firstPart = names[0];
           String otherPart = "";
           for (int i = 1; i < names.length; i++) {
               String word = names[i].replaceFirst(names[i].substring(0, 1), names[i].substring(0, 1).toUpperCase());
               otherPart += word;
           }
           str = firstPart + otherPart;
       }
       return str;
   }

   /**
    * 转换为map
    * @param list
    * @return
    */
   public static List<Map<String,Object>> transformMap(List<Map<String,Object>> list){
       List<Map<String,Object>> resultMapList = new ArrayList<>();

       for (Map<String, Object> map : list) {
           Map<String,Object> tempMap = new HashMap<>();
           for (String s : map.keySet()) {
               tempMap.put(transformStr(s),map.get(s));
           }
           resultMapList.add(tempMap);
       }
       return resultMapList;
   }

   public static String clearHtml(String content,int p) {
       if(null==content) return "";
       if(0==p) return "";

       Pattern p_script;
       Matcher m_script;
       Pattern p_style;
       Matcher m_style;
       Pattern p_html;
       Matcher m_html;

       try {
           String regEx_script = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>";
           //定义script的正则表达式{或<script[^>]*?>[\\s\\S]*?<\\/script> }
           String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>";
           //定义style的正则表达式{或<style[^>]*?>[\\s\\S]*?<\\/style> }
           String regEx_html = "<[^>]+>"; //定义HTML标签的正则表达式

           p_script = Pattern.compile(regEx_script,Pattern.CASE_INSENSITIVE);
           m_script = p_script.matcher(content);
           content = m_script.replaceAll(""); //过滤script标签
           p_style = Pattern.compile(regEx_style,Pattern.CASE_INSENSITIVE);
           m_style = p_style.matcher(content);
           content = m_style.replaceAll(""); //过滤style标签

           p_html = Pattern.compile(regEx_html,Pattern.CASE_INSENSITIVE);
           m_html = p_html.matcher(content);

           content = m_html.replaceAll(""); //过滤html标签
       }catch(Exception e) {
           return "";
       }

       if(content.length()>p){
           content = content.substring(0, p)+"...";
       }else{
           content = content + "...";
       }

       return content;
   }

   public static String md5(String str) {
       try {
           MessageDigest md = MessageDigest.getInstance("MD5");
           md.update(str.getBytes());
           byte b[] = md.digest();

           int i;

           StringBuffer buf = new StringBuffer("");
           for (int offset = 0; offset < b.length; offset++) {
               i = b[offset];
               if (i < 0)
                   i += 256;
               if (i < 16)
                   buf.append("0");
               buf.append(Integer.toHexString(i));
           }
           str = buf.toString();
       } catch (Exception e) {
           e.printStackTrace();

       }
       return str;
   }


}

总结

到现在基本上已经完成了,大家照着我的方法来一定能成功。

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值