订阅在首次支付时建立合约,之后按计费周期重复扣款。初始订阅可以在收银台、Web SDK 或 API 直连任一接入方式完成;自主管理订阅的续费与托管订阅的升降级由商户服务端通过 API 直连发起。
概念与选择
计费方式由 subscription.selfExecute 决定,在初始订阅时选定:
- 托管订阅(
selfExecute=1):Onerway 按订阅的计费设置自动发起每期扣款,每期均推送 Webhook;配置notificationEmail后,Onerway 向客户发送订阅确认与扣款邮件,客户可通过响应中的subscriptionManageUrl自助管理订阅。 - 自主管理订阅(
selfExecute=2):商户自行维护计费周期,使用初始订阅返回的contractId和tokenId,通过创建直连交易(subscription.requestType=1)发起每期扣款;计费频率仅支持frequencyType=D,但这不表示只能按天扣款:字段subscription.frequencyPoint按天表示计费周期,例如,月度订阅可传30,年度订阅可传365。该值仅用于记录,实际续费扣款时间由商户自行确定。首期扣款是否由商户发起取决于subscription.mode,默认2时由 Onerway 在客户授权后完成。
卡与钱包都可用于订阅,两种计费方式对 Apple Pay 与 Google Pay 同样适用;钱包订阅的发起方式与差异见 Apple Pay 与 Google Pay。部分本地支付方式也可用于订阅,但只支持自主管理订阅,且订阅授权会单独产生一条通知,见本地支付方式。
计费周期、期数或截止日期、试用期等均在字段 subscription 中定义,字段含义见 API Reference。客户标识通过外层 merchantCustId 传入(subscription.merchantCustId 选填,传则须一致),取值要求与保存支付方式一致。
生命周期与通知
- 合约凭证:初始订阅成功后,保存订阅扣款通知中的
contractId与tokenId,它们是后续扣款、查询与取消的凭证。这里的tokenId是订阅 token,与卡 token 不可混用,见场景概览。 - 生命周期事件:首购、续费、换卡、变更、取消、到期与合约状态以订阅扣款通知的字段
scenarios与字段subscriptionStatus为准。 - 自主管理订阅续费:服务端传入
contractId、tokenId、merchantCustId与本期金额发起扣款,不传卡信息,因此续费环节没有 PCI DSS 要求。每期扣款金额由商户指定,不受首次订阅金额约束。扣款成功后收到scenarios=SUBSCRIPTION_RENEWAL的通知;对账由商户负责。 - 托管订阅续费:Onerway 按生效中的计划金额自动扣款,未变更计划前即首次订阅时的金额。单期自动扣款的金额不能单独修改,需要调整后续金额时按下述升降级更新计划。
- 托管订阅升降级:使用已存储的
contractId与tokenId发起(requestType=2),生效方式由subscription.changeMode决定。更新成功后收到scenarios=SUBSCRIPTION_CHANGED的通知,变更生效后 Onerway 按新计划金额继续自动扣款。changeMode=1立即生效:Onerway 按当前计费周期剩余天数计算差价(prorationMode=1,默认),或由商户通过proration提交差价(prorationMode=0)。升级立即扣取差价;降级如需向客户退还差额,由商户调用申请或取消退款完成。changeMode=2下个计费周期生效:不立即扣款,适合需要提前告知客户的涨价。
- 订阅并绑卡:托管卡订阅同时传入
subscription.bindCard=true时,系统创建两笔交易并发送两条独立通知:保存支付方式结果通知(txnType=BIND_CARD)返回可用于后续 token 支付的卡 token,订阅扣款通知(txnType=SALE)返回订阅侧的contractId与tokenId。两条通知的transactionId不同,应各自幂等处理。 - 查询与取消:合约详情见查询订阅详情,取消见取消订阅合约。
扣款失败与重试
自主管理订阅(selfExecute=2)由商户自行决定是否重试、重试次数与间隔,每次重试以续费扣款(requestType=1)通过 API 直连发起。
托管订阅(selfExecute=1)由 Onerway 自动重试:每期最多尝试 3 次(含首次扣款),即最多重试 2 次。重试间隔取决于计费周期:
| 计费周期 | 重试间隔 |
|---|---|
| 每天 | 1 小时 |
| 每 2–3 天 | 12 小时 |
| 超过 3 天 | 24 小时 |
重试期间 subscriptionStatus 为 pastdue;3 次全部失败后变为 paused,合约仍处于启用状态(dataStatus=1)。两个字段可从订阅扣款通知和查询订阅详情获取。
扣款重试与 Webhook 重发是两回事,后者见 Webhook 通知。
更换订阅用卡
仅托管订阅(selfExecute=1)支持换卡,且只能由客户在 subscriptionManageUrl 指向的订阅管理页面完成,没有对应 API。换卡结果通过 scenarios=SUBSCRIPTION_CARD_REPLACEMENT 的订阅扣款通知推送。
各接入方式的参数差异
| 接入方式 | 接口 | 关键参数 | 差异说明 | 接入指南 |
|---|---|---|---|---|
| 收银台 | 创建收银台支付 | subProductType=SUBSCRIBE、subscription(requestType=0) | 只做初始订阅;外层 merchantCustId 如同时传入须与 subscription.merchantCustId 一致 | 收银台接入 |
| Web SDK | 创建 SDK 交易 | subProductType=SUBSCRIBE、subscription(requestType=0) | 只做初始订阅;服务端维护允许购买的计划映射 | Web SDK 接入 |
| API 直连 | 创建直连交易 | subProductType=SUBSCRIBE、subscription.requestType | 初始订阅、续费与升降级都在此接口,以 requestType 取 0 / 1 / 2 区分 | API 直连接入 |