Skip to content

Commit 86ef11f

Browse files
Copilotbinarywang
andcommitted
新增:创建 wx-java-pay-multi-spring-boot-starter 模块支持多公众号关联配置
Co-authored-by: binarywang <[email protected]>
1 parent 7e18bde commit 86ef11f

File tree

10 files changed

+778
-0
lines changed

10 files changed

+778
-0
lines changed

spring-boot-starters/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<module>wx-java-mp-multi-spring-boot-starter</module>
2424
<module>wx-java-mp-spring-boot-starter</module>
2525
<module>wx-java-pay-spring-boot-starter</module>
26+
<module>wx-java-pay-multi-spring-boot-starter</module>
2627
<module>wx-java-open-spring-boot-starter</module>
2728
<module>wx-java-qidian-spring-boot-starter</module>
2829
<module>wx-java-cp-multi-spring-boot-starter</module>
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
# wx-java-pay-multi-spring-boot-starter
2+
3+
## 快速开始
4+
5+
本starter支持微信支付多公众号关联配置,适用于以下场景:
6+
- 一个服务商需要为多个公众号提供支付服务
7+
- 一个系统需要支持多个公众号的支付业务
8+
- 需要根据不同的appId动态切换支付配置
9+
10+
## 使用说明
11+
12+
### 1. 引入依赖
13+
14+
在项目的 `pom.xml` 中添加以下依赖:
15+
16+
```xml
17+
<dependency>
18+
<groupId>com.github.binarywang</groupId>
19+
<artifactId>wx-java-pay-multi-spring-boot-starter</artifactId>
20+
<version>${version}</version>
21+
</dependency>
22+
```
23+
24+
### 2. 添加配置
25+
26+
`application.yml``application.properties` 中配置多个公众号的支付信息。
27+
28+
#### 配置示例(application.yml)
29+
30+
##### V2版本配置
31+
```yml
32+
wx:
33+
pay:
34+
configs:
35+
# 配置1 - 可以使用appId作为key
36+
wx1234567890abcdef:
37+
appId: wx1234567890abcdef
38+
mchId: 1234567890
39+
mchKey: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
40+
keyPath: classpath:cert/app1/apiclient_cert.p12
41+
notifyUrl: https://example.com/pay/notify
42+
# 配置2 - 也可以使用自定义标识作为key
43+
config2:
44+
appId: wx9876543210fedcba
45+
mchId: 9876543210
46+
mchKey: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
47+
keyPath: classpath:cert/app2/apiclient_cert.p12
48+
notifyUrl: https://example.com/pay/notify
49+
```
50+
51+
##### V3版本配置
52+
```yml
53+
wx:
54+
pay:
55+
configs:
56+
# 公众号1配置
57+
wx1234567890abcdef:
58+
appId: wx1234567890abcdef
59+
mchId: 1234567890
60+
apiV3Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
61+
certSerialNo: 62C6CEAA360BCxxxxxxxxxxxxxxx
62+
privateKeyPath: classpath:cert/app1/apiclient_key.pem
63+
privateCertPath: classpath:cert/app1/apiclient_cert.pem
64+
notifyUrl: https://example.com/pay/notify
65+
# 公众号2配置
66+
wx9876543210fedcba:
67+
appId: wx9876543210fedcba
68+
mchId: 9876543210
69+
apiV3Key: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
70+
certSerialNo: 73D7DFBB471CDxxxxxxxxxxxxxxx
71+
privateKeyPath: classpath:cert/app2/apiclient_key.pem
72+
privateCertPath: classpath:cert/app2/apiclient_cert.pem
73+
notifyUrl: https://example.com/pay/notify
74+
```
75+
76+
##### V3服务商版本配置
77+
```yml
78+
wx:
79+
pay:
80+
configs:
81+
# 服务商为公众号1提供服务
82+
config1:
83+
appId: wxe97b2x9c2b3d # 服务商appId
84+
mchId: 16486610 # 服务商商户号
85+
subAppId: wx118cexxe3c07679 # 子商户公众号appId
86+
subMchId: 16496705 # 子商户号
87+
apiV3Key: Dc1DBwSc094jAKDGR5aqqb7PTHr
88+
privateKeyPath: classpath:cert/apiclient_key.pem
89+
privateCertPath: classpath:cert/apiclient_cert.pem
90+
# 服务商为公众号2提供服务
91+
config2:
92+
appId: wxe97b2x9c2b3d # 服务商appId(可以相同)
93+
mchId: 16486610 # 服务商商户号(可以相同)
94+
subAppId: wx228dexxf4d18890 # 子商户公众号appId(不同)
95+
subMchId: 16496706 # 子商户号(不同)
96+
apiV3Key: Dc1DBwSc094jAKDGR5aqqb7PTHr
97+
privateKeyPath: classpath:cert/apiclient_key.pem
98+
privateCertPath: classpath:cert/apiclient_cert.pem
99+
```
100+
101+
#### 配置示例(application.properties)
102+
103+
```properties
104+
# 公众号1配置
105+
wx.pay.configs.wx1234567890abcdef.app-id=wx1234567890abcdef
106+
wx.pay.configs.wx1234567890abcdef.mch-id=1234567890
107+
wx.pay.configs.wx1234567890abcdef.apiv3-key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
108+
wx.pay.configs.wx1234567890abcdef.cert-serial-no=62C6CEAA360BCxxxxxxxxxxxxxxx
109+
wx.pay.configs.wx1234567890abcdef.private-key-path=classpath:cert/app1/apiclient_key.pem
110+
wx.pay.configs.wx1234567890abcdef.private-cert-path=classpath:cert/app1/apiclient_cert.pem
111+
wx.pay.configs.wx1234567890abcdef.notify-url=https://example.com/pay/notify
112+
113+
# 公众号2配置
114+
wx.pay.configs.wx9876543210fedcba.app-id=wx9876543210fedcba
115+
wx.pay.configs.wx9876543210fedcba.mch-id=9876543210
116+
wx.pay.configs.wx9876543210fedcba.apiv3-key=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
117+
wx.pay.configs.wx9876543210fedcba.cert-serial-no=73D7DFBB471CDxxxxxxxxxxxxxxx
118+
wx.pay.configs.wx9876543210fedcba.private-key-path=classpath:cert/app2/apiclient_key.pem
119+
wx.pay.configs.wx9876543210fedcba.private-cert-path=classpath:cert/app2/apiclient_cert.pem
120+
wx.pay.configs.wx9876543210fedcba.notify-url=https://example.com/pay/notify
121+
```
122+
123+
### 3. 使用示例
124+
125+
自动注入的类型:`WxPayMultiServices`
126+
127+
```java
128+
import com.binarywang.spring.starter.wxjava.pay.service.WxPayMultiServices;
129+
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
130+
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
131+
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
132+
import com.github.binarywang.wxpay.service.WxPayService;
133+
import org.springframework.beans.factory.annotation.Autowired;
134+
import org.springframework.stereotype.Service;
135+
136+
@Service
137+
public class PayService {
138+
@Autowired
139+
private WxPayMultiServices wxPayMultiServices;
140+
141+
/**
142+
* 为不同的公众号创建支付订单
143+
*/
144+
public void createOrder(String appId, String openId, Integer totalFee, String body) throws Exception {
145+
// 根据appId获取对应的WxPayService
146+
WxPayService wxPayService = wxPayMultiServices.getWxPayService(appId);
147+
148+
if (wxPayService == null) {
149+
throw new IllegalArgumentException("未找到appId对应的微信支付配置: " + appId);
150+
}
151+
152+
// 使用WxPayService进行支付操作
153+
WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
154+
request.setOutTradeNo(generateOutTradeNo());
155+
request.setDescription(body);
156+
request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(totalFee));
157+
request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(openId));
158+
request.setNotifyUrl(wxPayService.getConfig().getNotifyUrl());
159+
160+
// V3统一下单
161+
WxPayUnifiedOrderV3Result.JsapiResult result =
162+
wxPayService.createOrderV3(TradeTypeEnum.JSAPI, request);
163+
164+
// 返回给前端用于调起支付
165+
// ...
166+
}
167+
168+
/**
169+
* 服务商模式示例
170+
*/
171+
public void serviceProviderExample(String configKey) throws Exception {
172+
// 使用配置标识获取WxPayService
173+
WxPayService wxPayService = wxPayMultiServices.getWxPayService(configKey);
174+
175+
if (wxPayService == null) {
176+
throw new IllegalArgumentException("未找到配置: " + configKey);
177+
}
178+
179+
// 获取子商户的配置信息
180+
String subAppId = wxPayService.getConfig().getSubAppId();
181+
String subMchId = wxPayService.getConfig().getSubMchId();
182+
183+
// 进行支付操作
184+
// ...
185+
}
186+
187+
/**
188+
* 查询订单示例
189+
*/
190+
public void queryOrder(String appId, String outTradeNo) throws Exception {
191+
WxPayService wxPayService = wxPayMultiServices.getWxPayService(appId);
192+
193+
if (wxPayService == null) {
194+
throw new IllegalArgumentException("未找到appId对应的微信支付配置: " + appId);
195+
}
196+
197+
// 查询订单
198+
var result = wxPayService.queryOrderV3(null, outTradeNo);
199+
// 处理查询结果
200+
// ...
201+
}
202+
203+
private String generateOutTradeNo() {
204+
// 生成商户订单号
205+
return "ORDER_" + System.currentTimeMillis();
206+
}
207+
}
208+
```
209+
210+
### 4. 配置说明
211+
212+
#### 必填配置项
213+
214+
| 配置项 | 说明 | 示例 |
215+
|--------|------|------|
216+
| appId | 公众号或小程序的appId | wx1234567890abcdef |
217+
| mchId | 商户号 | 1234567890 |
218+
219+
#### V2版本配置项
220+
221+
| 配置项 | 说明 | 是否必填 |
222+
|--------|------|----------|
223+
| mchKey | 商户密钥 | 是(V2) |
224+
| keyPath | p12证书文件路径 | 部分接口需要 |
225+
226+
#### V3版本配置项
227+
228+
| 配置项 | 说明 | 是否必填 |
229+
|--------|------|----------|
230+
| apiV3Key | API V3密钥 | 是(V3) |
231+
| certSerialNo | 证书序列号 | 是(V3) |
232+
| privateKeyPath | apiclient_key.pem路径 | 是(V3) |
233+
| privateCertPath | apiclient_cert.pem路径 | 是(V3) |
234+
235+
#### 服务商模式配置项
236+
237+
| 配置项 | 说明 | 是否必填 |
238+
|--------|------|----------|
239+
| subAppId | 子商户公众号appId | 服务商模式必填 |
240+
| subMchId | 子商户号 | 服务商模式必填 |
241+
242+
#### 可选配置项
243+
244+
| 配置项 | 说明 | 默认值 |
245+
|--------|------|--------|
246+
| notifyUrl | 支付结果通知URL ||
247+
| refundNotifyUrl | 退款结果通知URL ||
248+
| serviceId | 微信支付分serviceId ||
249+
| payScoreNotifyUrl | 支付分回调地址 ||
250+
| payScorePermissionNotifyUrl | 支付分授权回调地址 ||
251+
| useSandboxEnv | 是否使用沙箱环境 | false |
252+
| apiHostUrl | 自定义API主机地址 | https://api.mch.weixin.qq.com |
253+
| strictlyNeedWechatPaySerial | 是否所有V3请求都添加序列号头 | false |
254+
| fullPublicKeyModel | 是否完全使用公钥模式 | false |
255+
| publicKeyId | 公钥ID ||
256+
| publicKeyPath | 公钥文件路径 ||
257+
258+
## 常见问题
259+
260+
### 1. 如何选择配置的key?
261+
262+
配置的key(如 `wx.pay.configs.` 后面的部分)可以自由选择:
263+
- 可以使用appId作为key,便于直接通过appId获取服务
264+
- 可以使用自定义标识(如config1、config2),更灵活
265+
266+
### 2. V2和V3配置可以混用吗?
267+
268+
可以。不同的配置可以使用不同的版本,例如:
269+
```yml
270+
wx:
271+
pay:
272+
configs:
273+
app1: # V2配置
274+
appId: wx111
275+
mchId: 111
276+
mchKey: xxx
277+
app2: # V3配置
278+
appId: wx222
279+
mchId: 222
280+
apiV3Key: yyy
281+
privateKeyPath: xxx
282+
```
283+
284+
### 3. 证书文件如何放置?
285+
286+
证书文件可以放在以下位置:
287+
- `src/main/resources` 目录下,使用 `classpath:` 前缀
288+
- 服务器绝对路径,直接填写完整路径
289+
- 建议为不同配置使用不同的目录组织证书
290+
291+
### 4. 服务商模式如何配置?
292+
293+
服务商模式需要同时配置服务商信息和子商户信息:
294+
- `appId` 和 `mchId` 填写服务商的信息
295+
- `subAppId` 和 `subMchId` 填写子商户的信息
296+
297+
## 注意事项
298+
299+
1. **配置安全**:生产环境中的密钥、证书等敏感信息,建议使用配置中心或环境变量管理
300+
2. **证书管理**:不同公众号的证书文件要分开存放,避免混淆
301+
3. **懒加载**:WxPayService 实例采用懒加载策略,只有在首次调用时才会创建
302+
4. **线程安全**:WxPayMultiServices 的实现是线程安全的
303+
5. **配置更新**:如需动态更新配置,可调用 `removeWxPayService(configKey)` 方法移除缓存的实例
304+
305+
## 更多信息
306+
307+
- [WxJava 项目首页](https://github.com/Wechat-Group/WxJava)
308+
- [微信支付官方文档](https://pay.weixin.qq.com/wiki/doc/api/)
309+
- [微信支付V3接口文档](https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>wx-java-spring-boot-starters</artifactId>
7+
<groupId>com.github.binarywang</groupId>
8+
<version>4.8.0</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>wx-java-pay-multi-spring-boot-starter</artifactId>
13+
<name>WxJava - Spring Boot Starter for Pay::支持多公众号关联配置</name>
14+
<description>微信支付开发的 Spring Boot Starter::支持多公众号关联配置</description>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>com.github.binarywang</groupId>
19+
<artifactId>weixin-java-pay</artifactId>
20+
<version>${project.version}</version>
21+
</dependency>
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter-test</artifactId>
25+
<version>${spring.boot.version}</version>
26+
<scope>test</scope>
27+
</dependency>
28+
</dependencies>
29+
30+
<build>
31+
<plugins>
32+
<plugin>
33+
<groupId>org.springframework.boot</groupId>
34+
<artifactId>spring-boot-maven-plugin</artifactId>
35+
<version>${spring.boot.version}</version>
36+
</plugin>
37+
<plugin>
38+
<groupId>org.apache.maven.plugins</groupId>
39+
<artifactId>maven-source-plugin</artifactId>
40+
<version>2.2.1</version>
41+
<executions>
42+
<execution>
43+
<id>attach-sources</id>
44+
<goals>
45+
<goal>jar-no-fork</goal>
46+
</goals>
47+
</execution>
48+
</executions>
49+
</plugin>
50+
</plugins>
51+
</build>
52+
53+
</project>

0 commit comments

Comments
 (0)