> ## 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.

# 订阅集成

> 订阅（周期扣款）集成主线：从选择订阅模式到验收上线，按顺序走完即可完成接入。

本页介绍使用 Waffo Subscription 的订阅（周期扣款）集成主线。选择此方式后，按顺序走完即可完成接入。每一节只讲这一步的决策与要点，字段细节和枚举取值链接到对应参考页，不在此重复。

一次性支付的接入路线见[纯 API 集成概览](/docs/zh/developer-docs/integration/api/overview)。

## 选择订阅接入方式

Waffo 支持两种订阅接入方式。先根据谁负责管理订阅选择路径：

| 接入方式 | 谁管理订阅 | 扣款方式 | 下一步 |
| - | - | - | - |
| Waffo Subscription | Waffo | Waffo 根据支付方式能力处理续费 | 继续阅读本页 |
| 商户自管订阅 | 商户 | 商户保存 Token，并按自己的计划发起 MIT | 前往 [Waffo 收银台绑卡](/docs/zh/developer-docs/integration/tokenization/checkout-card-binding)或[商户侧绑卡](/docs/zh/developer-docs/integration/tokenization/overview) |

Waffo Subscription 为商户提供统一的接入方式。具体续费可能由 Waffo 或支付渠道执行。不同支付方式的少量能力差异见[订阅支付方式对比](/docs/zh/developer-docs/tools-and-references/references/subscription-payment-methods)。

商户自管订阅不使用 `/api/v1/subscription/*`。你的系统负责维护扣款周期、订阅状态、失败重试、升降级和取消；每期使用绑卡得到的 Token，通过 `ONE_TIME_PAYMENT` 发起 MIT。绑卡文档已经包含 Token 生命周期、CIT 验证、MIT 请求和支付结果确认，本页不再重复。

选择 Waffo Subscription 后，按下面的主线完成接入：

```mermaid theme={null}
flowchart LR
    A[选择订阅模式] --> B[配置周期与试用期]
    B --> C[创建订阅并处理授权]
    C --> D[监听通知]
    D --> E[处理续费与重试]
    E --> F[沙盒验证与验收]
```

## 集成前先确认三件事

| 确认项 | 在哪里确认 |
| - | - |
| 你要用的支付方式是否支持订阅，以及支持到什么程度 | [订阅支付方式对比](/docs/zh/developer-docs/tools-and-references/references/subscription-payment-methods) |
| 你的业务属于支付优先还是服务优先 | 本页[选择订阅模式](#选择订阅模式) |
| 你的计费周期能否用 `periodType` + `periodInterval` 表达 | 本页[配置计费周期与试用期](#配置计费周期与试用期) |

<Note>
  订阅是否可用、以及各支付方式的具体能力，最终以商户合约配置和 [`POST /api/v1/paymethodconfig/inquiry`](/docs/api-reference/pay-method-config-inquiry/pay-method-config-inquiry) 返回结果为准。
</Note>

## 选择订阅模式

Waffo 提供两种订阅模式，核心差别在**续费失败后的服务与计费处理**。选错会导致权益开关与账单节奏和你的预期不一致，所以要在接入前定下来。

| | 支付优先 | 服务优先 |
| - | - | - |
| 当期扣款失败时 | 暂停用户权益 | 继续提供服务 |
| 后续周期 | 不再发起扣款 | 按原计划继续发起 |
| 重试成功后的下期起算点 | 从重试成功时间重新起算 | 沿用原计划计费基准 |
| 多期账单并存 | 不允许 | 允许 |

用两个问题判断你属于哪一种：

1. 假设第二期原定 7 月 1 日扣费，一直失败到 7 月 3 日才成功。中间这两天，你是暂停用户权益（支付优先），还是继续提供服务（服务优先）？
2. 如果某一期续费彻底失败，后续周期你还要不要继续扣费？停止是支付优先，继续是服务优先。

<Info>
  请在接入前选择支付优先或服务优先，并将选择告知 Waffo。需要确认或调整订阅模式时，联系 Waffo 技术支持。
</Info>

PIX 只支持服务优先，其余支付方式两种都支持，见[订阅支付方式对比](/docs/zh/developer-docs/tools-and-references/references/subscription-payment-methods)。

## 配置计费周期与试用期

周期由 `productInfo.periodType` 与 `productInfo.periodInterval` 组合表达。

| periodType | periodInterval 取值范围 |
| - | - |
| `DAILY` | 1–365 |
| `WEEKLY` | 1–4 |
| `MONTHLY` | 大于等于 1，无上限 |

<Warning>
  **没有 `YEARLY` 类型。** 年度订阅用 `MONTHLY` + `periodInterval: "12"` 表达；同理季度是 `3`、半年是 `6`、两年是 `24`。
</Warning>

其余周期相关字段：

* `numberOfPeriod` — 总期数。留空表示无限期订阅。
* `trialPeriodAmount` — 试用期每期金额，须大于等于 0 且小于正式期金额。留空表示无试用期。设为 `0` 即免费试用。
* `numberOfTrialPeriod` — 试用期期数。
* `trialPeriodType` / `trialPeriodInterval` — 试用期的周期类型与间隔。可与正式期不同（例如按周试用、按月正式）；留空则沿用正式期的取值。PIX 要求试用期频次与正式期一致。
* `scheduledAmounts` — 每期预设金额列表，元素为 `{period, amount}`。当各期金额不同时使用，例如首期优惠、阶梯涨价。不需要逐期不同金额时不用传。该字段仅适用于支持此能力的 Waffo 托管订阅；渠道托管订阅（如 PIX、DANA）不支持。

完整字段定义见[创建订阅](/docs/api-reference/subscription-create/create-subscription)。

## 创建订阅与处理授权

调用 [`POST /api/v1/subscription/create`](/docs/api-reference/subscription-create/create-subscription) 创建订阅。除周期参数外，这几个字段最容易出问题：

* `subscriptionRequest` — 订阅幂等键，由你生成。重试创建请求时必须复用同一个值，见[幂等性](/docs/zh/developer-docs/core-concepts/idempotency)。
* `currency` 和 `amount` — 订阅用的是 `currency` 与 `amount`，**不是**一次性支付的 `orderCurrency` 与 `orderAmount`。用错字段名是最常见的接入错误。
* `paymentInfo.payMethodType` — 可选。传入时按支付方式类型过滤候选集；省略时不按类型过滤。仅当 `paymentInfo.payMethodName` 也省略时，Waffo 才根据商户合约展示所有符合条件的支付方式。
* `userInfo.userEmail` — 必填。没有真实邮箱时，传按用户 ID 构造的唯一兜底邮箱，不要用占位值，也不要多个用户共用一个邮箱。
* `subscriptionManagementUrl` — 必填，且必须是有鉴权的页面（不能是公开 URL）。建议传 Web 地址，不要传 deeplink，因为电脑端打不开。你可以用自己的订阅管理页，也可以包装 [`POST /api/v1/subscription/manage`](/docs/api-reference/subscription-manage/subscription-manage) 返回的 URL。
* `notifyUrl` — Webhook 回调地址，见本页[监听通知](#监听通知)。

### 处理授权跳转

响应中的 `subscriptionStatus` 为 `AUTHORIZATION_REQUIRED` 时，表示需要用户完成授权，此时必须把用户重定向到授权页面。授权地址在 `subscriptionAction` 字段里，它是一个 **JSON 字符串**，需要先解析再取其中的 `webUrl`：

```json theme={null}
{
  "code": "0",
  "msg": "Success",
  "data": {
    "subscriptionRequest": "sub_a1b2c3d4e5f6a1b2c3d4e5f6",
    "subscriptionId": "SUB20260805000001",
    "subscriptionStatus": "AUTHORIZATION_REQUIRED",
    "subscriptionAction": "{\"webUrl\":\"https://cashier.waffo.com/subscribe?token=xxx\"}"
  }
}
```

<Warning>
  不要把 `subscriptionAction` 当对象直接取属性——它是字符串，必须先 JSON 解析。同样的处理方式适用于查询订阅、订阅升降级和升降级查询的响应。
</Warning>

## 订阅状态与商户应对

订阅有 8 个状态。各状态下你该做什么，见[支付生命周期](/docs/zh/developer-docs/core-concepts/payment-lifecycle#订阅状态)；各状态是否为终态，见[数据参考](/docs/zh/developer-docs/tools-and-references/references/data-reference#订阅状态)。

<Warning>
  不要仅凭创建接口的同步响应就认定订阅最终状态。订阅激活、取消、关闭都通过 Webhook 通知，或用 [`POST /api/v1/subscription/inquiry`](/docs/api-reference/subscription-inquiry/subscription-inquiry) 主动查询确认。
</Warning>

## 监听通知

订阅涉及三类通知，按你关心的粒度选择监听。三者的触发时机、适用场景以及推荐组合，见 [Webhook 事件类型](/docs/zh/developer-docs/webhook/event-types#订阅通知选择指南)。

关键约束：`SUBSCRIPTION_STATUS_NOTIFICATION` 和 `SUBSCRIPTION_PERIOD_CHANGED_NOTIFICATION` 都是异步分发的，**到达顺序不保证**，不要用回调到达顺序驱动业务状态机。正确做法（幂等去重、收到任一回调后先查询最终状态）见 [Webhook 处理最佳实践](/docs/zh/developer-docs/webhook/best-practices)。

## 续费失败与重试

续费扣款失败后的重试由 Waffo 或支付渠道负责，取决于续费托管方式。

**Waffo 托管订阅**由 Waffo 自动重试当期扣款，你不需要自己实现重试：

* 重试策略按你的计费周期分档配置，每档包含**最大重试次数**与**重试间隔**。
* 重试间隔以**天**为单位。不存在小时级的重试间隔配置。
* 未客制化时，默认在当期扣款首次失败后每 1 天重试一次，最多重试 5 次。若需客制化重试次数或间隔，请联系 Waffo 客户经理或技术支持；该策略不能通过接口传参修改。
* 重试成功后的下期起算点，以及重试次数用尽后是否继续发起后续周期扣款，由你的[订阅模式](#选择订阅模式)决定。Waffo 托管续期时间的完整计算规则见[订阅支付方式对比](/docs/zh/developer-docs/tools-and-references/references/subscription-payment-methods#waffo-托管续期时间计算)。

**渠道托管订阅**由支付渠道按自身规则重试。PIX、DANA 使用钱包侧重试规则，不支持商户自定义；具体能力见[订阅支付方式对比](/docs/zh/developer-docs/tools-and-references/references/subscription-payment-methods)。

<Tip>
  对于 Waffo 托管订阅，通知粒度上要注意区分：重试期间**每一次**扣款失败都会发送 `PAYMENT_NOTIFICATION`；如果你只关心每期的最终结果、不关心中间重试过程，监听 `SUBSCRIPTION_PERIOD_CHANGED_NOTIFICATION` 即可。
</Tip>

## 升降级、修改与取消

这三件事用的是不同接口，不要混用：

| 你要做的事 | 用哪个接口 | 要点 |
| - | - | - |
| 换套餐（升级 / 降级） | [`POST /api/v1/subscription/change`](/docs/api-reference/subscription-change/subscription-change) | 需要新的 `subscriptionRequest`、原订阅的 `originSubscriptionRequest`、以及 `remainingAmount`（原订阅剩余价值抵扣到新订阅的金额）和 `productInfoList`（新套餐）。可用 `startTime` 指定生效时间，留空立即生效；可传 `promotionInfo` 应用促销。可能返回 `AUTHORIZATION_REQUIRED`，按[处理授权跳转](#处理授权跳转)处理。完成后发送 `SUBSCRIPTION_CHANGE_NOTIFICATION`。 |
| 调整金额或补差 | [`POST /api/v1/subscription/update`](/docs/api-reference/subscription-update/subscription-update) | 用 `subscriptionRequest` 或 `subscriptionId` 定位订阅。普通调价通过 `amount`、`productInfo.trialPeriodAmount` 或 `productInfo.scheduledAmounts` 完成；传 `topupInfo` 时会为当期创建补差单，并在补差支付成功后应用后续周期的新金额。 |
| 商户侧取消 | [`POST /api/v1/subscription/cancel`](/docs/api-reference/subscription-cancel/subscription-cancel) | 传 `subscriptionId`、`merchantId`、`requestedAt`。所有支付方式都支持商户侧取消。 |
| 给用户一个管理入口 | [`POST /api/v1/subscription/manage`](/docs/api-reference/subscription-manage/subscription-manage) | 返回订阅管理页 URL。订阅仍在处理中或已失败时该 URL 不可用，返回 `A0028`。 |

### 常见金额调整场景

`update` 只支持 `ACTIVE` 状态的 Waffo 托管订阅。每次请求至少要传 `amount`、`productInfo.trialPeriodAmount` 或 `productInfo.scheduledAmounts` 中的一项；即使同时传 `topupInfo` 创建补差单，也要给出补差成功后的目标金额。直接调价影响未来扣费；当前期已经生成，不能直接改金额。

| 场景 | 怎么传 | 生效方式 |
| - | - | - |
| 后续周期统一改成同一金额 | 传 `amount` | 从下一期起使用新金额。某期同时命中 `scheduledAmounts` 时，以该期预设金额为准。 |
| 临时调整一个或多个未来期 | 传 `productInfo.scheduledAmounts`，例如只为第 4 期设置优惠价 | 每个 `period` 都必须大于当前期号，最多 10 条；未列出的期继续使用 `amount`。本次列表会替换原列表，需要保留的其他未来期特殊金额也要一并传入。 |
| 调整尚未结束的试用期金额 | 传 `productInfo.trialPeriodAmount` | 仅试用期尚未结束时可用，从下一期生效。 |
| 当期增购并同步上调后续金额 | 传后续周期的新 `amount`，同时传 `topupInfo` | Waffo 先为当期差价创建即时补差单；补差支付成功后，新 `amount` 才从下一期起生效。补差关闭或支付失败时不修改后续金额。 |

当期增购并补差的请求示例：

```json theme={null}
{
  "subscriptionId": "SUB20260920000001",
  "amount": "130.00",
  "topupInfo": {
    "topupRequest": "seat-topup-001",
    "topupAmount": "15.00",
    "description": "本期新增 3 个席位的差价"
  }
}
```

`topupAmount` 是由你计算的当期补差金额，必须大于 0；`amount` 是补差成功后用于后续周期的新总金额。`topupRequest` 是补差单幂等键，重试同一请求时必须复用。响应中 `topupInfo.topupStatus` 为 `AUTHORIZATION_REQUIRED` 时，先将 `topupInfo.topupAction` 作为 JSON 字符串解析，再重定向到其中的 `webUrl`。同一订阅同时只能有一笔处理中的补差单。

部分支付方式的用户可以在支付方式侧自助解约（例如 Apple Wallet、Google Pay、PayPay App），解约状态会回传 Waffo 并触发 `CHANNEL_CANCELLED` 或 `USER_CANCELLED`。哪些支付方式支持用户侧取消，见[订阅支付方式对比](/docs/zh/developer-docs/tools-and-references/references/subscription-payment-methods)。

## 沙盒验证与验收

<Steps>
  <Step title="首期支付">
    与一次性支付一样，在收银台页面操作。
  </Step>

  <Step title="续费模拟">
    调用 `POST /api/v1/subscription/manage` 拿管理页 URL，打开后用页面上的「模拟下期支付成功」「模拟下期支付失败」按钮逐期模拟。操作步骤见[沙盒与测试](/docs/zh/developer-docs/getting-started/sandbox)和[沙盒模拟器](/docs/zh/developer-docs/tools-and-references/developer-tools/sandbox-simulator)。
  </Step>

  <Step title="验收">
    按[集成验收标准](/docs/zh/developer-docs/tools-and-references/references/acceptance-criteria)执行订阅验收用例，填写订阅支付验收用例模板后提交给 Waffo 技术对接群。
  </Step>
</Steps>

上线前至少确认：首期成功与失败两条链路、订阅状态通知与订阅支付通知的验签、按 `subscriptionRequest` 与期数匹配到对应周期订单、以及 Unknown 状态下不自行关闭订阅。


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