微信支付v3——批量转账到零钱(提现)
进入正题之前先吐槽一下微信官方文档吧!一开始在官网上下载了微信支付版本V3的Demo,下载下来基本上难以投入使用,要学会使用的话,就要去看源码,看文档,稍微错过一点就会有一大堆的坑等着你跳,相比之下,阿里的文档就简单易懂,而且官方的文档下载下来之后,稍微改下参数就可以run!!
正文:
批量转账到零钱(服务商)的接入示例:
(微信文档地址:开发文档-微信支付批量转账到零钱)
微信支付V3版本,微信SDK,内部封装了部分方法(微信支付API v3的Apache HttpClient扩展,实现了请求签名的生成和应答签名的验证。)
第一步:在项目中引入Maven
<dependency><groupId>com.github.wechatpay-apiv3</groupId><artifactId>wechatpay-apache-httpclient</artifactId><version>0.4.8</version>
</dependency>
第二步:构造微信接口通讯Client,构造隐私数据加密公钥,每次调用微信V3的接口都需要用Client来发起,敏感数据需通过平台公钥加密。
package cn.schoolgroup.config.until;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 org.springframework.core.io.ClassPathResource;import java.io.IOException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;/*** @Author lulu* @Date 2022/09/07*/
public class WeChatClient {/*** 微信通讯client* @return CloseableHttpClient * 作者的文件都放在resour文件下的*/public static CloseableHttpClient getClient() throws IOException {/**商户私钥文件*/ClassPathResource classPathResource = new ClassPathResource("/cert/apiclient_key.pem");InputStream mchPrivateKeyInputStream = classPathResource.getInputStream();/**微信平台公钥文件*/ClassPathResource classPathResource1 = new ClassPathResource("/cert/wechatpay_64124ADAF002CDEEBA0A58215D8572D270F2A854.pem");InputStream platformKeyInputStream = classPathResource1.getInputStream();PrivateKey mchPrivateKey = PemUtil.loadPrivateKey(mchPrivateKeyInputStream);WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create().withMerchant("自己的商户号", "自己的商户证书序列号", mchPrivateKey).withWechatPay(Arrays.asList(PemUtil.loadCertificate(platformKeyInputStream)));CloseableHttpClient httpClient = builder.build();return httpClient;}}
第三步:调起商家转账API
需要在《转账到用户零钱》产品的开发设置中,添加安全ip,接口才能访问
/*** 发起商家转账API** @Author lulu* @Date 2022/07/09*/public static void merchantTransfers() throws IllegalBlockSizeException, IOException {CloseableHttpClient httpClient = WeChatClient.getClient();Map<String, Object> map = new HashMap<>();map.put("appid", "小程序appid");map.put("out_batch_no", "zcx04");map.put("batch_name", "1元");map.put("batch_remark", "1元");map.put("total_amount", 100);map.put("total_num", 1);List<Map> list = new ArrayList<>();Map<String, Object> subMap = new HashMap<>(4);subMap.put("out_detail_no", "detail1");subMap.put("transfer_amount", 100);subMap.put("transfer_remark", "1元");subMap.put("openid", "用户openid");//明细转账金额 >= 2000,收款用户姓名必填//subMap.put("user_name", RsaCryptoUtil.encryptOAEP("收款用户姓名", zcx));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 result= EntityUtils.toString(response.getEntity());System.out.println("返回参数:" + result);//业务逻辑自己处理} finally {response.close();}}
第四步:商家批次单号查询批次单API
/*** 商家批次单号查询批次单API* @throws URISyntaxException* @throws IOException*/public static void queryBatch() throws URISyntaxException, IOException {CloseableHttpClient httpClient = WeChatClient.getClient();//批次号String batchCode = "zcx03";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 (AjaxResult.me().getCode().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();}}
获取平台证书的方法