📝 为微信支付新增商户转账接口添加全面的文档和示例

This commit is contained in:
Copilot 2025-07-24 16:43:23 +08:00 committed by GitHub
parent 3a1d8075fc
commit 262a02b606
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 526 additions and 0 deletions

129
NEW_TRANSFER_API_SUPPORT.md Normal file
View File

@ -0,0 +1,129 @@
# 微信支付新版商户转账API支持
## 问题解答
**问题**: 新开通的商户号只能使用最新版本的商户转账接口WxJava是否支持
**答案**: **WxJava 已经完整支持新版商户转账API** 从2025年1月15日开始生效的新版转账API已在WxJava中实现。
## 新版转账API特性
### 1. API接口对比
| 特性 | 传统转账API | 新版转账API (2025.1.15+) |
|------|-------------|-------------------------|
| **服务类** | `MerchantTransferService` | `TransferService` |
| **API路径** | `/v3/transfer/batches` | `/v3/fund-app/mch-transfer/transfer-bills` |
| **转账方式** | 批量转账 | 单笔转账 |
| **场景支持** | 基础场景 | 丰富场景(如佣金报酬等) |
| **撤销功能** | ❌ 不支持 | ✅ 支持 |
| **适用范围** | 所有商户 | **新开通商户必须使用** |
### 2. 新版API功能列表
**发起转账** - `transferBills()`
**查询转账** - `getBillsByOutBillNo()` / `getBillsByTransferBillNo()`
**撤销转账** - `transformBillsCancel()`
**回调通知** - `parseTransferBillsNotifyResult()`
**RSA加密** - 自动处理用户姓名加密
**场景支持** - 支持多种转账场景ID
## 快速开始
### 1. 获取服务实例
```java
// 获取WxPayService实例
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(config);
// 获取新版转账服务 - 这就是新开通商户需要使用的服务!
TransferService transferService = wxPayService.getTransferService();
```
### 2. 发起转账新版API
```java
// 构建转账请求
TransferBillsRequest request = TransferBillsRequest.newBuilder()
.appid("your_appid") // 应用ID
.outBillNo("T" + System.currentTimeMillis()) // 商户转账单号
.transferSceneId("1005") // 转账场景ID佣金报酬
.openid("user_openid") // 用户openid
.userName("张三") // 收款用户姓名(可选,自动加密)
.transferAmount(100) // 转账金额(分)
.transferRemark("佣金报酬") // 转账备注
.build();
// 发起转账
TransferBillsResult result = transferService.transferBills(request);
System.out.println("转账成功,微信转账单号:" + result.getTransferBillNo());
```
### 3. 查询转账结果
```java
// 通过商户单号查询
TransferBillsGetResult result = transferService.getBillsByOutBillNo("T1642567890123");
// 通过微信转账单号查询
TransferBillsGetResult result2 = transferService.getBillsByTransferBillNo("wx_transfer_bill_no");
System.out.println("转账状态:" + result.getState());
```
### 4. 撤销转账(新功能)
```java
// 撤销转账
TransferBillsCancelResult cancelResult = transferService.transformBillsCancel("T1642567890123");
System.out.println("撤销状态:" + cancelResult.getState());
```
## 重要说明
### 转账场景ID (transfer_scene_id)
- **1005**: 佣金报酬(常用场景)
- 其他场景需要在微信商户平台申请
### 转账状态说明
- **ACCEPTED**: 转账已受理
- **PROCESSING**: 转账处理中
- **SUCCESS**: 转账成功
- **FAIL**: 转账失败
- **CANCELLED**: 转账撤销完成
### 新开通商户使用建议
1. **优先使用** `TransferService` 新版API
2. **不要使用** `MerchantTransferService` (可能不支持)
3. **必须设置** 转账场景ID (`transfer_scene_id`)
4. **建议开启** 回调通知以实时获取转账结果
## 完整示例代码
详细的使用示例请参考:
- 📄 [NEW_TRANSFER_API_USAGE.md](./NEW_TRANSFER_API_USAGE.md) - 详细使用指南
- 💻 [NewTransferApiExample.java](./weixin-java-pay/src/main/java/com/github/binarywang/wxpay/example/NewTransferApiExample.java) - 完整代码示例
## 常见问题
**Q: 我是新开通的商户,应该使用哪个服务?**
A: 使用 `TransferService`这是专为新版API设计的服务。
**Q: 新版API和旧版API有什么区别**
A: 新版API使用单笔转账模式支持更丰富的转账场景并且支持撤销功能。
**Q: 如何设置转账场景ID**
A: 在商户平台申请相应场景常用的佣金报酬场景ID是"1005"。
**Q: 用户姓名需要加密吗?**
A: WxJava会自动处理RSA加密您只需要传入明文姓名即可。
## 版本要求
- WxJava 版本4.7.0+
- 支持时间2025年1月15日+
- 适用商户:所有商户(新开通商户强制使用)
通过以上说明新开通的微信支付商户可以放心使用WxJava进行商户转账操作

148
NEW_TRANSFER_API_USAGE.md Normal file
View File

@ -0,0 +1,148 @@
# 微信支付新版商户转账API使用指南
## 概述
从2025年1月15日开始微信支付推出了新版的商户转账API。新开通的商户号只能使用最新版本的商户转账接口。WxJava 已经完整支持新版转账API。
## API对比
### 传统转账API (仍然支持)
- **服务类**: `MerchantTransferService`
- **API前缀**: `/v3/transfer/batches`
- **特点**: 支持批量转账,一次可以转账给多个用户
### 新版转账API (2025.1.15+)
- **服务类**: `TransferService`
- **API前缀**: `/v3/fund-app/mch-transfer/transfer-bills`
- **特点**: 单笔转账,支持更丰富的转账场景
## 使用新版转账API
### 1. 获取服务实例
```java
// 获取WxPayService实例
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(config);
// 获取新版转账服务
TransferService transferService = wxPayService.getTransferService();
```
### 2. 发起转账
```java
// 构建转账请求
TransferBillsRequest request = TransferBillsRequest.newBuilder()
.appid("your_appid") // 应用ID
.outBillNo("T" + System.currentTimeMillis()) // 商户转账单号
.transferSceneId("1005") // 转账场景ID佣金报酬
.openid("user_openid") // 用户openid
.userName("张三") // 收款用户姓名(可选,需要加密)
.transferAmount(100) // 转账金额(分)
.transferRemark("佣金报酬") // 转账备注
.notifyUrl("https://your-domain.com/notify") // 回调地址(可选)
.userRecvPerception("Y") // 用户收款感知(可选)
.build();
try {
TransferBillsResult result = transferService.transferBills(request);
System.out.println("转账成功,微信转账单号:" + result.getTransferBillNo());
System.out.println("状态:" + result.getState());
} catch (WxPayException e) {
System.err.println("转账失败:" + e.getMessage());
}
```
### 3. 查询转账结果
```java
// 通过商户单号查询
String outBillNo = "T1642567890123";
TransferBillsGetResult result = transferService.getBillsByOutBillNo(outBillNo);
// 通过微信转账单号查询
String transferBillNo = "1000000000000000000000000001";
TransferBillsGetResult result2 = transferService.getBillsByTransferBillNo(transferBillNo);
System.out.println("转账状态:" + result.getState());
System.out.println("转账金额:" + result.getTransferAmount());
```
### 4. 撤销转账
```java
// 撤销转账(仅在特定状态下可撤销)
String outBillNo = "T1642567890123";
TransferBillsCancelResult cancelResult = transferService.transformBillsCancel(outBillNo);
System.out.println("撤销结果:" + cancelResult.getState());
```
### 5. 处理回调通知
```java
// 在回调接口中处理通知
@PostMapping("/transfer/notify")
public String handleTransferNotify(HttpServletRequest request) throws Exception {
String notifyData = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
// 构建签名头
SignatureHeader header = new SignatureHeader();
header.setTimeStamp(request.getHeader("Wechatpay-Timestamp"));
header.setNonce(request.getHeader("Wechatpay-Nonce"));
header.setSignature(request.getHeader("Wechatpay-Signature"));
header.setSerial(request.getHeader("Wechatpay-Serial"));
try {
TransferBillsNotifyResult notifyResult = transferService.parseTransferBillsNotifyResult(notifyData, header);
// 处理业务逻辑
String outBillNo = notifyResult.getOutBillNo();
String state = notifyResult.getState();
System.out.println("转账单号:" + outBillNo + ",状态:" + state);
return "SUCCESS";
} catch (WxPayException e) {
System.err.println("验签失败:" + e.getMessage());
return "FAIL";
}
}
```
## 重要参数说明
### 转账场景ID (transfer_scene_id)
- **1005**: 佣金报酬(常用)
- 其他场景ID需要在商户平台申请
### 转账状态
- **PROCESSING**: 转账中
- **SUCCESS**: 转账成功
- **FAILED**: 转账失败
- **REFUNDED**: 已退款
### 用户收款感知 (user_recv_perception)
- **Y**: 用户会收到微信转账通知
- **N**: 用户不会收到微信转账通知
## 新旧API对比总结
| 特性 | 传统API (MerchantTransferService) | 新版API (TransferService) |
|------|----------------------------------|---------------------------|
| 发起方式 | 批量转账 | 单笔转账 |
| API路径 | `/v3/transfer/batches` | `/v3/fund-app/mch-transfer/transfer-bills` |
| 场景支持 | 基础转账场景 | 丰富的转账场景 |
| 回调通知 | 支持 | 支持 |
| 撤销功能 | 不支持 | 支持 |
| 适用商户 | 所有商户 | 新开通商户必须使用 |
## 注意事项
1. **新开通的商户号**: 必须使用新版API (`TransferService`)
2. **转账场景ID**: 需要在商户平台申请相应的转账场景
3. **用户姓名加密**: 如果传入用户姓名会自动进行RSA加密
4. **回调验签**: 建议开启回调验签以确保安全性
5. **错误处理**: 妥善处理各种异常情况
通过以上指南您可以轻松使用WxJava的新版商户转账API功能。

View File

@ -0,0 +1,249 @@
package com.github.binarywang.wxpay.example;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.transfer.*;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.TransferService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
/**
* 新版商户转账API使用示例
*
* 从2025年1月15日开始微信支付推出了新版的商户转账API
* 新开通的商户号只能使用最新版本的商户转账接口
*
* @author WxJava Team
* @since 2025-01-15
*/
public class NewTransferApiExample {
private final TransferService transferService;
public NewTransferApiExample(WxPayConfig config) {
// 初始化微信支付服务
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(config);
// 获取新版转账服务
this.transferService = wxPayService.getTransferService();
}
/**
* 发起单笔转账示例
* 新版API使用 /v3/fund-app/mch-transfer/transfer-bills 接口
*/
public void transferExample() {
try {
// 构建转账请求
TransferBillsRequest request = TransferBillsRequest.newBuilder()
.appid("wx1234567890123456") // 应用ID
.outBillNo("TRANSFER_" + System.currentTimeMillis()) // 商户转账单号确保唯一
.transferSceneId("1005") // 转账场景ID1005=佣金报酬
.openid("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o") // 收款用户的openid
.userName("张三") // 收款用户真实姓名可选会自动加密
.transferAmount(100) // 转账金额单位此处为1元
.transferRemark("佣金报酬") // 转账备注用户可见
.notifyUrl("https://your-domain.com/transfer/notify") // 异步通知地址可选
.userRecvPerception("Y") // 用户收款感知Y=会收到通知N=不会收到通知
.build();
// 发起转账
TransferBillsResult result = transferService.transferBills(request);
// 输出结果
System.out.println("=== 转账发起成功 ===");
System.out.println("商户单号: " + result.getOutBillNo());
System.out.println("微信转账单号: " + result.getTransferBillNo());
System.out.println("创建时间: " + result.getCreateTime());
System.out.println("状态: " + result.getState());
System.out.println("跳转领取页面信息: " + result.getPackageInfo());
} catch (WxPayException e) {
System.err.println("转账失败: " + e.getMessage());
System.err.println("错误代码: " + e.getErrCode());
System.err.println("错误描述: " + e.getErrCodeDes());
}
}
/**
* 通过商户单号查询转账结果
*/
public void queryByOutBillNoExample() {
try {
String outBillNo = "TRANSFER_1642567890123";
TransferBillsGetResult result = transferService.getBillsByOutBillNo(outBillNo);
System.out.println("=== 查询转账结果(商户单号)===");
System.out.println("商户单号: " + result.getOutBillNo());
System.out.println("微信转账单号: " + result.getTransferBillNo());
System.out.println("状态: " + result.getState());
System.out.println("转账金额: " + result.getTransferAmount() + "");
System.out.println("用户openid: " + result.getOpenid());
System.out.println("转账备注: " + result.getTransferRemark());
} catch (WxPayException e) {
System.err.println("查询失败: " + e.getMessage());
}
}
/**
* 通过微信转账单号查询转账结果
*/
public void queryByTransferBillNoExample() {
try {
String transferBillNo = "1000000000000000000000000001";
TransferBillsGetResult result = transferService.getBillsByTransferBillNo(transferBillNo);
System.out.println("=== 查询转账结果(微信单号)===");
System.out.println("微信转账单号: " + result.getTransferBillNo());
System.out.println("状态: " + result.getState());
System.out.println("失败原因: " + result.getFailReason());
} catch (WxPayException e) {
System.err.println("查询失败: " + e.getMessage());
}
}
/**
* 撤销转账示例
* 注意只有在特定状态下才能撤销
*/
public void cancelTransferExample() {
try {
String outBillNo = "TRANSFER_1642567890123";
TransferBillsCancelResult result = transferService.transformBillsCancel(outBillNo);
System.out.println("=== 撤销转账结果 ===");
System.out.println("商户单号: " + result.getOutBillNo());
System.out.println("状态: " + result.getState());
System.out.println("更新时间: " + result.getUpdateTime());
} catch (WxPayException e) {
System.err.println("撤销失败: " + e.getMessage());
}
}
/**
* 处理转账回调通知示例
* 这个方法通常在您的Web服务器的回调接口中调用
*/
public void handleNotifyExample(String notifyData, String timestamp, String nonce, String signature, String serial) {
try {
// 构建签名头信息
SignatureHeader header = new SignatureHeader();
header.setTimeStamp(timestamp);
header.setNonce(nonce);
header.setSignature(signature);
header.setSerial(serial);
// 解析并验签回调数据
TransferBillsNotifyResult notifyResult = transferService.parseTransferBillsNotifyResult(notifyData, header);
System.out.println("=== 处理转账回调通知 ===");
System.out.println("商户单号: " + notifyResult.getResult().getOutBillNo());
System.out.println("微信转账单号: " + notifyResult.getResult().getTransferBillNo());
System.out.println("状态: " + notifyResult.getResult().getState());
System.out.println("转账金额: " + notifyResult.getResult().getTransferAmount() + "");
System.out.println("更新时间: " + notifyResult.getResult().getUpdateTime());
// 根据状态处理业务逻辑
switch (notifyResult.getResult().getState()) {
case "SUCCESS":
System.out.println("转账成功,进行业务处理...");
// 更新订单状态发送通知等
break;
case "FAIL":
System.out.println("转账失败,失败原因: " + notifyResult.getResult().getFailReason());
// 处理失败逻辑
break;
default:
System.out.println("其他状态: " + notifyResult.getResult().getState());
}
} catch (WxPayException e) {
System.err.println("回调处理失败: " + e.getMessage());
}
}
/**
* 批量转账示例使用传统API
* 注意新商户可能无法使用此API建议使用新版单笔转账API
*/
public void batchTransferExample() {
try {
// 构建转账明细列表
TransferBatchesRequest.TransferDetail detail1 = TransferBatchesRequest.TransferDetail.newBuilder()
.outDetailNo("DETAIL_" + System.currentTimeMillis() + "_1")
.transferAmount(100) // 1元
.transferRemark("佣金1")
.openid("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o")
.userName("张三")
.build();
TransferBatchesRequest.TransferDetail detail2 = TransferBatchesRequest.TransferDetail.newBuilder()
.outDetailNo("DETAIL_" + System.currentTimeMillis() + "_2")
.transferAmount(200) // 2元
.transferRemark("佣金2")
.openid("oUpF8uMuAJO_M2pxb1Q9zNjWeS6p")
.userName("李四")
.build();
// 构建批量转账请求
TransferBatchesRequest batchRequest = TransferBatchesRequest.newBuilder()
.appid("wx1234567890123456")
.outBatchNo("BATCH_" + System.currentTimeMillis())
.batchName("佣金批量发放")
.batchRemark("2024年1月佣金")
.totalAmount(300) // 总金额3元
.totalNum(2) // 总笔数2笔
.transferDetailList(java.util.Arrays.asList(detail1, detail2))
.transferSceneId("1005") // 转账场景ID
.build();
// 发起批量转账
TransferBatchesResult batchResult = transferService.transferBatches(batchRequest);
System.out.println("=== 批量转账发起成功 ===");
System.out.println("商户批次单号: " + batchResult.getOutBatchNo());
System.out.println("微信批次单号: " + batchResult.getBatchId());
System.out.println("批次状态: " + batchResult.getBatchStatus());
} catch (WxPayException e) {
System.err.println("批量转账失败: " + e.getMessage());
}
}
/**
* 使用配置示例
*/
public static void main(String[] args) {
// 配置微信支付参数
WxPayConfig config = new WxPayConfig();
config.setAppId("wx1234567890123456"); // 应用ID
config.setMchId("1234567890"); // 商户ID
config.setApiV3Key("your_api_v3_key_32_chars"); // APIv3密钥
config.setPrivateKeyPath("path/to/private.pem"); // 商户私钥文件路径
config.setCertSerialNo("your_certificate_serial"); // 商户证书序列号
// 创建示例实例
NewTransferApiExample example = new NewTransferApiExample(config);
// 运行示例
System.out.println("新版商户转账API使用示例");
System.out.println("===============================");
// 1. 发起单笔转账
example.transferExample();
// 2. 查询转账结果
// example.queryByOutBillNoExample();
// 3. 撤销转账
// example.cancelTransferExample();
// 4. 批量转账传统API
// example.batchTransferExample();
}
}