微信支付超过2000元配置
📔 千寻简笔记介绍
千寻简笔记已开源,Gitee与GitHub搜索chihiro-notes
,包含笔记源文件.md
,以及PDF版本方便阅读,且是用了精美主题,阅读体验更佳,如果文章对你有帮助请帮我点一个Star
~
更新:支持在线阅读文章,根据发布日期分类。
@[toc]
简介
微信转账大于等于2000元需要携带收款人的姓名,建议大家使用官方示例中的方法进行转账,由于我的项目比较老,属于二开所以我重新写了一个工具类,用于获取平台证书,大家根据自己的需求去做参考,文章仅做参考,不承担任何责任。
如有疑问请留言讨论,文章有误欢迎指正,感谢您的观看。
发起商家转账
更新时间:2023.06.06
发起商家转账接口。商户可以通过该接口同时向多个用户微信零钱进行转账操作。请求消息中应包含商家批次单号、转账名称、appid、转账总金额、转账总笔数、转账openid、收款用户姓名等信息。注意受理成功将返回批次单号,此时并不代表转账成功,请通过查单接口查询单据的付款状态。
接口说明
支持商户:【普通商户】
请求方式:【POST】/v3/transfer/batches
请求域名:
- 【主域名】https: //api.mch.weixin.qq.com 使用该域名将访问就近的接入点
- 【备域名】https: //api2.mch.weixin.qq.com 使用该域名将访问异地的接入点 ,指引点击查看
本文关键词
导入依赖
、微信工具类
、请求示例
、超过2000元转账参数
、姓名加密
、请求参数
、微信支付平台证书序列号
、Wechatpay-Serial
官方示例
1 导入依赖
<!-- 微信支付所需依赖-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.10</version>
</dependency>
<!-- 微信支付所需依赖end-->
2 请求参数
2.1【Header】HTTP头参数
- Authorization必填string
请参考 签名认证 生成认证信息
- Accept必填string
请设置为
application/json
- Content-Type必填string
请设置为
application/json
- Wechatpay-Serial必填string
【微信支付平台证书序列号】 请求参数中的敏感字段,需要使用微信支付平台证书公钥加密。请设置为该证书的证书序列号。详见敏感信息加解密
2.2【Body】包体参数
- appid必填string(32)
【商户appid】 申请商户号的appid或商户号绑定的appid(企业号corpid即为此appid)
- out_batch_no必填string(32)
【商家批次单号】 商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
- batch_name必填string(32)
【批次名称】 该笔批量转账的名称
- batch_remark必填string(32)
【批次备注】 转账说明,UTF8编码,最多允许32个字符
- total_amount必填integer
【转账总金额】 转账金额单位为“分”。转账总金额必须与批次内所有明细转账金额之和保持一致,否则无法发起转账操作
- total_num必填integer
【转账总笔数】 一个转账批次单最多发起一千笔转账。转账总笔数必须与批次内所有明细之和保持一致,否则无法发起转账操作
transfer_detail_list必填array[TransferDetailInput]
【转账明细列表】 发起批量转账的明细列表,最多一千笔
- out_detail_no必填string(32)
【商家明细单号】 商户系统内部区分转账批次单下不同转账明细单的唯一标识,要求此参数只能由数字、大小写字母组成
- transfer_amount必填integer
【转账金额】 转账金额单位为“分”
- transfer_remark必填string(32)
【转账备注】 单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符
- openid必填string(64)
【收款用户openid】 商户appid下,某用户的openid
- user_name选填string(1024)
【收款用户姓名】 收款方真实姓名。支持标准RSA算法和国密算法,公钥由微信侧提供
明细转账金额<0.3元时,不允许填写收款用户姓名
明细转账金额 >= 2,000元时,该笔明细必须填写收款用户姓名
同一批次转账明细中的姓名字段传入规则需保持一致,也即全部填写、或全部不填写
若商户传入收款用户姓名,微信支付会校验用户openID与姓名是否一致,并提供电子回单
- out_detail_no必填string(32)
- transfer_scene_id选填string(36)
【转账场景ID】 该批次转账使用的转账场景,如不填写则使用商家的默认场景,如无默认场景可为空,可前往“商家转账到零钱-前往功能”中申请。
如:1001-现金营销
3 请求示例
config.createEncryptor().encrypt("张三");
:方法可以使用平台证书帮我们对姓名进行加密
package com.ruoyi.project;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.cipher.PrivacyEncryptor;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
public class WeChatPayTest {
/** 商户号 */
public static String merchantId = "190000****";
/** 商户API私钥路径 */
public static String privateKeyPath = "/Users/yourname/your/path/apiclient_key.pem";
/** 商户证书序列号 */
public static String merchantSerialNumber = "5157F09EFDC096DE15EBE81A47057A72********";
/** 商户APIV3密钥 */
public static String apiV3Key = "...";
public static TransferBatchService service;
public static void main1(String[] args) {
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
service = new TransferBatchService.Builder().config(config).build();
//【Body】包体参数
InitiateBatchTransferRequest initiateBatchTransferRequest = new InitiateBatchTransferRequest();
initiateBatchTransferRequest.setAppid("wxf636efh567hg4356");
initiateBatchTransferRequest.setOutBatchNo("plfk2020042013");
initiateBatchTransferRequest.setBatchName("2019年1月深圳分部报销单");
initiateBatchTransferRequest.setBatchRemark("2019年1月深圳分部报销单");
initiateBatchTransferRequest.setTotalAmount(4000000L);
initiateBatchTransferRequest.setTotalNum(200);
{
List<TransferDetailInput> transferDetailListList = new ArrayList<>();
{
TransferDetailInput transferDetailInput = new TransferDetailInput();
transferDetailInput.setOutDetailNo("x23zy545Bd5436");
transferDetailInput.setTransferAmount(200000L);
transferDetailInput.setTransferRemark("2020年4月报销");
transferDetailInput.setOpenid("o-MYE42l80oelYMDE34nYD456Xoy");
// 官方例子:
// transferDetailInput.setUserName("757b340b45ebef5467rter35gf464344v3542sdf4t6re4tb4f54ty45t4yyry45");
// 转账金额大于等于两千元,需要携带收款用户姓名
if (initiateBatchTransferRequest.getTotalAmount().longValue() >= 200000){
/**
* Config config = new RSAAutoCertificateConfig.Builder()
* 具有自动下载并更新平台证书能力的RSA配置类。 每次构造,都会立即使用传入的商户参数下载微信支付平台证书。 如果下载成功,SDK 会将商户参数注册或更新至
* AutoCertificateService。若下载失败,将会抛出异常。 为了提高性能,建议将配置类作为全局变量,减少不必要的证书下载,避免资源浪费
* ----------------------------------------------------------------
* 我们直接通过config获取到加密的方法encryptor.encrypt("张三")。对姓名进行加密,
* 平台证书序列号在service.initiateBatchTransfer(initiateBatchTransferRequest);中会帮我们增加。
* */
PrivacyEncryptor encryptor = config.createEncryptor();
transferDetailInput.setUserName(encryptor.encrypt("张三"));
}
transferDetailListList.add(transferDetailInput);
}
initiateBatchTransferRequest.setTransferDetailList(transferDetailListList);
}
initiateBatchTransferRequest.setTransferSceneId("1000");
InitiateBatchTransferResponse response = service.initiateBatchTransfer(initiateBatchTransferRequest);
}
}
4 应答参数
-
200 OK
- out_batch_no必填string(32)
【商家批次单号】 商户系统内部的商家批次单号,在商户系统内部唯一
- batch_id必填string(64)
【微信批次单号】 微信批次单号,微信商家转账系统返回的唯一标识
- create_time必填string(32)
【批次创建时间】 批次受理成功时返回,按照使用rfc3339所定义的格式,格式为YYYY-MM-DDThh:mm:ss+TIMEZONE
batch_status选填string
【批次状态】
ACCEPTED
:已受理。批次已受理成功,若发起批量转账的30分钟后,转账批次单仍处于该状态,可能原因是商户账户余额不足等。商户可查询账户资金流水,若该笔转账批次单的扣款已经发生,则表示批次已经进入转账中,请再次查单确认PROCESSING
:转账中。已开始处理批次内的转账明细单FINISHED
:已完成。批次内的所有转账明细单都已处理完成CLOSED
:已关闭。可查询具体的批次关闭原因确认
5 应答示例
{
"out_batch_no" : "plfk2020042013",
"batch_id" : "1030000071100999991182020050700019480001",
"create_time" : "2015-05-20T13:29:35.120+08:00",
"batch_status" : "ACCEPTED"
}
6 错误码
6.1 公共错误码
状态码 | 错误码 | 描述 | 解决方案 |
---|---|---|---|
400 | PARAM_ERROR | 参数错误 | 请根据错误提示正确传入参数 |
400 | INVALID_REQUEST | HTTP 请求不符合微信支付 APIv3 接口规则 | 请参阅 接口规则(opens new window) |
401 | SIGN_ERROR | 验证不通过 | 请参阅 签名常见问题(opens new window) |
500 | SYSTEM_ERROR | 系统异常,请稍后重试 | 请稍后重试 |
6.2 业务错误码
状态码 | 错误码 | 描述 | 解决方案 |
---|---|---|---|
400 | INVALID_REQUEST | 请求参数符合参数格式,但不符合业务规则 | 根据错误提示,传入正确参数 |
400 | INVALID_REQUEST | 创建订单冲突,请勿并发调用 | 根据错误提示调整调用策略 |
400 | INVALID_REQUEST | 对应单号已超出重试期,请查单确认后决定是否换单请求 | 根据错误提示调整调用策略 |
400 | INVALID_REQUEST | 此IP地址不允许调用该接口 | 请在商户平台-商家转账产品详情-转账发起方式-API发起-接口安全 中配置发起转账的IP地址 |
400 | INVALID_REQUEST | API通道未开启 | 请在商户平台-商家转账产品设置中配置API发起转账 |
400 | INVALID_REQUEST | 产品权限异常 | 请在商户平台-商家转账产品设置中开通产品权限 |
400 | INVALID_REQUEST | 超出商户单日转账额度,请核实产品设置是否准确 | 请在商户平台-商家转账产品设置中配置转账日限额 |
400 | INVALID_REQUEST | 你尚未获取该转账场景 | 请在商户平台-商家转账产品设置中申请该场景 |
400 | INVALID_REQUEST | 未配置收款用户列表 | 请前往商户平台-商家转账到零钱-转账场景中添加 |
400 | PARAM_ERROR | 参数错误 | 根据错误提示,传入正确参数 |
400 | PARAM_ERROR | 单批次明细笔数不合法,最高支持1000笔明细用 | 根据错误提示调整策略 |
401 | APPID_MCHID_NOT_MATCH | 商户号和appid没有绑定关系 | 商户号和appid没有绑定关系 |
403 | ACCOUNTERROR | 商户账户付款受限 | 可前往商户平台-违约记录获取解除功能限制指引 |
403 | NO_AUTH | 商户信息不合法 | 登录商户平台核对,传入正确信息 |
403 | NOT_ENOUGH | 资金不足 | 商户账户资金不足,请充值后原单重试,请勿更换商家转账批次单号 |
429 | FREQUENCY_LIMITED | 频率超限 | 该笔请求未受理,请降低频率后原单重试,请勿更换商家转账批次单号 |
500 | SYSTEM_ERROR | 系统错误 | 请勿更换商家转账批次单号,请使用相同参数再次调用API。否则可能造成资金损失 |
自定义工具类
如果不使用wechatpay-java
依赖(推荐使用,方便),只是用wechatpay-apache-httpclient
可以参考下面的方式。
<!-- 微信支付所需依赖-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.8</version>
</dependency>
<!-- 微信支付所需依赖end-->
1 微信工具类
WeChatPayUtils.java
- 帮助获取维护证书。
/**
* 获取平台证书
* @param certificateSerialNo 商户API证书序列号serial_no
* @param mchId 发起请求的商户(包括直连商户、服务商或渠道商)的商户号
* @param apiV3KeyParam API v3密钥
* @return
*/
public static X509Certificate getWechatpaySerial(String certificateSerialNo, String mchId, String apiV3KeyParam){
// 设置请求头
Map<String, String> headers = new TreeMap<>();
headers.put("Accept", "application/json");
headers.put("User-Agent", "application/json");
HttpUrl httpUrl = HttpUrl.parse("https://api.mch.weixin.qq.com/v3/certificates");
LOG.info("开始获取平台证书");
try {
String authHeader = getToken(
"GET",
httpUrl,
certificateSerialNo,
"",
mchId
);
headers.put("Authorization", authHeader);
String res = HttpUtil.createGet("https://api.mch.weixin.qq.com/v3/certificates").addHeaders(headers).execute().body();
LOG.info("获取平台证书返回结果:" + res);
// 处理返回的结果
cn.hutool.json.JSONArray data = JSONUtil.parseObj(res).getJSONArray("data");
cn.hutool.json.JSONObject encryptCertificate = data.getJSONObject(0).getJSONObject("encrypt_certificate");
String ciphertext = encryptCertificate.getStr("ciphertext");
String associatedDataStr = encryptCertificate.getStr("associated_data");
String nonceStr = encryptCertificate.getStr("nonce");
LOG.info("================================");
LOG.info("加密的平台证书:{}", ciphertext);
// 这里填写你的apiV3Key
byte[] apiV3Key = apiV3KeyParam.getBytes();
//附加数据包(可能为空)
byte[] associatedData = associatedDataStr.getBytes();
//加密使用的随机串初始化向量)
byte[] nonce = nonceStr.getBytes();
com.wechat.pay.contrib.apache.httpclient.util.AesUtil aesUtil = new com.wechat.pay.contrib.apache.httpclient.util.AesUtil(apiV3Key);
String ciphertextDecrypt = aesUtil.decryptToString(associatedData, nonce, ciphertext);
LOG.info("解密后的平台证书:{}", ciphertextDecrypt);
// 获取证书工厂实例
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
// 将证书字符串解析为证书对象
InputStream inputStream = new ByteArrayInputStream(ciphertextDecrypt.getBytes());
Certificate certificate = certificateFactory.generateCertificate(inputStream);
return (X509Certificate) certificate;
} catch (Exception e) {
throw new ServerException("获取平台证书失败:" + e);
}
}
2 携带参数
设置请求头Wechatpay-Serial
和使用平台证书加密姓名
public static void main(String[] args) {
X509Certificate certificate= new WeChatPayUtils.getWechatpaySerial(certificateSerialNo, mchId, wxMerchantConfig.getWechatKey());
// 微信平台证书的序列号(Wechatpay-Serial)
header.put("Wechatpay-Serial", certificate.getSerialNumber().toString(16));
// 收款用户姓名,使用平台证书进行加密
String userName = WeChatUtils.rsaEncryptOAEP(wxTransferView.getName(), certificate);
}