打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Java 微信支付 发起商家转账API 2022年v3 transfer batches

1 概述

接口说明适用对象:直连商户请求URL:https://api.mch.weixin.qq.com/v3/transfer/batches请求方式:POST接口限频:单个商户 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。是否需要证书:是

商户可以通过该接口同时向多个用户微信零钱进行转账操作。

2 发起微信转账 请求参数组装

import cn.hutool.core.util.IdUtil;import com.google.gson.Gson;import com.google.gson.GsonBuilder;import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;
@Slf4jpublic class WechatPay { private static final Gson GSON = new GsonBuilder().create();
public static void main(String[] args) throws Exception { //测试微信转账 weixinTransferBat(); } //请看下面 public static String weixinTransferBat() {...};
  public static String weixinTransferBat() throws Exception {

//商户号 String mchid = ""; //申请商户号的appid或商户号绑定的appid(企业号corpid即为此appid) String appId = ""; //用户在直连商户应用下的用户标示 String openId = ""; //商户证书编号 String wechatPayserialNo = ""; //商户证书路径(在你本机测试时放你本机路径中的就可以) String privatekeypath = "";

Map<String, Object> postMap = new HashMap<String, Object>();
//商家批次单号 长度 1~32 String outNo = IdUtil.getSnowflake(0, 0).nextIdStr();
postMap.put("appid", appId); postMap.put("out_batch_no", outNo); //该笔批量转账的名称 postMap.put("batch_name", "测试转账"); //转账说明,UTF8编码,最多允许32个字符 postMap.put("batch_remark", "测试转账"); //转账金额单位为“分”。总金额 postMap.put("total_amount", 100); //。转账总笔数 postMap.put("total_num", 1);

List<Map> list = new ArrayList<>(); Map<String, Object> subMap = new HashMap<>(4); //商家明细单号 subMap.put("out_detail_no", outNo); //转账金额 subMap.put("transfer_amount", 100); //转账备注 subMap.put("transfer_remark", "明细备注1"); //用户在直连商户应用下的用户标示 subMap.put("openid", openId);// subMap.put("user_name", RsaCryptoUtil.encryptOAEP(userName, x509Certificate)); list.add(subMap); postMap.put("transfer_detail_list", list);
//发起转账操作 String resStr = HttpUtil.postTransBatRequest( "https://api.mch.weixin.qq.com/v3/transfer/batches", GSON.toJson(postMap), wechatPayserialNo, mchid, privatekeypath);}

商家批次单号,因为我这里只有一笔,所以用了同一个,读者可以生成多个批次单号, IdUtil 我用的是 cn.hutool 中的工具类,依赖如下:

<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all --><dependency>    <groupId>cn.hutool</groupId>    <artifactId>hutool-all</artifactId>    <version>5.8.2</version></dependency>

证书序列号需要在微信商户后台申请

3 发起微信转账 请求核心代码

import lombok.extern.slf4j.Slf4j;import org.apache.http.HttpEntity;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.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

/** * 微信支付专用类 请求操作方法 * * @author Administrator */@Slf4jpublic class HttpUtil { /** * 发起批量转账API 批量转账到零钱 * * @param requestUrl * @param requestJson 组合参数 * @param wechatPayserialNo 商户证书序列号 * @param mchID4M 商户号 * @param privatekeypath 商户私钥证书路径 * @return */ public static String postTransBatRequest( String requestUrl, String requestJson, String wechatPayserialNo, String mchID4M, String privatekeypath) { CloseableHttpClient httpclient = HttpClients.createDefault(); CloseableHttpResponse response = null; HttpEntity entity = null; try { //商户私钥证书 HttpPost httpPost = new HttpPost(requestUrl); // NOTE: 建议指定charset=utf-8。低于4.4.6版本的HttpCore,不能正确的设置字符集,可能导致签名错误 httpPost.addHeader("Content-Type", "application/json"); httpPost.addHeader("Accept", "application/json"); //"55E551E614BAA5A3EA38AE03849A76D8C7DA735A"); httpPost.addHeader("Wechatpay-Serial", wechatPayserialNo); //-------------------------核心认证 start----------------------------------------------------------------- String strToken = VechatPayV3Util.getToken("POST", "/v3/transfer/batches", requestJson,mchID4M,wechatPayserialNo, privatekeypath); log.error("微信转账token "+strToken); // 添加认证信息 httpPost.addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048" + " " + strToken); //---------------------------核心认证 end--------------------------------------------------------------- httpPost.setEntity(new StringEntity(requestJson, "UTF-8")); //发起转账请求 response = httpclient.execute(httpPost); entity = response.getEntity();//获取返回的数据 log.info("-----getHeaders.Request-ID:"+response.getHeaders("Request-ID")); return EntityUtils.toString(entity); } catch (Exception e) { e.printStackTrace(); } finally { // 关闭流 } return null; }
}
3.1 微信转账 token

微信转账 token 签名认证 这套是固定的,大家可以直接复制使用


import java.io.IOException;import java.nio.file.Files;import java.nio.file.Paths;import java.security.KeyFactory;import java.security.NoSuchAlgorithmException;import java.security.PrivateKey;import java.security.Signature;import java.security.spec.InvalidKeySpecException;import java.security.spec.PKCS8EncodedKeySpec;import java.util.Random;
import lombok.extern.slf4j.Slf4j;import org.apache.commons.codec.binary.Base64;
import org.springframework.util.StringUtils;
@Slf4jpublic class VechatPayV3Util {
/** * * @param method 请求方法 post * @param canonicalUrl 请求地址 * @param body 请求参数 * @param merchantId 这里用的商户号 * @param certSerialNo 商户证书序列号 * @param keyPath 商户证书地址 * @return * @throws Exception */ public static String getToken( String method, String canonicalUrl, String body, String merchantId, String certSerialNo, String keyPath) throws Exception { String signStr = ""; //获取32位随机字符串 String nonceStr = getRandomString(32); //当前系统运行时间 long timestamp = System.currentTimeMillis() / 1000; if (StringUtils.isEmpty(body)) { body = ""; } //签名操作 String message = buildMessage(method, canonicalUrl, timestamp, nonceStr, body); //签名操作 String signature = sign(message.getBytes("utf-8"), keyPath); //组装参数 signStr = "mchid=\"" + merchantId + "\",timestamp=\"" + timestamp+ "\",nonce_str=\"" + nonceStr + "\",serial_no=\"" + certSerialNo + "\",signature=\"" + signature + "\""; return signStr; }
public static String buildMessage(String method, String canonicalUrl, long timestamp, String nonceStr, String body) {// String canonicalUrl = url.encodedPath();// if (url.encodedQuery() != null) {// canonicalUrl += "?" + url.encodedQuery();// } return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n"; }
public static String sign(byte[] message, String keyPath) throws Exception { Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(getPrivateKey(keyPath)); sign.update(message); return Base64.encodeBase64String(sign.sign()); }
/** * 微信支付-前端唤起支付参数-获取商户私钥 * * @param filename 私钥文件路径 (required) * @return 私钥对象 */ public static PrivateKey getPrivateKey(String filename) throws IOException {
log.error("签名 证书地址是 "+filename); String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8"); try { String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s+", ""); //System.out.println("--------privateKey---------:"+privateKey); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePrivate( new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey))); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("当前Java环境不支持RSA", e); } catch (InvalidKeySpecException e) { throw new RuntimeException("无效的密钥格式"); } } /** * 获取随机位数的字符串 * @param length * @return */ public static String getRandomString(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(); }}

4 处理微信转账结果

如果正常微信转账成功,则会返回 JSON 格式的单号等信息

{  "out_batch_no": "plfk2020042013",  "batch_id": "1030000071100999991182020050700019480001",  "create_time": "2015-05-20T13:29:35.120+08:00"}

小编这里定义的java bean 解析

//GSON 文章开头有声明//resStr 微信转账请求结果 发起转账操作结果TransferResponsEntity transferResponsEntity GSON.fromJson(resStr, TransferResponsEntity.class);

TransferResponsEntity 的定义如下:

@Data@NoArgsConstructorpublic class TransferResponsEntity implements Serializable {
private String code; private String message; private String batch_id; private String out_batch_no;
@Override public String toString() { return "TransferResponsEntity{" + "code='" + code + '\'' + ", message='" + message + '\'' + ", batch_id='" + batch_id + '\'' + ", out_batch_no='" + out_batch_no + '\'' + '}'; }}

转账成功的时候 code 值为null ,转账失败里,code 、message 值不为 null.

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
微信支付商家转账到零钱(超详细JAVA教程,全源码)
spring集成httpclient连接池配置
HTTP
java接入钉钉机器人实现告警通知
JAVA利用HttpClient进行POST请求(HTTPS)
Java 时间戳格式化
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服