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

# 订阅

> 快速了解 HaiPay 订阅支付的接入方式、关键业务规则、上线要求和核心 API 入口，帮助你更快完成信用卡订阅集成。

HaiPay 订阅支付适用于会员、周期性服务和自动续费场景。这个页面不再堆叠完整参数大表，而是先帮助你理解接入模式、业务约束和实施顺序，再跳转到对应的 API Reference 查看详细字段与调试能力。

<Columns cols={3}>
  <Card title="快速开始" icon="rocket" href="#quick-start">
    先看接入步骤、必备能力和推荐阅读路径。
  </Card>

  <Card title="集成模式" icon="layer-group" href="#integration-modes">
    对比 Apple Pay / Google Pay、信用卡组件和组合模式。
  </Card>

  <Card title="核心 API" icon="list-check" href="#core-apis">
    直接进入订阅申请、查询、取消和退款接口。
  </Card>
</Columns>

<Warning title="上线前必须满足的产品与服务要求">
  * 提供明确的订阅入口、扣款周期说明和退订入口。
  * 在订阅成功以及每次扣款前后，通过邮件或其他站外方式通知用户。
  * 提供可确认的订阅协议，并在用户明确授权后再发起订阅。
  * 保持客服可用，至少覆盖主要运营地区的服务时间。
</Warning>

<Tip title="信用卡争议与费用提示">
  * 信用卡交易在扣款后 180 天内，消费者可能发起争议。
  * 每笔争议会产生 20 美元争议处理费。
  * 如果选择抗辩，将额外产生 20 美元抗辩费。
  * 抗辩成功时，HaiPay 会退还抗辩费；原始争议处理费默认不退。
</Tip>

<Danger title="PCI DSS 合规要求">
  PCI DSS 严格禁止在 `WebView` 或 `iframe` 中嵌入信用卡输入页。请始终在受信任的浏览器环境中完成卡信息采集与支付授权，避免合规风险和敏感数据泄露。
</Danger>

<a id="quick-start" />

## 快速开始

<Steps>
  <Step title="确认业务模式与前置条件">
    先阅读 [接口说明](/docs/zh/guide/api_description_guide) 和 [订阅流程](/docs/zh/api/version2/subscription-process)，确认你的场景是自动续费而不是一次性支付。
  </Step>

  <Step title="选择接入模式">
    订阅支付提供两种接入模式：**PayUrl 跳转模式**（服务端获取授权链接，跳转到 HaiPay 托管页面完成支付）和 **JS CDN 嵌入模式**（使用客户端令牌在你自己的页面中嵌入支付组件）。根据你的收银台设计和前端开发能力选择合适的方式。
  </Step>

  <Step title="接入 API、通知与取消能力">
    完成订阅申请后，需要能查询订阅状态、处理扣款回调，并在用户侧提供取消订阅入口。详细字段与调试入口放在本页下方的 API Reference 卡片中。
  </Step>
</Steps>

<a id="integration-modes" />

## 集成模式

订阅支付提供两种接入模式，它们的核心区别在于支付页面由谁承载：

| 对比项    | PayUrl 跳转模式        | JS 嵌入模式              |
| :----- | :----------------- | :------------------- |
| 支付页面   | 跳转到 HaiPay 托管的支付页面 | 在商户自己的页面中嵌入支付组件      |
| 前端工作量  | 几乎为零，只需处理跳转        | 需要引入 JS 脚本并监听事件      |
| UI 控制力 | 低，页面样式由 HaiPay 控制  | 高，可自定义主题、布局和交互       |
| 关键返回字段 | 使用响应中的 `payUrl`    | 使用响应中的 `clientToken` |
| 适用场景   | 快速接入、对支付页面无定制需求    | 自建收银台、需要沉浸式支付体验      |

<Tabs sync={false}>
  <Tab title="PayUrl 跳转模式" icon="arrow-up-right-from-square">
    最简单的接入方式。商户服务端调用订阅申请接口后，使用返回的 `payUrl` 将用户跳转到 HaiPay 托管的支付页面，用户在该页面完成授权和首笔支付。

    **接入流程**

    <Steps>
      <Step title="服务端调用订阅申请接口">
        商户服务端向 HaiPay 发起 [订阅申请](/docs/zh/V20260701/api-reference/subscription/api/subscriptionApply)，在请求中传入订阅金额、周期、回调地址以及可选的 `cancelUrl` 等必要参数。
      </Step>

      <Step title="获取 payUrl 并跳转">
        接口返回中包含 `payUrl` 字段，商户将用户浏览器重定向到该地址。
      </Step>

      <Step title="用户完成支付">
        用户在 HaiPay 托管页面完成信用卡信息填写和支付授权。支付完成后，HaiPay 会将用户重定向回商户的 `callBackUrl`；如果用户在支付页面取消支付且传递了 `cancelUrl`，则可点击返回到该地址；同时向 `notifyUrl` 发送异步通知。
      </Step>
    </Steps>

    **示例代码（服务端获取 payUrl 后的前端跳转）**

    ```javascript theme={null}
    // 假设你的服务端已调用订阅申请接口，并将 payUrl 返回给前端
    const payUrl = response.data.payUrl;

    // 在当前窗口跳转
    window.location.href = payUrl;

    // 或者在新窗口打开
    // window.open(payUrl, '_blank');
    ```

    **适用建议**

    * 希望最快速度完成订阅接入，前端改动最少。
    * 对支付页面的 UI 没有强定制需求。
    * 移动端 H5 场景下，跳转体验通常也能满足需求。
  </Tab>

  <Tab title="JS 嵌入模式" icon="code">
    通过引入 HaiPay JS 脚本，将支付组件直接嵌入商户页面。商户服务端调用订阅申请接口获取 `clientToken`，前端使用该令牌初始化组件，用户无需跳转即可在当前页面完成订阅支付。

    JS 提供以下组件形态，请根据收银台设计需求选择：

    <Tabs sync={false}>
      <Tab title="自定义组合模式" icon="table-cells-large">
        与组合模式类似，但额外支持通过 `mode` 参数切换普通支付与订阅支付，适合需要统一管理多种支付场景的商户。

        **引入脚本**

        `https://cashier.haipay.top/js/composite-component_1.0.0.min.js`

        **配置参数**

        | 参数                    | 必填 | 说明                                                   |
        | :-------------------- | :- | :--------------------------------------------------- |
        | `sandbox`             | 是  | `true` 为测试环境，`false` 为生产环境                           |
        | `clientToken`         | 是  | 订阅申请接口返回的客户端令牌                                       |
        | `mode`                | 是  | 支付场景：`payment`（普通支付）或 `subscription`（订阅支付）           |
        | `showPayButton`       | 否  | 是否显示信用卡提交按钮，默认 `false`（不显示）                          |
        | `themeMode`           | 否  | 主题模式，可选 `light` / `dark`，默认 `light`                  |
        | `paymentMethodConfig` | 否  | 支付方式组合，默认 `["GOOGLE_PAY","APPLE_PAY","CREDIT_CARD"]` |
        | `applePayButtonType`  | 否  | Apple Pay 按钮文案，可选 `buy`、`subscribe` 等                |
        | `googlePayButtonType` | 否  | Google Pay 按钮文案，可选 `buy`、`subscribe` 等               |
        | `cardPosition`        | 否  | 信用卡放置位置，默认 `bottom`，可选：`bottom`、`top`                |

        **事件回调**

        * `ready` — 组件渲染完成，可进行交互
        * `error` — 初始化失败或支付过程出错
        * `confirm` — 表单提交结果（成功/失败）
        * `paymentStatus` — 支付结果通知，失败会返回错误信息（若未监听，组件将自动处理支付结果展示）
        * `paymentMethodType` — 当前选中的支付方式

        **适用建议**

        * 需要在同一套代码中同时支持普通支付和订阅支付时，推荐此模式。
        * 如仅开放部分支付方式，只需传入所需的组合即可。

        ```javascript theme={null}
        <script src="https://cashier.haipay.top/js/composite-component_1.0.0.min.js"></script>

        <div>
          <div id="paymentId"></div>
        </div>

        const elements = haiPay.create({
          mode: 'payment',
          sandbox: true,
          clientToken: 'your-client-token',
          themeMode: 'light',
          paymentMethodConfig: ['GOOGLE_PAY', 'APPLE_PAY', 'CREDIT_CARD']
        });

        elements.mount('#paymentId');

        elements.on('ready', (event) => {
          console.log('rendered successfully:', event);
        });

        elements.on('error', (event) => {
          console.error('Payment error:', event);
        });

        elements.on('paymentMethodType', (event) => {
          console.log('payment method type', event);
        });

        elements.submitCardPayment();

        elements.on('confirm', (event) => {
          console.log('confirm:', event);
        });

        elements.on('confirm', (event) => {
          console.log('confirm:', event);
        });

        elements.on('paymentStatus', (event, message) => {
          console.log('payment status:', event, message);
        });

        ```
      </Tab>

      <Tab title="Apple Pay / Google Pay（即将废弃）" icon="wallet">
        <Warning>该组件即将废弃，推荐使用「自定义组合模式」替代。</Warning>

        适合已具备钱包支付入口、希望快速拉起 Apple Pay / Google Pay 授权的场景。

        **引入脚本**

        `https://cashier.haipay.top/js/applePayGooglePaySubscribe_1.0.0.min.js`

        **配置参数**

        | 参数             | 必填 | 说明                            |
        | :------------- | :- | :---------------------------- |
        | `sandbox`      | 是  | `true` 为测试环境，`false` 为生产环境    |
        | `clientToken`  | 是  | 订阅申请接口返回的客户端令牌                |
        | `buttonType`   | 否  | 按钮文案类型，可选 `buy`、`subscribe` 等 |
        | `buttonTheme`  | 否  | 按钮主题色，可选 `black`、`white`      |
        | `buttonHeight` | 否  | 按钮高度（px），默认 `50`              |

        **事件回调**

        * `ready` — 按钮渲染完成，可进行交互
        * `error` — 初始化失败或支付过程出错
        * `paymentStatus` — 支付结果通知（若未监听，组件将自动处理支付结果展示）
        * `paymentMethodType` — 当前选中的支付方式

        ```javascript theme={null}
        <script src="https://cashier.haipay.top/js/applePayGooglePaySubscribe_1.0.0.min.js"></script>

        <div>
          <div id="applePayGooglePayId"></div>
        </div>

        const elements = applePayGooglePaySubscribe.create('applePayGooglePaySubscribe', {
          sandbox: true,
          clientToken: 'your-client-token',
          buttonType: 'subscribe',
          buttonTheme: 'black',
          buttonHeight: 50
        });

        elements.mount('#applePayGooglePayId');

        elements.on('ready', (data) => {
          console.log('Button rendered successfully:', data);
        });

        elements.on('error', (data) => {
          console.error('Payment error:', data);
        });

        elements.on('paymentMethodType', (event) => {
          console.log('payment method type', event);
        });

        elements.on('paymentStatus', (data) => {
          console.log('payment status:', data);
        });
        ```
      </Tab>

      <Tab title="Credit Card（即将废弃）" icon="credit-card">
        <Warning>该组件即将废弃，推荐使用「自定义组合模式」替代。</Warning>

        适合需要直接展示信用卡订阅表单、并由商户自行控制提交时机的场景。

        **引入脚本**

        `https://cashier.haipay.top/js/creditCardSubscription_1.0.0.min.js`

        **配置参数**

        | 参数            | 必填 | 说明                         |
        | :------------ | :- | :------------------------- |
        | `sandbox`     | 是  | `true` 为测试环境，`false` 为生产环境 |
        | `clientToken` | 是  | 订阅申请接口返回的客户端令牌             |
        | `cardTheme`   | 否  | 表单主题色，默认 `white`           |

        **事件回调**

        * `ready` — 信用卡表单渲染完成，可进行交互
        * `confirm` — 表单提交结果（成功/失败）
        * `paymentStatus` — 支付结果通知（若未监听，组件将自动处理支付结果展示）

        ```javascript theme={null}
        <script src="https://cashier.haipay.top/js/creditCardSubscription_1.0.0.min.js"></script>

        <div>
          <div id="creditCardId"></div>
        </div>

        const elements = creditCardSubscription.create('card', {
          sandbox: true,
          clientToken: 'your-client-token',
          cardTheme: 'white'
        });

        elements.mount('#creditCardId');

        elements.on('ready', (data) => {
          console.log('Card rendered successfully:', data);
        });

        elements.on('error', (data) => {
          console.error('Payment error:', data);
        });

        elements.submit();

        elements.on('confirm', (data) => {
          console.log('confirm:', data);
        });

        elements.on('paymentStatus', (data) => {
          console.log('payment status:', data);
        });
        ```
      </Tab>
    </Tabs>
  </Tab>
</Tabs>

<a id="core-apis" />

## 核心 API

详细请求字段、响应结构和在线调试，请直接进入 API Reference：

<Columns cols={2}>
  <Card title="订阅申请" icon="circle-plus" href="/docs/zh/V20260701/api-reference/subscription/api/subscriptionApply">
    创建订阅订单，拉起用户授权并完成首笔订阅支付。
  </Card>

  <Card title="查询订阅" icon="magnifying-glass" href="/docs/zh/api-reference/subscription/api/subscriptionQuery">
    查询订阅状态、平台订阅号和扣款记录。
  </Card>

  <Card title="取消订阅" icon="ban" href="/docs/zh/api-reference/subscription/api/subscriptionCancel">
    由商户主动发起取消，停止后续周期扣款。
  </Card>
</Columns>

## 异步通知

当订阅状态变更或周期扣款完成时，HaiPay 会向商户在订阅申请时提供的 `notifyUrl` 推送异步通知。

<Tip title="接收建议">
  为保障接口的扩展性和兼容性，请遵循以下回调参数接收方式：

  1. **禁止**声明具体 POJO 对象接收回调参数。
  2. **必须**使用通用数据结构（如 JSONObject、Map 等）进行参数解析。
  3. 此设计可确保后续扩展回调字段时，不会影响验签逻辑。
</Tip>

收到通知后，请在响应体中返回 **SUCCESS**（大写）。

<Tabs sync={false}>
  <Tab title="订阅状态通知" icon="bell">
    当订阅状态发生变更时触发（如订阅成功、订阅取消、订阅失败等）。

    | 参数名                      | 类型      | 说明                                               |
    | :----------------------- | :------ | :----------------------------------------------- |
    | `type`                   | String  | 通知类型，固定为 `SUBSCRIPTION`                          |
    | `appId`                  | Long    | 业务 ID                                            |
    | `currency`               | String  | 币种                                               |
    | `subscriptionOrderId`    | String  | 商户订阅号                                            |
    | `subscriptionNo`         | String  | 平台订阅号                                            |
    | `amount`                 | String  | 订阅金额                                             |
    | `status`                 | Integer | 订阅状态（1: 处理中, 2: 订阅成功, 3: 订阅失败, 4: 订阅取消, 5: 订阅完成） |
    | `recurringInterval`      | String  | 循环周期类型（D/W/M/Y）                                  |
    | `recurringIntervalCount` | Integer | 循环周期间隔                                           |
    | `subject`                | String  | 订阅标题                                             |
    | `sign`                   | String  | 签名（需要使用币种对应的业务密钥信息进行验签）                          |
  </Tab>

  <Tab title="订阅扣款通知" icon="money-bill-wave">
    当每期周期扣款成功或失败时触发。

    | 参数名                   | 类型      | 说明                                 |
    | :-------------------- | :------ | :--------------------------------- |
    | `type`                | String  | 通知类型，固定为 `SUBSCRIPTIONS_DEDUCT`    |
    | `appId`               | Long    | 业务 ID                              |
    | `currency`            | String  | 币种                                 |
    | `subscriptionOrderId` | String  | 商户订阅号                              |
    | `subscriptionNo`      | String  | 平台订阅号                              |
    | `deductNo`            | String  | 扣款编号                               |
    | `orderNo`             | String  | 平台订单号                              |
    | `amount`              | String  | 本次扣款金额                             |
    | `status`              | Integer | 扣款状态（2: 成功, 3: 失败）                 |
    | `startTime`           | String  | 本期扣款周期开始时间，格式: yyyy-MM-dd HH:mm:ss |
    | `endTime`             | String  | 本期扣款周期结束时间，格式: yyyy-MM-dd HH:mm:ss |
    | `sign`                | String  | 签名（需要使用币种对应的业务密钥信息进行验签）            |
  </Tab>
</Tabs>

## 关键业务规则

| 项目     | 说明                                                                                                                                    |
| :----- | :------------------------------------------------------------------------------------------------------------------------------------ |
| 交易限额   | 订阅金额范围为 `0.99 - 1000 USD`                                                                                                             |
| 周期上限   | 无论周期单位是什么，最长订阅时长不能超过 3 年                                                                                                              |
| 周期单位   | Apple Pay 不支持 `W`，周订阅请改用 `D` + `7` 天                                                                                                  |
| 支付类型   | 订阅场景固定使用 `SUBSCRIPTION`                                                                                                               |
| 用户通知   | 订阅成功、扣款前、扣款后都建议向用户发送站外通知                                                                                                              |
| 退订能力   | 生产环境必须在用户侧提供清晰、可访问的取消订阅入口                                                                                                             |
| 支付方式参数 | 订阅申请接口中 `inBankCode` 和 `paymentMethods` 二选一：`inBankCode` 传入单个支付方式编码，`paymentMethods` 传入英文逗号分隔的支付方式列表。`paymentMethods` 暂时仅支持前置组件组合模式使用 |

## 测试卡号

<Warning>仅限测试环境使用</Warning>

**模拟支付成功**

使用以下测试卡号，CVC 填写任意 3 位数字，有效期填写任意未来日期：

* `4242424242424242`

**模拟支付失败**

使用以下测试卡号并填写无效信息：

* 卡号：`4000000000009995`
* 无效月份：`13`
* 无效 CVV：`99`

## 常见理解问题

<AccordionGroup>
  <Accordion title="什么时候看这个页面，什么时候看 API Reference？" defaultOpen>
    这个页面用于理解产品能力、选择接入模式和把握业务规则。进入开发联调阶段后，请直接查看 [订阅 API Reference](/docs/zh/V20260701/api-reference/subscription/api/subscriptionApply) 获取完整字段、示例报文和调试入口。
  </Accordion>

  <Accordion title="为什么订阅业务必须强调退订入口和站外通知？">
    订阅类交易更容易产生用户争议。清晰的授权、通知和退订能力可以显著降低拒付、投诉和风控问题，这也是订阅业务能否稳定上线的关键前置条件。
  </Accordion>

  <Accordion title="生产环境出现 Unsupported region: CN 怎么处理？">
    这通常表示当前生产环境不支持该地区，或当前测试方式与目标地区配置不匹配。请先确认商户配置、目标市场和支付方式组合，再联系 HaiPay 支持团队确认生产可用范围。
  </Accordion>

  <Accordion title="PayUrl 跳转和 JS CDN 嵌入应该怎么选？">
    如果你希望最快上线、前端改动最少，选 PayUrl 跳转模式即可。如果你需要自定义收银台 UI、希望用户不离开你的页面完成支付，选 JS CDN 嵌入模式。两种模式都通过同一个订阅申请接口发起，区别只在于你使用返回的 `payUrl` 还是 `clientToken`。
  </Accordion>

  <Accordion title="inBankCode 和 paymentMethods 应该传哪个？">
    两个参数二选一，不可同时传入，也不可同时为空。如果只使用一种支付方式，传 `inBankCode`；如果需要同时支持多种支付方式（如组合模式），传 `paymentMethods`。注意 `paymentMethods` 暂时仅支持前置组件组合模式使用，其他模式请继续使用 `inBankCode`。
  </Accordion>

  <Accordion title="如果只想尽快做一个现代化订阅页面，推荐哪种模式？">
    如果选择 JS CDN 嵌入模式，优先使用"组合模式"。它能在一个容器内统一呈现多种支付方式，页面更完整，用户理解成本更低，前端开发量也更可控。
  </Accordion>
</AccordionGroup>

## 相关主题

* [公共接口](/docs/zh/api/version2/CommonApi)
* [HaiPay 接口说明与公共规则](/docs/zh/guide/api_description_guide)
* [订阅支付流程图](/docs/zh/api/version2/subscription-process)
