> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 订阅创建与校验 FAQ

> 创建订阅时 Waffo 接受的精确取值范围、哪些字段必须一起提供、订阅状态的完整列表及其合法流转，以及创建重试、查询和未来开始日期的行为。

在订阅存在**之前**必须正确的一切：接受的取值范围、必须一起提供的字段，以及创建响应的含义。

金额配置见
[订阅金额与按期排程 FAQ](/docs/zh/support/subscription-amount-scheduling-faq)。
跨币种行为和 Portal 查询见
[跨币种订阅与 Portal FAQ](/docs/zh/support/subscription-cross-currency-and-portal-faq)。

## 我可以配置哪些计费周期？

`periodType` 和 `periodInterval` 共同定义周期长度。两者都是必填，
且 `periodInterval` 的接受范围取决于 `periodType`：

| `periodType` | `periodInterval` 范围 | 周期长度 |
| - | - | - |
| `DAILY` | 1–365 | 每 N 天 |
| `WEEKLY` | 1–4 | 每 N 周 |
| `MONTHLY` | 1 或更大 | 每 N 个月 |

**没有 `YEARLY` 周期类型。** 年度订阅请使用
`periodType: MONTHLY` 加 `periodInterval: 12`。

这两个字段同样覆盖了商户通常按名称询问的周期：

| 您想要 | 配置 |
| - | - |
| 每周 | `WEEKLY` + `1` |
| 每两周 | `WEEKLY` + `2` |
| 每月 | `MONTHLY` + `1` |
| 每季度 | `MONTHLY` + `3` |
| 每半年 | `MONTHLY` + `6` |
| 每年 | `MONTHLY` + `12` |

个别支付方式可能进一步收窄范围——有些拒绝按日周期，有些只接受特定的周期/间隔组合。
通过了上述范围的周期，仍可能被您选择的支付方式拒绝。

## 试用期可以使用与正式周期不同的周期吗？

可以。试用周期通过 `trialPeriodType` 和 `trialPeriodInterval` 独立于正式周期配置，
接受范围与正式周期相同：

| `trialPeriodType` | `trialPeriodInterval` 范围 |
| - | - |
| `DAILY` | 1–365 |
| `WEEKLY` | 1–4 |
| `MONTHLY` | 1 或更大 |

* `trialPeriodType` 和 `trialPeriodInterval` 必须**一起**提供——只提供其中一个会被拒绝。
* 如果两者都省略，试用期直接沿用 `periodType` 和 `periodInterval`。
* 独立配置**不**意味着没有交叉校验。见下一个问题。

## 哪些订阅字段必须一起提供？

| 组 | 规则 |
| - | - |
| `trialPeriodType` + `trialPeriodInterval` | 要么都有，要么都无 |
| `trialPeriodAmount` + `numberOfTrialPeriod` | 要么都有，要么都无——只要您配置了试用期就必须成对提供，而不仅仅是在设置试用周期时 |
| `amount` | 创建时始终必填 |

即使试用周期是独立配置的，以下跨字段规则仍会强制执行：

| 规则 | 违反时的行为 |
| - | - |
| `numberOfTrialPeriod` 必须 `≤` `numberOfPeriod`（仅在提供了 `numberOfPeriod` 时检查） | 拒绝 |
| `trialPeriodAmount` 必须**严格小于** `amount`（允许 `0`；不允许相等） | 拒绝——`trial period amount should be less than subscription amount` |
| `trialPeriodAmount` 必须符合该币种的小数位数 | 拒绝——金额小数位错误 |
| 对于由支付方式驱动排程的支付方式，试用周期必须与正式周期**完全相同** | 拒绝 |

## 一个订阅可以运行多少期？

| `numberOfPeriod` | 含义 |
| - | - |
| 已提供 | 必须**大于 1**。订阅在该期数之后结束。 |
| 省略 / 为空 | 没有商户定义的结束。 |

"没有商户定义的结束"并不是字面意义上的无限：没有 `numberOfPeriod` 的订阅仍受您订阅协议上的最长时限约束，
Waffo 会根据该时限和您的周期推导出一个有效的最终期数。有些支付方式也有自己的最长时限。
所以即使您从未设置过，也要为订阅最终到达最后一期做好准备。

订阅一旦到达最后一期，金额更新会被拒绝——见[金额页面](/docs/zh/support/subscription-amount-scheduling-faq)。

## 创建订阅时 `goodsUrl` 和 `appName` 是必填的吗？

**不是。** 在 `/api/v1/subscription/create` 上两者都是可选的，您可以两者都不提供地创建订阅。

您可能见过的"提供 `goodsUrl` 或 `appName` 之一"规则属于**一次性订单创建**，而不是订阅创建。
个别协议出于合规和风控原因仍可能要求 `goodsUrl`，所以提供两者之一仍是好的做法——
但它不是订阅创建的通用校验，把集成建立在它是通用校验的假设上，会在您排查拒绝原因时误导您。

## 订阅有哪些状态，哪些流转是合法的？

共有九个状态。每个状态只能从特定的一组来源状态到达，所以一个您没预料到的状态，
通常是您漏掉了更早的一次流转，而不是状态本身无效：

| 状态 | 含义 | 合法来源状态 |
| - | - | - |
| `IN_PROGRESS` | 用户尚未完成订阅确认 | —（仅创建时；永不会被流转进入） |
| `AUTHORIZATION_REQUIRED` | 支付方式需要用户授权 | `IN_PROGRESS` |
| `ACTIVE` | 运行中 | `AUTHORIZATION_REQUIRED`、`IN_PROGRESS` |
| `CLOSE` | 因激活前超时或失败而关闭 | `ACTIVE`、`AUTHORIZATION_REQUIRED`、`IN_PROGRESS` |
| `MERCHANT_CANCELLED` | 您取消了它 | `ACTIVE` |
| `USER_CANCELLED` | 终端用户取消了它 | `ACTIVE` |
| `CHANNEL_CANCELLED` | 在支付方式侧被取消，例如用户在钱包里撤销了授权 | `ACTIVE`、`AUTHORIZATION_REQUIRED`、`IN_PROGRESS` |
| `PLATFORM_CANCELLED` | 其某笔扣款出现争议、拒付或欺诈信号后，Waffo 自动取消了它 | `ACTIVE` |
| `EXPIRED` | 到达了最后一期 | `ACTIVE` |

按您的集成大概需要的方式分组：

| 组 | 状态 |
| - | - |
| 尚未激活 | `IN_PROGRESS`、`AUTHORIZATION_REQUIRED` |
| 运行中 | `ACTIVE` |
| 终态 | `CLOSE`、`MERCHANT_CANCELLED`、`USER_CANCELLED`、`CHANNEL_CANCELLED`、`PLATFORM_CANCELLED`、`EXPIRED` |
| 已取消（终态的子集） | `MERCHANT_CANCELLED`、`USER_CANCELLED`、`CHANNEL_CANCELLED`、`PLATFORM_CANCELLED` |

把整个终态组当作"不会再开启新的计费期"，并按组而不是按单个值做分支——这样一个您没有映射到的状态就不会悄悄地看起来像一个活跃订阅。

## `PLATFORM_CANCELLED` 是什么？

这是当 Waffo **主动**取消订阅时设置的终态，原因是该订阅的某笔扣款产生了争议、拒付或欺诈信号。
它和其他任何取消一样通过订阅状态通知到达您这里，通知中带有取消原因，标明是哪种信号触发的。

对您的集成重要的几点：

* 它只能从 `ACTIVE` 到达。
* 自动取消是**按商户、按信号类型可配置的**，且未配置时默认关闭——所以您可能永远见不到这个值，
  它的缺席并不意味着争议被忽视了。
* 因为是 Waffo 而不是您发起的，在您处理该通知之前，您自己的记录会显示订阅仍是活跃的。请把 Webhook Webhook 通知当作事实来源。

争议和拒付处理本身见
[拒付 FAQ](/docs/zh/support/chargeback-faq)。

## 创建返回了 `AUTHORIZATION_REQUIRED`，我该怎么做？

用户仍需授权这个周期性扣款授权。创建响应带有 `subscriptionAction`，其中包含要把用户引导去的 URL（`webUrl`）。
把用户重定向到那里；授权成功后订阅进入 `ACTIVE`。

`subscriptionAction` 只在订阅处于 `IN_PROGRESS` 或 `AUTHORIZATION_REQUIRED` 时才有值。
订阅一旦激活或进入终态就不再返回，这是预期行为而非错误。

授权链接有时效。创建请求上的 `orderExpiredAt` 设置该截止时间；如果省略，则应用平台默认值，而且它**很短**——
是分钟级而不是小时级——所以请立即把用户引导到链接。授权在窗口期内始终未完成的订阅会进入 `CLOSE`。

## 如果我用同一个请求 ID 再次调用创建会怎样？

创建按 **`subscriptionRequest`**——您生成的请求 ID——幂等，而不是按 `merchantSubscriptionId`。
在同一 MID 下用相同的 `subscriptionRequest` 重复调用不会创建第二个订阅。

返回的内容取决于已存在的订阅走到了哪一步：

| 现有状态 | 重复创建的行为 |
| - | - |
| `AUTHORIZATION_REQUIRED` | 返回该订阅及其**已存储**的授权链接。不会生成新链接。 |
| `IN_PROGRESS` | Waffo 重新驱动待处理的支付步骤，所以这次调用**可能**返回一个新签发的链接。 |
| 任何其他状态 | 返回现有订阅。不包含链接。 |

重复调用还会做一致性校验：如果 `amount` 或 `currency` 与原请求不同，该调用会被作为重复请求拒绝，而不是被悄悄接受。
所以请用**完全相同**的载荷重试，且不要把一个 `subscriptionRequest` 用于两个不同的订阅。

如果用户放弃了授权而您需要给他们一个新链接，请在订阅仍处于激活前状态时重试创建，并使用返回的 `subscriptionAction`；
不要缓存链接并假定它一直有效。

## 我如何查询订阅，以及如何获取每期的扣款？

调用 `/api/v1/subscription/inquiry`，传 **`subscriptionRequest` 或 `subscriptionId`**——至少需要一个。两个都传也可以。

每期扣款默认**不**返回。把 `paymentDetails` 设为 `1` 才能获取（默认值 `0` 不返回；任何其他值会被拒绝）：

| `paymentDetails` | 响应 |
| - | - |
| `0`（默认） | 仅订阅 |
| `1` | 订阅加一个 `paymentDetails` 列表，每个计费期一条 |

每个 `paymentDetails` 条目带有 `acquiringOrderId`、`orderCurrency`、
`orderAmount`、`orderStatus`、`orderUpdatedAt` 和 `period`，在有额外（席位加购）扣款的期上还有一个
`topupDetails` 列表——这些行如何编号见
[跨币种订阅与 Portal FAQ](/docs/zh/support/subscription-cross-currency-and-portal-faq)。

反过来——从一笔扣款找到它的订阅——订单查询响应带有一个 `subscriptionInfo` 块，包含 `subscriptionId`、`period`、
`subscriptionRequest` 和一个 `subscriptionEvent`，告诉您这笔扣款是周期扣款还是支付方式添加。一次性订单没有 `subscriptionInfo`。

订阅响应还会报告 `productInfo.currentPeriod` 和已存储的 `productInfo.scheduledAmounts`，这是您在排程金额变更之前要读取的内容。

## 一个用户可以有多少个订阅？

任意数量。在一个 MID 下，同一个 `merchantUserId` 可以持有您创建的任意多个订阅——没有按用户的上限，
创建时也不对 `merchantUserId` 做唯一性检查。

这是多产品、多席位业务的常态：一个用户、多个并行订阅，每个有自己的 `subscriptionId` 和自己的生命周期。
因此，永远不要把 `merchantUserId` 当作"该用户的订阅"的键——请以 `subscriptionId` 或您自己的 `merchantSubscriptionId` 为键。

## 订阅可以在未来某个日期开始吗？

可以——创建时传 `startTime`。校验规则：

| 规则 | 行为 |
| - | - |
| 省略 `startTime` | 订阅立即开始（沿用原有行为，不变） |
| `startTime` 严格在未来 | 接受 |
| `startTime` 在过去，或等于当前时间 | 拒绝——无效的开始时间 |
| `startTime` 超过 **365 天**以后 | 拒绝——开始时间过远 |

**不支持回溯。** 无法创建一个表现得像更早就已开始的订阅，也无法让第一期落在过去。
如果您需要对已经过去的周期收费，请作为单独的一次性订单收取。

延后开始时还有两个约束：

* 开始日期必须落在将要使用的协议的有效期窗口内，所以即使在 365 天以内，过远的开始日期也可能因此被拒绝。
* 变更套餐时，`startTime` 不得早于原订阅的下一次计划扣款。

从创建到开始日期之间您能做和不能做什么，见
[订阅生命周期与支付方式管理](/docs/zh/support/subscription-lifecycle-management)。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.