微信提现工具类

103 阅读2分钟

微信的业务文档一向看了都想让人恶心,所以我自己排完坑就总结出来 这个微信提现的功能,大家放心使用,经过企业实践的,有问题可以留言沟通

package com.ydwl.shantui.user.util;

import com.ydwl.shantui.util.SignUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.*;

/**
 * 微信工具类
 *
 * @author weiyimin
 * @create 2022-02-28 09:42
 **/
@Slf4j
@Data
public class WxPayUtil {

    public static final String WITHDRAW_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
    private static final String CERTIFICATE = "/data/project/certificate/apiclient_cert.p12";
    public static String appId;
    public static String mchId;
    public static String wxSingKey;
    
    /**
     * 微信提现(企业付款)
     *
     * @param orderNo 自己生成的订单号
     * @param openId  微信提供的openId
     * @param amount  提现金额
     * @param ip      默认传null
     * @param desc    备注
     * @return
     */
    public static Map payForUser(String orderNo, String openId, Long amount, String ip, String desc) {
        Map map;
        SortedMap<String, Object> params = new TreeMap<>();
        params.put("mch_appid", appId);
        params.put("mchid", mchId);
        params.put("partner_trade_no", orderNo);
        params.put("nonce_str", getRandomStr(32).toUpperCase());
        params.put("openid", openId);
        params.put("check_name", WxPayConstant.CHECK_NAME);
        params.put("amount", amount.toString());
        params.put("desc", desc);
        String sign = SignUtil.getSign(params, wxSingKey);
        params.put("sign", sign);
        String xmlInfo = getRequestXml(params);
        try {
            CloseableHttpResponse response = wxPost(WITHDRAW_URL, xmlInfo, true);
            String transfersXml = EntityUtils.toString(response.getEntity(), "utf-8");
            map = doXmlParse(transfersXml);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return map;
    }

    /**
     * 查询微信提现是否成功(企业付款)
     */
    public static Map checkPay(String orderNo) {
        Map map;
        SortedMap<String, Object> params = new TreeMap<>();
        params.put("appid", appId);
        params.put("mch_id", mchId);
        params.put("partner_trade_no", orderNo);
        params.put("nonce_str", getRandomStr(32).toUpperCase());
        String sign = SignUtil.getSign(params, wxSingKey);
        params.put("sign", sign);
        String xmlInfo = getRequestXml(params);
        try {
            CloseableHttpResponse response = wxPost(WxPayConstant.CHECK_SUCCESS_URL, xmlInfo, true);
            String transfersXml = EntityUtils.toString(response.getEntity(), "utf-8");
            map = doXmlParse(transfersXml);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return map;
    }

    /**
     * 生成随机数
     *
     * @param length 生成字符串的长度
     * @return
     */
    public static String getRandomStr(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }


    /**
     * 请求xml组装
     *
     * @return
     */
    private static String getRequestXml(SortedMap<String, Object> parameters) {
        StringBuilder sb = new StringBuilder();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        for (Object e : es) {
            Map.Entry entry = (Map.Entry) e;
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {
                sb.append("<").append(key).append(">").append("<![CDATA[").append(value).append("]]></").append(key).append(">");
            } else {
                sb.append("<").append(key).append(">").append(value).append("</").append(key).append(">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * xml解析
     *
     * @return
     */
    private static Map doXmlParse(String strxml) throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=".*"", "encoding="UTF-8"");
        if ("".equals(strxml)) {
            return null;
        }
        Map m = new HashMap(16);
        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        for (Object aList : list) {
            Element e = (Element) aList;
            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;
    }

    private static String getChildrenText(List children) {
        StringBuilder sb = new StringBuilder();
        if (!children.isEmpty()) {
            for (Object aChildren : children) {
                Element e = (Element) aChildren;
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<").append(name).append(">");
                if (!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</").append(name).append(">");
            }
        }
        return sb.toString();
    }

    /**
     * 发送post请求
     *
     * @param url          请求地址
     * @param outputEntity 发送内容
     * @param isLoadCert   是否加载证书
     */
    public static CloseableHttpResponse wxPost(String url, String outputEntity, boolean isLoadCert) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        // 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(new StringEntity(outputEntity, "UTF-8"));
        if (isLoadCert) {
            SSLConnectionSocketFactory sslConnectionSocketFactory = initCert();
            // 加载含有证书的http请求
            return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build().execute(httpPost);
        } else {
            return HttpClients.custom().build().execute(httpPost);
        }
    }

    /**
     * 加载证书
     */
    public static SSLConnectionSocketFactory initCert() throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        FileInputStream inputStream = new FileInputStream(CERTIFICATE);
        if (null == inputStream) {
            return null;
        }
//        InputStream inputStream = WxPayUtil.class.getClassLoader().getResourceAsStream("static/apiclient_cert.p12");
        keyStore.load(inputStream, WxPayConstant.MCH_ID.toCharArray());
        if (null != inputStream) {
            inputStream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WxPayConstant.MCH_ID.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, new DefaultHostnameVerifier());
        return sslsf;
    }

}