news 2026/9/19 19:09:40

WxJava 微信支付预约扣费(连续包月)功能实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WxJava 微信支付预约扣费(连续包月)功能实战指南

WxJava 微信支付预约扣费(连续包月)功能实战指南

【免费下载链接】WxJava微信开发 Java SDK ,支持包括微信支付,开放平台,小程序,企业微信,视频号,公众号等的后端开发项目地址: https://gitcode.com/gh_mirrors/wx/WxJava

导读

本文基于 WxJava 微信支付 SDK 的subscriptionbilling模块,完整讲解"预约扣费(连续包月)"能力的接入与使用。该功能允许商户在用户签约授权后,按约定时间与金额自动从用户支付账户扣费,是视频会员、云服务、知识付费等订阅制业务的标准支付方案。读完本文,你将掌握从服务实例获取、预约创建、查询、取消、立即扣费到扣费记录查询的完整调用链,并了解各请求参数在源码中的字段映射与底层实现。

功能特性

微信支付预约扣费(连续包月)功能在 WxJava 中由SubscriptionBillingService统一封装,通过 SubscriptionBillingService.java 对外暴露以下五个能力:

能力接口方法说明
预约扣费scheduleSubscription创建未来某个时间点的扣费计划,可附带周期计划实现连续包月
查询预约querySubscriptionsubscription_id查询已创建的扣费计划状态
取消预约cancelSubscription取消尚未执行的扣费计划
立即扣费instantBilling立即执行扣费,常用于补扣失败费用或特殊情况即时扣费
扣费记录查询queryTransactions按时间窗口分页查询历史扣费记录

快速开始

1. 获取服务实例

预约扣费服务无需单独构建,直接通过WxPayService获取:

SubscriptionBillingService subscriptionService = wxPayService.getSubscriptionBillingService();

从源码看,该服务由BaseWxPayServiceImpl在内部以new SubscriptionBillingServiceImpl(this)的方式持有(见 BaseWxPayServiceImpl.java),并通过 WxPayService.java 的getSubscriptionBillingService()方法暴露。因此你只需保证wxPayService已正确配置(含商户号、APIv3 密钥、商户证书序列号与私钥),即可直接使用。

2. 创建预约扣费

// 创建预约扣费请求 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());
预约扣费请求参数详解

SubscriptionScheduleRequest(源码见 SubscriptionScheduleRequest.java)通过@SerializedName注解完成 Java 字段与微信 API JSON 字段的映射,各参数说明如下:

参数对应JSON字段必填类型说明
outTradeNoout_trade_nostring(32)商户系统内部订单号,只能是数字、大小写字母、_-*,同一商户号下唯一
openidopenidstring(128)用户在直连商户 appid 下的唯一标识
descriptiondescriptionstring(127)订单描述,如"腾讯充值中心-QQ会员充值"
amountamountobject预约扣费金额信息,见下节
scheduleTimeschedule_timestring(32)预约扣费时间,遵循 RFC3339,格式YYYY-MM-DDTHH:mm:ss+TIMEZONE,如2018-06-08T10:34:56+08:00
billingPlanbilling_planobject扣费计划信息,用于连续包月等场景
notifyUrlnotify_urlstring(256)异步接收微信支付结果通知的回调地址,必须为外网可访问的 URL 且不能携带参数
attachattachstring(128)附加数据,在查询 API 和支付通知中原样返回,可作为自定义参数使用
金额与扣费计划对象

SubscriptionAmount(SubscriptionAmount.java):

  • total:订单总金额,单位为(int 类型);
  • currency:货币类型,CNY表示人民币,境内商户号仅支持人民币。

BillingPlan(BillingPlan.java):

  • planTypeplan_type,必填):计划类型,取值MONTHLY/WEEKLY/DAILY/YEARLY
  • period(必填):扣费周期,配合planType使用,例如planType=MONTHLY, period=1表示每 1 个月扣费一次;
  • totalCounttotal_count,选填):总扣费次数,不填表示无限次扣费
  • executedCountexecuted_count,选填):已扣费次数,查询时由微信返回;
  • startTimestart_time,选填)与endTimeend_time,选填):计划开始/结束时间,同样遵循 RFC3339 格式。

3. 查询预约扣费

// 通过预约扣费ID查询 String subscriptionId = "从预约扣费结果中获取的ID"; SubscriptionQueryResult queryResult = subscriptionService.querySubscription(subscriptionId); System.out.println("预约状态: " + queryResult.getStatus());

subscription_id即创建预约时返回的SubscriptionScheduleResult.getSubscriptionId()。查询返回结果包含预约状态status、预约时间schedule_time、创建时间create_time、金额amount以及扣费计划billing_plan等字段(见 SubscriptionScheduleResult.java)。

4. 取消预约扣费

// 创建取消请求 SubscriptionCancelRequest cancelRequest = new SubscriptionCancelRequest(); cancelRequest.setSubscriptionId(subscriptionId); cancelRequest.setCancelReason("用户主动取消"); // 取消预约扣费 SubscriptionCancelResult cancelResult = subscriptionService.cancelSubscription(cancelRequest); System.out.println("取消结果: " + cancelResult.getStatus());

SubscriptionCancelRequest(SubscriptionCancelRequest.java)仅有两个字段:必填的subscriptionIdsubscription_id,微信支付预约扣费 ID)和选填的cancelReasoncancel_reason,取消原因描述,string(256))。

5. 立即扣费

立即扣费用于在预约计划之外临时发起扣费,例如补扣上月会员费、处理失败重试等:

// 创建立即扣费请求 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());

SubscriptionInstantBillingRequest(SubscriptionInstantBillingRequest.java)字段与预约扣费请求大体一致,包括必填的outTradeNoopeniddescriptionamount,以及选填的notifyUrlattach(注意:立即扣费请求不包含billing_planschedule_time)。

6. 查询扣费记录

// 创建查询请求 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()); }

查询结果SubscriptionTransactionQueryResult(SubscriptionTransactionQueryResult.java)包含总数量totalCount与扣费记录列表data。每条记录SubscriptionTransaction字段包括:微信支付订单号transactionId、商户订单号outTradeNo、预约扣费 IDsubscriptionId(仅预约扣费产生的交易有此字段)、交易状态tradeState、支付完成时间successTime、扣费金额amount、用户标识openid、订单描述description及附加数据attach

扣费计划类型

BillingPlan.planType支持的取值:

  • MONTHLY:按月扣费
  • WEEKLY:按周扣费
  • DAILY:按日扣费
  • YEARLY:按年扣费

配合period使用,例如MONTHLY + period=2表示每 2 个月扣费一次;totalCount不填则代表无限期扣费,直到用户主动解约。

预约状态说明

预约扣费计划(SubscriptionScheduleResult.status)可能处于以下状态:

  • SCHEDULED:已预约(待执行)
  • CANCELLED:已取消
  • EXECUTED:已执行
  • FAILED:执行失败

交易状态说明

扣费记录(SubscriptionTransaction.tradeState)的取值与微信支付通用交易状态一致:

  • SUCCESS:支付成功
  • REFUND:转入退款
  • NOTPAY:未支付
  • CLOSED:已关闭
  • REVOKED:已撤销(刷卡支付)
  • USERPAYING:用户支付中
  • PAYERROR:支付失败

源码级实现原理

从 SubscriptionBillingServiceImpl.java 可以看到,五个接口方法均复用WxPayService的统一 APIv3 请求通道(postV3/getV3),并通过 Gson 完成请求序列化与响应反序列化,对应端点如下:

方法HTTP请求URL(前缀 + 路径)是否需要证书
scheduleSubscriptionPOST/v3/subscription-billing/schedule
querySubscriptionGET/v3/subscription-billing/schedule/{subscription_id}
cancelSubscriptionPOST/v3/subscription-billing/schedule/{subscription_id}/cancel
instantBillingPOST/v3/subscription-billing/instant-billing
queryTransactionsGET/v3/subscription-billing/transactions?openid=&begin_time=&end_time=&limit=&offset=

URL 前缀来自payService.getPayBaseUrl()(默认https://api.mch.weixin.qq.com),服务内所有 APIv3 请求统一走该基础地址。几个值得注意的实现细节:

  • 取消预约cancelSubscriptionsubscription_id拼接进 URL 路径,请求体仍整体序列化发送,其中subscription_id字段同时存在于 URL 与 body;
  • 记录查询的分页与过滤queryTransactions内部按openidbegin_timeend_timelimitoffset顺序拼接查询串,limitoffset用于分页,时间字段均采用 RFC3339 格式;查询走 GET 请求,不需要商户证书;
  • 异常处理:所有方法统一抛出WxPayException,由底层 APIv3 通道在请求失败时封装错误码与错误信息。

注意事项

  1. 用户授权:使用预约扣费功能前,需要用户在微信内完成签约授权,未签约用户无法发起预约扣费;
  2. 商户资质:需要具备相应的业务资质才能开通此功能,且 APIv3 密钥、商户证书序列号与私钥必须正确配置;
  3. 金额限制:扣费金额需要在签约模板规定的范围内,total单位为分;
  4. 频率限制:API 调用有频率限制,请注意控制调用频次,避免触发限流;
  5. 异常处理:建议对所有 API 调用进行异常处理(捕获WxPayException),并对支付结果通知做签名验签与幂等处理;
  6. 时间格式:所有时间字段必须遵循 RFC3339 标准格式(如2024-09-01T10:00:00+08:00),否则微信侧会校验失败;
  7. 取消时机:取消操作通常只对SCHEDULED(已预约未执行)状态的计划生效,取消后状态变为CANCELLED

示例完整代码

将上述步骤串联起来,即得到完整的可运行示例(类路径对应源码包com.github.binarywang.wxpay.bean.subscriptionbilling):

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); } } }

相关代码与文档索引

  • 服务接口:SubscriptionBillingService.java
  • 服务实现:SubscriptionBillingServiceImpl.java
  • 服务获取入口:WxPayService.java、BaseWxPayServiceImpl.java
  • 请求/响应模型:weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/subscriptionbilling/ 目录下的 11 个 Bean 类
  • 使用文档:本指南的原始出处 SUBSCRIPTION_BILLING_USAGE.md
  • 更多用法:微信支付模块的 MULTI_APPID_USAGE.md(多商户号场景)与 CONNECTION_POOL.md(连接池调优)可帮助你完善生产环境部署

【免费下载链接】WxJava微信开发 Java SDK ,支持包括微信支付,开放平台,小程序,企业微信,视频号,公众号等的后端开发项目地址: https://gitcode.com/gh_mirrors/wx/WxJava

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 19:09:08

msvcr100.dll丢失怎么修复?VC++ 2010运行库安装与DLL报错排查指南

1. 先搞清楚 msvcr100.dll 到底是个什么东西很多人一看到弹窗里冒出个msvcr100.dll,第一反应就是“电脑中毒了”或者“系统坏了”,然后开始满世界找下载站。先别急,这个文件本身不是什么病毒,它是Microsoft Visual C 2010 运行库里…

作者头像 李华
网站建设 2026/9/19 19:08:25

Stata 18.0安装激活全攻略:从下载到正版授权一步到位

研究生阶段,Stata基本是躲不开的。写计量课程论文要用,做毕业论文跑数据要用,给导师做课题整理面板数据要用,连搞循证医学的同学做网状meta分析也经常被推荐用Stata。正因如此,拥有一套能正常跑的Stata 18.0格外重要。…

作者头像 李华
网站建设 2026/9/19 19:06:05

PaddleOCR自定义训练实战:从数据标注到模型部署全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 19:03:41

ASTM A640标准文件识别与工程应用核验指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 18:59:51

基于模型的系统工程教案设计:从SysML图到课堂编排的实用指南

简介:基于模型的系统工程(MBSE)PPT教案是一份面向系统工程专业学生、工程师和项目管理人员的学习课件,系统讲解了系统工程从传统文档驱动向模型驱动转型的核心背景与必要性。资源共1个pptx文件,大小3.75MB,…

作者头像 李华