微信企业账户提现到微信用户钱包零钱(通过openid)

较快完成开发

1. apiclient_cert.p12:微信商户支付证书,这个是一个文件,配置在项目里或者服务器

2.工具类

        



import org.apache.commons.codec.digest.DigestUtils;

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.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.*;

public class CommonUtil {
    /**
     * 获取一定长度的随机字符串,范围0-9,a-z
     * @param length:指定字符串长度
     * @return 一定长度的随机字符串
     */
    public static String getRandomStringByLength(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();
    }
    /**
     * 除去数组中的空值和签名参数
     *
     * @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<String>(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 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));
    }
    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);
        }
    }
    /**
     * 将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;
    }
 /**
     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
     *
     * @param strxml
     * @return
     * @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;
    }
}

3.结果实体

        


import lombok.Data;

/**
 * @author ljchen
 */
@Data
public class TransferResultDto{
    /**
     * 转账结果:成功或失败(SUCCESS/FAIL)
     */
    private String resultCode;
    /**
     * 商户转账订单号
     */
    private String partnerTradeNo;
    /**
     * 微信订单号
     */
    private String paymentNo;
    /**
     * 微信支付成功时间
     */
    private  String paymentTime;

    /**
     * 错误代码
     */
    private String errorCode;
   
    /**
     * 错误代码描述
     */
    private  String errorCodeDes;
}

4.实现代码


import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
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.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.springframework.util.CollectionUtils;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.security.*;
import java.util.*;

/**
 * 微信提现
 * @author
 */
public class WxPayServiceImpl{


    /**
     * 企业微信提现到用户
     * @param amount  提现金额  整数 单位分
     * @param openId  微信用户 openid
     * @param transactionNo   自定义 提现记录全局唯一标识
     * @param request
     * @param msg         企业付款操作说明
     * @throws Exception
     */

    public TransferResultDto wxWithdrawalTransferSample(HttpServletRequest request, String openId,String amount,String transactionNo,String msg) throws Exception {
        //封装提现请求参数map
        Map<String, String> packageParams = handleInputParamMap(request,openId, amount, transactionNo,msg);
        //将map转成xml
        String mapToXml = CommonUtil.mapToXml(packageParams);
        //调用接口
        String result = requestOnce("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers", mapToXml, 30000, 30000, true);
        //调用提现请求接口,返回结果是xml格式
        log.info("提现接口{}",result);
        //处理微信提现接口返回结果
        return handleTransferResultDto(result);
    }

    private Map<String, String> handleInputParamMap(HttpServletRequest request,String openId,String amount,String transactionNo,String msg) throws Exception {
        Map<String, String> packageParams = new HashMap<String, String>();

        //1.0 拼凑企业支付需要的参数
        //微信小程序的appid
        String mchAppId ="微信小程序的appid";
        //商户号
        String mchId = "商户号";
        //生成随机数
        String nonceStr =CommonUtil.getRandomStringByLength(32);
        //是否验证真实姓名呢:NO_CHECK:不校验真实姓名; FORCE_CHECK:强校验真实姓名
        String checkName = "NO_CHECK";
        //企业付款操作说明信息, 默认为提现到微信零钱
        //获取调用接口的机器ip
        String spbillCreateIp = request.getRemoteHost();
        //微信小程序的appid
        packageParams.put("mch_appid", mchAppId);
        //商户号
        packageParams.put("mchid", mchId);
        //随机生成后数字,保证安全性
        packageParams.put("nonce_str", nonceStr);
        //生成商户订单号
        packageParams.put("partner_trade_no", transactionNo);
        // 支付给用户openid
        packageParams.put("openid", openId);
        //是否验证真实姓名呢
        packageParams.put("check_name", checkName);
        //企业付款金额,单位为分
        packageParams.put("amount",amount);
        //企业付款操作说明信息。必填。
        packageParams.put("desc", msg);
        //调用接口的机器Ip地址
        packageParams.put("spbill_create_ip", spbillCreateIp);
        //3.0 生成自己的签名
        // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
        packageParams=CommonUtil.paraFilter(packageParams);

        String prestr = CommonUtil.createLinkString(packageParams);
        //签名
        String sign = CommonUtil.sign(prestr,mchAppId,"utf-8").toUpperCase();
        //封装入参map
        packageParams.put("sign", sign);
        return packageParams;
    }

    private TransferResultDto handleTransferResultDto(String resultXml) throws Exception {
        TransferResultDto resultDto = new TransferResultDto();

        Map<String, String> resultMap = CommonUtil.doXMLParse(resultXml);
        if (!CollectionUtils.isEmpty(resultMap) && "SUCCESS".equals(resultMap.get("result_code"))&&"SUCCESS".equals(resultMap.get("return_code"))) {
            log.info("转账成功");
            resultDto.setResultCode(resultMap.get("result_code"));
            // 商户转账订单号
            resultDto.setPartnerTradeNo(resultMap.get("partner_trade_no"));
            // 微信订单号
            resultDto.setPaymentNo(resultMap.get("payment_no"));
            // 微信支付成功时间
            resultDto.setPaymentTime(resultMap.get("payment_time"));
        } else {
            resultDto.setResultCode(resultMap.get("result_code"));
            //错误代码
            resultDto.setErrorCode(resultMap.get("err_code"));
            //错误代码描述
            resultDto.setErrorCodeDes(resultMap.get("err_code_des"));
        }
        return resultDto;
    }
    /**
     * @param data
     * @param connectTimeoutMs
     * @param readTimeoutMs
     * @param useCert 是否使用证书,针对退款、撤销等操作
     * @return
     * @throws Exception
     */
    private String requestOnce(String url,String data, int connectTimeoutMs, int readTimeoutMs, boolean useCert) throws Exception {
        //证书路径
        String path = "你的证书路径";
        BasicHttpClientConnectionManager connManager;
        if (useCert) {
            //商户id
            char[] password = "小程序商户id".toCharArray();

            //加载证书
            FileInputStream certStream = new FileInputStream(new File(path));
            KeyStore ks = KeyStore.getInstance("PKCS12");
            ks.load(certStream, password);

            // 实例化密钥库 & 初始化密钥工厂
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, password);

            // 创建 SSLContext
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());

            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                    sslContext,
                    new String[]{"TLSv1"},
                    null,
                    new DefaultHostnameVerifier());

            connManager = new BasicHttpClientConnectionManager(
                    RegistryBuilder.<ConnectionSocketFactory>create()
                            .register("http", PlainConnectionSocketFactory.getSocketFactory())
                            .register("https", sslConnectionSocketFactory)
                            .build(),
                    null,
                    null,
                    null
            );
        }
        else {
            connManager = new BasicHttpClientConnectionManager(
                    RegistryBuilder.<ConnectionSocketFactory>create()
                            .register("http", PlainConnectionSocketFactory.getSocketFactory())
                            .register("https", SSLConnectionSocketFactory.getSocketFactory())
                            .build(),
                    null,
                    null,
                    null
            );
        }

        HttpClient httpClient = HttpClientBuilder.create()
                .setConnectionManager(connManager)
                .build();

        HttpPost httpPost = new HttpPost(url);

        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeoutMs).setConnectTimeout(connectTimeoutMs).build();
        httpPost.setConfig(requestConfig);

        StringEntity postEntity = new StringEntity(data, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.addHeader("User-Agent",  "WXPaySDK/3.0.9" +
                " (" + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version") +
                ") Java/" + System.getProperty("java.version") + " HttpClient/" + HttpClient.class.getPackage().getImplementationVersion() + " " + path);
        httpPost.setEntity(postEntity);
        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        return EntityUtils.toString(httpEntity, "UTF-8");

    }

}

小结:代码需要更换参数 具体位置请自行查找(有点乱,请自行配置修改在同一个位置)

依赖:

<dependency>
    <groupId>org.jdom</groupId>
    <artifactId>jdom2</artifactId>
<version>2.0.6</version>
</dependency>

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值