Skip to content

Commit 092e992

Browse files
authored
🆕 #3688 【微信支付】 实现预约扣费服务的相关接口
1 parent 69a2ab9 commit 092e992

17 files changed

+1754
-0
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# 微信支付预约扣费功能使用说明
2+
3+
## 概述
4+
5+
微信支付预约扣费功能(连续包月功能)允许商户在用户授权的情况下,按照约定的时间和金额,自动从用户的支付账户中扣取费用。主要适用于连续包月、订阅服务等场景。
6+
7+
## 功能特性
8+
9+
- **预约扣费**:创建未来某个时间点的扣费计划
10+
- **查询预约**:查询已创建的扣费计划状态
11+
- **取消预约**:取消已创建的扣费计划
12+
- **立即扣费**:立即执行扣费操作
13+
- **扣费记录查询**:查询历史扣费记录
14+
15+
## 快速开始
16+
17+
### 1. 获取服务实例
18+
19+
```java
20+
// 通过 WxPayService 获取预约扣费服务
21+
SubscriptionBillingService subscriptionService = wxPayService.getSubscriptionBillingService();
22+
```
23+
24+
### 2. 创建预约扣费
25+
26+
```java
27+
// 创建预约扣费请求
28+
SubscriptionScheduleRequest request = new SubscriptionScheduleRequest();
29+
request.setOutTradeNo("subscription_" + System.currentTimeMillis());
30+
request.setOpenid("用户的openid");
31+
request.setDescription("腾讯视频VIP会员");
32+
request.setScheduleTime("2024-09-01T10:00:00+08:00");
33+
34+
// 设置扣费金额
35+
SubscriptionAmount amount = new SubscriptionAmount();
36+
amount.setTotal(3000); // 30元,单位为分
37+
amount.setCurrency("CNY");
38+
request.setAmount(amount);
39+
40+
// 设置扣费计划(可选)
41+
BillingPlan billingPlan = new BillingPlan();
42+
billingPlan.setPlanType("MONTHLY"); // 按月扣费
43+
billingPlan.setPeriod(1); // 每1个月
44+
billingPlan.setTotalCount(12); // 总共12次
45+
request.setBillingPlan(billingPlan);
46+
47+
// 发起预约扣费
48+
SubscriptionScheduleResult result = subscriptionService.scheduleSubscription(request);
49+
System.out.println("预约扣费ID: " + result.getSubscriptionId());
50+
```
51+
52+
### 3. 查询预约扣费
53+
54+
```java
55+
// 通过预约扣费ID查询
56+
String subscriptionId = "从预约扣费结果中获取的ID";
57+
SubscriptionQueryResult queryResult = subscriptionService.querySubscription(subscriptionId);
58+
System.out.println("预约状态: " + queryResult.getStatus());
59+
```
60+
61+
### 4. 取消预约扣费
62+
63+
```java
64+
// 创建取消请求
65+
SubscriptionCancelRequest cancelRequest = new SubscriptionCancelRequest();
66+
cancelRequest.setSubscriptionId(subscriptionId);
67+
cancelRequest.setCancelReason("用户主动取消");
68+
69+
// 取消预约扣费
70+
SubscriptionCancelResult cancelResult = subscriptionService.cancelSubscription(cancelRequest);
71+
System.out.println("取消结果: " + cancelResult.getStatus());
72+
```
73+
74+
### 5. 立即扣费
75+
76+
```java
77+
// 创建立即扣费请求
78+
SubscriptionInstantBillingRequest instantRequest = new SubscriptionInstantBillingRequest();
79+
instantRequest.setOutTradeNo("instant_" + System.currentTimeMillis());
80+
instantRequest.setOpenid("用户的openid");
81+
instantRequest.setDescription("补扣上月会员费");
82+
83+
// 设置扣费金额
84+
SubscriptionAmount instantAmount = new SubscriptionAmount();
85+
instantAmount.setTotal(3000); // 30元
86+
instantAmount.setCurrency("CNY");
87+
instantRequest.setAmount(instantAmount);
88+
89+
// 执行立即扣费
90+
SubscriptionInstantBillingResult instantResult = subscriptionService.instantBilling(instantRequest);
91+
System.out.println("扣费结果: " + instantResult.getTradeState());
92+
```
93+
94+
### 6. 查询扣费记录
95+
96+
```java
97+
// 创建查询请求
98+
SubscriptionTransactionQueryRequest queryRequest = new SubscriptionTransactionQueryRequest();
99+
queryRequest.setOpenid("用户的openid");
100+
queryRequest.setBeginTime("2024-08-01T00:00:00+08:00");
101+
queryRequest.setEndTime("2024-08-31T23:59:59+08:00");
102+
queryRequest.setLimit(20);
103+
queryRequest.setOffset(0);
104+
105+
// 查询扣费记录
106+
SubscriptionTransactionQueryResult transactionResult = subscriptionService.queryTransactions(queryRequest);
107+
System.out.println("总记录数: " + transactionResult.getTotalCount());
108+
for (SubscriptionTransactionQueryResult.SubscriptionTransaction transaction : transactionResult.getData()) {
109+
System.out.println("订单号: " + transaction.getOutTradeNo() + ", 状态: " + transaction.getTradeState());
110+
}
111+
```
112+
113+
## 扣费计划类型
114+
115+
- `MONTHLY`:按月扣费
116+
- `WEEKLY`:按周扣费
117+
- `DAILY`:按日扣费
118+
- `YEARLY`:按年扣费
119+
120+
## 预约状态说明
121+
122+
- `SCHEDULED`:已预约
123+
- `CANCELLED`:已取消
124+
- `EXECUTED`:已执行
125+
- `FAILED`:执行失败
126+
127+
## 交易状态说明
128+
129+
- `SUCCESS`:支付成功
130+
- `REFUND`:转入退款
131+
- `NOTPAY`:未支付
132+
- `CLOSED`:已关闭
133+
- `REVOKED`:已撤销(刷卡支付)
134+
- `USERPAYING`:用户支付中
135+
- `PAYERROR`:支付失败
136+
137+
## 注意事项
138+
139+
1. **用户授权**:使用预约扣费功能前,需要用户在微信内完成签约授权
140+
2. **商户资质**:需要具备相应的业务资质才能开通此功能
141+
3. **金额限制**:扣费金额需要在签约模板规定的范围内
142+
4. **频率限制**:API调用有频率限制,请注意控制调用频次
143+
5. **异常处理**:建议对所有API调用进行异常处理
144+
145+
## 相关文档
146+
147+
- [微信支付预约扣费API文档](https://pay.weixin.qq.com/doc/v3/merchant/4012161105)
148+
- [微信支付开发指南](https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml)
149+
150+
## 示例完整代码
151+
152+
```java
153+
import com.github.binarywang.wxpay.service.SubscriptionBillingService;
154+
import com.github.binarywang.wxpay.bean.subscriptionbilling.*;
155+
156+
public class SubscriptionBillingExample {
157+
158+
private SubscriptionBillingService subscriptionService;
159+
160+
public void example() throws Exception {
161+
// 1. 创建预约扣费
162+
SubscriptionScheduleRequest request = new SubscriptionScheduleRequest();
163+
request.setOutTradeNo("subscription_" + System.currentTimeMillis());
164+
request.setOpenid("用户openid");
165+
request.setDescription("VIP会员续费");
166+
request.setScheduleTime("2024-09-01T10:00:00+08:00");
167+
168+
SubscriptionAmount amount = new SubscriptionAmount();
169+
amount.setTotal(3000);
170+
amount.setCurrency("CNY");
171+
request.setAmount(amount);
172+
173+
BillingPlan plan = new BillingPlan();
174+
plan.setPlanType("MONTHLY");
175+
plan.setPeriod(1);
176+
plan.setTotalCount(12);
177+
request.setBillingPlan(plan);
178+
179+
SubscriptionScheduleResult result = subscriptionService.scheduleSubscription(request);
180+
181+
// 2. 查询预约状态
182+
SubscriptionQueryResult query = subscriptionService.querySubscription(result.getSubscriptionId());
183+
184+
// 3. 如需取消
185+
if ("SCHEDULED".equals(query.getStatus())) {
186+
SubscriptionCancelRequest cancelReq = new SubscriptionCancelRequest();
187+
cancelReq.setSubscriptionId(result.getSubscriptionId());
188+
cancelReq.setCancelReason("用户取消");
189+
190+
SubscriptionCancelResult cancelResult = subscriptionService.cancelSubscription(cancelReq);
191+
}
192+
}
193+
}
194+
```
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package com.github.binarywang.wxpay.bean.subscriptionbilling;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import lombok.Data;
5+
import lombok.NoArgsConstructor;
6+
7+
import java.io.Serializable;
8+
9+
/**
10+
* 扣费计划信息
11+
* <pre>
12+
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
13+
* </pre>
14+
*
15+
* @author Binary Wang
16+
*/
17+
@Data
18+
@NoArgsConstructor
19+
public class BillingPlan implements Serializable {
20+
private static final long serialVersionUID = 1L;
21+
22+
/**
23+
* <pre>
24+
* 字段名:计划类型
25+
* 变量名:plan_type
26+
* 是否必填:是
27+
* 类型:string(32)
28+
* 描述:
29+
* 扣费计划类型
30+
* MONTHLY:按月扣费
31+
* WEEKLY:按周扣费
32+
* DAILY:按日扣费
33+
* YEARLY:按年扣费
34+
* 示例值:MONTHLY
35+
* </pre>
36+
*/
37+
@SerializedName("plan_type")
38+
private String planType;
39+
40+
/**
41+
* <pre>
42+
* 字段名:扣费周期
43+
* 变量名:period
44+
* 是否必填:是
45+
* 类型:int
46+
* 描述:
47+
* 扣费周期,配合plan_type使用
48+
* 例如:plan_type为MONTHLY,period为1,表示每1个月扣费一次
49+
* 示例值:1
50+
* </pre>
51+
*/
52+
@SerializedName("period")
53+
private Integer period;
54+
55+
/**
56+
* <pre>
57+
* 字段名:总扣费次数
58+
* 变量名:total_count
59+
* 是否必填:否
60+
* 类型:int
61+
* 描述:
62+
* 总扣费次数,不填表示无限次扣费
63+
* 示例值:12
64+
* </pre>
65+
*/
66+
@SerializedName("total_count")
67+
private Integer totalCount;
68+
69+
/**
70+
* <pre>
71+
* 字段名:已扣费次数
72+
* 变量名:executed_count
73+
* 是否必填:否
74+
* 类型:int
75+
* 描述:
76+
* 已扣费次数,查询时返回
77+
* 示例值:2
78+
* </pre>
79+
*/
80+
@SerializedName("executed_count")
81+
private Integer executedCount;
82+
83+
/**
84+
* <pre>
85+
* 字段名:计划开始时间
86+
* 变量名:start_time
87+
* 是否必填:否
88+
* 类型:string(32)
89+
* 描述:
90+
* 计划开始时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE
91+
* 示例值:2018-06-08T10:34:56+08:00
92+
* </pre>
93+
*/
94+
@SerializedName("start_time")
95+
private String startTime;
96+
97+
/**
98+
* <pre>
99+
* 字段名:计划结束时间
100+
* 变量名:end_time
101+
* 是否必填:否
102+
* 类型:string(32)
103+
* 描述:
104+
* 计划结束时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE
105+
* 示例值:2019-06-08T10:34:56+08:00
106+
* </pre>
107+
*/
108+
@SerializedName("end_time")
109+
private String endTime;
110+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.github.binarywang.wxpay.bean.subscriptionbilling;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import lombok.Data;
5+
import lombok.NoArgsConstructor;
6+
7+
import java.io.Serializable;
8+
9+
/**
10+
* 预约扣费金额信息
11+
* <pre>
12+
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
13+
* </pre>
14+
*
15+
* @author Binary Wang
16+
*/
17+
@Data
18+
@NoArgsConstructor
19+
public class SubscriptionAmount implements Serializable {
20+
private static final long serialVersionUID = 1L;
21+
22+
/**
23+
* <pre>
24+
* 字段名:总金额
25+
* 变量名:total
26+
* 是否必填:是
27+
* 类型:int
28+
* 描述:
29+
* 订单总金额,单位为分
30+
* 示例值:100
31+
* </pre>
32+
*/
33+
@SerializedName("total")
34+
private Integer total;
35+
36+
/**
37+
* <pre>
38+
* 字段名:货币类型
39+
* 变量名:currency
40+
* 是否必填:否
41+
* 类型:string(16)
42+
* 描述:
43+
* CNY:人民币,境内商户号仅支持人民币
44+
* 示例值:CNY
45+
* </pre>
46+
*/
47+
@SerializedName("currency")
48+
private String currency;
49+
}

0 commit comments

Comments
 (0)