mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2026-02-13 03:26:23 +08:00
🎨 #3849 【微信支付】支持一个商户号配置多个小程序appId
This commit is contained in:
200
weixin-java-pay/MULTI_APPID_USAGE.md
Normal file
200
weixin-java-pay/MULTI_APPID_USAGE.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# 支持一个商户号对应多个 appId 的使用说明
|
||||
|
||||
## 背景
|
||||
|
||||
在实际业务中,经常会遇到一个微信支付商户号需要绑定多个小程序的场景。例如:
|
||||
- 一个商家有多个小程序(主店、分店、活动小程序等)
|
||||
- 所有小程序共用同一个支付商户号
|
||||
- 支付配置(商户号、密钥、证书等)完全相同,只有 appId 不同
|
||||
|
||||
## 解决方案
|
||||
|
||||
WxJava 支持在配置多个相同商户号、不同 appId 的情况下,**可以仅通过商户号进行配置切换**,无需每次都指定 appId。
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 1. 配置多个 appId
|
||||
|
||||
```java
|
||||
WxPayService payService = new WxPayServiceImpl();
|
||||
|
||||
String mchId = "1234567890"; // 商户号
|
||||
|
||||
// 配置小程序1
|
||||
WxPayConfig config1 = new WxPayConfig();
|
||||
config1.setMchId(mchId);
|
||||
config1.setAppId("wx1111111111111111"); // 小程序1的appId
|
||||
config1.setMchKey("your_mch_key");
|
||||
config1.setApiV3Key("your_api_v3_key");
|
||||
// ... 其他配置
|
||||
|
||||
// 配置小程序2
|
||||
WxPayConfig config2 = new WxPayConfig();
|
||||
config2.setMchId(mchId);
|
||||
config2.setAppId("wx2222222222222222"); // 小程序2的appId
|
||||
config2.setMchKey("your_mch_key");
|
||||
config2.setApiV3Key("your_api_v3_key");
|
||||
// ... 其他配置
|
||||
|
||||
// 配置小程序3
|
||||
WxPayConfig config3 = new WxPayConfig();
|
||||
config3.setMchId(mchId);
|
||||
config3.setAppId("wx3333333333333333"); // 小程序3的appId
|
||||
config3.setMchKey("your_mch_key");
|
||||
config3.setApiV3Key("your_api_v3_key");
|
||||
// ... 其他配置
|
||||
|
||||
// 添加到配置映射
|
||||
Map<String, WxPayConfig> configMap = new HashMap<>();
|
||||
configMap.put(mchId + "_" + config1.getAppId(), config1);
|
||||
configMap.put(mchId + "_" + config2.getAppId(), config2);
|
||||
configMap.put(mchId + "_" + config3.getAppId(), config3);
|
||||
|
||||
payService.setMultiConfig(configMap);
|
||||
```
|
||||
|
||||
### 2. 切换配置的方式
|
||||
|
||||
#### 方式一:精确切换(原有方式,向后兼容)
|
||||
|
||||
```java
|
||||
// 切换到小程序1的配置
|
||||
payService.switchover("1234567890", "wx1111111111111111");
|
||||
|
||||
// 切换到小程序2的配置
|
||||
payService.switchover("1234567890", "wx2222222222222222");
|
||||
```
|
||||
|
||||
#### 方式二:仅使用商户号切换(新功能)
|
||||
|
||||
```java
|
||||
// 仅使用商户号切换,会自动匹配该商户号的某个配置
|
||||
// 适用于不关心具体使用哪个 appId 的场景
|
||||
boolean success = payService.switchover("1234567890");
|
||||
```
|
||||
|
||||
**注意**:当使用仅商户号切换时,会按照以下逻辑查找配置:
|
||||
1. 先尝试精确匹配商户号(针对只配置商户号、没有 appId 的情况)
|
||||
2. 如果未找到,则尝试前缀匹配(查找以 `商户号_` 开头的配置)
|
||||
3. 如果有多个匹配项,将返回其中任意一个匹配项,具体选择结果不保证稳定或可预测,如需确定性行为请使用精确匹配方式(同时指定商户号和 appId)
|
||||
|
||||
#### 方式三:链式调用
|
||||
|
||||
```java
|
||||
// 精确切换,支持链式调用
|
||||
WxPayUnifiedOrderResult result = payService
|
||||
.switchoverTo("1234567890", "wx1111111111111111")
|
||||
.unifiedOrder(request);
|
||||
|
||||
// 仅商户号切换,支持链式调用
|
||||
WxPayUnifiedOrderResult result = payService
|
||||
.switchoverTo("1234567890")
|
||||
.unifiedOrder(request);
|
||||
```
|
||||
|
||||
### 3. 动态添加配置
|
||||
|
||||
```java
|
||||
// 运行时动态添加新的 appId 配置
|
||||
WxPayConfig newConfig = new WxPayConfig();
|
||||
newConfig.setMchId("1234567890");
|
||||
newConfig.setAppId("wx4444444444444444");
|
||||
// ... 其他配置
|
||||
|
||||
payService.addConfig("1234567890", "wx4444444444444444", newConfig);
|
||||
|
||||
// 切换到新添加的配置
|
||||
payService.switchover("1234567890", "wx4444444444444444");
|
||||
```
|
||||
|
||||
### 4. 移除配置
|
||||
|
||||
```java
|
||||
// 移除特定的 appId 配置
|
||||
payService.removeConfig("1234567890", "wx1111111111111111");
|
||||
```
|
||||
|
||||
## 实际应用场景
|
||||
|
||||
### 场景1:根据用户来源切换 appId
|
||||
|
||||
```java
|
||||
// 在支付前,根据订单来源切换到对应小程序的配置
|
||||
String orderSource = order.getSource(); // 例如: "miniapp1", "miniapp2"
|
||||
String appId = getAppIdBySource(orderSource);
|
||||
|
||||
// 精确切换到特定小程序
|
||||
payService.switchover(mchId, appId);
|
||||
|
||||
// 创建订单
|
||||
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
|
||||
// ... 设置订单参数
|
||||
WxPayUnifiedOrderResult result = payService.unifiedOrder(request);
|
||||
```
|
||||
|
||||
### 场景2:处理支付回调
|
||||
|
||||
```java
|
||||
@PostMapping("/pay/notify")
|
||||
public String handlePayNotify(@RequestBody String xmlData) {
|
||||
try {
|
||||
// 解析回调通知
|
||||
WxPayOrderNotifyResult notifyResult = payService.parseOrderNotifyResult(xmlData);
|
||||
|
||||
// 注意:parseOrderNotifyResult 方法内部会自动调用
|
||||
// switchover(notifyResult.getMchId(), notifyResult.getAppid())
|
||||
// 切换到正确的配置进行签名验证
|
||||
|
||||
// 处理业务逻辑
|
||||
processOrder(notifyResult);
|
||||
|
||||
return WxPayNotifyResponse.success("成功");
|
||||
} catch (WxPayException e) {
|
||||
log.error("支付回调处理失败", e);
|
||||
return WxPayNotifyResponse.fail("失败");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 场景3:不关心具体 appId 的场景
|
||||
|
||||
```java
|
||||
// 某些场景下,只要是该商户号的配置即可,不关心具体是哪个 appId
|
||||
// 例如:查询订单、退款等操作
|
||||
|
||||
// 仅使用商户号切换
|
||||
payService.switchover(mchId);
|
||||
|
||||
// 查询订单
|
||||
WxPayOrderQueryResult queryResult = payService.queryOrder(null, outTradeNo);
|
||||
|
||||
// 申请退款
|
||||
WxPayRefundRequest refundRequest = new WxPayRefundRequest();
|
||||
// ... 设置退款参数
|
||||
WxPayRefundResult refundResult = payService.refund(refundRequest);
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **向后兼容**:所有原有的使用方式继续有效,不需要修改现有代码。
|
||||
|
||||
2. **配置隔离**:每个 `mchId + appId` 组合都是独立的配置,修改一个配置不会影响其他配置。
|
||||
|
||||
3. **线程安全**:配置切换使用 `WxPayConfigHolder`(基于 `ThreadLocal`),是线程安全的。
|
||||
|
||||
4. **自动切换**:在处理支付回调时,SDK 会自动根据回调中的 `mchId` 和 `appId` 切换到正确的配置。
|
||||
|
||||
5. **推荐实践**:
|
||||
- 如果知道具体的 appId,建议使用精确切换方式,避免歧义
|
||||
- 如果使用仅商户号切换,确保该商户号下至少有一个可用的配置
|
||||
|
||||
## 相关 API
|
||||
|
||||
| 方法 | 参数 | 返回值 | 说明 |
|
||||
|-----|------|--------|------|
|
||||
| `switchover(String mchId, String appId)` | 商户号, appId | boolean | 精确切换到指定配置 |
|
||||
| `switchover(String mchId)` | 商户号 | boolean | 仅使用商户号切换 |
|
||||
| `switchoverTo(String mchId, String appId)` | 商户号, appId | WxPayService | 精确切换,支持链式调用 |
|
||||
| `switchoverTo(String mchId)` | 商户号 | WxPayService | 仅商户号切换,支持链式调用 |
|
||||
| `addConfig(String mchId, String appId, WxPayConfig)` | 商户号, appId, 配置 | void | 动态添加配置 |
|
||||
| `removeConfig(String mchId, String appId)` | 商户号, appId | void | 移除指定配置 |
|
||||
@@ -78,6 +78,18 @@ public interface WxPayService {
|
||||
*/
|
||||
boolean switchover(String mchId, String appId);
|
||||
|
||||
/**
|
||||
* 仅根据商户号进行切换.
|
||||
* 适用于一个商户号对应多个appId的场景,切换时会匹配符合该商户号的配置.
|
||||
* 注意:由于HashMap迭代顺序不确定,当存在多个匹配项时返回的配置是不可预测的,建议使用精确匹配方式.
|
||||
*
|
||||
* @param mchId 商户标识
|
||||
* @return 切换是否成功,如果找不到匹配的配置则返回false
|
||||
*/
|
||||
default boolean switchover(String mchId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行相应的商户切换.
|
||||
*
|
||||
@@ -87,6 +99,19 @@ public interface WxPayService {
|
||||
*/
|
||||
WxPayService switchoverTo(String mchId, String appId);
|
||||
|
||||
/**
|
||||
* 仅根据商户号进行切换.
|
||||
* 适用于一个商户号对应多个appId的场景,切换时会匹配符合该商户号的配置.
|
||||
* 注意:由于HashMap迭代顺序不确定,当存在多个匹配项时返回的配置是不可预测的,建议使用精确匹配方式.
|
||||
*
|
||||
* @param mchId 商户标识
|
||||
* @return 切换成功,则返回当前对象,方便链式调用
|
||||
* @throws me.chanjar.weixin.common.error.WxRuntimeException 如果找不到匹配的配置
|
||||
*/
|
||||
default WxPayService switchoverTo(String mchId) {
|
||||
throw new me.chanjar.weixin.common.error.WxRuntimeException("子类需要实现此方法");
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送post请求,得到响应字节数组.
|
||||
*
|
||||
|
||||
@@ -212,6 +212,34 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean switchover(String mchId) {
|
||||
// 参数校验
|
||||
if (StringUtils.isBlank(mchId)) {
|
||||
log.error("商户号mchId不能为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 先尝试精确匹配(针对只有mchId没有appId的配置)
|
||||
if (this.configMap.containsKey(mchId)) {
|
||||
WxPayConfigHolder.set(mchId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 尝试前缀匹配(查找以 mchId_ 开头的配置)
|
||||
String prefix = mchId + "_";
|
||||
for (String key : this.configMap.keySet()) {
|
||||
if (key.startsWith(prefix)) {
|
||||
WxPayConfigHolder.set(key);
|
||||
log.debug("根据mchId=【{}】找到配置key=【{}】", mchId, key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
log.error("无法找到对应mchId=【{}】的商户号配置信息,请核实!", mchId);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WxPayService switchoverTo(String mchId, String appId) {
|
||||
String configKey = this.getConfigKey(mchId, appId);
|
||||
@@ -222,6 +250,32 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
|
||||
throw new WxRuntimeException(String.format("无法找到对应mchId=【%s】,appId=【%s】的商户号配置信息,请核实!", mchId, appId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WxPayService switchoverTo(String mchId) {
|
||||
// 参数校验
|
||||
if (StringUtils.isBlank(mchId)) {
|
||||
throw new WxRuntimeException("商户号mchId不能为空");
|
||||
}
|
||||
|
||||
// 先尝试精确匹配(针对只有mchId没有appId的配置)
|
||||
if (this.configMap.containsKey(mchId)) {
|
||||
WxPayConfigHolder.set(mchId);
|
||||
return this;
|
||||
}
|
||||
|
||||
// 尝试前缀匹配(查找以 mchId_ 开头的配置)
|
||||
String prefix = mchId + "_";
|
||||
for (String key : this.configMap.keySet()) {
|
||||
if (key.startsWith(prefix)) {
|
||||
WxPayConfigHolder.set(key);
|
||||
log.debug("根据mchId=【{}】找到配置key=【{}】", mchId, key);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
throw new WxRuntimeException(String.format("无法找到对应mchId=【%s】的商户号配置信息,请核实!", mchId));
|
||||
}
|
||||
|
||||
public String getConfigKey(String mchId, String appId) {
|
||||
return mchId + "_" + appId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.github.binarywang.wxpay.service.impl;
|
||||
|
||||
import com.github.binarywang.wxpay.config.WxPayConfig;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 手动验证多appId切换功能
|
||||
*/
|
||||
public class MultiAppIdSwitchoverManualTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
WxPayService payService = new WxPayServiceImpl();
|
||||
|
||||
String testMchId = "1234567890";
|
||||
String testAppId1 = "wx1111111111111111";
|
||||
String testAppId2 = "wx2222222222222222";
|
||||
String testAppId3 = "wx3333333333333333";
|
||||
|
||||
// 配置同一个商户号,三个不同的appId
|
||||
WxPayConfig config1 = new WxPayConfig();
|
||||
config1.setMchId(testMchId);
|
||||
config1.setAppId(testAppId1);
|
||||
config1.setMchKey("test_key_1");
|
||||
|
||||
WxPayConfig config2 = new WxPayConfig();
|
||||
config2.setMchId(testMchId);
|
||||
config2.setAppId(testAppId2);
|
||||
config2.setMchKey("test_key_2");
|
||||
|
||||
WxPayConfig config3 = new WxPayConfig();
|
||||
config3.setMchId(testMchId);
|
||||
config3.setAppId(testAppId3);
|
||||
config3.setMchKey("test_key_3");
|
||||
|
||||
Map<String, WxPayConfig> configMap = new HashMap<>();
|
||||
configMap.put(testMchId + "_" + testAppId1, config1);
|
||||
configMap.put(testMchId + "_" + testAppId2, config2);
|
||||
configMap.put(testMchId + "_" + testAppId3, config3);
|
||||
|
||||
payService.setMultiConfig(configMap);
|
||||
|
||||
// 测试1: 使用 mchId + appId 精确切换
|
||||
System.out.println("=== 测试1: 使用 mchId + appId 精确切换 ===");
|
||||
boolean success = payService.switchover(testMchId, testAppId1);
|
||||
System.out.println("切换结果: " + success);
|
||||
System.out.println("当前配置 - MchId: " + payService.getConfig().getMchId() + ", AppId: " + payService.getConfig().getAppId() + ", MchKey: " + payService.getConfig().getMchKey());
|
||||
verify(success, "切换应该成功");
|
||||
verify(testAppId1.equals(payService.getConfig().getAppId()), "AppId应该是 " + testAppId1);
|
||||
System.out.println("✓ 测试1通过\n");
|
||||
|
||||
// 测试2: 仅使用 mchId 切换
|
||||
System.out.println("=== 测试2: 仅使用 mchId 切换 ===");
|
||||
success = payService.switchover(testMchId);
|
||||
System.out.println("切换结果: " + success);
|
||||
System.out.println("当前配置 - MchId: " + payService.getConfig().getMchId() + ", AppId: " + payService.getConfig().getAppId() + ", MchKey: " + payService.getConfig().getMchKey());
|
||||
verify(success, "仅使用mchId切换应该成功");
|
||||
verify(testMchId.equals(payService.getConfig().getMchId()), "MchId应该是 " + testMchId);
|
||||
System.out.println("✓ 测试2通过\n");
|
||||
|
||||
// 测试3: 使用 switchoverTo 链式调用(精确匹配)
|
||||
System.out.println("=== 测试3: 使用 switchoverTo 链式调用(精确匹配) ===");
|
||||
WxPayService result = payService.switchoverTo(testMchId, testAppId2);
|
||||
System.out.println("返回对象: " + (result == payService ? "同一实例" : "不同实例"));
|
||||
System.out.println("当前配置 - MchId: " + payService.getConfig().getMchId() + ", AppId: " + payService.getConfig().getAppId() + ", MchKey: " + payService.getConfig().getMchKey());
|
||||
verify(result == payService, "应该返回同一实例");
|
||||
verify(testAppId2.equals(payService.getConfig().getAppId()), "AppId应该是 " + testAppId2);
|
||||
System.out.println("✓ 测试3通过\n");
|
||||
|
||||
// 测试4: 使用 switchoverTo 链式调用(仅mchId)
|
||||
System.out.println("=== 测试4: 使用 switchoverTo 链式调用(仅mchId) ===");
|
||||
result = payService.switchoverTo(testMchId);
|
||||
System.out.println("返回对象: " + (result == payService ? "同一实例" : "不同实例"));
|
||||
System.out.println("当前配置 - MchId: " + payService.getConfig().getMchId() + ", AppId: " + payService.getConfig().getAppId() + ", MchKey: " + payService.getConfig().getMchKey());
|
||||
verify(result == payService, "应该返回同一实例");
|
||||
verify(testMchId.equals(payService.getConfig().getMchId()), "MchId应该是 " + testMchId);
|
||||
System.out.println("✓ 测试4通过\n");
|
||||
|
||||
// 测试5: 切换到不存在的商户号
|
||||
System.out.println("=== 测试5: 切换到不存在的商户号 ===");
|
||||
success = payService.switchover("nonexistent_mch_id");
|
||||
System.out.println("切换结果: " + success);
|
||||
verify(!success, "切换到不存在的商户号应该失败");
|
||||
System.out.println("✓ 测试5通过\n");
|
||||
|
||||
// 测试6: 切换到不存在的 appId
|
||||
System.out.println("=== 测试6: 切换到不存在的 appId ===");
|
||||
success = payService.switchover(testMchId, "wx9999999999999999");
|
||||
System.out.println("切换结果: " + success);
|
||||
verify(!success, "切换到不存在的appId应该失败");
|
||||
System.out.println("✓ 测试6通过\n");
|
||||
|
||||
// 测试7: 添加新配置后切换
|
||||
System.out.println("=== 测试7: 添加新配置后切换 ===");
|
||||
String newAppId = "wx4444444444444444";
|
||||
WxPayConfig newConfig = new WxPayConfig();
|
||||
newConfig.setMchId(testMchId);
|
||||
newConfig.setAppId(newAppId);
|
||||
newConfig.setMchKey("test_key_4");
|
||||
payService.addConfig(testMchId, newAppId, newConfig);
|
||||
|
||||
success = payService.switchover(testMchId, newAppId);
|
||||
System.out.println("切换结果: " + success);
|
||||
System.out.println("当前配置 - MchId: " + payService.getConfig().getMchId() + ", AppId: " + payService.getConfig().getAppId() + ", MchKey: " + payService.getConfig().getMchKey());
|
||||
verify(success, "切换到新添加的配置应该成功");
|
||||
verify(newAppId.equals(payService.getConfig().getAppId()), "AppId应该是 " + newAppId);
|
||||
System.out.println("✓ 测试7通过\n");
|
||||
|
||||
System.out.println("==================");
|
||||
System.out.println("所有测试通过! ✓");
|
||||
System.out.println("==================");
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证条件是否为真,如果为假则抛出异常
|
||||
*
|
||||
* @param condition 待验证的条件
|
||||
* @param message 验证失败时的错误信息
|
||||
*/
|
||||
private static void verify(boolean condition, String message) {
|
||||
if (!condition) {
|
||||
throw new RuntimeException("验证失败: " + message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
package com.github.binarywang.wxpay.service.impl;
|
||||
|
||||
import com.github.binarywang.wxpay.config.WxPayConfig;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import me.chanjar.weixin.common.error.WxRuntimeException;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/**
|
||||
* 测试一个商户号配置多个appId的场景
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
public class MultiAppIdSwitchoverTest {
|
||||
|
||||
private WxPayService payService;
|
||||
private final String testMchId = "1234567890";
|
||||
private final String testAppId1 = "wx1111111111111111";
|
||||
private final String testAppId2 = "wx2222222222222222";
|
||||
private final String testAppId3 = "wx3333333333333333";
|
||||
|
||||
@BeforeMethod
|
||||
public void setup() {
|
||||
payService = new WxPayServiceImpl();
|
||||
|
||||
// 配置同一个商户号,三个不同的appId
|
||||
WxPayConfig config1 = new WxPayConfig();
|
||||
config1.setMchId(testMchId);
|
||||
config1.setAppId(testAppId1);
|
||||
config1.setMchKey("test_key_1");
|
||||
|
||||
WxPayConfig config2 = new WxPayConfig();
|
||||
config2.setMchId(testMchId);
|
||||
config2.setAppId(testAppId2);
|
||||
config2.setMchKey("test_key_2");
|
||||
|
||||
WxPayConfig config3 = new WxPayConfig();
|
||||
config3.setMchId(testMchId);
|
||||
config3.setAppId(testAppId3);
|
||||
config3.setMchKey("test_key_3");
|
||||
|
||||
Map<String, WxPayConfig> configMap = new HashMap<>();
|
||||
configMap.put(testMchId + "_" + testAppId1, config1);
|
||||
configMap.put(testMchId + "_" + testAppId2, config2);
|
||||
configMap.put(testMchId + "_" + testAppId3, config3);
|
||||
|
||||
payService.setMultiConfig(configMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试使用 mchId + appId 精确切换(原有功能,确保向后兼容)
|
||||
*/
|
||||
@Test
|
||||
public void testSwitchoverWithMchIdAndAppId() {
|
||||
// 切换到第一个配置
|
||||
boolean success = payService.switchover(testMchId, testAppId1);
|
||||
assertTrue(success);
|
||||
assertEquals(payService.getConfig().getAppId(), testAppId1);
|
||||
assertEquals(payService.getConfig().getMchKey(), "test_key_1");
|
||||
|
||||
// 切换到第二个配置
|
||||
success = payService.switchover(testMchId, testAppId2);
|
||||
assertTrue(success);
|
||||
assertEquals(payService.getConfig().getAppId(), testAppId2);
|
||||
assertEquals(payService.getConfig().getMchKey(), "test_key_2");
|
||||
|
||||
// 切换到第三个配置
|
||||
success = payService.switchover(testMchId, testAppId3);
|
||||
assertTrue(success);
|
||||
assertEquals(payService.getConfig().getAppId(), testAppId3);
|
||||
assertEquals(payService.getConfig().getMchKey(), "test_key_3");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试仅使用 mchId 切换(新功能)
|
||||
* 应该能够成功切换到该商户号的某个配置
|
||||
*/
|
||||
@Test
|
||||
public void testSwitchoverWithMchIdOnly() {
|
||||
// 仅使用商户号切换,应该能够成功切换到该商户号的某个配置
|
||||
boolean success = payService.switchover(testMchId);
|
||||
assertTrue(success, "应该能够通过mchId切换配置");
|
||||
|
||||
// 验证配置确实是该商户号的配置之一
|
||||
WxPayConfig currentConfig = payService.getConfig();
|
||||
assertNotNull(currentConfig);
|
||||
assertEquals(currentConfig.getMchId(), testMchId);
|
||||
|
||||
// appId应该是三个中的一个
|
||||
String currentAppId = currentConfig.getAppId();
|
||||
assertTrue(
|
||||
testAppId1.equals(currentAppId) || testAppId2.equals(currentAppId) || testAppId3.equals(currentAppId),
|
||||
"当前appId应该是配置的appId之一"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 switchoverTo 方法(带链式调用,使用 mchId + appId)
|
||||
*/
|
||||
@Test
|
||||
public void testSwitchoverToWithMchIdAndAppId() {
|
||||
WxPayService result = payService.switchoverTo(testMchId, testAppId2);
|
||||
assertNotNull(result);
|
||||
assertEquals(result, payService, "switchoverTo应该返回当前服务实例,支持链式调用");
|
||||
assertEquals(payService.getConfig().getAppId(), testAppId2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 switchoverTo 方法(带链式调用,仅使用 mchId)
|
||||
*/
|
||||
@Test
|
||||
public void testSwitchoverToWithMchIdOnly() {
|
||||
WxPayService result = payService.switchoverTo(testMchId);
|
||||
assertNotNull(result);
|
||||
assertEquals(result, payService, "switchoverTo应该返回当前服务实例,支持链式调用");
|
||||
assertEquals(payService.getConfig().getMchId(), testMchId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试切换到不存在的商户号
|
||||
*/
|
||||
@Test
|
||||
public void testSwitchoverToNonexistentMchId() {
|
||||
boolean success = payService.switchover("nonexistent_mch_id");
|
||||
assertFalse(success, "切换到不存在的商户号应该失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 switchoverTo 切换到不存在的商户号(应该抛出异常)
|
||||
*/
|
||||
@Test(expectedExceptions = WxRuntimeException.class)
|
||||
public void testSwitchoverToNonexistentMchIdThrowsException() {
|
||||
payService.switchoverTo("nonexistent_mch_id");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试切换到不存在的 mchId + appId 组合
|
||||
*/
|
||||
@Test
|
||||
public void testSwitchoverToNonexistentAppId() {
|
||||
boolean success = payService.switchover(testMchId, "wx9999999999999999");
|
||||
assertFalse(success, "切换到不存在的appId应该失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试添加配置后能够正常切换
|
||||
*/
|
||||
@Test
|
||||
public void testAddConfigAndSwitchover() {
|
||||
String newAppId = "wx4444444444444444";
|
||||
|
||||
// 动态添加一个新的配置
|
||||
WxPayConfig newConfig = new WxPayConfig();
|
||||
newConfig.setMchId(testMchId);
|
||||
newConfig.setAppId(newAppId);
|
||||
newConfig.setMchKey("test_key_4");
|
||||
|
||||
payService.addConfig(testMchId, newAppId, newConfig);
|
||||
|
||||
// 切换到新添加的配置
|
||||
boolean success = payService.switchover(testMchId, newAppId);
|
||||
assertTrue(success);
|
||||
assertEquals(payService.getConfig().getAppId(), newAppId);
|
||||
assertEquals(payService.getConfig().getMchKey(), "test_key_4");
|
||||
|
||||
// 使用仅mchId切换也应该能够找到配置
|
||||
success = payService.switchover(testMchId);
|
||||
assertTrue(success);
|
||||
assertEquals(payService.getConfig().getMchId(), testMchId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试移除配置后切换
|
||||
*/
|
||||
@Test
|
||||
public void testRemoveConfigAndSwitchover() {
|
||||
// 移除一个配置
|
||||
payService.removeConfig(testMchId, testAppId1);
|
||||
|
||||
// 切换到已移除的配置应该失败
|
||||
boolean success = payService.switchover(testMchId, testAppId1);
|
||||
assertFalse(success);
|
||||
|
||||
// 但仍然能够切换到其他配置
|
||||
success = payService.switchover(testMchId, testAppId2);
|
||||
assertTrue(success);
|
||||
|
||||
// 使用仅mchId切换应该仍然有效(因为还有其他appId的配置)
|
||||
success = payService.switchover(testMchId);
|
||||
assertTrue(success);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试单个配置的场景(确保向后兼容)
|
||||
*/
|
||||
@Test
|
||||
public void testSingleConfig() {
|
||||
WxPayService singlePayService = new WxPayServiceImpl();
|
||||
WxPayConfig singleConfig = new WxPayConfig();
|
||||
singleConfig.setMchId("single_mch_id");
|
||||
singleConfig.setAppId("single_app_id");
|
||||
singleConfig.setMchKey("single_key");
|
||||
|
||||
singlePayService.setConfig(singleConfig);
|
||||
|
||||
// 直接获取配置应该成功
|
||||
assertEquals(singlePayService.getConfig().getMchId(), "single_mch_id");
|
||||
assertEquals(singlePayService.getConfig().getAppId(), "single_app_id");
|
||||
|
||||
// 使用精确匹配切换
|
||||
boolean success = singlePayService.switchover("single_mch_id", "single_app_id");
|
||||
assertTrue(success);
|
||||
|
||||
// 使用仅mchId切换
|
||||
success = singlePayService.switchover("single_mch_id");
|
||||
assertTrue(success);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试空参数或null参数的处理
|
||||
*/
|
||||
@Test
|
||||
public void testSwitchoverWithNullOrEmptyMchId() {
|
||||
// 测试 null 参数
|
||||
boolean success = payService.switchover(null);
|
||||
assertFalse(success, "使用null作为mchId应该返回false");
|
||||
|
||||
// 测试空字符串
|
||||
success = payService.switchover("");
|
||||
assertFalse(success, "使用空字符串作为mchId应该返回false");
|
||||
|
||||
// 测试空白字符串
|
||||
success = payService.switchover(" ");
|
||||
assertFalse(success, "使用空白字符串作为mchId应该返回false");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 switchoverTo 方法对空参数或null参数的处理
|
||||
*/
|
||||
@Test(expectedExceptions = WxRuntimeException.class)
|
||||
public void testSwitchoverToWithNullMchId() {
|
||||
payService.switchoverTo((String) null);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = WxRuntimeException.class)
|
||||
public void testSwitchoverToWithEmptyMchId() {
|
||||
payService.switchoverTo("");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = WxRuntimeException.class)
|
||||
public void testSwitchoverToWithBlankMchId() {
|
||||
payService.switchoverTo(" ");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试商户号存在包含关系的场景
|
||||
* 例如同时配置 "123" 和 "1234",验证前缀匹配不会错误匹配
|
||||
*/
|
||||
@Test
|
||||
public void testSwitchoverWithOverlappingMchIds() {
|
||||
WxPayService testService = new WxPayServiceImpl();
|
||||
|
||||
// 配置两个有包含关系的商户号
|
||||
String mchId1 = "123";
|
||||
String mchId2 = "1234";
|
||||
String appId1 = "wx_app_123";
|
||||
String appId2 = "wx_app_1234";
|
||||
|
||||
WxPayConfig config1 = new WxPayConfig();
|
||||
config1.setMchId(mchId1);
|
||||
config1.setAppId(appId1);
|
||||
config1.setMchKey("key_123");
|
||||
|
||||
WxPayConfig config2 = new WxPayConfig();
|
||||
config2.setMchId(mchId2);
|
||||
config2.setAppId(appId2);
|
||||
config2.setMchKey("key_1234");
|
||||
|
||||
Map<String, WxPayConfig> configMap = new HashMap<>();
|
||||
configMap.put(mchId1 + "_" + appId1, config1);
|
||||
configMap.put(mchId2 + "_" + appId2, config2);
|
||||
testService.setMultiConfig(configMap);
|
||||
|
||||
// 切换到 "123",应该只匹配 "123_wx_app_123"
|
||||
boolean success = testService.switchover(mchId1);
|
||||
assertTrue(success);
|
||||
assertEquals(testService.getConfig().getMchId(), mchId1);
|
||||
assertEquals(testService.getConfig().getAppId(), appId1);
|
||||
|
||||
// 切换到 "1234",应该只匹配 "1234_wx_app_1234"
|
||||
success = testService.switchover(mchId2);
|
||||
assertTrue(success);
|
||||
assertEquals(testService.getConfig().getMchId(), mchId2);
|
||||
assertEquals(testService.getConfig().getAppId(), appId2);
|
||||
|
||||
// 精确切换验证
|
||||
success = testService.switchover(mchId1, appId1);
|
||||
assertTrue(success);
|
||||
assertEquals(testService.getConfig().getAppId(), appId1);
|
||||
|
||||
success = testService.switchover(mchId2, appId2);
|
||||
assertTrue(success);
|
||||
assertEquals(testService.getConfig().getAppId(), appId2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user