🎨 #3898 【微信支付】支持单参数 switchover 自定义键及通知回调空 appId 降级处理
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# 支持一个商户号对应多个 appId 的使用说明
|
||||
# 支持一个商户号对应多个 appId 及自定义配置键的使用说明
|
||||
|
||||
## 背景
|
||||
|
||||
@@ -7,13 +7,19 @@
|
||||
- 所有小程序共用同一个支付商户号
|
||||
- 支付配置(商户号、密钥、证书等)完全相同,只有 appId 不同
|
||||
|
||||
此外,也存在多租户(SaaS)场景,需要以自定义唯一键(如租户ID)来管理多个不同商户的配置。
|
||||
|
||||
## 解决方案
|
||||
|
||||
WxJava 支持在配置多个相同商户号、不同 appId 的情况下,**可以仅通过商户号进行配置切换**,无需每次都指定 appId。
|
||||
WxJava 支持以下几种多配置管理方式:
|
||||
|
||||
1. **mchId + appId 精确匹配**:适用于一个商户号对应多个 appId 的场景
|
||||
2. **仅使用商户号(mchId)匹配**:适用于一个商户号对应单个 appId 或不关心具体 appId 的场景
|
||||
3. **自定义唯一键**:适用于多租户场景,使用任意字符串(如租户ID)作为配置键
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 1. 配置多个 appId
|
||||
### 1. 配置多个 appId(按 mchId + appId 格式)
|
||||
|
||||
```java
|
||||
WxPayService payService = new WxPayServiceImpl();
|
||||
@@ -53,7 +59,47 @@ configMap.put(mchId + "_" + config3.getAppId(), config3);
|
||||
payService.setMultiConfig(configMap);
|
||||
```
|
||||
|
||||
### 2. 获取配置的方式
|
||||
### 2. 使用自定义唯一键配置(多租户场景)
|
||||
|
||||
适用于多租户 SaaS 系统,使用任意唯一标识符(如租户ID)管理不同商户的配置:
|
||||
|
||||
```java
|
||||
WxPayService payService = new WxPayServiceImpl();
|
||||
|
||||
// 使用租户ID作为配置键
|
||||
WxPayConfig tenant1Config = new WxPayConfig();
|
||||
tenant1Config.setMchId("1234567890");
|
||||
tenant1Config.setAppId("wx1111111111111111");
|
||||
tenant1Config.setMchKey("tenant1_mch_key");
|
||||
// ... 其他配置
|
||||
|
||||
WxPayConfig tenant2Config = new WxPayConfig();
|
||||
tenant2Config.setMchId("9876543210");
|
||||
tenant2Config.setAppId("wx2222222222222222");
|
||||
tenant2Config.setMchKey("tenant2_mch_key");
|
||||
// ... 其他配置
|
||||
|
||||
// 方式一:通过 setMultiConfig 批量设置
|
||||
Map<String, WxPayConfig> configMap = new HashMap<>();
|
||||
configMap.put("tenant_001", tenant1Config);
|
||||
configMap.put("tenant_002", tenant2Config);
|
||||
payService.setMultiConfig(configMap);
|
||||
|
||||
// 方式二:通过 addConfig(key, config) 逐个添加(推荐,兼容单参数 switchover 方式)
|
||||
payService.addConfig("tenant_001", tenant1Config);
|
||||
payService.addConfig("tenant_002", tenant2Config);
|
||||
|
||||
// 切换配置:直接使用自定义键
|
||||
payService.switchover("tenant_001");
|
||||
WxPayConfig config = payService.getConfig(); // 获取 tenant_001 对应的配置
|
||||
|
||||
// 链式调用
|
||||
WxPayUnifiedOrderResult result = payService
|
||||
.switchoverTo("tenant_001")
|
||||
.unifiedOrder(request);
|
||||
```
|
||||
|
||||
### 3. 获取配置的方式
|
||||
|
||||
#### 方式一:直接获取配置(推荐,新功能)
|
||||
|
||||
@@ -122,7 +168,7 @@ payService.switchover("1234567890", "wx1111111111111111");
|
||||
payService.switchover("1234567890", "wx2222222222222222");
|
||||
```
|
||||
|
||||
#### 方式二:仅使用商户号切换(新功能)
|
||||
#### 方式二:仅使用商户号切换
|
||||
|
||||
```java
|
||||
// 仅使用商户号切换,会自动匹配该商户号的某个配置
|
||||
@@ -135,7 +181,19 @@ boolean success = payService.switchover("1234567890");
|
||||
2. 如果未找到,则尝试前缀匹配(查找以 `商户号_` 开头的配置)
|
||||
3. 如果有多个匹配项,将返回其中任意一个匹配项,具体选择结果不保证稳定或可预测,如需确定性行为请使用精确匹配方式(同时指定商户号和 appId)
|
||||
|
||||
#### 方式三:链式调用
|
||||
#### 方式三:使用自定义唯一键切换(多租户场景)
|
||||
|
||||
```java
|
||||
// 使用通过 addConfig(key, config) 或 setMultiConfig(map) 注册的自定义键切换
|
||||
boolean success = payService.switchover("tenant_001");
|
||||
|
||||
// 链式调用
|
||||
WxPayUnifiedOrderResult result = payService
|
||||
.switchoverTo("tenant_001")
|
||||
.unifiedOrder(request);
|
||||
```
|
||||
|
||||
#### 方式四:链式调用
|
||||
|
||||
```java
|
||||
// 精确切换,支持链式调用
|
||||
@@ -152,7 +210,7 @@ WxPayUnifiedOrderResult result = payService
|
||||
### 4. 动态添加配置
|
||||
|
||||
```java
|
||||
// 运行时动态添加新的 appId 配置
|
||||
// 方式一:使用 mchId + appId 添加配置
|
||||
WxPayConfig newConfig = new WxPayConfig();
|
||||
newConfig.setMchId("1234567890");
|
||||
newConfig.setAppId("wx4444444444444444");
|
||||
@@ -162,13 +220,27 @@ payService.addConfig("1234567890", "wx4444444444444444", newConfig);
|
||||
|
||||
// 切换到新添加的配置
|
||||
payService.switchover("1234567890", "wx4444444444444444");
|
||||
|
||||
// 方式二:使用自定义唯一键添加配置(多租户场景)
|
||||
WxPayConfig tenantConfig = new WxPayConfig();
|
||||
tenantConfig.setMchId("1234567890");
|
||||
tenantConfig.setAppId("wx4444444444444444");
|
||||
// ... 其他配置
|
||||
|
||||
payService.addConfig("tenant_003", tenantConfig);
|
||||
|
||||
// 使用自定义键切换
|
||||
payService.switchover("tenant_003");
|
||||
```
|
||||
|
||||
### 5. 移除配置
|
||||
|
||||
```java
|
||||
// 移除特定的 appId 配置
|
||||
// 方式一:使用 mchId + appId 移除配置
|
||||
payService.removeConfig("1234567890", "wx1111111111111111");
|
||||
|
||||
// 方式二:使用自定义唯一键移除配置(多租户场景)
|
||||
payService.removeConfig("tenant_001");
|
||||
```
|
||||
|
||||
## 实际应用场景
|
||||
@@ -201,6 +273,7 @@ public String handlePayNotify(@RequestBody String xmlData) {
|
||||
// 注意:parseOrderNotifyResult 方法内部会自动调用
|
||||
// switchover(notifyResult.getMchId(), notifyResult.getAppid())
|
||||
// 切换到正确的配置进行签名验证
|
||||
// 若回调中 appId 为空,会自动降级为仅使用 mchId 匹配
|
||||
|
||||
// 处理业务逻辑
|
||||
processOrder(notifyResult);
|
||||
@@ -277,24 +350,30 @@ public void processRefund(String mchId, String outTradeNo) {
|
||||
| `getConfig(String mchId, String appId)` | 直接获取指定配置 | **否** | 多商户管理、异步场景、线程池 |
|
||||
| `getConfig(String mchId)` | 根据商户号获取配置 | **否** | 不确定 appId 的场景 |
|
||||
| `switchover(String mchId, String appId)` | 精确切换配置 | 是 | 需要切换上下文的场景 |
|
||||
| `switchover(String mchId)` | 根据商户号切换 | 是 | 不关心 appId 的切换场景 |
|
||||
| `switchover(String mchIdOrKey)` | 根据商户号或自定义键切换 | 是 | 不关心 appId 或多租户场景 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **向后兼容**:所有原有的使用方式继续有效,不需要修改现有代码。
|
||||
|
||||
2. **配置隔离**:每个 `mchId + appId` 组合都是独立的配置,修改一个配置不会影响其他配置。
|
||||
2. **自定义键支持**:可以通过 `addConfig(String configKey, WxPayConfig)` 或 `setMultiConfig(Map)` 注册任意键,
|
||||
然后直接用 `switchover(key)` 进行精确匹配切换,无需关心 mchId 或 appId 的格式。
|
||||
|
||||
3. **线程安全**:
|
||||
3. **通知回调兼容**:当 `switchover(mchId, appId)` 的 appId 为空(通知回调中可能出现此情况)时,
|
||||
SDK 会自动降级为仅使用 mchId 进行匹配,避免因 appId 缺失导致的切换失败。
|
||||
|
||||
4. **配置隔离**:每个配置键对应独立的配置,修改一个配置不会影响其他配置。
|
||||
|
||||
5. **线程安全**:
|
||||
- 配置切换使用 `WxPayConfigHolder`(基于 `ThreadLocal`),是线程安全的
|
||||
- 直接获取配置方法(`getConfig(mchId, appId)`)不依赖 ThreadLocal,可以在任何上下文中安全使用
|
||||
|
||||
4. **自动切换**:在处理支付回调时,SDK 会自动根据回调中的 `mchId` 和 `appId` 切换到正确的配置。
|
||||
6. **自动切换**:在处理支付回调时,SDK 会自动根据回调中的 `mchId` 和 `appId` 切换到正确的配置。
|
||||
|
||||
5. **推荐实践**:
|
||||
- 如果知道具体的 appId,建议使用精确切换或获取方式,避免歧义
|
||||
7. **推荐实践**:
|
||||
- 多租户 SaaS 场景:使用自定义键(如租户ID)管理配置,通过 `switchover(tenantId)` 切换
|
||||
- 一商户多 appId 场景:使用 `mchId_appId` 格式的键,通过 `switchover(mchId, appId)` 精确切换
|
||||
- 在多商户管理、异步场景、线程池等环境中,建议使用 `getConfig(mchId, appId)` 直接获取配置
|
||||
- 如果使用仅商户号切换或获取,确保该商户号下至少有一个可用的配置
|
||||
|
||||
## 相关 API
|
||||
|
||||
@@ -303,9 +382,11 @@ public void processRefund(String mchId, String outTradeNo) {
|
||||
| `getConfig()` | 无 | WxPayConfig | 获取当前配置(依赖 ThreadLocal) |
|
||||
| `getConfig(String mchId, String appId)` | 商户号, appId | WxPayConfig | 直接获取指定配置(不依赖 ThreadLocal) |
|
||||
| `getConfig(String mchId)` | 商户号 | WxPayConfig | 根据商户号获取配置(不依赖 ThreadLocal) |
|
||||
| `switchover(String mchId, String appId)` | 商户号, appId | boolean | 精确切换到指定配置 |
|
||||
| `switchover(String mchId)` | 商户号 | boolean | 仅使用商户号切换 |
|
||||
| `switchover(String mchId, String appId)` | 商户号, appId | boolean | 精确切换到指定配置;appId 为空时自动降级为仅 mchId 匹配 |
|
||||
| `switchover(String mchIdOrKey)` | 商户号或自定义键 | 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 | 移除指定配置 |
|
||||
| `switchoverTo(String mchIdOrKey)` | 商户号或自定义键 | WxPayService | 根据商户号或自定义键切换,支持链式调用 |
|
||||
| `addConfig(String mchId, String appId, WxPayConfig)` | 商户号, appId, 配置 | void | 动态添加配置(键为 mchId_appId 格式) |
|
||||
| `addConfig(String configKey, WxPayConfig)` | 自定义键, 配置 | void | 动态添加配置(使用自定义键) |
|
||||
| `removeConfig(String mchId, String appId)` | 商户号, appId | void | 移除指定配置(键为 mchId_appId 格式) |
|
||||
| `removeConfig(String configKey)` | 自定义键 | void | 移除指定配置(使用自定义键) |
|
||||
|
||||
Reference in New Issue
Block a user