🆕 #3688 【微信支付】 实现预约扣费服务的相关接口
This commit is contained in:
194
weixin-java-pay/SUBSCRIPTION_BILLING_USAGE.md
Normal file
194
weixin-java-pay/SUBSCRIPTION_BILLING_USAGE.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# 微信支付预约扣费功能使用说明
|
||||
|
||||
## 概述
|
||||
|
||||
微信支付预约扣费功能(连续包月功能)允许商户在用户授权的情况下,按照约定的时间和金额,自动从用户的支付账户中扣取费用。主要适用于连续包月、订阅服务等场景。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- **预约扣费**:创建未来某个时间点的扣费计划
|
||||
- **查询预约**:查询已创建的扣费计划状态
|
||||
- **取消预约**:取消已创建的扣费计划
|
||||
- **立即扣费**:立即执行扣费操作
|
||||
- **扣费记录查询**:查询历史扣费记录
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 获取服务实例
|
||||
|
||||
```java
|
||||
// 通过 WxPayService 获取预约扣费服务
|
||||
SubscriptionBillingService subscriptionService = wxPayService.getSubscriptionBillingService();
|
||||
```
|
||||
|
||||
### 2. 创建预约扣费
|
||||
|
||||
```java
|
||||
// 创建预约扣费请求
|
||||
SubscriptionScheduleRequest request = new SubscriptionScheduleRequest();
|
||||
request.setOutTradeNo("subscription_" + System.currentTimeMillis());
|
||||
request.setOpenid("用户的openid");
|
||||
request.setDescription("腾讯视频VIP会员");
|
||||
request.setScheduleTime("2024-09-01T10:00:00+08:00");
|
||||
|
||||
// 设置扣费金额
|
||||
SubscriptionAmount amount = new SubscriptionAmount();
|
||||
amount.setTotal(3000); // 30元,单位为分
|
||||
amount.setCurrency("CNY");
|
||||
request.setAmount(amount);
|
||||
|
||||
// 设置扣费计划(可选)
|
||||
BillingPlan billingPlan = new BillingPlan();
|
||||
billingPlan.setPlanType("MONTHLY"); // 按月扣费
|
||||
billingPlan.setPeriod(1); // 每1个月
|
||||
billingPlan.setTotalCount(12); // 总共12次
|
||||
request.setBillingPlan(billingPlan);
|
||||
|
||||
// 发起预约扣费
|
||||
SubscriptionScheduleResult result = subscriptionService.scheduleSubscription(request);
|
||||
System.out.println("预约扣费ID: " + result.getSubscriptionId());
|
||||
```
|
||||
|
||||
### 3. 查询预约扣费
|
||||
|
||||
```java
|
||||
// 通过预约扣费ID查询
|
||||
String subscriptionId = "从预约扣费结果中获取的ID";
|
||||
SubscriptionQueryResult queryResult = subscriptionService.querySubscription(subscriptionId);
|
||||
System.out.println("预约状态: " + queryResult.getStatus());
|
||||
```
|
||||
|
||||
### 4. 取消预约扣费
|
||||
|
||||
```java
|
||||
// 创建取消请求
|
||||
SubscriptionCancelRequest cancelRequest = new SubscriptionCancelRequest();
|
||||
cancelRequest.setSubscriptionId(subscriptionId);
|
||||
cancelRequest.setCancelReason("用户主动取消");
|
||||
|
||||
// 取消预约扣费
|
||||
SubscriptionCancelResult cancelResult = subscriptionService.cancelSubscription(cancelRequest);
|
||||
System.out.println("取消结果: " + cancelResult.getStatus());
|
||||
```
|
||||
|
||||
### 5. 立即扣费
|
||||
|
||||
```java
|
||||
// 创建立即扣费请求
|
||||
SubscriptionInstantBillingRequest instantRequest = new SubscriptionInstantBillingRequest();
|
||||
instantRequest.setOutTradeNo("instant_" + System.currentTimeMillis());
|
||||
instantRequest.setOpenid("用户的openid");
|
||||
instantRequest.setDescription("补扣上月会员费");
|
||||
|
||||
// 设置扣费金额
|
||||
SubscriptionAmount instantAmount = new SubscriptionAmount();
|
||||
instantAmount.setTotal(3000); // 30元
|
||||
instantAmount.setCurrency("CNY");
|
||||
instantRequest.setAmount(instantAmount);
|
||||
|
||||
// 执行立即扣费
|
||||
SubscriptionInstantBillingResult instantResult = subscriptionService.instantBilling(instantRequest);
|
||||
System.out.println("扣费结果: " + instantResult.getTradeState());
|
||||
```
|
||||
|
||||
### 6. 查询扣费记录
|
||||
|
||||
```java
|
||||
// 创建查询请求
|
||||
SubscriptionTransactionQueryRequest queryRequest = new SubscriptionTransactionQueryRequest();
|
||||
queryRequest.setOpenid("用户的openid");
|
||||
queryRequest.setBeginTime("2024-08-01T00:00:00+08:00");
|
||||
queryRequest.setEndTime("2024-08-31T23:59:59+08:00");
|
||||
queryRequest.setLimit(20);
|
||||
queryRequest.setOffset(0);
|
||||
|
||||
// 查询扣费记录
|
||||
SubscriptionTransactionQueryResult transactionResult = subscriptionService.queryTransactions(queryRequest);
|
||||
System.out.println("总记录数: " + transactionResult.getTotalCount());
|
||||
for (SubscriptionTransactionQueryResult.SubscriptionTransaction transaction : transactionResult.getData()) {
|
||||
System.out.println("订单号: " + transaction.getOutTradeNo() + ", 状态: " + transaction.getTradeState());
|
||||
}
|
||||
```
|
||||
|
||||
## 扣费计划类型
|
||||
|
||||
- `MONTHLY`:按月扣费
|
||||
- `WEEKLY`:按周扣费
|
||||
- `DAILY`:按日扣费
|
||||
- `YEARLY`:按年扣费
|
||||
|
||||
## 预约状态说明
|
||||
|
||||
- `SCHEDULED`:已预约
|
||||
- `CANCELLED`:已取消
|
||||
- `EXECUTED`:已执行
|
||||
- `FAILED`:执行失败
|
||||
|
||||
## 交易状态说明
|
||||
|
||||
- `SUCCESS`:支付成功
|
||||
- `REFUND`:转入退款
|
||||
- `NOTPAY`:未支付
|
||||
- `CLOSED`:已关闭
|
||||
- `REVOKED`:已撤销(刷卡支付)
|
||||
- `USERPAYING`:用户支付中
|
||||
- `PAYERROR`:支付失败
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **用户授权**:使用预约扣费功能前,需要用户在微信内完成签约授权
|
||||
2. **商户资质**:需要具备相应的业务资质才能开通此功能
|
||||
3. **金额限制**:扣费金额需要在签约模板规定的范围内
|
||||
4. **频率限制**:API调用有频率限制,请注意控制调用频次
|
||||
5. **异常处理**:建议对所有API调用进行异常处理
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [微信支付预约扣费API文档](https://pay.weixin.qq.com/doc/v3/merchant/4012161105)
|
||||
- [微信支付开发指南](https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml)
|
||||
|
||||
## 示例完整代码
|
||||
|
||||
```java
|
||||
import com.github.binarywang.wxpay.service.SubscriptionBillingService;
|
||||
import com.github.binarywang.wxpay.bean.subscriptionbilling.*;
|
||||
|
||||
public class SubscriptionBillingExample {
|
||||
|
||||
private SubscriptionBillingService subscriptionService;
|
||||
|
||||
public void example() throws Exception {
|
||||
// 1. 创建预约扣费
|
||||
SubscriptionScheduleRequest request = new SubscriptionScheduleRequest();
|
||||
request.setOutTradeNo("subscription_" + System.currentTimeMillis());
|
||||
request.setOpenid("用户openid");
|
||||
request.setDescription("VIP会员续费");
|
||||
request.setScheduleTime("2024-09-01T10:00:00+08:00");
|
||||
|
||||
SubscriptionAmount amount = new SubscriptionAmount();
|
||||
amount.setTotal(3000);
|
||||
amount.setCurrency("CNY");
|
||||
request.setAmount(amount);
|
||||
|
||||
BillingPlan plan = new BillingPlan();
|
||||
plan.setPlanType("MONTHLY");
|
||||
plan.setPeriod(1);
|
||||
plan.setTotalCount(12);
|
||||
request.setBillingPlan(plan);
|
||||
|
||||
SubscriptionScheduleResult result = subscriptionService.scheduleSubscription(request);
|
||||
|
||||
// 2. 查询预约状态
|
||||
SubscriptionQueryResult query = subscriptionService.querySubscription(result.getSubscriptionId());
|
||||
|
||||
// 3. 如需取消
|
||||
if ("SCHEDULED".equals(query.getStatus())) {
|
||||
SubscriptionCancelRequest cancelReq = new SubscriptionCancelRequest();
|
||||
cancelReq.setSubscriptionId(result.getSubscriptionId());
|
||||
cancelReq.setCancelReason("用户取消");
|
||||
|
||||
SubscriptionCancelResult cancelResult = subscriptionService.cancelSubscription(cancelReq);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.github.binarywang.wxpay.bean.subscriptionbilling;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 扣费计划信息
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class BillingPlan implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:计划类型
|
||||
* 变量名:plan_type
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 扣费计划类型
|
||||
* MONTHLY:按月扣费
|
||||
* WEEKLY:按周扣费
|
||||
* DAILY:按日扣费
|
||||
* YEARLY:按年扣费
|
||||
* 示例值:MONTHLY
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("plan_type")
|
||||
private String planType;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:扣费周期
|
||||
* 变量名:period
|
||||
* 是否必填:是
|
||||
* 类型:int
|
||||
* 描述:
|
||||
* 扣费周期,配合plan_type使用
|
||||
* 例如:plan_type为MONTHLY,period为1,表示每1个月扣费一次
|
||||
* 示例值:1
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("period")
|
||||
private Integer period;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:总扣费次数
|
||||
* 变量名:total_count
|
||||
* 是否必填:否
|
||||
* 类型:int
|
||||
* 描述:
|
||||
* 总扣费次数,不填表示无限次扣费
|
||||
* 示例值:12
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("total_count")
|
||||
private Integer totalCount;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:已扣费次数
|
||||
* 变量名:executed_count
|
||||
* 是否必填:否
|
||||
* 类型:int
|
||||
* 描述:
|
||||
* 已扣费次数,查询时返回
|
||||
* 示例值:2
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("executed_count")
|
||||
private Integer executedCount;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:计划开始时间
|
||||
* 变量名:start_time
|
||||
* 是否必填:否
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 计划开始时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("start_time")
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:计划结束时间
|
||||
* 变量名:end_time
|
||||
* 是否必填:否
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 计划结束时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE
|
||||
* 示例值:2019-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("end_time")
|
||||
private String endTime;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.github.binarywang.wxpay.bean.subscriptionbilling;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 预约扣费金额信息
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class SubscriptionAmount implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:总金额
|
||||
* 变量名:total
|
||||
* 是否必填:是
|
||||
* 类型:int
|
||||
* 描述:
|
||||
* 订单总金额,单位为分
|
||||
* 示例值:100
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("total")
|
||||
private Integer total;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:货币类型
|
||||
* 变量名:currency
|
||||
* 是否必填:否
|
||||
* 类型:string(16)
|
||||
* 描述:
|
||||
* CNY:人民币,境内商户号仅支持人民币
|
||||
* 示例值:CNY
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("currency")
|
||||
private String currency;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.github.binarywang.wxpay.bean.subscriptionbilling;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 取消预约扣费请求参数
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class SubscriptionCancelRequest implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约扣费ID
|
||||
* 变量名:subscription_id
|
||||
* 是否必填:是
|
||||
* 类型:string(64)
|
||||
* 描述:
|
||||
* 微信支付预约扣费ID
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("subscription_id")
|
||||
private String subscriptionId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:取消原因
|
||||
* 变量名:cancel_reason
|
||||
* 是否必填:否
|
||||
* 类型:string(256)
|
||||
* 描述:
|
||||
* 取消原因描述
|
||||
* 示例值:用户主动取消
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("cancel_reason")
|
||||
private String cancelReason;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.github.binarywang.wxpay.bean.subscriptionbilling;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 取消预约扣费响应结果
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class SubscriptionCancelResult implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约扣费ID
|
||||
* 变量名:subscription_id
|
||||
* 是否必填:是
|
||||
* 类型:string(64)
|
||||
* 描述:
|
||||
* 微信支付预约扣费ID
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("subscription_id")
|
||||
private String subscriptionId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约状态
|
||||
* 变量名:status
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 预约状态,取消后应为CANCELLED
|
||||
* 示例值:CANCELLED
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("status")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:取消时间
|
||||
* 变量名:cancel_time
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 取消时间,遵循rfc3339标准格式
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("cancel_time")
|
||||
private String cancelTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:取消原因
|
||||
* 变量名:cancel_reason
|
||||
* 是否必填:否
|
||||
* 类型:string(256)
|
||||
* 描述:
|
||||
* 取消原因描述
|
||||
* 示例值:用户主动取消
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("cancel_reason")
|
||||
private String cancelReason;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.github.binarywang.wxpay.bean.subscriptionbilling;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 立即扣费请求参数
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class SubscriptionInstantBillingRequest implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商户订单号
|
||||
* 变量名:out_trade_no
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("out_trade_no")
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:用户标识
|
||||
* 变量名:openid
|
||||
* 是否必填:是
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 用户在直连商户appid下的唯一标识
|
||||
* 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("openid")
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:订单描述
|
||||
* 变量名:description
|
||||
* 是否必填:是
|
||||
* 类型:string(127)
|
||||
* 描述:
|
||||
* 订单描述
|
||||
* 示例值:腾讯充值中心-QQ会员充值
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("description")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:扣费金额
|
||||
* 变量名:amount
|
||||
* 是否必填:是
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* 扣费金额信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("amount")
|
||||
private SubscriptionAmount amount;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:通知地址
|
||||
* 变量名:notify_url
|
||||
* 是否必填:否
|
||||
* 类型:string(256)
|
||||
* 描述:
|
||||
* 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
|
||||
* 示例值:https://www.weixin.qq.com/wxpay/pay.php
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("notify_url")
|
||||
private String notifyUrl;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:附加数据
|
||||
* 变量名:attach
|
||||
* 是否必填:否
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
|
||||
* 示例值:自定义数据
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("attach")
|
||||
private String attach;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.github.binarywang.wxpay.bean.subscriptionbilling;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 立即扣费响应结果
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class SubscriptionInstantBillingResult implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:微信支付订单号
|
||||
* 变量名:transaction_id
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 微信支付系统生成的订单号
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("transaction_id")
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商户订单号
|
||||
* 变量名:out_trade_no
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 商户系统内部订单号
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("out_trade_no")
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:交易状态
|
||||
* 变量名:trade_state
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 交易状态
|
||||
* SUCCESS:支付成功
|
||||
* REFUND:转入退款
|
||||
* NOTPAY:未支付
|
||||
* CLOSED:已关闭
|
||||
* REVOKED:已撤销(刷卡支付)
|
||||
* USERPAYING:用户支付中
|
||||
* PAYERROR:支付失败
|
||||
* 示例值:SUCCESS
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("trade_state")
|
||||
private String tradeState;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:交易状态描述
|
||||
* 变量名:trade_state_desc
|
||||
* 是否必填:是
|
||||
* 类型:string(256)
|
||||
* 描述:
|
||||
* 交易状态描述
|
||||
* 示例值:支付成功
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("trade_state_desc")
|
||||
private String tradeStateDesc;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:支付完成时间
|
||||
* 变量名:success_time
|
||||
* 是否必填:否
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 支付完成时间,遵循rfc3339标准格式
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("success_time")
|
||||
private String successTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:扣费金额
|
||||
* 变量名:amount
|
||||
* 是否必填:是
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* 扣费金额信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("amount")
|
||||
private SubscriptionAmount amount;
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package com.github.binarywang.wxpay.bean.subscriptionbilling;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 预约扣费查询结果
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class SubscriptionQueryResult implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约扣费ID
|
||||
* 变量名:subscription_id
|
||||
* 是否必填:是
|
||||
* 类型:string(64)
|
||||
* 描述:
|
||||
* 微信支付预约扣费ID
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("subscription_id")
|
||||
private String subscriptionId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商户订单号
|
||||
* 变量名:out_trade_no
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 商户系统内部订单号
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("out_trade_no")
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:用户标识
|
||||
* 变量名:openid
|
||||
* 是否必填:是
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 用户在直连商户appid下的唯一标识
|
||||
* 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("openid")
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:订单描述
|
||||
* 变量名:description
|
||||
* 是否必填:是
|
||||
* 类型:string(127)
|
||||
* 描述:
|
||||
* 订单描述
|
||||
* 示例值:腾讯充值中心-QQ会员充值
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("description")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约状态
|
||||
* 变量名:status
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 预约状态
|
||||
* SCHEDULED:已预约
|
||||
* CANCELLED:已取消
|
||||
* EXECUTED:已执行
|
||||
* FAILED:执行失败
|
||||
* 示例值:SCHEDULED
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("status")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约扣费时间
|
||||
* 变量名:schedule_time
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 预约扣费的时间,遵循rfc3339标准格式
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("schedule_time")
|
||||
private String scheduleTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:创建时间
|
||||
* 变量名:create_time
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 预约创建时间,遵循rfc3339标准格式
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("create_time")
|
||||
private String createTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:更新时间
|
||||
* 变量名:update_time
|
||||
* 是否必填:否
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 预约更新时间,遵循rfc3339标准格式
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("update_time")
|
||||
private String updateTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约扣费金额
|
||||
* 变量名:amount
|
||||
* 是否必填:是
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* 预约扣费金额信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("amount")
|
||||
private SubscriptionAmount amount;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:扣费计划
|
||||
* 变量名:billing_plan
|
||||
* 是否必填:否
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* 扣费计划信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("billing_plan")
|
||||
private BillingPlan billingPlan;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:附加数据
|
||||
* 变量名:attach
|
||||
* 是否必填:否
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 附加数据
|
||||
* 示例值:自定义数据
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("attach")
|
||||
private String attach;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.github.binarywang.wxpay.bean.subscriptionbilling;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 预约扣费请求参数
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class SubscriptionScheduleRequest implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商户订单号
|
||||
* 变量名:out_trade_no
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("out_trade_no")
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:用户标识
|
||||
* 变量名:openid
|
||||
* 是否必填:是
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 用户在直连商户appid下的唯一标识
|
||||
* 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("openid")
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:订单描述
|
||||
* 变量名:description
|
||||
* 是否必填:是
|
||||
* 类型:string(127)
|
||||
* 描述:
|
||||
* 订单描述
|
||||
* 示例值:腾讯充值中心-QQ会员充值
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("description")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约扣费金额
|
||||
* 变量名:amount
|
||||
* 是否必填:是
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* 预约扣费金额信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("amount")
|
||||
private SubscriptionAmount amount;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约扣费时间
|
||||
* 变量名:schedule_time
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 预约扣费的时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("schedule_time")
|
||||
private String scheduleTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:扣费计划
|
||||
* 变量名:billing_plan
|
||||
* 是否必填:否
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* 扣费计划信息,用于连续包月等场景
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("billing_plan")
|
||||
private BillingPlan billingPlan;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:通知地址
|
||||
* 变量名:notify_url
|
||||
* 是否必填:否
|
||||
* 类型:string(256)
|
||||
* 描述:
|
||||
* 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
|
||||
* 示例值:https://www.weixin.qq.com/wxpay/pay.php
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("notify_url")
|
||||
private String notifyUrl;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:附加数据
|
||||
* 变量名:attach
|
||||
* 是否必填:否
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
|
||||
* 示例值:自定义数据
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("attach")
|
||||
private String attach;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.github.binarywang.wxpay.bean.subscriptionbilling;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 预约扣费响应结果
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class SubscriptionScheduleResult implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约扣费ID
|
||||
* 变量名:subscription_id
|
||||
* 是否必填:是
|
||||
* 类型:string(64)
|
||||
* 描述:
|
||||
* 微信支付预约扣费ID
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("subscription_id")
|
||||
private String subscriptionId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商户订单号
|
||||
* 变量名:out_trade_no
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 商户系统内部订单号
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("out_trade_no")
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约状态
|
||||
* 变量名:status
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 预约状态
|
||||
* SCHEDULED:已预约
|
||||
* CANCELLED:已取消
|
||||
* EXECUTED:已执行
|
||||
* FAILED:执行失败
|
||||
* 示例值:SCHEDULED
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("status")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约扣费时间
|
||||
* 变量名:schedule_time
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 预约扣费的时间,遵循rfc3339标准格式
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("schedule_time")
|
||||
private String scheduleTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:创建时间
|
||||
* 变量名:create_time
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 预约创建时间,遵循rfc3339标准格式
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("create_time")
|
||||
private String createTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约扣费金额
|
||||
* 变量名:amount
|
||||
* 是否必填:是
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* 预约扣费金额信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("amount")
|
||||
private SubscriptionAmount amount;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:扣费计划
|
||||
* 变量名:billing_plan
|
||||
* 是否必填:否
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* 扣费计划信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("billing_plan")
|
||||
private BillingPlan billingPlan;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.github.binarywang.wxpay.bean.subscriptionbilling;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 查询扣费记录请求参数
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class SubscriptionTransactionQueryRequest implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:用户标识
|
||||
* 变量名:openid
|
||||
* 是否必填:否
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 用户在直连商户appid下的唯一标识
|
||||
* 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("openid")
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:开始时间
|
||||
* 变量名:begin_time
|
||||
* 是否必填:否
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 查询开始时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("begin_time")
|
||||
private String beginTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:结束时间
|
||||
* 变量名:end_time
|
||||
* 是否必填:否
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 查询结束时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("end_time")
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:分页大小
|
||||
* 变量名:limit
|
||||
* 是否必填:否
|
||||
* 类型:int
|
||||
* 描述:
|
||||
* 分页大小,不超过50
|
||||
* 示例值:20
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("limit")
|
||||
private Integer limit;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:分页偏移量
|
||||
* 变量名:offset
|
||||
* 是否必填:否
|
||||
* 类型:int
|
||||
* 描述:
|
||||
* 分页偏移量
|
||||
* 示例值:0
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("offset")
|
||||
private Integer offset;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package com.github.binarywang.wxpay.bean.subscriptionbilling;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 查询扣费记录响应结果
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class SubscriptionTransactionQueryResult implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:总数量
|
||||
* 变量名:total_count
|
||||
* 是否必填:是
|
||||
* 类型:int
|
||||
* 描述:
|
||||
* 符合条件的记录总数量
|
||||
* 示例值:100
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("total_count")
|
||||
private Integer totalCount;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:扣费记录列表
|
||||
* 变量名:data
|
||||
* 是否必填:是
|
||||
* 类型:array
|
||||
* 描述:
|
||||
* 扣费记录列表
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("data")
|
||||
private List<SubscriptionTransaction> data;
|
||||
|
||||
/**
|
||||
* 扣费记录
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class SubscriptionTransaction implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:微信支付订单号
|
||||
* 变量名:transaction_id
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 微信支付系统生成的订单号
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("transaction_id")
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商户订单号
|
||||
* 变量名:out_trade_no
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 商户系统内部订单号
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("out_trade_no")
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:预约扣费ID
|
||||
* 变量名:subscription_id
|
||||
* 是否必填:否
|
||||
* 类型:string(64)
|
||||
* 描述:
|
||||
* 微信支付预约扣费ID,预约扣费产生的交易才有此字段
|
||||
* 示例值:1217752501201407033233368018
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("subscription_id")
|
||||
private String subscriptionId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:交易状态
|
||||
* 变量名:trade_state
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 交易状态
|
||||
* SUCCESS:支付成功
|
||||
* REFUND:转入退款
|
||||
* NOTPAY:未支付
|
||||
* CLOSED:已关闭
|
||||
* REVOKED:已撤销(刷卡支付)
|
||||
* USERPAYING:用户支付中
|
||||
* PAYERROR:支付失败
|
||||
* 示例值:SUCCESS
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("trade_state")
|
||||
private String tradeState;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:支付完成时间
|
||||
* 变量名:success_time
|
||||
* 是否必填:否
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 支付完成时间,遵循rfc3339标准格式
|
||||
* 示例值:2018-06-08T10:34:56+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("success_time")
|
||||
private String successTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:扣费金额
|
||||
* 变量名:amount
|
||||
* 是否必填:是
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* 扣费金额信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("amount")
|
||||
private SubscriptionAmount amount;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:用户标识
|
||||
* 变量名:openid
|
||||
* 是否必填:是
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 用户在直连商户appid下的唯一标识
|
||||
* 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("openid")
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:订单描述
|
||||
* 变量名:description
|
||||
* 是否必填:是
|
||||
* 类型:string(127)
|
||||
* 描述:
|
||||
* 订单描述
|
||||
* 示例值:腾讯充值中心-QQ会员充值
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("description")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:附加数据
|
||||
* 变量名:attach
|
||||
* 是否必填:否
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 附加数据
|
||||
* 示例值:自定义数据
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("attach")
|
||||
private String attach;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.github.binarywang.wxpay.service;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.subscriptionbilling.*;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
|
||||
/**
|
||||
* 微信支付-预约扣费服务 (连续包月功能)
|
||||
* <pre>
|
||||
* 微信支付预约扣费功能,支持商户在用户授权的情况下,
|
||||
* 按照约定的时间和金额,自动从用户的支付账户中扣取费用。
|
||||
* 主要用于连续包月、订阅服务等场景。
|
||||
*
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
* created on 2024-08-31
|
||||
*/
|
||||
public interface SubscriptionBillingService {
|
||||
|
||||
/**
|
||||
* 预约扣费
|
||||
* <pre>
|
||||
* 商户可以通过该接口预约未来某个时间点的扣费。
|
||||
* 适用于连续包月、订阅服务等场景。
|
||||
*
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* 请求URL: https://api.mch.weixin.qq.com/v3/subscription-billing/schedule
|
||||
* 请求方式: POST
|
||||
* 是否需要证书: 是
|
||||
* </pre>
|
||||
*
|
||||
* @param request 预约扣费请求参数
|
||||
* @return 预约扣费结果
|
||||
* @throws WxPayException 微信支付异常
|
||||
*/
|
||||
SubscriptionScheduleResult scheduleSubscription(SubscriptionScheduleRequest request) throws WxPayException;
|
||||
|
||||
/**
|
||||
* 查询预约扣费
|
||||
* <pre>
|
||||
* 商户可以通过该接口查询已预约的扣费信息。
|
||||
*
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* 请求URL: https://api.mch.weixin.qq.com/v3/subscription-billing/schedule/{subscription_id}
|
||||
* 请求方式: GET
|
||||
* </pre>
|
||||
*
|
||||
* @param subscriptionId 预约扣费ID
|
||||
* @return 预约扣费查询结果
|
||||
* @throws WxPayException 微信支付异常
|
||||
*/
|
||||
SubscriptionQueryResult querySubscription(String subscriptionId) throws WxPayException;
|
||||
|
||||
/**
|
||||
* 取消预约扣费
|
||||
* <pre>
|
||||
* 商户可以通过该接口取消已预约的扣费。
|
||||
*
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* 请求URL: https://api.mch.weixin.qq.com/v3/subscription-billing/schedule/{subscription_id}/cancel
|
||||
* 请求方式: POST
|
||||
* 是否需要证书: 是
|
||||
* </pre>
|
||||
*
|
||||
* @param request 取消预约扣费请求参数
|
||||
* @return 取消预约扣费结果
|
||||
* @throws WxPayException 微信支付异常
|
||||
*/
|
||||
SubscriptionCancelResult cancelSubscription(SubscriptionCancelRequest request) throws WxPayException;
|
||||
|
||||
/**
|
||||
* 立即扣费
|
||||
* <pre>
|
||||
* 商户可以通过该接口立即执行扣费操作。
|
||||
* 通常用于补扣失败的费用或者特殊情况下的即时扣费。
|
||||
*
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* 请求URL: https://api.mch.weixin.qq.com/v3/subscription-billing/instant-billing
|
||||
* 请求方式: POST
|
||||
* 是否需要证书: 是
|
||||
* </pre>
|
||||
*
|
||||
* @param request 立即扣费请求参数
|
||||
* @return 立即扣费结果
|
||||
* @throws WxPayException 微信支付异常
|
||||
*/
|
||||
SubscriptionInstantBillingResult instantBilling(SubscriptionInstantBillingRequest request) throws WxPayException;
|
||||
|
||||
/**
|
||||
* 查询扣费记录
|
||||
* <pre>
|
||||
* 商户可以通过该接口查询扣费记录。
|
||||
*
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* 请求URL: https://api.mch.weixin.qq.com/v3/subscription-billing/transactions
|
||||
* 请求方式: GET
|
||||
* </pre>
|
||||
*
|
||||
* @param request 查询扣费记录请求参数
|
||||
* @return 扣费记录查询结果
|
||||
* @throws WxPayException 微信支付异常
|
||||
*/
|
||||
SubscriptionTransactionQueryResult queryTransactions(SubscriptionTransactionQueryRequest request) throws WxPayException;
|
||||
}
|
||||
@@ -337,6 +337,13 @@ public interface WxPayService {
|
||||
*/
|
||||
BrandMerchantTransferService getBrandMerchantTransferService();
|
||||
|
||||
/**
|
||||
* 获取微信支付预约扣费服务类 (连续包月功能)
|
||||
*
|
||||
* @return the subscription billing service
|
||||
*/
|
||||
SubscriptionBillingService getSubscriptionBillingService();
|
||||
|
||||
/**
|
||||
* 设置企业付款服务类,允许开发者自定义实现类.
|
||||
*
|
||||
|
||||
@@ -133,6 +133,9 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
|
||||
@Getter
|
||||
private final BrandMerchantTransferService brandMerchantTransferService = new BrandMerchantTransferServiceImpl(this);
|
||||
|
||||
@Getter
|
||||
private final SubscriptionBillingService subscriptionBillingService = new SubscriptionBillingServiceImpl(this);
|
||||
|
||||
@Getter
|
||||
private final BusinessOperationTransferService businessOperationTransferService = new BusinessOperationTransferServiceImpl(this);
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.github.binarywang.wxpay.service.impl;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.subscriptionbilling.*;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
import com.github.binarywang.wxpay.service.SubscriptionBillingService;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 微信支付-预约扣费服务实现 (连续包月功能)
|
||||
* <pre>
|
||||
* 微信支付预约扣费功能,支持商户在用户授权的情况下,
|
||||
* 按照约定的时间和金额,自动从用户的支付账户中扣取费用。
|
||||
* 主要用于连续包月、订阅服务等场景。
|
||||
*
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012161105
|
||||
* </pre>
|
||||
*
|
||||
* @author Binary Wang
|
||||
* created on 2024-08-31
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class SubscriptionBillingServiceImpl implements SubscriptionBillingService {
|
||||
|
||||
private static final Gson GSON = new GsonBuilder().create();
|
||||
private final WxPayService payService;
|
||||
|
||||
@Override
|
||||
public SubscriptionScheduleResult scheduleSubscription(SubscriptionScheduleRequest request) throws WxPayException {
|
||||
String url = String.format("%s/v3/subscription-billing/schedule", this.payService.getPayBaseUrl());
|
||||
String response = this.payService.postV3(url, GSON.toJson(request));
|
||||
return GSON.fromJson(response, SubscriptionScheduleResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubscriptionQueryResult querySubscription(String subscriptionId) throws WxPayException {
|
||||
String url = String.format("%s/v3/subscription-billing/schedule/%s", this.payService.getPayBaseUrl(), subscriptionId);
|
||||
String response = this.payService.getV3(url);
|
||||
return GSON.fromJson(response, SubscriptionQueryResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubscriptionCancelResult cancelSubscription(SubscriptionCancelRequest request) throws WxPayException {
|
||||
String url = String.format("%s/v3/subscription-billing/schedule/%s/cancel",
|
||||
this.payService.getPayBaseUrl(), request.getSubscriptionId());
|
||||
String response = this.payService.postV3(url, GSON.toJson(request));
|
||||
return GSON.fromJson(response, SubscriptionCancelResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubscriptionInstantBillingResult instantBilling(SubscriptionInstantBillingRequest request) throws WxPayException {
|
||||
String url = String.format("%s/v3/subscription-billing/instant-billing", this.payService.getPayBaseUrl());
|
||||
String response = this.payService.postV3(url, GSON.toJson(request));
|
||||
return GSON.fromJson(response, SubscriptionInstantBillingResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubscriptionTransactionQueryResult queryTransactions(SubscriptionTransactionQueryRequest request) throws WxPayException {
|
||||
String url = String.format("%s/v3/subscription-billing/transactions", this.payService.getPayBaseUrl());
|
||||
|
||||
StringBuilder queryString = new StringBuilder();
|
||||
if (request.getOpenid() != null) {
|
||||
queryString.append("openid=").append(request.getOpenid()).append("&");
|
||||
}
|
||||
if (request.getBeginTime() != null) {
|
||||
queryString.append("begin_time=").append(request.getBeginTime()).append("&");
|
||||
}
|
||||
if (request.getEndTime() != null) {
|
||||
queryString.append("end_time=").append(request.getEndTime()).append("&");
|
||||
}
|
||||
if (request.getLimit() != null) {
|
||||
queryString.append("limit=").append(request.getLimit()).append("&");
|
||||
}
|
||||
if (request.getOffset() != null) {
|
||||
queryString.append("offset=").append(request.getOffset()).append("&");
|
||||
}
|
||||
|
||||
if (queryString.length() > 0) {
|
||||
// Remove trailing &
|
||||
queryString.setLength(queryString.length() - 1);
|
||||
url += "?" + queryString.toString();
|
||||
}
|
||||
|
||||
String response = this.payService.getV3(url);
|
||||
return GSON.fromJson(response, SubscriptionTransactionQueryResult.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.github.binarywang.wxpay.service.impl;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.subscriptionbilling.*;
|
||||
import com.github.binarywang.wxpay.service.SubscriptionBillingService;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import com.github.binarywang.wxpay.testbase.ApiTestModule;
|
||||
import com.google.inject.Inject;
|
||||
import org.testng.annotations.Guice;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* 微信支付预约扣费服务测试类
|
||||
* <p>
|
||||
* 注意:由于预约扣费功能需要用户授权和实际的签约关系,
|
||||
* 这些测试主要用于验证接口调用的正确性,而不是功能的完整性。
|
||||
* 实际测试需要在具有有效签约关系的环境中进行。
|
||||
* </p>
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Test(enabled = false) // 默认关闭,需要实际环境配置才能测试
|
||||
@Guice(modules = ApiTestModule.class)
|
||||
public class SubscriptionBillingServiceImplTest {
|
||||
|
||||
@Inject
|
||||
private WxPayService wxPayService;
|
||||
|
||||
@Test
|
||||
public void testScheduleSubscription() {
|
||||
try {
|
||||
SubscriptionBillingService service = this.wxPayService.getSubscriptionBillingService();
|
||||
|
||||
SubscriptionScheduleRequest request = new SubscriptionScheduleRequest();
|
||||
request.setOutTradeNo("test_subscription_" + System.currentTimeMillis());
|
||||
request.setOpenid("test_openid");
|
||||
request.setDescription("测试预约扣费");
|
||||
request.setScheduleTime("2024-09-01T10:00:00+08:00");
|
||||
|
||||
SubscriptionAmount amount = new SubscriptionAmount();
|
||||
amount.setTotal(100); // 1元,单位分
|
||||
amount.setCurrency("CNY");
|
||||
request.setAmount(amount);
|
||||
|
||||
BillingPlan billingPlan = new BillingPlan();
|
||||
billingPlan.setPlanType("MONTHLY");
|
||||
billingPlan.setPeriod(1);
|
||||
billingPlan.setTotalCount(12);
|
||||
request.setBillingPlan(billingPlan);
|
||||
|
||||
SubscriptionScheduleResult result = service.scheduleSubscription(request);
|
||||
|
||||
System.out.println("预约扣费结果:" + result.toString());
|
||||
assert result.getSubscriptionId() != null;
|
||||
assert "SCHEDULED".equals(result.getStatus());
|
||||
|
||||
} catch (Exception e) {
|
||||
// 预期会因为测试环境没有有效的签约关系而失败
|
||||
System.out.println("预约扣费测试异常(预期):" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuerySubscription() {
|
||||
try {
|
||||
SubscriptionBillingService service = this.wxPayService.getSubscriptionBillingService();
|
||||
SubscriptionQueryResult result = service.querySubscription("test_subscription_id");
|
||||
|
||||
System.out.println("查询预约扣费结果:" + result.toString());
|
||||
|
||||
} catch (Exception e) {
|
||||
// 预期会因为测试数据不存在而失败
|
||||
System.out.println("查询预约扣费测试异常(预期):" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelSubscription() {
|
||||
try {
|
||||
SubscriptionBillingService service = this.wxPayService.getSubscriptionBillingService();
|
||||
|
||||
SubscriptionCancelRequest request = new SubscriptionCancelRequest();
|
||||
request.setSubscriptionId("test_subscription_id");
|
||||
request.setCancelReason("测试取消");
|
||||
|
||||
SubscriptionCancelResult result = service.cancelSubscription(request);
|
||||
|
||||
System.out.println("取消预约扣费结果:" + result.toString());
|
||||
assert "CANCELLED".equals(result.getStatus());
|
||||
|
||||
} catch (Exception e) {
|
||||
// 预期会因为测试数据不存在而失败
|
||||
System.out.println("取消预约扣费测试异常(预期):" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstantBilling() {
|
||||
try {
|
||||
SubscriptionBillingService service = this.wxPayService.getSubscriptionBillingService();
|
||||
|
||||
SubscriptionInstantBillingRequest request = new SubscriptionInstantBillingRequest();
|
||||
request.setOutTradeNo("test_instant_" + System.currentTimeMillis());
|
||||
request.setOpenid("test_openid");
|
||||
request.setDescription("测试立即扣费");
|
||||
|
||||
SubscriptionAmount amount = new SubscriptionAmount();
|
||||
amount.setTotal(100); // 1元,单位分
|
||||
amount.setCurrency("CNY");
|
||||
request.setAmount(amount);
|
||||
|
||||
SubscriptionInstantBillingResult result = service.instantBilling(request);
|
||||
|
||||
System.out.println("立即扣费结果:" + result.toString());
|
||||
assert result.getTransactionId() != null;
|
||||
|
||||
} catch (Exception e) {
|
||||
// 预期会因为测试环境没有有效的签约关系而失败
|
||||
System.out.println("立即扣费测试异常(预期):" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryTransactions() {
|
||||
try {
|
||||
SubscriptionBillingService service = this.wxPayService.getSubscriptionBillingService();
|
||||
|
||||
SubscriptionTransactionQueryRequest request = new SubscriptionTransactionQueryRequest();
|
||||
request.setOpenid("test_openid");
|
||||
request.setBeginTime("2024-08-01T00:00:00+08:00");
|
||||
request.setEndTime("2024-08-31T23:59:59+08:00");
|
||||
request.setLimit(20);
|
||||
request.setOffset(0);
|
||||
|
||||
SubscriptionTransactionQueryResult result = service.queryTransactions(request);
|
||||
|
||||
System.out.println("查询扣费记录结果:" + result.toString());
|
||||
assert result.getTotalCount() != null;
|
||||
|
||||
} catch (Exception e) {
|
||||
// 预期会因为测试环境数据问题而失败
|
||||
System.out.println("查询扣费记录测试异常(预期):" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user