我可以配置哪些计费周期?
periodType 和 periodInterval 共同定义周期长度。两者都是必填,
且 periodInterval 的接受范围取决于 periodType:
没有
YEARLY 周期类型。 年度订阅请使用
periodType: MONTHLY 加 periodInterval: 12。
这两个字段同样覆盖了商户通常按名称询问的周期:
个别支付方式可能进一步收窄范围——有些拒绝按日周期,有些只接受特定的周期/间隔组合。
通过了上述范围的周期,仍可能被您选择的支付方式拒绝。
试用期可以使用与正式周期不同的周期吗?
可以。试用周期通过trialPeriodType 和 trialPeriodInterval 独立于正式周期配置,
接受范围与正式周期相同:
trialPeriodType和trialPeriodInterval必须一起提供——只提供其中一个会被拒绝。- 如果两者都省略,试用期直接沿用
periodType和periodInterval。 - 独立配置不意味着没有交叉校验。见下一个问题。
哪些订阅字段必须一起提供?
即使试用周期是独立配置的,以下跨字段规则仍会强制执行:
一个订阅可以运行多少期?
“没有商户定义的结束”并不是字面意义上的无限:没有
numberOfPeriod 的订阅仍受您订阅协议上的最长时限约束,
Waffo 会根据该时限和您的周期推导出一个有效的最终期数。有些支付方式也有自己的最长时限。
所以即使您从未设置过,也要为订阅最终到达最后一期做好准备。
订阅一旦到达最后一期,金额更新会被拒绝——见金额页面。
创建订阅时 goodsUrl 和 appName 是必填的吗?
不是。 在 /api/v1/subscription/create 上两者都是可选的,您可以两者都不提供地创建订阅。
您可能见过的”提供 goodsUrl 或 appName 之一”规则属于一次性订单创建,而不是订阅创建。
个别协议出于合规和风控原因仍可能要求 goodsUrl,所以提供两者之一仍是好的做法——
但它不是订阅创建的通用校验,把集成建立在它是通用校验的假设上,会在您排查拒绝原因时误导您。
订阅有哪些状态,哪些流转是合法的?
共有九个状态。每个状态只能从特定的一组来源状态到达,所以一个您没预料到的状态, 通常是您漏掉了更早的一次流转,而不是状态本身无效:
按您的集成大概需要的方式分组:
把整个终态组当作”不会再开启新的计费期”,并按组而不是按单个值做分支——这样一个您没有映射到的状态就不会悄悄地看起来像一个活跃订阅。
PLATFORM_CANCELLED 是什么?
这是当 Waffo 主动取消订阅时设置的终态,原因是该订阅的某笔扣款产生了争议、拒付或欺诈信号。
它和其他任何取消一样通过订阅状态通知到达您这里,通知中带有取消原因,标明是哪种信号触发的。
对您的集成重要的几点:
- 它只能从
ACTIVE到达。 - 自动取消是按商户、按信号类型可配置的,且未配置时默认关闭——所以您可能永远见不到这个值, 它的缺席并不意味着争议被忽视了。
- 因为是 Waffo 而不是您发起的,在您处理该通知之前,您自己的记录会显示订阅仍是活跃的。请把 Webhook Webhook 通知当作事实来源。
创建返回了 AUTHORIZATION_REQUIRED,我该怎么做?
用户仍需授权这个周期性扣款授权。创建响应带有 subscriptionAction,其中包含要把用户引导去的 URL(webUrl)。
把用户重定向到那里;授权成功后订阅进入 ACTIVE。
subscriptionAction 只在订阅处于 IN_PROGRESS 或 AUTHORIZATION_REQUIRED 时才有值。
订阅一旦激活或进入终态就不再返回,这是预期行为而非错误。
授权链接有时效。创建请求上的 orderExpiredAt 设置该截止时间;如果省略,则应用平台默认值,而且它很短——
是分钟级而不是小时级——所以请立即把用户引导到链接。授权在窗口期内始终未完成的订阅会进入 CLOSE。
如果我用同一个请求 ID 再次调用创建会怎样?
创建按subscriptionRequest——您生成的请求 ID——幂等,而不是按 merchantSubscriptionId。
在同一 MID 下用相同的 subscriptionRequest 重复调用不会创建第二个订阅。
返回的内容取决于已存在的订阅走到了哪一步:
重复调用还会做一致性校验:如果
amount 或 currency 与原请求不同,该调用会被作为重复请求拒绝,而不是被悄悄接受。
所以请用完全相同的载荷重试,且不要把一个 subscriptionRequest 用于两个不同的订阅。
如果用户放弃了授权而您需要给他们一个新链接,请在订阅仍处于激活前状态时重试创建,并使用返回的 subscriptionAction;
不要缓存链接并假定它一直有效。
我如何查询订阅,以及如何获取每期的扣款?
调用/api/v1/subscription/inquiry,传 subscriptionRequest 或 subscriptionId——至少需要一个。两个都传也可以。
每期扣款默认不返回。把 paymentDetails 设为 1 才能获取(默认值 0 不返回;任何其他值会被拒绝):
每个
paymentDetails 条目带有 acquiringOrderId、orderCurrency、
orderAmount、orderStatus、orderUpdatedAt 和 period,在有额外(席位加购)扣款的期上还有一个
topupDetails 列表——这些行如何编号见
跨币种订阅与 Portal FAQ。
反过来——从一笔扣款找到它的订阅——订单查询响应带有一个 subscriptionInfo 块,包含 subscriptionId、period、
subscriptionRequest 和一个 subscriptionEvent,告诉您这笔扣款是周期扣款还是支付方式添加。一次性订单没有 subscriptionInfo。
订阅响应还会报告 productInfo.currentPeriod 和已存储的 productInfo.scheduledAmounts,这是您在排程金额变更之前要读取的内容。
一个用户可以有多少个订阅?
任意数量。在一个 MID 下,同一个merchantUserId 可以持有您创建的任意多个订阅——没有按用户的上限,
创建时也不对 merchantUserId 做唯一性检查。
这是多产品、多席位业务的常态:一个用户、多个并行订阅,每个有自己的 subscriptionId 和自己的生命周期。
因此,永远不要把 merchantUserId 当作”该用户的订阅”的键——请以 subscriptionId 或您自己的 merchantSubscriptionId 为键。
订阅可以在未来某个日期开始吗?
可以——创建时传startTime。校验规则:
不支持回溯。 无法创建一个表现得像更早就已开始的订阅,也无法让第一期落在过去。
如果您需要对已经过去的周期收费,请作为单独的一次性订单收取。
延后开始时还有两个约束:
- 开始日期必须落在将要使用的协议的有效期窗口内,所以即使在 365 天以内,过远的开始日期也可能因此被拒绝。
- 变更套餐时,
startTime不得早于原订阅的下一次计划扣款。