支付工具类PayUtil  主要用来获取生成微信调取支付页面的参数

package com.systek.scenic.utils.wechatApp;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;


import javax.servlet.http.HttpServletRequest;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



public class PayUtil {


    private final static String appId = "xxxxxxxxxx";//自己的配置appid
    private final static String mchId = "xxxxxxxxxx"; //微信支付分配的商户号
    private final static String key = "xxxxxxxxxxxxxxxxxxxxxxx"; //微信支付分配的商户号key
    private final static String spbill_create_ip = "xx.xxx.xxx.xx"; //调用微信支付API的机器IP 本机IP地址
    private final static String notify_url = "xxxxxxxxxxxxxxxxxxxxxxxxx"; //异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。


 /*
 *
  * @description:
  * @author: YinZengXiang
  * @date: 2020/3/5 11:04
  * @param: openid  //微信用户身份标识
  * @param: body //支付才用的参数
  * @param: total_fee //支付总价格分
  * @param: out_trade_no //订单号
  * @return: java.util.Map<java.lang.String,java.lang.Object>
  */
    public static Map<String, Object> takeOrder(String openid,String body,String total_fee,String out_trade_no) throws Exception {

        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        String nonce_str =  UUID.randomUUID().toString().substring(0, 32).replace("-","");//生成随机数,可直接用系统提供的方法  ; //随机字符串,长度要求在32位以内。推荐随机数生成算法
        Map<String,Object> params = new HashMap<>();
        params.put("appid",appId);
        params.put("mch_id",mchId);
        params.put("device_info",total_fee);
        params.put("nonce_str",nonce_str);
        params.put("sign_type","MD5"); //签名方式选用md5
        params.put("body",body);
        params.put("out_trade_no",out_trade_no); //商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一。
        params.put("total_fee",Integer.valueOf(total_fee)); //订单总金额,单位为分,
        params.put("spbill_create_ip",spbill_create_ip); //回调IP地址,
        params.put("notify_url",notify_url); //回调函数
        params.put("trade_type","JSAPI"); //交易类型 小程序取值如下:JSAPI
        params.put("openid",openid); //交易类型 小程序取值如下:JSAPI
        String sign = OrderUtil.sign(params, key);//生成签名
        params.put("sign",sign);  // 签名一定要放在最后面!!!!!!!
        String xmlResult = OrderUtil.ArrayToXml(params);//将map集合转成xml,供回调接口使用

        //发送一次签名
        String result = null;
        result = post(url,xmlResult);
        //解析一次签名获得的数据
        Map<String ,Object> signMap = new HashMap<>();
        Map<String,Object> bean = readStringXmlOut(result);
        System.out.println("==========签名返回参数===============");
        System.out.println(bean);
        System.out.println("==========签名返回参数===============");
        String prepayId = bean.get("prepay_id").toString();
        signMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
        signMap.put("nonceStr", nonce_str);
        signMap.put("package", "prepay_id="+prepayId);
        signMap.put("signType", "MD5");
        signMap.put("appId", appId);
        String paySign2 = OrderUtil.sign(signMap, key);
        signMap.put("paySign", paySign2);
        return  signMap ;
}
    @SuppressWarnings("deprecation")
    public static String post(String url,String xmlFileName){
        //关闭
        System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
        System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
        System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "stdout");
        //创建httpclient工具对象
        HttpClient client = new HttpClient();
        //创建post请求方法
        PostMethod myPost = new PostMethod(url);
        //设置请求超时时间
        client.setConnectionTimeout(300*1000);
        String responseString = null;
        try{
            //设置请求头部类型
            myPost.setRequestHeader("Content-Type","text/xml");
            myPost.setRequestHeader("charset","utf-8");
            myPost.setRequestBody(xmlFileName);
            int statusCode = client.executeMethod(myPost);
            if(statusCode == HttpStatus.SC_OK){
                BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());
                byte[] bytes = new byte[1024];
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                int count = 0;
                while((count = bis.read(bytes))!= -1){
                    bos.write(bytes, 0, count);
                }
                byte[] strByte = bos.toByteArray();
                responseString = new String(strByte,0,strByte.length,"utf-8");
                bos.close();
                bis.close();
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        myPost.releaseConnection();
        return responseString;
    }

    public static Map<String, Object> readStringXmlOut(String xml) {
        Map<String, Object> map = new HashMap<String, Object>();
        Document doc = null;
        try {
            doc = DocumentHelper.parseText(xml); // 将字符串转为XML
            Element rootElt = doc.getRootElement(); // 获取根节点
            @SuppressWarnings("unchecked")
            List<Element> list = rootElt.elements();// 获取xx根节点下所有节点
            for (Element element : list) { // 遍历节点
                map.put(element.getName(), element.getText()); // 节点的name为map的key,text为map的value
            }
        } catch (DocumentException e) {
            e.printStackTrace();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }
}

 

OrderUtil  此工具主要用来生成签名

package com.systek.scenic.utils.wechatApp;

import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/*
*
 * @description:此工具类用来生成支付所需的签名
 * @author: YinZengXiang
 * @date: 2020/3/5 11:11
 * @param: null
 * @return:
 */
public class OrderUtil {

    /**
     * sha1加密
     * @param str
     * @return
     */
    public static String sha1(String str){
        if(str==null||str.length()==0){
            return null;
        }
        char hexDigits[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
        try {
            MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
            mdTemp.update(str.getBytes("UTF-8"));
            byte[] md = mdTemp.digest();
            int j = md.length;
            char buf[] = new char[j*2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
                buf[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(buf);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 生成签名sign
     * 第一步:非空参数值的参数按照参数名ASCII码从小到大排序,按照键值对的形式。生成字符串StringA
     * stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
     * 第二部:拼接API密钥,这里的秘钥是微信商户平台的秘钥,是自己设置的,不是公众号的秘钥
     * stringSignTemp="stringA&key=192006250b4c09247ec02edce69f6a2d"
     * 第三部:MD5加密
     * sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7"
     *
     * @param map 不包含空字符串的map
     * @return
     * @throws Exception
     */
    public static String sign(Map<String, Object> map,String key) throws Exception {
        String bizString = FormatBizQueryParaMap(map, false);
        return MD5.sign(bizString, key);
    }

    public static String FormatBizQueryParaMap(Map<String, Object> paraMap, boolean urlencode) throws Exception {
        String buff = "";
        try {
            List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(paraMap.entrySet());
            Collections.sort(infoIds,
                    new Comparator<Map.Entry<String, Object>>() {
                        public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
                            return (o1.getKey()).toString().compareTo(o2.getKey());
                        }
                    });
            for (int i = 0; i < infoIds.size(); i++) {
                Map.Entry<String, Object> item = infoIds.get(i);
                //System.out.println(item.getKey());
                if (item.getKey() != "") {
                    String key = item.getKey();
                    String val = item.getValue().toString();
                    if (urlencode) {
                        val = URLEncoder.encode(val, "utf-8");
                    }
                    buff += key + "=" + val + "&";
                }
            }
            if (buff.isEmpty() == false) {
                buff = buff.substring(0, buff.length() - 1);
            }
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        }
        return buff;
    }

    /**
     * 返回key的值
     * @param map
     * @param key
     * @return
     */
    private static String getParamString(Map<String,Object> map, String key) {
        String buf = "";
        if (map.get(key) instanceof String[]) {
            buf = ((String[]) map.get(key))[0];
        } else {
            buf = (String) map.get(key);
        }
        return buf;
    }

    /**
     * 字符串列表从大到小排序
     * @param data
     * @return
     */
    @SuppressWarnings("unused")
    private static List<String> sort(List<String> data) {
        Collections.sort(data, new Comparator<String>() {
            public int compare(String obj1, String obj2) {
                return obj1.compareTo(obj2);
            }
        });
        return data;
    }

    /**
     * Map转Xml
     * @param arr
     * @return
     */
    public static String MapToXml(Map<String, String> arr) {
        String xml = "<xml>";
        Iterator<Entry<String, String>> iter = arr.entrySet().iterator();
        while (iter.hasNext()) {
            Entry<String, String> entry = iter.next();
            String key = entry.getKey();
            String val = entry.getValue();
            if (IsNumeric(val)) {
                xml += "<" + key + ">" + val + "</" + key + ">";
            } else
                xml += "<" + key + "><![CDATA[" + val + "]]></" + key + ">";
        }
        xml += "</xml>";
        return xml;
    }

    private static boolean IsNumeric(String str) {
        if (str.matches("\\d *")) {
            return true;
        } else {
            return false;
        }
    }

    public static String ArrayToXml(Map<String, Object> arr) {
        String xml = "<xml>";
        Iterator<Entry<String, Object>> iter = arr.entrySet().iterator();
        while (iter.hasNext()) {
            Entry<String, Object> entry = iter.next();
            String key = entry.getKey();
            String val = entry.getValue().toString();
            if (IsNumeric(val)) {
                xml += "<" + key + ">" + val + "</" + key + ">";
            } else
                xml += "<" + key + "><![CDATA[" + val + "]]></" + key + ">";
        }
        xml += "</xml>";
        return xml;
    }

    /**
     * 对参数列表进行排序,并拼接key=value&key=value形式
     * @param map
     * @return
     */
    public static String sortParameters(Map<String, Object> map) {
        Set<String> keys = map.keySet();
        List<String> paramsBuf = new ArrayList<String>();
        for (String k : keys) {
            paramsBuf.add((k + "=" + getParamString(map, k)));
        }
        // 对参数排序
        Collections.sort(paramsBuf);
        String result="";
        int count=paramsBuf.size();
        for(int i=0;i<count;i++){
            if(i<(count-1)){
                result+=paramsBuf.get(i)+"&";
            }else {
                result+=paramsBuf.get(i);
            }
        }
        return result;
    }

}

 MD5 工具类 使用MD5算法获取加密后的参数 主要用于生成sign

package com.systek.scenic.utils.wechatApp;



import java.security.MessageDigest;

public class MD5 {

    private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};

    /**
     * 转换字节数组为16进制字串
     *
     * @param b
     *            字节数组
     * @return 16进制字串
     */
    public static String byteArrayToHexString(byte[] b) {
        StringBuilder resultSb = new StringBuilder();
        for (byte aB : b) {
            resultSb.append(byteToHexString(aB));
        }
        return resultSb.toString();
    }

    /**
     * 转换byte到16进制
     *
     * @param b
     *            要转换的byte
     * @return 16进制格式
     */
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n = 256 + n;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    /**
     * MD5编码
     *
     * @param origin
     *            原始字符串
     * @return 经过MD5加密之后的结果
     */
    public static String MD5Encode(String origin) {
        String resultString = null;
        try {
            resultString = origin;
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(resultString.getBytes("UTF-8"));
            resultString = byteArrayToHexString(md.digest());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }

    /**
     * 微信支付获取prepay_id
     * @param content
     * @param key
     * @return
     * @throws Exception
     */
    public static String sign(String content, String key) throws Exception{
        String signStr = "";
        signStr = content + "&key=" + key;
        return MD5COVER(signStr).toUpperCase();
    }

    /**
     * 微信支付获取prepay_id加密使用
     *
     * @param s
     * @return
     */
    public final static String MD5COVER(String s) {
        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        try {
            byte[] btInput = s.getBytes();
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            mdInst.update(btInput);
            byte[] md = mdInst.digest();
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}

 获取支付参数的Service

public Map<String, Object> takeOrder(AppUser appUser, PayOrder payOrder) throws Exception {

        //创建订单
        //String openid,String body,String total_fee,String out_trade_no
        String openid = appUser.getWeChatOpenId();
        String body = payOrder.getBody();
        double totalFeeD = payOrder.getTotalFee();
        int s = (int) (totalFeeD*100);
        String totalFee = String.valueOf(s);
        String prepayId = "";
        //生成订单号
        String outTradeNo = StringUtil.getUuidNoneHyphen();
        //需要返回的字段
        Map<String, Object> stringObjectMap = PayUtil.takeOrder(openid, body, totalFee, outTradeNo);
        //获取prepayId
        System.out.println(stringObjectMap);
        return stringObjectMap;
    }

 小程序调用方式

wx.request({
          url: 'https://systek.imdo.co/appsrv/api/PayOrder/takeOrder',
          data:
          {
            weChatOpenId: res.data.weChatOpenId,
            body: "systek",
            totalFee: 0.01
          },
          // url: 'https://systek.imdo.co/appsrv/pay/wx/request_payment',
          // data:
          // {
          //   app_user_id: res.data.id,
          //   access_token: res.data.accessToken,
          //   vip_type:3,
          //   pay_mode:1,
          //   scenic_code: 4103000001 
          // },
          method: 'GET',
          success: function (res) {
            console.log(res.data.data)
              var date = new Date() 
            wx.requestPayment({
              'timeStamp': res.data.data.timeStamp,
              'nonceStr': res.data.data.nonceStr,
              'package': res.data.data.package,
              'signType': 'MD5',
              'paySign': res.data.data.paySign,
              'success': function (res) { 
                console.log("============成功==============")
                console.log(res)
              },
              'fail': function (res) { 
                console.log("============失败==============")
                console.log(res)
              },
              'complete': function (res) {
                console.log("============完成==============")
                console.log(res)
               }
            })

          },
        })
      }
    });

返回参数

uniapp springboot微信小程序支付_java