【Wechat】微信支付APIV3(商户) 接入批量转账到零钱

微信支付V3(商户)》批量转账到零钱

微信支付V3版本,微信SDK,内部封装了部分方法,参照着官方Demo,下载地址:

GitHub - wechatpay-apiv3/wechatpay-apache-httpclient: 微信支付 APIv3 Apache HttpClient装饰器(decorator)

1. 引入微信SDK依赖

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.3.0</version>
</dependency>
 
<!--另外需要使用到的依赖-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.6.6</version>
</dependency>

2. 下载微信支付平台证书

构造微信接口通讯Client,构造隐私数据加密公钥,每次调用微信V3的接口都需要用Client来发起,敏感数据需通过平台公钥加密。下载他们的jar包去获取平台公钥。

下载地址在这里:

GitHub - wechatpay-apiv3/CertificateDownloader: Java 微信支付 APIv3 平台证书的命令行下载工具

具体的配置方法,在文档地址里面都有说明,需要详细阅读,按照步骤操作即可。

以下参数都需要从微信商户后台获取或设置:

mchId:商户号(例如:1161161166)
mchSerialNo:商户证书序列号(例如:3A936D302300073457C8B22CB1111DAA1111DDDD)
mchPrivateKeyFilePath:商户证书私钥地址(例如:E:\wechat\apiclient_key.pem)
apiV3key:商户API V3密钥(例如:4a9f2c433adcc2698ba7704faedeaf82)MD5加密生成密钥在微信商户后台设置
outputFilePath:保存下载微信支付平台证书文件地址(例如:E:\wechat)

下载证书文件解压后得到 apiclient_cert.p12,apiclient_cert.pem(公钥),apiclient_key.pem(私钥)

我执行jar包的命令:

java -jar CertificateDownloader.jar -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}

完整的执行命令为:

java -jar CertificateDownloader.jar -k 4a9f2c433adcc2698ba7704faedeaf82 -m 1161161166 -f E:\wechat\apiclient_key.pem -s 3A936D302300073457C8B22CB1111DAA1111DDDD -o E:\wechat

获取后,保存下来平台公钥、平台公钥序列号,可多次使用。

例如下载文件为:E:\wechat\wechatpay_61A8BC31485A8BD61CAC935EDEB9D5F0CF6EEEEE.pem
平台公钥:wechatpay_61A8BC31485A8BD61CAC935EDEB9D5F0CF6EEEEE.pem 后续改名为 wechatpay.pem
平台公钥序列号:61A8BC31485A8BD61CAC935EDEB9D5F0CF6EEEEE

期间遇到问题参考文献【下载微信支付平台证书及首次下载报错处理】

​下面是构造微信Client的代码:

package Util;
 
import cn.hutool.core.io.FileUtil;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.impl.client.CloseableHttpClient;
 
import java.io.File;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;
 
/**
 * @Author jakori
 * @Date 2022/07/20 10:00
 */
public class WeChatClient {
    
    /**
     * 微信通讯client
     * @return CloseableHttpClient 
     */
    public static CloseableHttpClient getClient() {
        /**商户私钥文件*/
        File mchPrivateKeyFile = new File("E:\\wechat\\apiclient_key.pem");
        InputStream mchPrivateKeyInputStream = FileUtil.getInputStream(mchPrivateKeyFile);

        /**微信平台公钥文件*/
        File platformKeyFile = new File("E:\\wechat\\wechatpay.pem");
        InputStream platformKeyInputStream = FileUtil.getInputStream(platformKeyFile);
 
        PrivateKey mchPrivateKey = PemUtil.loadPrivateKey(mchPrivateKeyInputStream);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant("商户号", "商户证书序列号", mchPrivateKey)
                .withWechatPay(Arrays.asList(PemUtil.loadCertificate(platformKeyInputStream)));
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }
 
    
    /**
     * 微信敏感数据加密公钥
     * @return
     */
    public static X509Certificate getSaveCertificates() {
    	/**商户公钥证书文件*/
        File mchPublicKeyFile = new File("E:\\wechat\\apiclient_cert.pem");
        InputStream mchPublicKeyInputStream = FileUtil.getInputStream(mchPublicKeyFile);
        return PemUtil.loadCertificate(mchPublicKeyInputStream);
    }
 
}

3. 发起批量转账API

前置:

1、需用户与小程序绑定,获取用户的openId;

2、也需在特约商户后台,服务商后台绑定对应的小程序的appId。

具体步骤可以参考:

​​​​​​商家商户号与AppID账号关联管理

绑定成功后,即可用调用接口,

微信接口说明地址:开发文档-微信支付批量转账到零钱

代码如下:

import Util.WeChatClient;
import cn.hutool.json.JSONUtil;
import com.wechat.pay.contrib.apache.httpclient.util.RsaCryptoUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
 
import javax.crypto.IllegalBlockSizeException;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.HttpHeaders.CONTENT_TYPE;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
 
/**
 * @Author zkinghao
 * @Date 2022/1/11 14:19
 */
public class TestBatch {
 
    /**
     * 发起批量转账API
     *
	 * @Author jakori
	 * @Date 2022/07/20 10:00
     */
    public static void batchPay() throws IllegalBlockSizeException, IOException {
        CloseableHttpClient httpClient = WeChatClient.getClient();
        X509Certificate x509Certificate = WeChatClient.getSaveCertificates();
        Map<String, Object> map = new HashMap<>();
        map.put("appid", "服务商的appid");
        map.put("out_batch_no", "batch01");
        map.put("batch_name", "测试3");
        map.put("batch_remark", "批次备注出款");
        map.put("total_amount", 50);
        map.put("total_num", 1);
        List<Map> list = new ArrayList<>();
        Map<String, Object> subMap = new HashMap<>(4);
        subMap.put("out_detail_no", "detail01");
        subMap.put("transfer_amount", 50);
        subMap.put("transfer_remark", "明细备注1");
        subMap.put("openid", "收款用户openid");
        //明细转账金额 >= 2000,收款用户姓名必填
        //subMap.put("user_name", RsaCryptoUtil.encryptOAEP("收款用户姓名", x509Certificate));
        list.add(subMap);
        map.put("transfer_detail_list", list);
        
        String body = JSONUtil.toJsonStr(map);
        System.out.println("请求参数:" + body);
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/transfer/batches");
        httpPost.addHeader(ACCEPT, APPLICATION_JSON.toString());
        httpPost.addHeader(CONTENT_TYPE, APPLICATION_JSON.toString());
        httpPost.addHeader("Wechatpay-Serial", "商户证书序列号");
 
        httpPost.setEntity(new StringEntity(body, "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            System.out.println("返回参数:" + bodyAsString);
        } finally {
            response.close();
        }
    }
 
    public static void main(String[] args) throws Exception {
        /**发起批量转账API*/
        batchPay();
    }
}

3. 商家明细单号查询明细单API

微信接口说明地址:开发文档-微信支付批量转账到零钱

代码如下:

import Util.WeChatClient;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
 
import java.io.IOException;
import java.net.URISyntaxException;
 
import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
 
/**
 * @Author jakori
 * @Date 2022/07/20 10:00
 */
public class TestQueryBatch {
 
    public static final Integer SUCCESS_CODE = 200;
 
 
    /**
     * 商家明细单号查询明细单API
     * @throws URISyntaxException
     * @throws IOException
     */
    public static void queryBatch() throws URISyntaxException, IOException {
        CloseableHttpClient httpClient = WeChatClient.getClient();
        //批次号
        String batchCode = "batch01";
        //明细号
        String detailCode = "detail01";
        StringBuilder url = new StringBuilder("https://api.mch.weixin.qq.com/v3/transfer/batches/out-batch-no/");
        url.append(batchCode).append("?need_query_detail=true").append("&detail_status=ALL");
        URIBuilder uriBuilder = new URIBuilder(url.toString());
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        httpGet.addHeader(ACCEPT, APPLICATION_JSON.toString());
        CloseableHttpResponse response = httpClient.execute(httpGet);
        try {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            System.out.println("微信支付查询返回:" + bodyAsString);
            JSONObject jsonObject = JSONUtil.parseObj(bodyAsString);
            if (SUCCESS_CODE.equals(response.getStatusLine().getStatusCode())) {
                //转账批次单基本信息
                JSONObject transferBatch = jsonObject.getJSONObject("transfer_batch");
                //批次状态
                String batchStatus = transferBatch.getStr("batch_status");
                System.out.println("交易状态:" + batchStatus);
                //已完成
                if ("FINISHED".equals(batchStatus)){
                    JSONArray transferDetailList = jsonObject.getJSONArray("transfer_detail_list");
                    for (int i = 0; i < transferDetailList.size(); i++){
                        JSONObject detail = (JSONObject) transferDetailList.get(i);
                        //明细单号
                        String outDetailNo = detail.getStr("out_detail_no");
                        //明细状态
                        String detailStatus = detail.getStr("detail_status");
                        System.out.println("交易明细单号:"+ outDetailNo +",明细状态:" + detailStatus);
                    }
                }else if ("CLOSED".equals(batchStatus)){
                    //批次关闭原因
                    String closeReason = transferBatch.getStr("close_reason");
                    System.out.println("交易关闭原因:" + closeReason);
                }
            } else {
                //失败
            }
        } finally {
            response.close();
        }
    }
 
    public static void main(String[] args) throws Exception {
        /**商家明细单号查询明细单API*/
        queryBatch();
    }
}

参考文献:微信支付API V3版本JAVA开发指南

你可能感兴趣的:(wechat,微信)