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

# 创建托管订阅

> ### 选择订阅模式

推荐使用 `customer` 和 `plan` 嵌套参数。客户信息二选一：已有客户填写 `customer.customer_id`；新客户填写 `customer.firstName`、`customer.lastName`、`customer.emailAddress`，不要同时提供已有客户标识。

| 模式 | 提交参数 | 条件及限制 |
| --- | --- | --- |
| 手填订阅 | `plan.plan_name`，以及 `plan.recurring`、`plan.one_off` 中至少一个启用段 | 启用段的 `enabled` 设为 1；可同时启用周期和一次性段 |
| 模板订阅 | `plan.template_id` 及模板对应日期 | 模板含周期段时提供 `plan.start_date`，含一次性段时提供 `plan.payment_date`；`plan_name` 可省略继承模板 |
| By agreement | `plan.plan_name` 和 `plan.schedule` | 首条计划填写 `schedule_name`、`type`、`amount`、`start_date`；类型只允许 1、2、3、4、6 |

三种模式互斥。手填周期段启用时，须提供 `schedule_name`、`recurring_type`、`amount`、`start_date`、`day_of_debit`。一次性段启用时，须提供 `schedule_name`、`amount`、`payment_date`。日期至少为业务时区的明天。

| 周期结束条件 | 额外必填参数 |
| --- | --- |
| `recurring_type=1` / `schedule.type=1`，按成功期数 | `end_terms` |
| `recurring_type=2` / `schedule.type=2`，按累计金额 | `total_amount`，且 `amount` 不得大于 `total_amount` |
| `recurring_type=3` / `schedule.type=3`，持续至取消 | 无额外结束条件 |
| `recurring_type=4` / `schedule.type=4`，按结束日期 | `end_date`，不得早于同段的 `start_date` |
| `schedule.type=6`，一次性 | 不要求 `day_of_debit`；此类型不适用于 `recurring_type` |

季度、半年、年度频率须提供 `day_of_debit_dtstart`。自定义频率须提供 `process_type=12`、`custom_cycle_type` 和 `custom_cycle`，并与 RRULE 一致。具体范围及日期窗口见字段说明。

顶层同名替代参数仍受支持；建议新接入统一使用嵌套形式。两种形式同时出现时，同名的嵌套字段优先。

创建成功后 status=2 Inactive，订阅尚未激活。将返回的完整 activation_url 原样提供给客户打开，或设为 iframe 的 src；也可将 activation_html 用作 iframe 的 srcdoc。客户在 BillBuddy 提供的页面填写支付信息并完成授权和激活。无需自行拼接激活链接或解析凭证，Client Secret 和 access_token 仅保存在商户服务端。三种定义模式互斥：recurring/one_off 手填模式、template_id 模板模式、schedule By agreement 模式。模板模式须提供其启用部分的日期（周期 start_date、一次性 payment_date）。新客户信息与已有客户引用互斥。推荐使用 customer/plan 嵌套参数；替代参数形式见字段说明。成功响应可能不含激活链接，此时请联系 BillBuddy 核对已创建订阅后再处理；接口未提供调用方幂等编号，请勿盲目重试创建。 周期订阅及 By agreement 须在 start_date 当天之前激活；纯一次性订阅须在创建后 30 天内提交激活。页面可以打开不保证仍可提交，以激活接口结果为准。

所有示例均为格式示例，客户、订单、卡及账户数据须替换为测试环境获准使用的数据。BASE_URL 为服务根域名，不附加 /api/v2；ACCESS_TOKEN 为 /oauth/token 返回的 access_token。日期示例为 2030 年，执行时请改为满足当前日期限制的实际日期。



## OpenAPI

````yaml /openapi/billbuddy-v2-openapi.yaml post /api/v2/subscription/create
openapi: 3.0.3
info:
  title: BillBuddy API 接入文档
  version: 2.0.0
  description: >
    BillBuddy API
    接入文档，本文收录第三方服务端主动调用的认证、交易和订阅接口。请求与响应示例均使用完整但虚构的业务数据及离线生成的演示令牌，不能用于实际认证或交易。


    ## 接入步骤

    1. 向 BillBuddy 获取 Client ID、Client Secret、商户标识及业务标识。如启用 IP 白名单，请使用已获准的固定出口
    IP 调用。

    2. 通过 POST /oauth/token 获取 access_token。业务请求携带 Authorization: Bearer
    {access_token} 和 Accept: application/json。JSON 请求使用 Content-Type:
    application/json；获取令牌使用表单。

    3. 按需调用支付或订阅接口。客户通过 activation_url 打开托管激活页；Client Secret 仅可保存在商户服务端。


    ## 标识及金额

    商户身份由 OAuth 凭据确定，无需在请求中传 business_id。需要指定业务的接口使用 merchant_id；单笔交易查询和批次结果查询的
    merchant_id 可选，省略或为空时在当前商户全部业务范围内查询；交易列表使用可选的
    Mid（区分大小写），省略时查询当前商户的全部业务。业务标识必须属于凭据所属商户。响应中的标识请按各接口字段说明读取。

    除 TXT 批量上传文件及其 amount_cents/declared_total_amount_cents
    使用分外，金额使用主货币单位，最多两位小数，例如 10.50 表示 AUD 10.50。银行账号、卡号、月份、年份和邮编建议使用字符串，以保留前导零。


    ## 成功和失败

    业务 JSON 成功 code=0，常规失败 code=1。交易及订阅业务接口参数校验失败通常为 code=400、data={}，HTTP 状态仍为
    200。V2 业务接口认证失败为 code=1，plan/list 标识业务校验失败为 code=1。HTTP 429 表示请求超过频率限制。OAuth
    端点采用标准 OAuth 响应（HTTP 200/400/401/429）。

    code=0 表示接口处理成功，扣款结果须另行判断：批量 accepted 仅表示请求已受理，将异步处理；银行 Pending、预约 Pending 和
    3DS 待验证均不代表支付成功。交易、DDR 订阅和扣费计划各自使用不同的 status 枚举，请参阅对应字段说明。


    ## 日期及频率

    请求日期建议使用 YYYY-MM-DD。今天及明天均按服务业务时区 Australia/Sydney 计算（适用夏令时）；预约取消时间以
    Australia/Sydney 为准（适用夏令时），仅允许在处理日 08:15 之前取消，到达该时刻即不可取消。支付 complete_date
    与收银台 expires_at 使用 DD/MM/YYYY HH:mm:ss，其他时间格式见字段说明。

    RRULE 标准示例：Daily FREQ=DAILY;INTERVAL=1；Weekly
    FREQ=WEEKLY;INTERVAL=1;BYDAY=MO；Fortnightly
    FREQ=WEEKLY;INTERVAL=2;BYDAY=MO；Monthly
    FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15；Quarterly
    FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=15；Half Yearly
    FREQ=MONTHLY;INTERVAL=6;BYMONTHDAY=15；Yearly
    FREQ=YEARLY;INTERVAL=1;BYMONTH=2;BYMONTHDAY=15。星期缩写为
    MO/TU/WE/TH/FR/SA/SU。Weekly/Monthly 省略具体扣费日时按起算日确定；单一 BYDAY=SA 或 SU
    按周一处理，未指定具体日期且起算日在周末时也按周一处理。单值 BYMONTHDAY=29/30/31 在短月按月末处理；BYMONTHDAY=-1
    表示月底。所有规则须符合有效 RRULE 格式。

    非标准频率（如每 10 天）须同时传 process_type=12、custom_cycle_type=1、custom_cycle=10，且
    day_of_debit=FREQ=DAILY;INTERVAL=10。单位 1/2/3/4 对应
    DAILY/WEEKLY/MONTHLY/YEARLY，以天为单位时间隔最小为 5，其余单位最小为 1，INTERVAL 须等于
    custom_cycle。季度、半年及年度还须提供 day_of_debit_dtstart；完整日期须严格晚于同段
    start_date，并在其后一个计费周期内。createSchedule 的实际首次扣费日期由 DDR 起算日、扣费频率及最早明天的限制共同确定。


    ## 使用示例

    参数类型、可空、长度、枚举及数值范围见 Schema；条件必填、日期及业务限制见字段 description。请结合两者使用。curl 示例中的
    BASE_URL、ACCESS_TOKEN、CLIENT_ID 和 CLIENT_SECRET 需由调用方设置；实际测试或生产域名由 BillBuddy
    提供。示例中的客户、订单、卡号、账户及日期须替换为测试环境允许使用且满足日期限制的数据。


    ## 托管订阅接入

    调用 POST /api/v2/subscription/create 创建订阅，取得 activation_url 后原样用于浏览器跳转或
    iframe 的 src；也可将 activation_html 用作 iframe 的 srcdoc。客户在 BillBuddy
    提供的页面填写支付信息并完成授权和激活。请使用返回的完整链接，无需自行拼接地址或解析链接中的凭证。Client Secret 和
    access_token 仅保存在商户服务端，不得放入 iframe 地址或页面内容。


    周期订阅及 By agreement 须在 start_date 当天之前激活；纯一次性订阅须在创建后 30
    天内提交激活。页面可以打开不保证仍可提交，以激活接口结果为准。


    ## 示例数据约定

    示例中的商户、客户、订单、支付 Token、会话链接和 OAuth 凭据均为虚构，不能用于真实调用。银行卡使用标准测试卡号；银行账户为虚构账户；邮箱使用
    example.com 保留域名，电话号码使用澳大利亚保留的演示号码。请求中的 BASE_URL、ACCESS_TOKEN、CLIENT_ID 和
    CLIENT_SECRET 仍需替换为获准测试环境的实际值。示例中必要的空字符串、null、空数组、0 和 false
    表示实际接口状态，不能统一替换为非空值。

    支付响应的 sign 按各支付场景的签名规则，使用公开演示密钥 bb_docs_demo_signing_key_2030 离线计算。3DS
    待验证响应的 html 不参与签名计算。该密钥仅用于文档，不能用于实际签名或认证。


    ## TXT 批量交易

    使用 multipart/form-data 上传固定宽度 TXT 文件。文件金额及
    amount_cents、declared_total_amount_cents 均以分计，不含附加费；文件格式和示例文件见“上传 TXT
    批量交易文件”。上传成功仅表示已接收，请保存 batch_id 和 file_identifier，并使用批次结果接口查询逐笔状态。批次
    completed 不代表所有支付成功。查询结果须使用同一商户的 OAuth 凭据；merchant_id
    可省略，如传入则须与原上传请求的业务标识一致。响应字段含义以对应接口说明为准。


    ## 开发语言请求示例

    请求示例提供 cURL、Python、JavaScript、PHP、Go、Java 和 Ruby，同一语言下可切换各业务场景。各语言使用与 cURL
    对应场景相同的请求参数和类型。


    运行环境：cURL；Python 3.9+（标准库）；JavaScript 使用 Node.js 18+，保存为 .mjs 后运行；PHP 8+ 并启用
    cURL 扩展；Go 1.20+；Java 11+（示例类名为 Example，保存为 Example.java）；Ruby 3+（标准库）。


    运行前设置 BASE_URL；业务接口还需设置 ACCESS_TOKEN，获取令牌需设置 CLIENT_ID 和
    CLIENT_SECRET。BASE_URL 使用 API 根地址，不附加 /api/v2。TXT 上传示例从当前目录读取
    payments.txt。请先替换为获准测试数据，并结合响应 code 和业务状态判断结果。
servers:
  - url: https://staging-api.billbuddyhq.com
    description: Staging 测试环境
security:
  - oauth2: []
tags:
  - name: 认证
    description: OAuth2 Client Credentials。
  - name: 交易
    description: 单笔、批量与预约取消。
  - name: 订阅
    description: 订阅模板、创建订阅、创建并激活 DDR 及推送扣费计划。
paths:
  /api/v2/subscription/create:
    post:
      tags:
        - 订阅
      summary: 创建托管订阅
      description: >-
        ### 选择订阅模式


        推荐使用 `customer` 和 `plan` 嵌套参数。客户信息二选一：已有客户填写
        `customer.customer_id`；新客户填写
        `customer.firstName`、`customer.lastName`、`customer.emailAddress`，不要同时提供已有客户标识。


        | 模式 | 提交参数 | 条件及限制 |

        | --- | --- | --- |

        | 手填订阅 | `plan.plan_name`，以及 `plan.recurring`、`plan.one_off` 中至少一个启用段 |
        启用段的 `enabled` 设为 1；可同时启用周期和一次性段 |

        | 模板订阅 | `plan.template_id` 及模板对应日期 | 模板含周期段时提供
        `plan.start_date`，含一次性段时提供 `plan.payment_date`；`plan_name` 可省略继承模板 |

        | By agreement | `plan.plan_name` 和 `plan.schedule` | 首条计划填写
        `schedule_name`、`type`、`amount`、`start_date`；类型只允许 1、2、3、4、6 |


        三种模式互斥。手填周期段启用时，须提供
        `schedule_name`、`recurring_type`、`amount`、`start_date`、`day_of_debit`。一次性段启用时，须提供
        `schedule_name`、`amount`、`payment_date`。日期至少为业务时区的明天。


        | 周期结束条件 | 额外必填参数 |

        | --- | --- |

        | `recurring_type=1` / `schedule.type=1`，按成功期数 | `end_terms` |

        | `recurring_type=2` / `schedule.type=2`，按累计金额 | `total_amount`，且
        `amount` 不得大于 `total_amount` |

        | `recurring_type=3` / `schedule.type=3`，持续至取消 | 无额外结束条件 |

        | `recurring_type=4` / `schedule.type=4`，按结束日期 | `end_date`，不得早于同段的
        `start_date` |

        | `schedule.type=6`，一次性 | 不要求 `day_of_debit`；此类型不适用于 `recurring_type` |


        季度、半年、年度频率须提供 `day_of_debit_dtstart`。自定义频率须提供
        `process_type=12`、`custom_cycle_type` 和 `custom_cycle`，并与 RRULE
        一致。具体范围及日期窗口见字段说明。


        顶层同名替代参数仍受支持；建议新接入统一使用嵌套形式。两种形式同时出现时，同名的嵌套字段优先。


        创建成功后 status=2 Inactive，订阅尚未激活。将返回的完整 activation_url 原样提供给客户打开，或设为
        iframe 的 src；也可将 activation_html 用作 iframe 的 srcdoc。客户在 BillBuddy
        提供的页面填写支付信息并完成授权和激活。无需自行拼接激活链接或解析凭证，Client Secret 和 access_token
        仅保存在商户服务端。三种定义模式互斥：recurring/one_off 手填模式、template_id 模板模式、schedule By
        agreement 模式。模板模式须提供其启用部分的日期（周期 start_date、一次性
        payment_date）。新客户信息与已有客户引用互斥。推荐使用 customer/plan
        嵌套参数；替代参数形式见字段说明。成功响应可能不含激活链接，此时请联系 BillBuddy
        核对已创建订阅后再处理；接口未提供调用方幂等编号，请勿盲目重试创建。 周期订阅及 By agreement 须在 start_date
        当天之前激活；纯一次性订阅须在创建后 30 天内提交激活。页面可以打开不保证仍可提交，以激活接口结果为准。


        所有示例均为格式示例，客户、订单、卡及账户数据须替换为测试环境获准使用的数据。BASE_URL 为服务根域名，不附加
        /api/v2；ACCESS_TOKEN 为 /oauth/token 返回的 access_token。日期示例为 2030
        年，执行时请改为满足当前日期限制的实际日期。
      operationId: v2SubscriptionCreate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubscriptionCreateRequest'
            examples:
              manual:
                summary: 手填周期订阅
                value:
                  merchant_id: '48290713'
                  customer:
                    customer_id: CUS48290713
                  plan:
                    plan_name: Monthly membership
                    recurring:
                      enabled: 1
                      schedule_name: Monthly fee
                      recurring_type: 3
                      amount: '25.50'
                      start_date: '2030-01-10'
                      day_of_debit: FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15
                    surcharge_config: []
                  notify_email: false
                  notify_sms: false
              newCustomer:
                summary: 新客户和一次性订阅
                value:
                  merchant_id: '48290713'
                  customer:
                    firstName: Emily
                    lastName: Carter
                    emailAddress: emily.carter@example.com
                    mobilePhoneNumber: 491570006
                    addressLine1: 18 Paperbark Avenue
                    addressSuburb: Sydney
                    addressState: NSW
                    addressPostCode: '2000'
                  plan:
                    plan_name: One time fee
                    one_off:
                      enabled: 1
                      schedule_name: Initial fee
                      amount: '20.00'
                      payment_date: '2030-01-15'
              template:
                summary: 模板订阅
                value:
                  merchant_id: '48290713'
                  customer:
                    customer_id: CUS48290713
                  plan:
                    template_id: TPL62038591
                    start_date: '2030-01-10'
              agreement:
                summary: By agreement 首条计划
                value:
                  merchant_id: '48290713'
                  customer:
                    customer_id: CUS48290713
                  plan:
                    plan_name: Usage based DDR
                    schedule:
                      type: 6
                      schedule_name: First usage charge
                      amount: '10.00'
                      start_date: '2030-01-15'
              custom:
                summary: 每十天自定义频率
                value:
                  merchant_id: '48290713'
                  customer:
                    customer_id: CUS48290713
                  plan:
                    plan_name: Every ten days
                    recurring:
                      enabled: 1
                      schedule_name: Ten-day membership fee
                      recurring_type: 3
                      amount: '25.50'
                      start_date: '2030-01-10'
                      day_of_debit: FREQ=DAILY;INTERVAL=10
                      process_type: 12
                      custom_cycle_type: 1
                      custom_cycle: 10
              quarterly:
                summary: 季度频率及首次锚点
                value:
                  merchant_id: '48290713'
                  customer:
                    customer_id: CUS48290713
                  plan:
                    plan_name: Quarterly membership
                    recurring:
                      enabled: 1
                      schedule_name: Quarterly membership fee
                      recurring_type: 3
                      amount: '25.50'
                      start_date: '2030-01-10'
                      day_of_debit: FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=15
                      day_of_debit_dtstart: '2030-02-15'
      responses:
        '200':
          description: HTTP 200。成功、业务失败、参数校验失败或业务鉴权失败以响应体 code 区分。
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/SubscriptionCreateResponse'
                  - $ref: '#/components/schemas/BusinessError'
              examples:
                success:
                  summary: 待激活订阅
                  value:
                    code: 0
                    message: ok
                    data:
                      customer_id: CUS48290713
                      plan_id: DDR73019482
                      status: 2
                      activation_url: >-
                        https://staging-api.billbuddyhq.com/api/v2/subscription/activatePage/73a9c4f128d60b5e9a2746cd801fb352
                      activation_html: >-
                        <!DOCTYPE html><html><head><meta charset="UTF-8"><meta
                        http-equiv="refresh"
                        content="0;url=https://staging-api.billbuddyhq.com/api/v2/subscription/activatePage/73a9c4f128d60b5e9a2746cd801fb352"><style>html,body{height:100%;margin:0}body{display:flex;align-items:center;justify-content:center;font-family:-apple-system,Segoe
                        UI,Roboto,Arial,sans-serif;color:#444;background:#fff}.tip{text-align:center;font-size:16px}.spin{width:28px;height:28px;margin:0
                        auto 12px;border:3px solid
                        #ddd;border-top-color:#888;border-radius:50%;animation:r
                        .8s linear infinite}@keyframes
                        r{to{transform:rotate(360deg)}}</style></head><body><div
                        class="tip"><div
                        class="spin"></div>Redirecting...</div><form
                        method="get"
                        action="https://staging-api.billbuddyhq.com/api/v2/subscription/activatePage/73a9c4f128d60b5e9a2746cd801fb352"
                        target="_self" id="checkform"
                        name="checkform"></form><script>window.onload=function(){document.checkform.submit();};</script></body></html>
                agreement:
                  summary: By agreement 待激活
                  value:
                    code: 0
                    message: ok
                    data:
                      customer_id: CUS48290713
                      plan_id: DDR73019482
                      status: 2
                      activation_url: >-
                        https://staging-api.billbuddyhq.com/api/v2/subscription/activatePage/73a9c4f128d60b5e9a2746cd801fb352
                      activation_html: >-
                        <!DOCTYPE html><html><head><meta charset="UTF-8"><meta
                        http-equiv="refresh"
                        content="0;url=https://staging-api.billbuddyhq.com/api/v2/subscription/activatePage/73a9c4f128d60b5e9a2746cd801fb352"><style>html,body{height:100%;margin:0}body{display:flex;align-items:center;justify-content:center;font-family:-apple-system,Segoe
                        UI,Roboto,Arial,sans-serif;color:#444;background:#fff}.tip{text-align:center;font-size:16px}.spin{width:28px;height:28px;margin:0
                        auto 12px;border:3px solid
                        #ddd;border-top-color:#888;border-radius:50%;animation:r
                        .8s linear infinite}@keyframes
                        r{to{transform:rotate(360deg)}}</style></head><body><div
                        class="tip"><div
                        class="spin"></div>Redirecting...</div><form
                        method="get"
                        action="https://staging-api.billbuddyhq.com/api/v2/subscription/activatePage/73a9c4f128d60b5e9a2746cd801fb352"
                        target="_self" id="checkform"
                        name="checkform"></form><script>window.onload=function(){document.checkform.submit();};</script></body></html>
                      is_by_agreement: true
                withoutTicket:
                  summary: 订阅已创建，未返回激活链接
                  value:
                    code: 0
                    message: ok
                    data:
                      customer_id: CUS48290713
                      plan_id: DDR73019482
                      status: 2
                failure:
                  summary: 客户不存在
                  value:
                    code: 1
                    message: Customer not found
                    data: []
                validation:
                  summary: 模式冲突
                  value:
                    code: 400
                    message: >-
                      Provide exactly one of: recurring/one_off, template_id, or
                      schedule.
                    data: {}
        '429':
          description: >-
            请求超过频率限制。API 请求共享每分钟 120 次的限额，通常按请求来源 IP 计数，并非每个接口单独 120 次。请遵循
            Retry-After 指定的等待秒数。
          headers:
            Retry-After:
              description: 再次请求前应等待的秒数。
              schema:
                type: integer
          content:
            application/json:
              schema:
                type: object
                description: 请求频率超限响应。
                properties:
                  message:
                    type: string
                    description: 限流提示。
              example:
                message: Too Many Attempts.
      security:
        - oauth2: []
      x-codeSamples:
        - lang: Shell
          label: 手填周期订阅
          source: |-
            curl -X POST "${BASE_URL}/api/v2/subscription/create" \
              -H "Authorization: Bearer ${ACCESS_TOKEN}" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data-raw '{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Monthly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Monthly fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15"
                },
                "surcharge_config": []
              },
              "notify_email": false,
              "notify_sms": false
            }'
        - lang: Shell
          label: 新客户和一次性订阅
          source: |-
            curl -X POST "${BASE_URL}/api/v2/subscription/create" \
              -H "Authorization: Bearer ${ACCESS_TOKEN}" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data-raw '{
              "merchant_id": "48290713",
              "customer": {
                "firstName": "Emily",
                "lastName": "Carter",
                "emailAddress": "emily.carter@example.com",
                "mobilePhoneNumber": "0491570006",
                "addressLine1": "18 Paperbark Avenue",
                "addressSuburb": "Sydney",
                "addressState": "NSW",
                "addressPostCode": "2000"
              },
              "plan": {
                "plan_name": "One time fee",
                "one_off": {
                  "enabled": 1,
                  "schedule_name": "Initial fee",
                  "amount": "20.00",
                  "payment_date": "2030-01-15"
                }
              }
            }'
        - lang: Shell
          label: 模板订阅
          source: |-
            curl -X POST "${BASE_URL}/api/v2/subscription/create" \
              -H "Authorization: Bearer ${ACCESS_TOKEN}" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data-raw '{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "template_id": "TPL62038591",
                "start_date": "2030-01-10"
              }
            }'
        - lang: Shell
          label: By agreement 首条计划
          source: |-
            curl -X POST "${BASE_URL}/api/v2/subscription/create" \
              -H "Authorization: Bearer ${ACCESS_TOKEN}" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data-raw '{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Usage based DDR",
                "schedule": {
                  "type": 6,
                  "schedule_name": "First usage charge",
                  "amount": "10.00",
                  "start_date": "2030-01-15"
                }
              }
            }'
        - lang: Shell
          label: 每十天自定义频率
          source: |-
            curl -X POST "${BASE_URL}/api/v2/subscription/create" \
              -H "Authorization: Bearer ${ACCESS_TOKEN}" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data-raw '{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Every ten days",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Ten-day membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=DAILY;INTERVAL=10",
                  "process_type": 12,
                  "custom_cycle_type": 1,
                  "custom_cycle": 10
                }
              }
            }'
        - lang: Shell
          label: 季度频率及首次锚点
          source: |-
            curl -X POST "${BASE_URL}/api/v2/subscription/create" \
              -H "Authorization: Bearer ${ACCESS_TOKEN}" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data-raw '{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Quarterly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Quarterly membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=15",
                  "day_of_debit_dtstart": "2030-02-15"
                }
              }
            }'
        - lang: Python
          label: 手填周期订阅
          source: >
            import json

            import os

            import urllib.parse

            import urllib.request


            base_url = os.environ["BASE_URL"].rstrip("/")

            url = base_url + "/api/v2/subscription/create"

            headers = {"Accept": "application/json"}

            headers["Authorization"] = "Bearer " + os.environ["ACCESS_TOKEN"]

            payload = json.loads(r'''{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Monthly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Monthly fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15"
                },
                "surcharge_config": []
              },
              "notify_email": false,
              "notify_sms": false
            }''')

            body = json.dumps(payload).encode("utf-8")

            headers["Content-Type"] = "application/json"


            request = urllib.request.Request(url, data=body, headers=headers,
            method="POST")

            with urllib.request.urlopen(request, timeout=30) as response:
                print(response.read().decode("utf-8"))
        - lang: Python
          label: 新客户和一次性订阅
          source: >
            import json

            import os

            import urllib.parse

            import urllib.request


            base_url = os.environ["BASE_URL"].rstrip("/")

            url = base_url + "/api/v2/subscription/create"

            headers = {"Accept": "application/json"}

            headers["Authorization"] = "Bearer " + os.environ["ACCESS_TOKEN"]

            payload = json.loads(r'''{
              "merchant_id": "48290713",
              "customer": {
                "firstName": "Emily",
                "lastName": "Carter",
                "emailAddress": "emily.carter@example.com",
                "mobilePhoneNumber": "0491570006",
                "addressLine1": "18 Paperbark Avenue",
                "addressSuburb": "Sydney",
                "addressState": "NSW",
                "addressPostCode": "2000"
              },
              "plan": {
                "plan_name": "One time fee",
                "one_off": {
                  "enabled": 1,
                  "schedule_name": "Initial fee",
                  "amount": "20.00",
                  "payment_date": "2030-01-15"
                }
              }
            }''')

            body = json.dumps(payload).encode("utf-8")

            headers["Content-Type"] = "application/json"


            request = urllib.request.Request(url, data=body, headers=headers,
            method="POST")

            with urllib.request.urlopen(request, timeout=30) as response:
                print(response.read().decode("utf-8"))
        - lang: Python
          label: 模板订阅
          source: >
            import json

            import os

            import urllib.parse

            import urllib.request


            base_url = os.environ["BASE_URL"].rstrip("/")

            url = base_url + "/api/v2/subscription/create"

            headers = {"Accept": "application/json"}

            headers["Authorization"] = "Bearer " + os.environ["ACCESS_TOKEN"]

            payload = json.loads(r'''{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "template_id": "TPL62038591",
                "start_date": "2030-01-10"
              }
            }''')

            body = json.dumps(payload).encode("utf-8")

            headers["Content-Type"] = "application/json"


            request = urllib.request.Request(url, data=body, headers=headers,
            method="POST")

            with urllib.request.urlopen(request, timeout=30) as response:
                print(response.read().decode("utf-8"))
        - lang: Python
          label: By agreement 首条计划
          source: >
            import json

            import os

            import urllib.parse

            import urllib.request


            base_url = os.environ["BASE_URL"].rstrip("/")

            url = base_url + "/api/v2/subscription/create"

            headers = {"Accept": "application/json"}

            headers["Authorization"] = "Bearer " + os.environ["ACCESS_TOKEN"]

            payload = json.loads(r'''{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Usage based DDR",
                "schedule": {
                  "type": 6,
                  "schedule_name": "First usage charge",
                  "amount": "10.00",
                  "start_date": "2030-01-15"
                }
              }
            }''')

            body = json.dumps(payload).encode("utf-8")

            headers["Content-Type"] = "application/json"


            request = urllib.request.Request(url, data=body, headers=headers,
            method="POST")

            with urllib.request.urlopen(request, timeout=30) as response:
                print(response.read().decode("utf-8"))
        - lang: Python
          label: 每十天自定义频率
          source: >
            import json

            import os

            import urllib.parse

            import urllib.request


            base_url = os.environ["BASE_URL"].rstrip("/")

            url = base_url + "/api/v2/subscription/create"

            headers = {"Accept": "application/json"}

            headers["Authorization"] = "Bearer " + os.environ["ACCESS_TOKEN"]

            payload = json.loads(r'''{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Every ten days",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Ten-day membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=DAILY;INTERVAL=10",
                  "process_type": 12,
                  "custom_cycle_type": 1,
                  "custom_cycle": 10
                }
              }
            }''')

            body = json.dumps(payload).encode("utf-8")

            headers["Content-Type"] = "application/json"


            request = urllib.request.Request(url, data=body, headers=headers,
            method="POST")

            with urllib.request.urlopen(request, timeout=30) as response:
                print(response.read().decode("utf-8"))
        - lang: Python
          label: 季度频率及首次锚点
          source: >
            import json

            import os

            import urllib.parse

            import urllib.request


            base_url = os.environ["BASE_URL"].rstrip("/")

            url = base_url + "/api/v2/subscription/create"

            headers = {"Accept": "application/json"}

            headers["Authorization"] = "Bearer " + os.environ["ACCESS_TOKEN"]

            payload = json.loads(r'''{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Quarterly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Quarterly membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=15",
                  "day_of_debit_dtstart": "2030-02-15"
                }
              }
            }''')

            body = json.dumps(payload).encode("utf-8")

            headers["Content-Type"] = "application/json"


            request = urllib.request.Request(url, data=body, headers=headers,
            method="POST")

            with urllib.request.urlopen(request, timeout=30) as response:
                print(response.read().decode("utf-8"))
        - lang: JavaScript
          label: 手填周期订阅
          source: |
            const env = (name) => {
              const value = process.env[name];
              if (!value) throw new Error(`Missing environment variable: ${name}`);
              return value;
            };
            const baseUrl = env("BASE_URL").replace(/\/+$/, "");
            const url = new URL(baseUrl + "/api/v2/subscription/create");
            const headers = { Accept: "application/json" };
            headers.Authorization = `Bearer ${env("ACCESS_TOKEN")}`;
            const payload = {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Monthly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Monthly fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15"
                },
                "surcharge_config": []
              },
              "notify_email": false,
              "notify_sms": false
            };
            const body = JSON.stringify(payload);
            headers["Content-Type"] = "application/json";

            const response = await fetch(url, {
              method: "POST",
              headers,
              body,
              signal: AbortSignal.timeout(30_000),
            });
            console.log(await response.text());
        - lang: JavaScript
          label: 新客户和一次性订阅
          source: |
            const env = (name) => {
              const value = process.env[name];
              if (!value) throw new Error(`Missing environment variable: ${name}`);
              return value;
            };
            const baseUrl = env("BASE_URL").replace(/\/+$/, "");
            const url = new URL(baseUrl + "/api/v2/subscription/create");
            const headers = { Accept: "application/json" };
            headers.Authorization = `Bearer ${env("ACCESS_TOKEN")}`;
            const payload = {
              "merchant_id": "48290713",
              "customer": {
                "firstName": "Emily",
                "lastName": "Carter",
                "emailAddress": "emily.carter@example.com",
                "mobilePhoneNumber": "0491570006",
                "addressLine1": "18 Paperbark Avenue",
                "addressSuburb": "Sydney",
                "addressState": "NSW",
                "addressPostCode": "2000"
              },
              "plan": {
                "plan_name": "One time fee",
                "one_off": {
                  "enabled": 1,
                  "schedule_name": "Initial fee",
                  "amount": "20.00",
                  "payment_date": "2030-01-15"
                }
              }
            };
            const body = JSON.stringify(payload);
            headers["Content-Type"] = "application/json";

            const response = await fetch(url, {
              method: "POST",
              headers,
              body,
              signal: AbortSignal.timeout(30_000),
            });
            console.log(await response.text());
        - lang: JavaScript
          label: 模板订阅
          source: |
            const env = (name) => {
              const value = process.env[name];
              if (!value) throw new Error(`Missing environment variable: ${name}`);
              return value;
            };
            const baseUrl = env("BASE_URL").replace(/\/+$/, "");
            const url = new URL(baseUrl + "/api/v2/subscription/create");
            const headers = { Accept: "application/json" };
            headers.Authorization = `Bearer ${env("ACCESS_TOKEN")}`;
            const payload = {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "template_id": "TPL62038591",
                "start_date": "2030-01-10"
              }
            };
            const body = JSON.stringify(payload);
            headers["Content-Type"] = "application/json";

            const response = await fetch(url, {
              method: "POST",
              headers,
              body,
              signal: AbortSignal.timeout(30_000),
            });
            console.log(await response.text());
        - lang: JavaScript
          label: By agreement 首条计划
          source: |
            const env = (name) => {
              const value = process.env[name];
              if (!value) throw new Error(`Missing environment variable: ${name}`);
              return value;
            };
            const baseUrl = env("BASE_URL").replace(/\/+$/, "");
            const url = new URL(baseUrl + "/api/v2/subscription/create");
            const headers = { Accept: "application/json" };
            headers.Authorization = `Bearer ${env("ACCESS_TOKEN")}`;
            const payload = {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Usage based DDR",
                "schedule": {
                  "type": 6,
                  "schedule_name": "First usage charge",
                  "amount": "10.00",
                  "start_date": "2030-01-15"
                }
              }
            };
            const body = JSON.stringify(payload);
            headers["Content-Type"] = "application/json";

            const response = await fetch(url, {
              method: "POST",
              headers,
              body,
              signal: AbortSignal.timeout(30_000),
            });
            console.log(await response.text());
        - lang: JavaScript
          label: 每十天自定义频率
          source: |
            const env = (name) => {
              const value = process.env[name];
              if (!value) throw new Error(`Missing environment variable: ${name}`);
              return value;
            };
            const baseUrl = env("BASE_URL").replace(/\/+$/, "");
            const url = new URL(baseUrl + "/api/v2/subscription/create");
            const headers = { Accept: "application/json" };
            headers.Authorization = `Bearer ${env("ACCESS_TOKEN")}`;
            const payload = {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Every ten days",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Ten-day membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=DAILY;INTERVAL=10",
                  "process_type": 12,
                  "custom_cycle_type": 1,
                  "custom_cycle": 10
                }
              }
            };
            const body = JSON.stringify(payload);
            headers["Content-Type"] = "application/json";

            const response = await fetch(url, {
              method: "POST",
              headers,
              body,
              signal: AbortSignal.timeout(30_000),
            });
            console.log(await response.text());
        - lang: JavaScript
          label: 季度频率及首次锚点
          source: |
            const env = (name) => {
              const value = process.env[name];
              if (!value) throw new Error(`Missing environment variable: ${name}`);
              return value;
            };
            const baseUrl = env("BASE_URL").replace(/\/+$/, "");
            const url = new URL(baseUrl + "/api/v2/subscription/create");
            const headers = { Accept: "application/json" };
            headers.Authorization = `Bearer ${env("ACCESS_TOKEN")}`;
            const payload = {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Quarterly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Quarterly membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=15",
                  "day_of_debit_dtstart": "2030-02-15"
                }
              }
            };
            const body = JSON.stringify(payload);
            headers["Content-Type"] = "application/json";

            const response = await fetch(url, {
              method: "POST",
              headers,
              body,
              signal: AbortSignal.timeout(30_000),
            });
            console.log(await response.text());
        - lang: PHP
          label: 手填周期订阅
          source: |
            <?php
            $env = static function (string $name): string {
                $value = getenv($name);
                if ($value === false || $value === '') {
                    throw new RuntimeException("Missing environment variable: " . $name);
                }
                return $value;
            };
            $baseUrl = rtrim($env('BASE_URL'), '/');
            $url = $baseUrl . "/api/v2/subscription/create";
            $headers = ['Accept: application/json'];
            $headers[] = 'Authorization: Bearer ' . $env('ACCESS_TOKEN');
            $body = <<<'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Monthly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Monthly fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15"
                },
                "surcharge_config": []
              },
              "notify_email": false,
              "notify_sms": false
            }
            JSON;
            $headers[] = 'Content-Type: application/json';

            $curl = curl_init($url);
            curl_setopt_array($curl, [
                CURLOPT_CUSTOMREQUEST => "POST",
                CURLOPT_HTTPHEADER => $headers,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 30,
                CURLOPT_POSTFIELDS => $body,
            ]);
            $response = curl_exec($curl);
            if ($response === false) {
                $error = curl_error($curl);
                curl_close($curl);
                throw new RuntimeException($error);
            }
            curl_close($curl);
            echo $response, PHP_EOL;
        - lang: PHP
          label: 新客户和一次性订阅
          source: |
            <?php
            $env = static function (string $name): string {
                $value = getenv($name);
                if ($value === false || $value === '') {
                    throw new RuntimeException("Missing environment variable: " . $name);
                }
                return $value;
            };
            $baseUrl = rtrim($env('BASE_URL'), '/');
            $url = $baseUrl . "/api/v2/subscription/create";
            $headers = ['Accept: application/json'];
            $headers[] = 'Authorization: Bearer ' . $env('ACCESS_TOKEN');
            $body = <<<'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "firstName": "Emily",
                "lastName": "Carter",
                "emailAddress": "emily.carter@example.com",
                "mobilePhoneNumber": "0491570006",
                "addressLine1": "18 Paperbark Avenue",
                "addressSuburb": "Sydney",
                "addressState": "NSW",
                "addressPostCode": "2000"
              },
              "plan": {
                "plan_name": "One time fee",
                "one_off": {
                  "enabled": 1,
                  "schedule_name": "Initial fee",
                  "amount": "20.00",
                  "payment_date": "2030-01-15"
                }
              }
            }
            JSON;
            $headers[] = 'Content-Type: application/json';

            $curl = curl_init($url);
            curl_setopt_array($curl, [
                CURLOPT_CUSTOMREQUEST => "POST",
                CURLOPT_HTTPHEADER => $headers,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 30,
                CURLOPT_POSTFIELDS => $body,
            ]);
            $response = curl_exec($curl);
            if ($response === false) {
                $error = curl_error($curl);
                curl_close($curl);
                throw new RuntimeException($error);
            }
            curl_close($curl);
            echo $response, PHP_EOL;
        - lang: PHP
          label: 模板订阅
          source: |
            <?php
            $env = static function (string $name): string {
                $value = getenv($name);
                if ($value === false || $value === '') {
                    throw new RuntimeException("Missing environment variable: " . $name);
                }
                return $value;
            };
            $baseUrl = rtrim($env('BASE_URL'), '/');
            $url = $baseUrl . "/api/v2/subscription/create";
            $headers = ['Accept: application/json'];
            $headers[] = 'Authorization: Bearer ' . $env('ACCESS_TOKEN');
            $body = <<<'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "template_id": "TPL62038591",
                "start_date": "2030-01-10"
              }
            }
            JSON;
            $headers[] = 'Content-Type: application/json';

            $curl = curl_init($url);
            curl_setopt_array($curl, [
                CURLOPT_CUSTOMREQUEST => "POST",
                CURLOPT_HTTPHEADER => $headers,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 30,
                CURLOPT_POSTFIELDS => $body,
            ]);
            $response = curl_exec($curl);
            if ($response === false) {
                $error = curl_error($curl);
                curl_close($curl);
                throw new RuntimeException($error);
            }
            curl_close($curl);
            echo $response, PHP_EOL;
        - lang: PHP
          label: By agreement 首条计划
          source: |
            <?php
            $env = static function (string $name): string {
                $value = getenv($name);
                if ($value === false || $value === '') {
                    throw new RuntimeException("Missing environment variable: " . $name);
                }
                return $value;
            };
            $baseUrl = rtrim($env('BASE_URL'), '/');
            $url = $baseUrl . "/api/v2/subscription/create";
            $headers = ['Accept: application/json'];
            $headers[] = 'Authorization: Bearer ' . $env('ACCESS_TOKEN');
            $body = <<<'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Usage based DDR",
                "schedule": {
                  "type": 6,
                  "schedule_name": "First usage charge",
                  "amount": "10.00",
                  "start_date": "2030-01-15"
                }
              }
            }
            JSON;
            $headers[] = 'Content-Type: application/json';

            $curl = curl_init($url);
            curl_setopt_array($curl, [
                CURLOPT_CUSTOMREQUEST => "POST",
                CURLOPT_HTTPHEADER => $headers,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 30,
                CURLOPT_POSTFIELDS => $body,
            ]);
            $response = curl_exec($curl);
            if ($response === false) {
                $error = curl_error($curl);
                curl_close($curl);
                throw new RuntimeException($error);
            }
            curl_close($curl);
            echo $response, PHP_EOL;
        - lang: PHP
          label: 每十天自定义频率
          source: |
            <?php
            $env = static function (string $name): string {
                $value = getenv($name);
                if ($value === false || $value === '') {
                    throw new RuntimeException("Missing environment variable: " . $name);
                }
                return $value;
            };
            $baseUrl = rtrim($env('BASE_URL'), '/');
            $url = $baseUrl . "/api/v2/subscription/create";
            $headers = ['Accept: application/json'];
            $headers[] = 'Authorization: Bearer ' . $env('ACCESS_TOKEN');
            $body = <<<'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Every ten days",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Ten-day membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=DAILY;INTERVAL=10",
                  "process_type": 12,
                  "custom_cycle_type": 1,
                  "custom_cycle": 10
                }
              }
            }
            JSON;
            $headers[] = 'Content-Type: application/json';

            $curl = curl_init($url);
            curl_setopt_array($curl, [
                CURLOPT_CUSTOMREQUEST => "POST",
                CURLOPT_HTTPHEADER => $headers,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 30,
                CURLOPT_POSTFIELDS => $body,
            ]);
            $response = curl_exec($curl);
            if ($response === false) {
                $error = curl_error($curl);
                curl_close($curl);
                throw new RuntimeException($error);
            }
            curl_close($curl);
            echo $response, PHP_EOL;
        - lang: PHP
          label: 季度频率及首次锚点
          source: |
            <?php
            $env = static function (string $name): string {
                $value = getenv($name);
                if ($value === false || $value === '') {
                    throw new RuntimeException("Missing environment variable: " . $name);
                }
                return $value;
            };
            $baseUrl = rtrim($env('BASE_URL'), '/');
            $url = $baseUrl . "/api/v2/subscription/create";
            $headers = ['Accept: application/json'];
            $headers[] = 'Authorization: Bearer ' . $env('ACCESS_TOKEN');
            $body = <<<'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Quarterly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Quarterly membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=15",
                  "day_of_debit_dtstart": "2030-02-15"
                }
              }
            }
            JSON;
            $headers[] = 'Content-Type: application/json';

            $curl = curl_init($url);
            curl_setopt_array($curl, [
                CURLOPT_CUSTOMREQUEST => "POST",
                CURLOPT_HTTPHEADER => $headers,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 30,
                CURLOPT_POSTFIELDS => $body,
            ]);
            $response = curl_exec($curl);
            if ($response === false) {
                $error = curl_error($curl);
                curl_close($curl);
                throw new RuntimeException($error);
            }
            curl_close($curl);
            echo $response, PHP_EOL;
        - lang: Go
          label: 手填周期订阅
          source: |
            package main

            import (
                "fmt"
                "io"
                "net/http"
                "os"
                "strings"
                "time"
            )

            func mustEnv(name string) string {
                value := os.Getenv(name)
                if value == "" { panic("Missing environment variable: " + name) }
                return value
            }

            func main() {
                baseURL := strings.TrimRight(mustEnv("BASE_URL"), "/")
                endpoint := baseURL + "/api/v2/subscription/create"
                payload := `{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Monthly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Monthly fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15"
                },
                "surcharge_config": []
              },
              "notify_email": false,
              "notify_sms": false
            }`
                body := strings.NewReader(payload)
                req, err := http.NewRequest("POST", endpoint, body)
                if err != nil { panic(err) }
                req.Header.Set("Accept", "application/json")
                req.Header.Set("Authorization", "Bearer " + mustEnv("ACCESS_TOKEN"))
                req.Header.Set("Content-Type", "application/json")
                client := &http.Client{Timeout: 30 * time.Second}
                response, err := client.Do(req)
                if err != nil { panic(err) }
                defer response.Body.Close()
                result, err := io.ReadAll(response.Body)
                if err != nil { panic(err) }
                fmt.Println(string(result))
            }
        - lang: Go
          label: 新客户和一次性订阅
          source: |
            package main

            import (
                "fmt"
                "io"
                "net/http"
                "os"
                "strings"
                "time"
            )

            func mustEnv(name string) string {
                value := os.Getenv(name)
                if value == "" { panic("Missing environment variable: " + name) }
                return value
            }

            func main() {
                baseURL := strings.TrimRight(mustEnv("BASE_URL"), "/")
                endpoint := baseURL + "/api/v2/subscription/create"
                payload := `{
              "merchant_id": "48290713",
              "customer": {
                "firstName": "Emily",
                "lastName": "Carter",
                "emailAddress": "emily.carter@example.com",
                "mobilePhoneNumber": "0491570006",
                "addressLine1": "18 Paperbark Avenue",
                "addressSuburb": "Sydney",
                "addressState": "NSW",
                "addressPostCode": "2000"
              },
              "plan": {
                "plan_name": "One time fee",
                "one_off": {
                  "enabled": 1,
                  "schedule_name": "Initial fee",
                  "amount": "20.00",
                  "payment_date": "2030-01-15"
                }
              }
            }`
                body := strings.NewReader(payload)
                req, err := http.NewRequest("POST", endpoint, body)
                if err != nil { panic(err) }
                req.Header.Set("Accept", "application/json")
                req.Header.Set("Authorization", "Bearer " + mustEnv("ACCESS_TOKEN"))
                req.Header.Set("Content-Type", "application/json")
                client := &http.Client{Timeout: 30 * time.Second}
                response, err := client.Do(req)
                if err != nil { panic(err) }
                defer response.Body.Close()
                result, err := io.ReadAll(response.Body)
                if err != nil { panic(err) }
                fmt.Println(string(result))
            }
        - lang: Go
          label: 模板订阅
          source: |
            package main

            import (
                "fmt"
                "io"
                "net/http"
                "os"
                "strings"
                "time"
            )

            func mustEnv(name string) string {
                value := os.Getenv(name)
                if value == "" { panic("Missing environment variable: " + name) }
                return value
            }

            func main() {
                baseURL := strings.TrimRight(mustEnv("BASE_URL"), "/")
                endpoint := baseURL + "/api/v2/subscription/create"
                payload := `{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "template_id": "TPL62038591",
                "start_date": "2030-01-10"
              }
            }`
                body := strings.NewReader(payload)
                req, err := http.NewRequest("POST", endpoint, body)
                if err != nil { panic(err) }
                req.Header.Set("Accept", "application/json")
                req.Header.Set("Authorization", "Bearer " + mustEnv("ACCESS_TOKEN"))
                req.Header.Set("Content-Type", "application/json")
                client := &http.Client{Timeout: 30 * time.Second}
                response, err := client.Do(req)
                if err != nil { panic(err) }
                defer response.Body.Close()
                result, err := io.ReadAll(response.Body)
                if err != nil { panic(err) }
                fmt.Println(string(result))
            }
        - lang: Go
          label: By agreement 首条计划
          source: |
            package main

            import (
                "fmt"
                "io"
                "net/http"
                "os"
                "strings"
                "time"
            )

            func mustEnv(name string) string {
                value := os.Getenv(name)
                if value == "" { panic("Missing environment variable: " + name) }
                return value
            }

            func main() {
                baseURL := strings.TrimRight(mustEnv("BASE_URL"), "/")
                endpoint := baseURL + "/api/v2/subscription/create"
                payload := `{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Usage based DDR",
                "schedule": {
                  "type": 6,
                  "schedule_name": "First usage charge",
                  "amount": "10.00",
                  "start_date": "2030-01-15"
                }
              }
            }`
                body := strings.NewReader(payload)
                req, err := http.NewRequest("POST", endpoint, body)
                if err != nil { panic(err) }
                req.Header.Set("Accept", "application/json")
                req.Header.Set("Authorization", "Bearer " + mustEnv("ACCESS_TOKEN"))
                req.Header.Set("Content-Type", "application/json")
                client := &http.Client{Timeout: 30 * time.Second}
                response, err := client.Do(req)
                if err != nil { panic(err) }
                defer response.Body.Close()
                result, err := io.ReadAll(response.Body)
                if err != nil { panic(err) }
                fmt.Println(string(result))
            }
        - lang: Go
          label: 每十天自定义频率
          source: |
            package main

            import (
                "fmt"
                "io"
                "net/http"
                "os"
                "strings"
                "time"
            )

            func mustEnv(name string) string {
                value := os.Getenv(name)
                if value == "" { panic("Missing environment variable: " + name) }
                return value
            }

            func main() {
                baseURL := strings.TrimRight(mustEnv("BASE_URL"), "/")
                endpoint := baseURL + "/api/v2/subscription/create"
                payload := `{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Every ten days",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Ten-day membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=DAILY;INTERVAL=10",
                  "process_type": 12,
                  "custom_cycle_type": 1,
                  "custom_cycle": 10
                }
              }
            }`
                body := strings.NewReader(payload)
                req, err := http.NewRequest("POST", endpoint, body)
                if err != nil { panic(err) }
                req.Header.Set("Accept", "application/json")
                req.Header.Set("Authorization", "Bearer " + mustEnv("ACCESS_TOKEN"))
                req.Header.Set("Content-Type", "application/json")
                client := &http.Client{Timeout: 30 * time.Second}
                response, err := client.Do(req)
                if err != nil { panic(err) }
                defer response.Body.Close()
                result, err := io.ReadAll(response.Body)
                if err != nil { panic(err) }
                fmt.Println(string(result))
            }
        - lang: Go
          label: 季度频率及首次锚点
          source: |
            package main

            import (
                "fmt"
                "io"
                "net/http"
                "os"
                "strings"
                "time"
            )

            func mustEnv(name string) string {
                value := os.Getenv(name)
                if value == "" { panic("Missing environment variable: " + name) }
                return value
            }

            func main() {
                baseURL := strings.TrimRight(mustEnv("BASE_URL"), "/")
                endpoint := baseURL + "/api/v2/subscription/create"
                payload := `{
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Quarterly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Quarterly membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=15",
                  "day_of_debit_dtstart": "2030-02-15"
                }
              }
            }`
                body := strings.NewReader(payload)
                req, err := http.NewRequest("POST", endpoint, body)
                if err != nil { panic(err) }
                req.Header.Set("Accept", "application/json")
                req.Header.Set("Authorization", "Bearer " + mustEnv("ACCESS_TOKEN"))
                req.Header.Set("Content-Type", "application/json")
                client := &http.Client{Timeout: 30 * time.Second}
                response, err := client.Do(req)
                if err != nil { panic(err) }
                defer response.Body.Close()
                result, err := io.ReadAll(response.Body)
                if err != nil { panic(err) }
                fmt.Println(string(result))
            }
        - lang: Java
          label: 手填周期订阅
          source: |
            import java.io.ByteArrayOutputStream;
            import java.net.URI;
            import java.net.URLEncoder;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;
            import java.nio.charset.StandardCharsets;
            import java.nio.file.Files;
            import java.nio.file.Path;
            import java.time.Duration;
            import java.util.LinkedHashMap;
            import java.util.Map;
            import java.util.UUID;
            import java.util.stream.Collectors;

            public class Example {
                static String requireEnv(String name) {
                    String value = System.getenv(name);
                    if (value == null || value.isEmpty()) {
                        throw new IllegalArgumentException("Missing environment variable: " + name);
                    }
                    return value;
                }

                public static void main(String[] args) throws Exception {
                    String baseUrl = requireEnv("BASE_URL").replaceAll("/+$", "");
                    String endpoint = baseUrl + "/api/v2/subscription/create";
                    String payload =
                        "{\n" +
                        "  \"merchant_id\": \"48290713\",\n" +
                        "  \"customer\": {\n" +
                        "    \"customer_id\": \"CUS48290713\"\n" +
                        "  },\n" +
                        "  \"plan\": {\n" +
                        "    \"plan_name\": \"Monthly membership\",\n" +
                        "    \"recurring\": {\n" +
                        "      \"enabled\": 1,\n" +
                        "      \"schedule_name\": \"Monthly fee\",\n" +
                        "      \"recurring_type\": 3,\n" +
                        "      \"amount\": \"25.50\",\n" +
                        "      \"start_date\": \"2030-01-10\",\n" +
                        "      \"day_of_debit\": \"FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15\"\n" +
                        "    },\n" +
                        "    \"surcharge_config\": []\n" +
                        "  },\n" +
                        "  \"notify_email\": false,\n" +
                        "  \"notify_sms\": false\n" +
                        "}";
                    HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(endpoint))
                        .timeout(Duration.ofSeconds(30))
                        .header("Accept", "application/json");
                    builder.header("Authorization", "Bearer " + requireEnv("ACCESS_TOKEN"));
                    builder.header("Content-Type", "application/json");
                    HttpRequest request = builder.method("POST", HttpRequest.BodyPublishers.ofString(payload, StandardCharsets.UTF_8)).build();
                    HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
                    System.out.println(response.body());
                }
            }
        - lang: Java
          label: 新客户和一次性订阅
          source: |
            import java.io.ByteArrayOutputStream;
            import java.net.URI;
            import java.net.URLEncoder;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;
            import java.nio.charset.StandardCharsets;
            import java.nio.file.Files;
            import java.nio.file.Path;
            import java.time.Duration;
            import java.util.LinkedHashMap;
            import java.util.Map;
            import java.util.UUID;
            import java.util.stream.Collectors;

            public class Example {
                static String requireEnv(String name) {
                    String value = System.getenv(name);
                    if (value == null || value.isEmpty()) {
                        throw new IllegalArgumentException("Missing environment variable: " + name);
                    }
                    return value;
                }

                public static void main(String[] args) throws Exception {
                    String baseUrl = requireEnv("BASE_URL").replaceAll("/+$", "");
                    String endpoint = baseUrl + "/api/v2/subscription/create";
                    String payload =
                        "{\n" +
                        "  \"merchant_id\": \"48290713\",\n" +
                        "  \"customer\": {\n" +
                        "    \"firstName\": \"Emily\",\n" +
                        "    \"lastName\": \"Carter\",\n" +
                        "    \"emailAddress\": \"emily.carter@example.com\",\n" +
                        "    \"mobilePhoneNumber\": \"0491570006\",\n" +
                        "    \"addressLine1\": \"18 Paperbark Avenue\",\n" +
                        "    \"addressSuburb\": \"Sydney\",\n" +
                        "    \"addressState\": \"NSW\",\n" +
                        "    \"addressPostCode\": \"2000\"\n" +
                        "  },\n" +
                        "  \"plan\": {\n" +
                        "    \"plan_name\": \"One time fee\",\n" +
                        "    \"one_off\": {\n" +
                        "      \"enabled\": 1,\n" +
                        "      \"schedule_name\": \"Initial fee\",\n" +
                        "      \"amount\": \"20.00\",\n" +
                        "      \"payment_date\": \"2030-01-15\"\n" +
                        "    }\n" +
                        "  }\n" +
                        "}";
                    HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(endpoint))
                        .timeout(Duration.ofSeconds(30))
                        .header("Accept", "application/json");
                    builder.header("Authorization", "Bearer " + requireEnv("ACCESS_TOKEN"));
                    builder.header("Content-Type", "application/json");
                    HttpRequest request = builder.method("POST", HttpRequest.BodyPublishers.ofString(payload, StandardCharsets.UTF_8)).build();
                    HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
                    System.out.println(response.body());
                }
            }
        - lang: Java
          label: 模板订阅
          source: |
            import java.io.ByteArrayOutputStream;
            import java.net.URI;
            import java.net.URLEncoder;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;
            import java.nio.charset.StandardCharsets;
            import java.nio.file.Files;
            import java.nio.file.Path;
            import java.time.Duration;
            import java.util.LinkedHashMap;
            import java.util.Map;
            import java.util.UUID;
            import java.util.stream.Collectors;

            public class Example {
                static String requireEnv(String name) {
                    String value = System.getenv(name);
                    if (value == null || value.isEmpty()) {
                        throw new IllegalArgumentException("Missing environment variable: " + name);
                    }
                    return value;
                }

                public static void main(String[] args) throws Exception {
                    String baseUrl = requireEnv("BASE_URL").replaceAll("/+$", "");
                    String endpoint = baseUrl + "/api/v2/subscription/create";
                    String payload =
                        "{\n" +
                        "  \"merchant_id\": \"48290713\",\n" +
                        "  \"customer\": {\n" +
                        "    \"customer_id\": \"CUS48290713\"\n" +
                        "  },\n" +
                        "  \"plan\": {\n" +
                        "    \"template_id\": \"TPL62038591\",\n" +
                        "    \"start_date\": \"2030-01-10\"\n" +
                        "  }\n" +
                        "}";
                    HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(endpoint))
                        .timeout(Duration.ofSeconds(30))
                        .header("Accept", "application/json");
                    builder.header("Authorization", "Bearer " + requireEnv("ACCESS_TOKEN"));
                    builder.header("Content-Type", "application/json");
                    HttpRequest request = builder.method("POST", HttpRequest.BodyPublishers.ofString(payload, StandardCharsets.UTF_8)).build();
                    HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
                    System.out.println(response.body());
                }
            }
        - lang: Java
          label: By agreement 首条计划
          source: |
            import java.io.ByteArrayOutputStream;
            import java.net.URI;
            import java.net.URLEncoder;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;
            import java.nio.charset.StandardCharsets;
            import java.nio.file.Files;
            import java.nio.file.Path;
            import java.time.Duration;
            import java.util.LinkedHashMap;
            import java.util.Map;
            import java.util.UUID;
            import java.util.stream.Collectors;

            public class Example {
                static String requireEnv(String name) {
                    String value = System.getenv(name);
                    if (value == null || value.isEmpty()) {
                        throw new IllegalArgumentException("Missing environment variable: " + name);
                    }
                    return value;
                }

                public static void main(String[] args) throws Exception {
                    String baseUrl = requireEnv("BASE_URL").replaceAll("/+$", "");
                    String endpoint = baseUrl + "/api/v2/subscription/create";
                    String payload =
                        "{\n" +
                        "  \"merchant_id\": \"48290713\",\n" +
                        "  \"customer\": {\n" +
                        "    \"customer_id\": \"CUS48290713\"\n" +
                        "  },\n" +
                        "  \"plan\": {\n" +
                        "    \"plan_name\": \"Usage based DDR\",\n" +
                        "    \"schedule\": {\n" +
                        "      \"type\": 6,\n" +
                        "      \"schedule_name\": \"First usage charge\",\n" +
                        "      \"amount\": \"10.00\",\n" +
                        "      \"start_date\": \"2030-01-15\"\n" +
                        "    }\n" +
                        "  }\n" +
                        "}";
                    HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(endpoint))
                        .timeout(Duration.ofSeconds(30))
                        .header("Accept", "application/json");
                    builder.header("Authorization", "Bearer " + requireEnv("ACCESS_TOKEN"));
                    builder.header("Content-Type", "application/json");
                    HttpRequest request = builder.method("POST", HttpRequest.BodyPublishers.ofString(payload, StandardCharsets.UTF_8)).build();
                    HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
                    System.out.println(response.body());
                }
            }
        - lang: Java
          label: 每十天自定义频率
          source: |
            import java.io.ByteArrayOutputStream;
            import java.net.URI;
            import java.net.URLEncoder;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;
            import java.nio.charset.StandardCharsets;
            import java.nio.file.Files;
            import java.nio.file.Path;
            import java.time.Duration;
            import java.util.LinkedHashMap;
            import java.util.Map;
            import java.util.UUID;
            import java.util.stream.Collectors;

            public class Example {
                static String requireEnv(String name) {
                    String value = System.getenv(name);
                    if (value == null || value.isEmpty()) {
                        throw new IllegalArgumentException("Missing environment variable: " + name);
                    }
                    return value;
                }

                public static void main(String[] args) throws Exception {
                    String baseUrl = requireEnv("BASE_URL").replaceAll("/+$", "");
                    String endpoint = baseUrl + "/api/v2/subscription/create";
                    String payload =
                        "{\n" +
                        "  \"merchant_id\": \"48290713\",\n" +
                        "  \"customer\": {\n" +
                        "    \"customer_id\": \"CUS48290713\"\n" +
                        "  },\n" +
                        "  \"plan\": {\n" +
                        "    \"plan_name\": \"Every ten days\",\n" +
                        "    \"recurring\": {\n" +
                        "      \"enabled\": 1,\n" +
                        "      \"schedule_name\": \"Ten-day membership fee\",\n" +
                        "      \"recurring_type\": 3,\n" +
                        "      \"amount\": \"25.50\",\n" +
                        "      \"start_date\": \"2030-01-10\",\n" +
                        "      \"day_of_debit\": \"FREQ=DAILY;INTERVAL=10\",\n" +
                        "      \"process_type\": 12,\n" +
                        "      \"custom_cycle_type\": 1,\n" +
                        "      \"custom_cycle\": 10\n" +
                        "    }\n" +
                        "  }\n" +
                        "}";
                    HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(endpoint))
                        .timeout(Duration.ofSeconds(30))
                        .header("Accept", "application/json");
                    builder.header("Authorization", "Bearer " + requireEnv("ACCESS_TOKEN"));
                    builder.header("Content-Type", "application/json");
                    HttpRequest request = builder.method("POST", HttpRequest.BodyPublishers.ofString(payload, StandardCharsets.UTF_8)).build();
                    HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
                    System.out.println(response.body());
                }
            }
        - lang: Java
          label: 季度频率及首次锚点
          source: |
            import java.io.ByteArrayOutputStream;
            import java.net.URI;
            import java.net.URLEncoder;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;
            import java.nio.charset.StandardCharsets;
            import java.nio.file.Files;
            import java.nio.file.Path;
            import java.time.Duration;
            import java.util.LinkedHashMap;
            import java.util.Map;
            import java.util.UUID;
            import java.util.stream.Collectors;

            public class Example {
                static String requireEnv(String name) {
                    String value = System.getenv(name);
                    if (value == null || value.isEmpty()) {
                        throw new IllegalArgumentException("Missing environment variable: " + name);
                    }
                    return value;
                }

                public static void main(String[] args) throws Exception {
                    String baseUrl = requireEnv("BASE_URL").replaceAll("/+$", "");
                    String endpoint = baseUrl + "/api/v2/subscription/create";
                    String payload =
                        "{\n" +
                        "  \"merchant_id\": \"48290713\",\n" +
                        "  \"customer\": {\n" +
                        "    \"customer_id\": \"CUS48290713\"\n" +
                        "  },\n" +
                        "  \"plan\": {\n" +
                        "    \"plan_name\": \"Quarterly membership\",\n" +
                        "    \"recurring\": {\n" +
                        "      \"enabled\": 1,\n" +
                        "      \"schedule_name\": \"Quarterly membership fee\",\n" +
                        "      \"recurring_type\": 3,\n" +
                        "      \"amount\": \"25.50\",\n" +
                        "      \"start_date\": \"2030-01-10\",\n" +
                        "      \"day_of_debit\": \"FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=15\",\n" +
                        "      \"day_of_debit_dtstart\": \"2030-02-15\"\n" +
                        "    }\n" +
                        "  }\n" +
                        "}";
                    HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(endpoint))
                        .timeout(Duration.ofSeconds(30))
                        .header("Accept", "application/json");
                    builder.header("Authorization", "Bearer " + requireEnv("ACCESS_TOKEN"));
                    builder.header("Content-Type", "application/json");
                    HttpRequest request = builder.method("POST", HttpRequest.BodyPublishers.ofString(payload, StandardCharsets.UTF_8)).build();
                    HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
                    System.out.println(response.body());
                }
            }
        - lang: Ruby
          label: 手填周期订阅
          source: |
            require "net/http"
            require "uri"

            base_url = ENV.fetch("BASE_URL").sub(%r{/+$}, "")
            uri = URI(base_url + "/api/v2/subscription/create")
            request = Net::HTTP::Post.new(uri)
            request["Accept"] = "application/json"
            request["Authorization"] = "Bearer " + ENV.fetch("ACCESS_TOKEN")
            request["Content-Type"] = "application/json"
            request.body = <<~'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Monthly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Monthly fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15"
                },
                "surcharge_config": []
              },
              "notify_email": false,
              "notify_sms": false
            }
            JSON
            http = Net::HTTP.new(uri.host, uri.port)
            http.use_ssl = uri.scheme == "https"
            http.open_timeout = 30
            http.read_timeout = 30
            puts http.request(request).body
        - lang: Ruby
          label: 新客户和一次性订阅
          source: |
            require "net/http"
            require "uri"

            base_url = ENV.fetch("BASE_URL").sub(%r{/+$}, "")
            uri = URI(base_url + "/api/v2/subscription/create")
            request = Net::HTTP::Post.new(uri)
            request["Accept"] = "application/json"
            request["Authorization"] = "Bearer " + ENV.fetch("ACCESS_TOKEN")
            request["Content-Type"] = "application/json"
            request.body = <<~'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "firstName": "Emily",
                "lastName": "Carter",
                "emailAddress": "emily.carter@example.com",
                "mobilePhoneNumber": "0491570006",
                "addressLine1": "18 Paperbark Avenue",
                "addressSuburb": "Sydney",
                "addressState": "NSW",
                "addressPostCode": "2000"
              },
              "plan": {
                "plan_name": "One time fee",
                "one_off": {
                  "enabled": 1,
                  "schedule_name": "Initial fee",
                  "amount": "20.00",
                  "payment_date": "2030-01-15"
                }
              }
            }
            JSON
            http = Net::HTTP.new(uri.host, uri.port)
            http.use_ssl = uri.scheme == "https"
            http.open_timeout = 30
            http.read_timeout = 30
            puts http.request(request).body
        - lang: Ruby
          label: 模板订阅
          source: |
            require "net/http"
            require "uri"

            base_url = ENV.fetch("BASE_URL").sub(%r{/+$}, "")
            uri = URI(base_url + "/api/v2/subscription/create")
            request = Net::HTTP::Post.new(uri)
            request["Accept"] = "application/json"
            request["Authorization"] = "Bearer " + ENV.fetch("ACCESS_TOKEN")
            request["Content-Type"] = "application/json"
            request.body = <<~'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "template_id": "TPL62038591",
                "start_date": "2030-01-10"
              }
            }
            JSON
            http = Net::HTTP.new(uri.host, uri.port)
            http.use_ssl = uri.scheme == "https"
            http.open_timeout = 30
            http.read_timeout = 30
            puts http.request(request).body
        - lang: Ruby
          label: By agreement 首条计划
          source: |
            require "net/http"
            require "uri"

            base_url = ENV.fetch("BASE_URL").sub(%r{/+$}, "")
            uri = URI(base_url + "/api/v2/subscription/create")
            request = Net::HTTP::Post.new(uri)
            request["Accept"] = "application/json"
            request["Authorization"] = "Bearer " + ENV.fetch("ACCESS_TOKEN")
            request["Content-Type"] = "application/json"
            request.body = <<~'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Usage based DDR",
                "schedule": {
                  "type": 6,
                  "schedule_name": "First usage charge",
                  "amount": "10.00",
                  "start_date": "2030-01-15"
                }
              }
            }
            JSON
            http = Net::HTTP.new(uri.host, uri.port)
            http.use_ssl = uri.scheme == "https"
            http.open_timeout = 30
            http.read_timeout = 30
            puts http.request(request).body
        - lang: Ruby
          label: 每十天自定义频率
          source: |
            require "net/http"
            require "uri"

            base_url = ENV.fetch("BASE_URL").sub(%r{/+$}, "")
            uri = URI(base_url + "/api/v2/subscription/create")
            request = Net::HTTP::Post.new(uri)
            request["Accept"] = "application/json"
            request["Authorization"] = "Bearer " + ENV.fetch("ACCESS_TOKEN")
            request["Content-Type"] = "application/json"
            request.body = <<~'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Every ten days",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Ten-day membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=DAILY;INTERVAL=10",
                  "process_type": 12,
                  "custom_cycle_type": 1,
                  "custom_cycle": 10
                }
              }
            }
            JSON
            http = Net::HTTP.new(uri.host, uri.port)
            http.use_ssl = uri.scheme == "https"
            http.open_timeout = 30
            http.read_timeout = 30
            puts http.request(request).body
        - lang: Ruby
          label: 季度频率及首次锚点
          source: |
            require "net/http"
            require "uri"

            base_url = ENV.fetch("BASE_URL").sub(%r{/+$}, "")
            uri = URI(base_url + "/api/v2/subscription/create")
            request = Net::HTTP::Post.new(uri)
            request["Accept"] = "application/json"
            request["Authorization"] = "Bearer " + ENV.fetch("ACCESS_TOKEN")
            request["Content-Type"] = "application/json"
            request.body = <<~'JSON'
            {
              "merchant_id": "48290713",
              "customer": {
                "customer_id": "CUS48290713"
              },
              "plan": {
                "plan_name": "Quarterly membership",
                "recurring": {
                  "enabled": 1,
                  "schedule_name": "Quarterly membership fee",
                  "recurring_type": 3,
                  "amount": "25.50",
                  "start_date": "2030-01-10",
                  "day_of_debit": "FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=15",
                  "day_of_debit_dtstart": "2030-02-15"
                }
              }
            }
            JSON
            http = Net::HTTP.new(uri.host, uri.port)
            http.use_ssl = uri.scheme == "https"
            http.open_timeout = 30
            http.read_timeout = 30
            puts http.request(request).body
components:
  schemas:
    SubscriptionCreateRequest:
      type: object
      description: >-
        创建待激活订阅。客户引用与新客户资料二选一；新客户必须完整提供
        firstName、lastName、emailAddress。订阅定义必须且只能选择手填、模板或 By agreement
        一种。手填模式至少启用一个收费段；非模板模式必须提供 plan_name。推荐使用 customer、plan 嵌套参数。
      properties:
        merchant_id:
          type: string
          description: 业务标识，由 BillBuddy 分配。
          maxLength: 20
          minLength: 1
        notify_email:
          description: >-
            是否允许本交易或订阅相关邮件通知。false 表示禁止，true 表示允许；省略或 null 时使用该业务的 API
            邮件通知设置，默认关闭。实际发送受通知偏好和适用规则约束。 也兼容 0/1 和字符串 "0"/"1"。
          nullable: true
          oneOf:
            - type: boolean
              nullable: true
            - type: integer
              enum:
                - 0
                - 1
            - type: string
              enum:
                - '0'
                - '1'
        notify_sms:
          description: >-
            是否允许本交易或订阅相关短信通知。false 表示禁止，true 表示允许；省略或 null 时使用该业务的 API
            短信通知设置，默认关闭。实际发送受通知偏好和适用规则约束。 也兼容 0/1 和字符串 "0"/"1"。
          nullable: true
          oneOf:
            - type: boolean
              nullable: true
            - type: integer
              enum:
                - 0
                - 1
            - type: string
              enum:
                - '0'
                - '1'
        customer:
          type: object
          description: 复用客户时传 customer_id；新建客户时传 firstName、lastName、emailAddress，二者互斥。
          properties:
            customer_id:
              type: string
              description: BillBuddy 客户标识，须为当前商户及业务范围内的有效客户。
              nullable: true
              maxLength: 32
            firstName:
              type: string
              description: 新客户名字。创建新客户时必填。
              nullable: true
              maxLength: 100
            lastName:
              type: string
              description: 新客户姓氏。创建新客户时必填。
              nullable: true
              maxLength: 100
            emailAddress:
              type: string
              description: 新客户主邮箱。创建新客户时必填；同一商户及业务范围内已有此邮箱时请改传已有 customer_id。
              nullable: true
              format: email
              maxLength: 500
            mobilePhoneNumber:
              type: string
              description: 客户手机号。
              nullable: true
              maxLength: 20
            addressLine1:
              type: string
              description: 客户地址第一行。
              nullable: true
              maxLength: 255
            addressSuburb:
              type: string
              description: 客户城市或区。
              nullable: true
              maxLength: 100
            addressState:
              type: string
              description: 客户州或省。
              nullable: true
              maxLength: 100
            addressPostCode:
              type: string
              description: 客户邮编。
              nullable: true
              maxLength: 50
        plan:
          type: object
          description: 恰好选择一种模式：recurring/one_off 手填；template_id 模板；schedule By agreement。
          properties:
            plan_name:
              type: string
              description: 订阅名称；模板模式可省略或传 null 并继承模板名称，其他模式必填且不能为空。
              maxLength: 255
              nullable: true
            description:
              type: string
              description: 说明。
              nullable: true
              maxLength: 500
            recurring:
              type: object
              properties:
                enabled:
                  description: >-
                    是否启用该段，推荐传整数 1 启用、0 关闭；亦兼容 true/false 和字符串
                    "0"/"1"。手填模式至少启用一段，启用时请完整提供该段标注的必填字段。
                  nullable: true
                  oneOf:
                    - type: integer
                      enum:
                        - 0
                        - 1
                        - null
                      nullable: true
                    - type: boolean
                    - type: string
                      enum:
                        - '0'
                        - '1'
                schedule_name:
                  type: string
                  description: 扣费计划名称。recurring.enabled=1（或 true）时必填且不能为 null，最长 255 字符。
                  nullable: true
                  maxLength: 255
                recurring_type:
                  type: integer
                  description: >-
                    1 按成功期数；2 按累计金额；3 持续至取消；4 按结束日期。此处不允许 5，By agreement 请使用
                    plan.schedule。recurring.enabled=true 时必填。
                  nullable: true
                  enum:
                    - 1
                    - 2
                    - 3
                    - 4
                    - null
                amount:
                  oneOf:
                    - type: number
                      multipleOf: 0.01
                      minimum: 0.01
                      maximum: 9999999.99
                      nullable: true
                    - type: string
                      pattern: ^\d+(\.\d{1,2})?$
                      allOf:
                        - pattern: >-
                            ^0*(?:[1-9]\d{0,6}(?:\.\d{1,2})?|0\.(?:0[1-9]|[1-9]\d?))$
                  description: >-
                    单次或每期金额。 金额以主货币单位计，例如 10.50 表示 AUD
                    10.50，最多两位小数。接受数值或数字字符串，最小 0.01、最大 9999999.99，两种类型遵守相同范围限制。
                    recurring.enabled=true 时必填。
                  nullable: true
                start_date:
                  type: string
                  description: >-
                    起始日期。 当 recurring.enabled 为 1 时必填，且不能为 null。 建议使用
                    YYYY-MM-DD。 必须不早于服务业务时区的明天。
                  nullable: true
                  format: date
                end_date:
                  type: string
                  description: >-
                    结束日期，建议使用 YYYY-MM-DD。启用 recurring 且 recurring_type=4 时必填且不能为
                    null；提供时不得早于同一段的 start_date。
                  nullable: true
                  format: date
                end_terms:
                  type: integer
                  description: >-
                    成功扣费期数，正整数，最小为 1。启用 recurring 且 recurring_type=1 时必填且不能为
                    null。
                  nullable: true
                  minimum: 1
                total_amount:
                  oneOf:
                    - type: number
                      multipleOf: 0.01
                      minimum: 0.01
                      nullable: true
                    - type: string
                      pattern: ^\d+(\.\d{1,2})?$
                      allOf:
                        - pattern: >-
                            ^0*(?:[1-9]\d*(?:\.\d{1,2})?|0\.(?:0[1-9]|[1-9]\d?))$
                  description: >-
                    目标累计金额，以主货币单位计，最多两位小数，最小为 0.01，接受数值或数字字符串。启用 recurring 且
                    recurring_type=2 时必填且不能为 null。同时提供同段 amount 和 total_amount
                    时，amount 不得大于 total_amount。
                  nullable: true
                day_of_debit:
                  type: string
                  description: >-
                    RFC 5545 RRULE 扣费规则。启用 recurring 段时必填且不能为 null；频率写法见接入说明。
                    不得使同周期扣费日早于该段 start_date。非标准间隔必须声明 process_type=12 并提供
                    custom_cycle_type/custom_cycle。
                  nullable: true
                  maxLength: 255
                process_type:
                  type: integer
                  description: 仅自定义频率时传 12。其他频率由 RRULE 推断，请省略此字段。 非标准 RRULE 间隔必须使用 Custom。
                  nullable: true
                  enum:
                    - 12
                    - null
                custom_cycle_type:
                  type: integer
                  description: >-
                    process_type=12 时必填。1 天、2 周、3 月、4 年；须与 RRULE 的 FREQ 对应。非
                    Custom 模式不得填写非空的自定义周期参数。 当 recurring.process_type 为 12
                    时必填，且不能为 null。
                  nullable: true
                  enum:
                    - 1
                    - 2
                    - 3
                    - 4
                    - null
                custom_cycle:
                  type: integer
                  description: >-
                    process_type=12 时必填，正整数且必须等于 RRULE 的 INTERVAL。以天为单位时最小
                    5，其他单位最小 1。 当 recurring.process_type 为 12 时必填，且不能为 null。
                  nullable: true
                  minimum: 1
                day_of_debit_dtstart:
                  type: string
                  description: >-
                    首次扣费锚点。季度、半年、年度频率必填，推荐 YYYY-MM-DD；必须严格晚于对应
                    start_date，且在其后一个计费周期内（3/6/12 个月，按月底截断）。也支持
                    MM-DD，此时须为合法月日，不适用上述完整日期窗口限制。
                  nullable: true
                  pattern: ^(\d{4}-)?\d{2}-\d{2}$
              description: 周期订阅定义。
              nullable: true
              not:
                anyOf:
                  - allOf:
                      - required:
                          - enabled
                        properties:
                          enabled:
                            enum:
                              - true
                              - 1
                              - '1'
                      - not:
                          required:
                            - schedule_name
                            - recurring_type
                            - amount
                            - start_date
                            - day_of_debit
                          properties:
                            schedule_name:
                              not:
                                enum:
                                  - null
                              minLength: 1
                            recurring_type:
                              not:
                                enum:
                                  - null
                            amount:
                              not:
                                enum:
                                  - null
                            start_date:
                              not:
                                enum:
                                  - null
                              minLength: 1
                            day_of_debit:
                              not:
                                enum:
                                  - null
                              minLength: 1
                  - allOf:
                      - required:
                          - enabled
                          - recurring_type
                        properties:
                          enabled:
                            enum:
                              - true
                              - 1
                              - '1'
                          recurring_type:
                            enum:
                              - 1
                      - not:
                          required:
                            - end_terms
                          properties:
                            end_terms:
                              not:
                                enum:
                                  - null
                  - allOf:
                      - required:
                          - enabled
                          - recurring_type
                        properties:
                          enabled:
                            enum:
                              - true
                              - 1
                              - '1'
                          recurring_type:
                            enum:
                              - 2
                      - not:
                          required:
                            - total_amount
                          properties:
                            total_amount:
                              not:
                                enum:
                                  - null
                  - allOf:
                      - required:
                          - enabled
                          - recurring_type
                        properties:
                          enabled:
                            enum:
                              - true
                              - 1
                              - '1'
                          recurring_type:
                            enum:
                              - 4
                      - not:
                          required:
                            - end_date
                          properties:
                            end_date:
                              not:
                                enum:
                                  - null
                              minLength: 1
                  - allOf:
                      - required:
                          - process_type
                        properties:
                          process_type:
                            enum:
                              - 12
                      - not:
                          required:
                            - custom_cycle_type
                            - custom_cycle
                          properties:
                            custom_cycle_type:
                              not:
                                enum:
                                  - null
                            custom_cycle:
                              not:
                                enum:
                                  - null
                  - allOf:
                      - required:
                          - process_type
                          - custom_cycle_type
                        properties:
                          process_type:
                            enum:
                              - 12
                          custom_cycle_type:
                            enum:
                              - 1
                      - not:
                          required:
                            - custom_cycle
                          properties:
                            custom_cycle:
                              not:
                                enum:
                                  - null
                              minimum: 5
                  - allOf:
                      - required:
                          - custom_cycle_type
                        properties:
                          custom_cycle_type:
                            not:
                              enum:
                                - null
                      - not:
                          required:
                            - process_type
                          properties:
                            process_type:
                              enum:
                                - 12
                  - allOf:
                      - required:
                          - custom_cycle
                        properties:
                          custom_cycle:
                            not:
                              enum:
                                - null
                      - not:
                          required:
                            - process_type
                          properties:
                            process_type:
                              enum:
                                - 12
            one_off:
              type: object
              properties:
                enabled:
                  description: >-
                    是否启用该段，推荐传整数 1 启用、0 关闭；亦兼容 true/false 和字符串
                    "0"/"1"。手填模式至少启用一段，启用时请完整提供该段标注的必填字段。
                  nullable: true
                  oneOf:
                    - type: integer
                      enum:
                        - 0
                        - 1
                        - null
                      nullable: true
                    - type: boolean
                    - type: string
                      enum:
                        - '0'
                        - '1'
                schedule_name:
                  type: string
                  description: 扣费计划名称。 当 one_off.enabled 为 1 时必填，且不能为 null。
                  nullable: true
                  maxLength: 255
                amount:
                  oneOf:
                    - type: number
                      multipleOf: 0.01
                      minimum: 0.01
                      maximum: 9999999.99
                      nullable: true
                    - type: string
                      pattern: ^\d+(\.\d{1,2})?$
                      allOf:
                        - pattern: >-
                            ^0*(?:[1-9]\d{0,6}(?:\.\d{1,2})?|0\.(?:0[1-9]|[1-9]\d?))$
                  description: >-
                    单次或每期金额。 金额以主货币单位计，例如 10.50 表示 AUD
                    10.50，最多两位小数。接受数值或数字字符串，最小 0.01、最大 9999999.99，两种类型遵守相同范围限制。
                    当 one_off.enabled 为 1 时必填，且不能为 null。
                  nullable: true
                description:
                  type: string
                  description: 说明。
                  nullable: true
                  maxLength: 500
                payment_date:
                  type: string
                  description: >-
                    一次性扣款日期，至少为服务业务时区的明天。 当 one_off.enabled 为 1 时必填，且不能为 null。
                    建议使用 YYYY-MM-DD。 必须不早于服务业务时区的明天。
                  nullable: true
                  format: date
              description: 一次性付款定义；和 recurring 同启用时作为 Deposit，单独使用时为 One-off。
              nullable: true
              not:
                anyOf:
                  - allOf:
                      - required:
                          - enabled
                        properties:
                          enabled:
                            enum:
                              - true
                              - 1
                              - '1'
                      - not:
                          required:
                            - schedule_name
                            - amount
                            - payment_date
                          properties:
                            schedule_name:
                              not:
                                enum:
                                  - null
                              minLength: 1
                            amount:
                              not:
                                enum:
                                  - null
                            payment_date:
                              not:
                                enum:
                                  - null
                              minLength: 1
            schedule:
              type: object
              properties:
                type:
                  type: integer
                  description: >-
                    By agreement 首条计划类型：1 按成功期数；2 按累计金额；3 持续至取消；4 按结束日期；6
                    One-off。不支持 5 或 7。
                  enum:
                    - 1
                    - 2
                    - 3
                    - 4
                    - 6
                amount:
                  oneOf:
                    - type: number
                      multipleOf: 0.01
                      minimum: 0.01
                      maximum: 9999999.99
                    - type: string
                      pattern: ^\d+(\.\d{1,2})?$
                      allOf:
                        - pattern: >-
                            ^0*(?:[1-9]\d{0,6}(?:\.\d{1,2})?|0\.(?:0[1-9]|[1-9]\d?))$
                  description: >-
                    单次或每期金额。 金额以主货币单位计，例如 10.50 表示 AUD
                    10.50，最多两位小数。接受数值或数字字符串，最小 0.01、最大 9999999.99，两种类型遵守相同范围限制。
                    当 schedule 非空时必填，且不能为 null。
                start_date:
                  type: string
                  description: >-
                    起始日期。 当 schedule 非空时必填，且不能为 null。 建议使用 YYYY-MM-DD。
                    必须不早于服务业务时区的明天。
                  format: date
                  minLength: 1
                schedule_name:
                  type: string
                  description: 扣费计划名称。提供非空 schedule 时必填且不能为 null，最长 255 字符。
                  maxLength: 255
                  minLength: 1
                total_amount:
                  oneOf:
                    - type: number
                      multipleOf: 0.01
                      minimum: 0.01
                      nullable: true
                    - type: string
                      pattern: ^\d+(\.\d{1,2})?$
                      allOf:
                        - pattern: >-
                            ^0*(?:[1-9]\d*(?:\.\d{1,2})?|0\.(?:0[1-9]|[1-9]\d?))$
                  description: >-
                    目标累计金额，以主货币单位计，最多两位小数，最小为 0.01，接受数值或数字字符串。schedule.type=2
                    时必填且不能为 null。同时提供同段 amount 和 total_amount 时，amount 不得大于
                    total_amount。
                  nullable: true
                day_of_debit:
                  type: string
                  description: >-
                    RFC 5545 RRULE 扣费规则。schedule.type=1/2/3/4 时必填且不能为
                    null；type=6（一次性）可省略。 不得使同周期扣费日早于该段 start_date。非标准间隔必须声明
                    process_type=12 并提供 custom_cycle_type/custom_cycle。
                  nullable: true
                  maxLength: 255
                end_date:
                  type: string
                  description: >-
                    结束日期，建议使用 YYYY-MM-DD。schedule.type=4 时必填且不能为
                    null；提供时不得早于同一段的 start_date。
                  nullable: true
                  format: date
                end_terms:
                  type: integer
                  description: 成功扣费期数，正整数，最小为 1。schedule.type=1 时必填且不能为 null。
                  nullable: true
                  minimum: 1
                day_of_debit_dtstart:
                  type: string
                  description: >-
                    首次扣费锚点。季度、半年、年度频率必填，推荐 YYYY-MM-DD；必须严格晚于对应
                    start_date，且在其后一个计费周期内（3/6/12 个月，按月底截断）。也支持
                    MM-DD，此时须为合法月日，不适用上述完整日期窗口限制。
                  nullable: true
                  pattern: ^(\d{4}-)?\d{2}-\d{2}$
                process_type:
                  type: integer
                  description: 仅自定义频率时传 12。其他频率由 RRULE 推断，请省略此字段。 非标准 RRULE 间隔必须使用 Custom。
                  nullable: true
                  enum:
                    - 12
                    - null
                custom_cycle_type:
                  type: integer
                  description: >-
                    process_type=12 时必填。1 天、2 周、3 月、4 年；须与 RRULE 的 FREQ 对应。非
                    Custom 模式不得填写非空的自定义周期参数。 当 schedule.process_type 为 12
                    时必填，且不能为 null。
                  nullable: true
                  enum:
                    - 1
                    - 2
                    - 3
                    - 4
                    - null
                custom_cycle:
                  type: integer
                  description: >-
                    process_type=12 时必填，正整数且必须等于 RRULE 的 INTERVAL。以天为单位时最小
                    5，其他单位最小 1。 当 schedule.process_type 为 12 时必填，且不能为 null。
                  nullable: true
                  minimum: 1
              description: By agreement 首条扣费计划定义。与 recurring/one_off 手填模式、template_id 模式互斥。
              nullable: true
              required:
                - type
                - amount
                - start_date
                - schedule_name
              not:
                anyOf:
                  - allOf:
                      - required:
                          - type
                        properties:
                          type:
                            enum:
                              - 1
                      - not:
                          required:
                            - end_terms
                          properties:
                            end_terms:
                              not:
                                enum:
                                  - null
                  - allOf:
                      - required:
                          - type
                        properties:
                          type:
                            enum:
                              - 2
                      - not:
                          required:
                            - total_amount
                          properties:
                            total_amount:
                              not:
                                enum:
                                  - null
                  - allOf:
                      - required:
                          - type
                        properties:
                          type:
                            enum:
                              - 4
                      - not:
                          required:
                            - end_date
                          properties:
                            end_date:
                              not:
                                enum:
                                  - null
                              minLength: 1
                  - allOf:
                      - required:
                          - type
                        properties:
                          type:
                            enum:
                              - 1
                              - 2
                              - 3
                              - 4
                      - not:
                          required:
                            - day_of_debit
                          properties:
                            day_of_debit:
                              not:
                                enum:
                                  - null
                              minLength: 1
                  - allOf:
                      - required:
                          - process_type
                        properties:
                          process_type:
                            enum:
                              - 12
                      - not:
                          required:
                            - custom_cycle_type
                            - custom_cycle
                          properties:
                            custom_cycle_type:
                              not:
                                enum:
                                  - null
                            custom_cycle:
                              not:
                                enum:
                                  - null
                  - allOf:
                      - required:
                          - process_type
                          - custom_cycle_type
                        properties:
                          process_type:
                            enum:
                              - 12
                          custom_cycle_type:
                            enum:
                              - 1
                      - not:
                          required:
                            - custom_cycle
                          properties:
                            custom_cycle:
                              not:
                                enum:
                                  - null
                              minimum: 5
                  - allOf:
                      - required:
                          - custom_cycle_type
                        properties:
                          custom_cycle_type:
                            not:
                              enum:
                                - null
                      - not:
                          required:
                            - process_type
                          properties:
                            process_type:
                              enum:
                                - 12
                  - allOf:
                      - required:
                          - custom_cycle
                        properties:
                          custom_cycle:
                            not:
                              enum:
                                - null
                      - not:
                          required:
                            - process_type
                          properties:
                            process_type:
                              enum:
                                - 12
            surcharge_config:
              type: array
              description: >-
                附加费选项，建议提交字符串数组，可选项见 Schema 和示例。省略、null
                或空数组表示全部关闭；非数组也按全部关闭处理，未知选项不启用收费。credit_card_surcharge
                表示信用卡附加费分组。实际是否收取受商户业务规则及支付方式限制。
                手填模式使用此设置；模板模式使用模板的附加费选项，本字段不覆盖模板；By agreement 的 schedule
                模式不支持此设置。 卡类附加费受适用收费政策限制；政策不允许收费时，即使提交卡类选项也不启用收费。
              nullable: true
              items:
                type: string
                description: 附加费选项。
                enum:
                  - direct_debit_surcharge
                  - visa_domestic_surcharge
                  - visa_international_surcharge
                  - mastercard_domestic_surcharge
                  - mastercard_international_surcharge
                  - amex_domestic_surcharge
                  - amex_international_surcharge
                  - credit_card_surcharge
            dishonour_fee_switch:
              description: >-
                是否收取客户扣款退票费，省略或 null 默认关闭。启用须已有适用的正数费额配置，实际金额以签署条款为准。也兼容 0/1
                和字符串 "0"/"1"。 仅适用于 recurring/one_off 手填模式；模板模式和 By agreement 的
                schedule 模式不支持通过本字段设置。
              nullable: true
              oneOf:
                - type: boolean
                  nullable: true
                - type: integer
                  enum:
                    - 0
                    - 1
                - type: string
                  enum:
                    - '0'
                    - '1'
            template_id:
              type: string
              description: >-
                可用模板 ID，来自 GET /api/v2/plan/list；只接受启用、ACTIVE、非阶梯且非 By agreement
                的模板。
              nullable: true
              maxLength: 20
            start_date:
              type: string
              description: 模板中 recurring 段的日期覆盖值。模板含 recurring 时必填；至少服务业务时区的明天。
              nullable: true
              format: date
            payment_date:
              type: string
              description: 模板中 one_off 段的日期覆盖值。模板含 one_off 时必填；至少服务业务时区的明天。
              nullable: true
              format: date
        customer_id:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `customer.customer_id`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            BillBuddy 客户标识，须为当前商户及业务范围内的有效客户。
          nullable: true
          maxLength: 32
        firstName:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `customer.firstName`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            新客户名字。创建新客户时必填。
          nullable: true
          maxLength: 100
        lastName:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `customer.lastName`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            新客户姓氏。创建新客户时必填。
          nullable: true
          maxLength: 100
        emailAddress:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `customer.emailAddress`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            新客户主邮箱。创建新客户时必填；同一商户及业务范围内已有此邮箱时请改传已有 customer_id。
          nullable: true
          format: email
          maxLength: 500
        mobilePhoneNumber:
          type: string
          description: >-
            仍支持的顶层替代参数；推荐使用
            `customer.mobilePhoneNumber`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。


            客户手机号。
          nullable: true
          maxLength: 20
        addressLine1:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `customer.addressLine1`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            客户地址第一行。
          nullable: true
          maxLength: 255
        addressSuburb:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `customer.addressSuburb`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            客户城市或区。
          nullable: true
          maxLength: 100
        addressState:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `customer.addressState`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            客户州或省。
          nullable: true
          maxLength: 100
        addressPostCode:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `customer.addressPostCode`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            客户邮编。
          nullable: true
          maxLength: 50
        plan_name:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `plan.plan_name`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            订阅名称；模板模式可省略或传 null 并继承模板名称，其他模式必填且不能为空。
          maxLength: 255
          nullable: true
        description:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `plan.description`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            说明。
          nullable: true
          maxLength: 500
        recurring:
          type: object
          properties:
            enabled:
              description: >-
                是否启用该段，推荐传整数 1 启用、0 关闭；亦兼容 true/false 和字符串
                "0"/"1"。手填模式至少启用一段，启用时请完整提供该段标注的必填字段。
              nullable: true
              oneOf:
                - type: integer
                  enum:
                    - 0
                    - 1
                    - null
                  nullable: true
                - type: boolean
                - type: string
                  enum:
                    - '0'
                    - '1'
            schedule_name:
              type: string
              description: 扣费计划名称。recurring.enabled=1（或 true）时必填且不能为 null，最长 255 字符。
              nullable: true
              maxLength: 255
            recurring_type:
              type: integer
              description: >-
                1 按成功期数；2 按累计金额；3 持续至取消；4 按结束日期。此处不允许 5，By agreement 请使用
                plan.schedule。recurring.enabled=true 时必填。
              nullable: true
              enum:
                - 1
                - 2
                - 3
                - 4
                - null
            amount:
              oneOf:
                - type: number
                  multipleOf: 0.01
                  minimum: 0.01
                  maximum: 9999999.99
                  nullable: true
                - type: string
                  pattern: ^\d+(\.\d{1,2})?$
                  allOf:
                    - pattern: >-
                        ^0*(?:[1-9]\d{0,6}(?:\.\d{1,2})?|0\.(?:0[1-9]|[1-9]\d?))$
              description: >-
                单次或每期金额。 金额以主货币单位计，例如 10.50 表示 AUD 10.50，最多两位小数。接受数值或数字字符串，最小
                0.01、最大 9999999.99，两种类型遵守相同范围限制。 recurring.enabled=true 时必填。
              nullable: true
            start_date:
              type: string
              description: >-
                起始日期。 当 recurring.enabled 为 1 时必填，且不能为 null。 建议使用 YYYY-MM-DD。
                必须不早于服务业务时区的明天。
              nullable: true
              format: date
            end_date:
              type: string
              description: >-
                结束日期，建议使用 YYYY-MM-DD。启用 recurring 且 recurring_type=4 时必填且不能为
                null；提供时不得早于同一段的 start_date。
              nullable: true
              format: date
            end_terms:
              type: integer
              description: 成功扣费期数，正整数，最小为 1。启用 recurring 且 recurring_type=1 时必填且不能为 null。
              nullable: true
              minimum: 1
            total_amount:
              oneOf:
                - type: number
                  multipleOf: 0.01
                  minimum: 0.01
                  nullable: true
                - type: string
                  pattern: ^\d+(\.\d{1,2})?$
                  allOf:
                    - pattern: ^0*(?:[1-9]\d*(?:\.\d{1,2})?|0\.(?:0[1-9]|[1-9]\d?))$
              description: >-
                目标累计金额，以主货币单位计，最多两位小数，最小为 0.01，接受数值或数字字符串。启用 recurring 且
                recurring_type=2 时必填且不能为 null。同时提供同段 amount 和 total_amount
                时，amount 不得大于 total_amount。
              nullable: true
            day_of_debit:
              type: string
              description: >-
                RFC 5545 RRULE 扣费规则。启用 recurring 段时必填且不能为 null；频率写法见接入说明。
                不得使同周期扣费日早于该段 start_date。非标准间隔必须声明 process_type=12 并提供
                custom_cycle_type/custom_cycle。
              nullable: true
              maxLength: 255
            process_type:
              type: integer
              description: 仅自定义频率时传 12。其他频率由 RRULE 推断，请省略此字段。 非标准 RRULE 间隔必须使用 Custom。
              nullable: true
              enum:
                - 12
                - null
            custom_cycle_type:
              type: integer
              description: >-
                process_type=12 时必填。1 天、2 周、3 月、4 年；须与 RRULE 的 FREQ 对应。非 Custom
                模式不得填写非空的自定义周期参数。 当 recurring.process_type 为 12 时必填，且不能为 null。
              nullable: true
              enum:
                - 1
                - 2
                - 3
                - 4
                - null
            custom_cycle:
              type: integer
              description: >-
                process_type=12 时必填，正整数且必须等于 RRULE 的 INTERVAL。以天为单位时最小 5，其他单位最小
                1。 当 recurring.process_type 为 12 时必填，且不能为 null。
              nullable: true
              minimum: 1
            day_of_debit_dtstart:
              type: string
              description: >-
                首次扣费锚点。季度、半年、年度频率必填，推荐 YYYY-MM-DD；必须严格晚于对应
                start_date，且在其后一个计费周期内（3/6/12 个月，按月底截断）。也支持
                MM-DD，此时须为合法月日，不适用上述完整日期窗口限制。
              nullable: true
              pattern: ^(\d{4}-)?\d{2}-\d{2}$
          description: |-
            仍支持的顶层替代参数；推荐使用 `plan.recurring`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            周期订阅定义。
          nullable: true
          not:
            anyOf:
              - allOf:
                  - required:
                      - enabled
                    properties:
                      enabled:
                        enum:
                          - true
                          - 1
                          - '1'
                  - not:
                      required:
                        - schedule_name
                        - recurring_type
                        - amount
                        - start_date
                        - day_of_debit
                      properties:
                        schedule_name:
                          not:
                            enum:
                              - null
                          minLength: 1
                        recurring_type:
                          not:
                            enum:
                              - null
                        amount:
                          not:
                            enum:
                              - null
                        start_date:
                          not:
                            enum:
                              - null
                          minLength: 1
                        day_of_debit:
                          not:
                            enum:
                              - null
                          minLength: 1
              - allOf:
                  - required:
                      - enabled
                      - recurring_type
                    properties:
                      enabled:
                        enum:
                          - true
                          - 1
                          - '1'
                      recurring_type:
                        enum:
                          - 1
                  - not:
                      required:
                        - end_terms
                      properties:
                        end_terms:
                          not:
                            enum:
                              - null
              - allOf:
                  - required:
                      - enabled
                      - recurring_type
                    properties:
                      enabled:
                        enum:
                          - true
                          - 1
                          - '1'
                      recurring_type:
                        enum:
                          - 2
                  - not:
                      required:
                        - total_amount
                      properties:
                        total_amount:
                          not:
                            enum:
                              - null
              - allOf:
                  - required:
                      - enabled
                      - recurring_type
                    properties:
                      enabled:
                        enum:
                          - true
                          - 1
                          - '1'
                      recurring_type:
                        enum:
                          - 4
                  - not:
                      required:
                        - end_date
                      properties:
                        end_date:
                          not:
                            enum:
                              - null
                          minLength: 1
              - allOf:
                  - required:
                      - process_type
                    properties:
                      process_type:
                        enum:
                          - 12
                  - not:
                      required:
                        - custom_cycle_type
                        - custom_cycle
                      properties:
                        custom_cycle_type:
                          not:
                            enum:
                              - null
                        custom_cycle:
                          not:
                            enum:
                              - null
              - allOf:
                  - required:
                      - process_type
                      - custom_cycle_type
                    properties:
                      process_type:
                        enum:
                          - 12
                      custom_cycle_type:
                        enum:
                          - 1
                  - not:
                      required:
                        - custom_cycle
                      properties:
                        custom_cycle:
                          not:
                            enum:
                              - null
                          minimum: 5
              - allOf:
                  - required:
                      - custom_cycle_type
                    properties:
                      custom_cycle_type:
                        not:
                          enum:
                            - null
                  - not:
                      required:
                        - process_type
                      properties:
                        process_type:
                          enum:
                            - 12
              - allOf:
                  - required:
                      - custom_cycle
                    properties:
                      custom_cycle:
                        not:
                          enum:
                            - null
                  - not:
                      required:
                        - process_type
                      properties:
                        process_type:
                          enum:
                            - 12
        one_off:
          type: object
          properties:
            enabled:
              description: >-
                是否启用该段，推荐传整数 1 启用、0 关闭；亦兼容 true/false 和字符串
                "0"/"1"。手填模式至少启用一段，启用时请完整提供该段标注的必填字段。
              nullable: true
              oneOf:
                - type: integer
                  enum:
                    - 0
                    - 1
                    - null
                  nullable: true
                - type: boolean
                - type: string
                  enum:
                    - '0'
                    - '1'
            schedule_name:
              type: string
              description: 扣费计划名称。 当 one_off.enabled 为 1 时必填，且不能为 null。
              nullable: true
              maxLength: 255
            amount:
              oneOf:
                - type: number
                  multipleOf: 0.01
                  minimum: 0.01
                  maximum: 9999999.99
                  nullable: true
                - type: string
                  pattern: ^\d+(\.\d{1,2})?$
                  allOf:
                    - pattern: >-
                        ^0*(?:[1-9]\d{0,6}(?:\.\d{1,2})?|0\.(?:0[1-9]|[1-9]\d?))$
              description: >-
                单次或每期金额。 金额以主货币单位计，例如 10.50 表示 AUD 10.50，最多两位小数。接受数值或数字字符串，最小
                0.01、最大 9999999.99，两种类型遵守相同范围限制。 当 one_off.enabled 为 1 时必填，且不能为
                null。
              nullable: true
            description:
              type: string
              description: 说明。
              nullable: true
              maxLength: 500
            payment_date:
              type: string
              description: >-
                一次性扣款日期，至少为服务业务时区的明天。 当 one_off.enabled 为 1 时必填，且不能为 null。 建议使用
                YYYY-MM-DD。 必须不早于服务业务时区的明天。
              nullable: true
              format: date
          description: |-
            仍支持的顶层替代参数；推荐使用 `plan.one_off`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            一次性付款定义；和 recurring 同启用时作为 Deposit，单独使用时为 One-off。
          nullable: true
          not:
            anyOf:
              - allOf:
                  - required:
                      - enabled
                    properties:
                      enabled:
                        enum:
                          - true
                          - 1
                          - '1'
                  - not:
                      required:
                        - schedule_name
                        - amount
                        - payment_date
                      properties:
                        schedule_name:
                          not:
                            enum:
                              - null
                          minLength: 1
                        amount:
                          not:
                            enum:
                              - null
                        payment_date:
                          not:
                            enum:
                              - null
                          minLength: 1
        schedule:
          type: object
          properties:
            type:
              type: integer
              description: >-
                By agreement 首条计划类型：1 按成功期数；2 按累计金额；3 持续至取消；4 按结束日期；6
                One-off。不支持 5 或 7。
              enum:
                - 1
                - 2
                - 3
                - 4
                - 6
            amount:
              oneOf:
                - type: number
                  multipleOf: 0.01
                  minimum: 0.01
                  maximum: 9999999.99
                - type: string
                  pattern: ^\d+(\.\d{1,2})?$
                  allOf:
                    - pattern: >-
                        ^0*(?:[1-9]\d{0,6}(?:\.\d{1,2})?|0\.(?:0[1-9]|[1-9]\d?))$
              description: >-
                单次或每期金额。 金额以主货币单位计，例如 10.50 表示 AUD 10.50，最多两位小数。接受数值或数字字符串，最小
                0.01、最大 9999999.99，两种类型遵守相同范围限制。 当 schedule 非空时必填，且不能为 null。
            start_date:
              type: string
              description: >-
                起始日期。 当 schedule 非空时必填，且不能为 null。 建议使用 YYYY-MM-DD。
                必须不早于服务业务时区的明天。
              format: date
              minLength: 1
            schedule_name:
              type: string
              description: 扣费计划名称。提供非空 schedule 时必填且不能为 null，最长 255 字符。
              maxLength: 255
              minLength: 1
            total_amount:
              oneOf:
                - type: number
                  multipleOf: 0.01
                  minimum: 0.01
                  nullable: true
                - type: string
                  pattern: ^\d+(\.\d{1,2})?$
                  allOf:
                    - pattern: ^0*(?:[1-9]\d*(?:\.\d{1,2})?|0\.(?:0[1-9]|[1-9]\d?))$
              description: >-
                目标累计金额，以主货币单位计，最多两位小数，最小为 0.01，接受数值或数字字符串。schedule.type=2
                时必填且不能为 null。同时提供同段 amount 和 total_amount 时，amount 不得大于
                total_amount。
              nullable: true
            day_of_debit:
              type: string
              description: >-
                RFC 5545 RRULE 扣费规则。schedule.type=1/2/3/4 时必填且不能为
                null；type=6（一次性）可省略。 不得使同周期扣费日早于该段 start_date。非标准间隔必须声明
                process_type=12 并提供 custom_cycle_type/custom_cycle。
              nullable: true
              maxLength: 255
            end_date:
              type: string
              description: >-
                结束日期，建议使用 YYYY-MM-DD。schedule.type=4 时必填且不能为 null；提供时不得早于同一段的
                start_date。
              nullable: true
              format: date
            end_terms:
              type: integer
              description: 成功扣费期数，正整数，最小为 1。schedule.type=1 时必填且不能为 null。
              nullable: true
              minimum: 1
            day_of_debit_dtstart:
              type: string
              description: >-
                首次扣费锚点。季度、半年、年度频率必填，推荐 YYYY-MM-DD；必须严格晚于对应
                start_date，且在其后一个计费周期内（3/6/12 个月，按月底截断）。也支持
                MM-DD，此时须为合法月日，不适用上述完整日期窗口限制。
              nullable: true
              pattern: ^(\d{4}-)?\d{2}-\d{2}$
            process_type:
              type: integer
              description: 仅自定义频率时传 12。其他频率由 RRULE 推断，请省略此字段。 非标准 RRULE 间隔必须使用 Custom。
              nullable: true
              enum:
                - 12
                - null
            custom_cycle_type:
              type: integer
              description: >-
                process_type=12 时必填。1 天、2 周、3 月、4 年；须与 RRULE 的 FREQ 对应。非 Custom
                模式不得填写非空的自定义周期参数。 当 schedule.process_type 为 12 时必填，且不能为 null。
              nullable: true
              enum:
                - 1
                - 2
                - 3
                - 4
                - null
            custom_cycle:
              type: integer
              description: >-
                process_type=12 时必填，正整数且必须等于 RRULE 的 INTERVAL。以天为单位时最小 5，其他单位最小
                1。 当 schedule.process_type 为 12 时必填，且不能为 null。
              nullable: true
              minimum: 1
          description: |-
            仍支持的顶层替代参数；推荐使用 `plan.schedule`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            By agreement 首条扣费计划定义。与 recurring/one_off 手填模式、template_id 模式互斥。
          nullable: true
          required:
            - type
            - amount
            - start_date
            - schedule_name
          not:
            anyOf:
              - allOf:
                  - required:
                      - type
                    properties:
                      type:
                        enum:
                          - 1
                  - not:
                      required:
                        - end_terms
                      properties:
                        end_terms:
                          not:
                            enum:
                              - null
              - allOf:
                  - required:
                      - type
                    properties:
                      type:
                        enum:
                          - 2
                  - not:
                      required:
                        - total_amount
                      properties:
                        total_amount:
                          not:
                            enum:
                              - null
              - allOf:
                  - required:
                      - type
                    properties:
                      type:
                        enum:
                          - 4
                  - not:
                      required:
                        - end_date
                      properties:
                        end_date:
                          not:
                            enum:
                              - null
                          minLength: 1
              - allOf:
                  - required:
                      - type
                    properties:
                      type:
                        enum:
                          - 1
                          - 2
                          - 3
                          - 4
                  - not:
                      required:
                        - day_of_debit
                      properties:
                        day_of_debit:
                          not:
                            enum:
                              - null
                          minLength: 1
              - allOf:
                  - required:
                      - process_type
                    properties:
                      process_type:
                        enum:
                          - 12
                  - not:
                      required:
                        - custom_cycle_type
                        - custom_cycle
                      properties:
                        custom_cycle_type:
                          not:
                            enum:
                              - null
                        custom_cycle:
                          not:
                            enum:
                              - null
              - allOf:
                  - required:
                      - process_type
                      - custom_cycle_type
                    properties:
                      process_type:
                        enum:
                          - 12
                      custom_cycle_type:
                        enum:
                          - 1
                  - not:
                      required:
                        - custom_cycle
                      properties:
                        custom_cycle:
                          not:
                            enum:
                              - null
                          minimum: 5
              - allOf:
                  - required:
                      - custom_cycle_type
                    properties:
                      custom_cycle_type:
                        not:
                          enum:
                            - null
                  - not:
                      required:
                        - process_type
                      properties:
                        process_type:
                          enum:
                            - 12
              - allOf:
                  - required:
                      - custom_cycle
                    properties:
                      custom_cycle:
                        not:
                          enum:
                            - null
                  - not:
                      required:
                        - process_type
                      properties:
                        process_type:
                          enum:
                            - 12
        surcharge_config:
          type: array
          description: >-
            仍支持的顶层替代参数；推荐使用 `plan.surcharge_config`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。


            附加费选项，建议提交字符串数组，可选项见 Schema 和示例。省略、null
            或空数组表示全部关闭；非数组也按全部关闭处理，未知选项不启用收费。credit_card_surcharge
            表示信用卡附加费分组。实际是否收取受商户业务规则及支付方式限制。
            手填模式使用此设置；模板模式使用模板的附加费选项，本字段不覆盖模板；By agreement 的 schedule 模式不支持此设置。
            卡类附加费受适用收费政策限制；政策不允许收费时，即使提交卡类选项也不启用收费。
          nullable: true
          items:
            type: string
            description: 附加费选项。
            enum:
              - direct_debit_surcharge
              - visa_domestic_surcharge
              - visa_international_surcharge
              - mastercard_domestic_surcharge
              - mastercard_international_surcharge
              - amex_domestic_surcharge
              - amex_international_surcharge
              - credit_card_surcharge
        dishonour_fee_switch:
          description: >-
            仍支持的顶层替代参数；推荐使用 `plan.dishonour_fee_switch`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。


            是否收取客户扣款退票费，省略或 null 默认关闭。启用须已有适用的正数费额配置，实际金额以签署条款为准。也兼容 0/1 和字符串
            "0"/"1"。 仅适用于 recurring/one_off 手填模式；模板模式和 By agreement 的 schedule
            模式不支持通过本字段设置。
          nullable: true
          oneOf:
            - type: boolean
              nullable: true
            - type: integer
              enum:
                - 0
                - 1
            - type: string
              enum:
                - '0'
                - '1'
        template_id:
          type: string
          description: >-
            仍支持的顶层替代参数；推荐使用 `plan.template_id`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。


            可用模板 ID，来自 GET /api/v2/plan/list；只接受启用、ACTIVE、非阶梯且非 By agreement
            的模板。
          nullable: true
          maxLength: 20
        start_date:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `plan.start_date`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            模板中 recurring 段的日期覆盖值。模板含 recurring 时必填；至少服务业务时区的明天。
          nullable: true
          format: date
        payment_date:
          type: string
          description: |-
            仍支持的顶层替代参数；推荐使用 `plan.payment_date`。同名嵌套字段与本字段同时提供时，以嵌套字段为准。

            模板中 one_off 段的日期覆盖值。模板含 one_off 时必填；至少服务业务时区的明天。
          nullable: true
          format: date
      required:
        - merchant_id
      not:
        anyOf:
          - not:
              oneOf:
                - allOf:
                    - anyOf:
                        - type: object
                          required:
                            - customer
                          properties:
                            customer:
                              type: object
                              required:
                                - customer_id
                              properties:
                                customer_id:
                                  type: string
                                  minLength: 1
                                  pattern: \S
                        - allOf:
                            - not:
                                type: object
                                required:
                                  - customer
                                properties:
                                  customer:
                                    type: object
                                    required:
                                      - customer_id
                                    properties:
                                      customer_id: {}
                            - type: object
                              required:
                                - customer_id
                              properties:
                                customer_id:
                                  type: string
                                  minLength: 1
                                  pattern: \S
                    - not:
                        anyOf:
                          - anyOf:
                              - type: object
                                required:
                                  - customer
                                properties:
                                  customer:
                                    type: object
                                    required:
                                      - firstName
                                    properties:
                                      firstName:
                                        type: string
                                        minLength: 1
                                        pattern: \S
                              - allOf:
                                  - not:
                                      type: object
                                      required:
                                        - customer
                                      properties:
                                        customer:
                                          type: object
                                          required:
                                            - firstName
                                          properties:
                                            firstName: {}
                                  - type: object
                                    required:
                                      - firstName
                                    properties:
                                      firstName:
                                        type: string
                                        minLength: 1
                                        pattern: \S
                          - anyOf:
                              - type: object
                                required:
                                  - customer
                                properties:
                                  customer:
                                    type: object
                                    required:
                                      - lastName
                                    properties:
                                      lastName:
                                        type: string
                                        minLength: 1
                                        pattern: \S
                              - allOf:
                                  - not:
                                      type: object
                                      required:
                                        - customer
                                      properties:
                                        customer:
                                          type: object
                                          required:
                                            - lastName
                                          properties:
                                            lastName: {}
                                  - type: object
                                    required:
                                      - lastName
                                    properties:
                                      lastName:
                                        type: string
                                        minLength: 1
                                        pattern: \S
                          - anyOf:
                              - type: object
                                required:
                                  - customer
                                properties:
                                  customer:
                                    type: object
                                    required:
                                      - emailAddress
                                    properties:
                                      emailAddress:
                                        type: string
                                        minLength: 1
                                        pattern: \S
                              - allOf:
                                  - not:
                                      type: object
                                      required:
                                        - customer
                                      properties:
                                        customer:
                                          type: object
                                          required:
                                            - emailAddress
                                          properties:
                                            emailAddress: {}
                                  - type: object
                                    required:
                                      - emailAddress
                                    properties:
                                      emailAddress:
                                        type: string
                                        minLength: 1
                                        pattern: \S
                - allOf:
                    - not:
                        anyOf:
                          - type: object
                            required:
                              - customer
                            properties:
                              customer:
                                type: object
                                required:
                                  - customer_id
                                properties:
                                  customer_id:
                                    type: string
                                    minLength: 1
                                    pattern: \S
                          - allOf:
                              - not:
                                  type: object
                                  required:
                                    - customer
                                  properties:
                                    customer:
                                      type: object
                                      required:
                                        - customer_id
                                      properties:
                                        customer_id: {}
                              - type: object
                                required:
                                  - customer_id
                                properties:
                                  customer_id:
                                    type: string
                                    minLength: 1
                                    pattern: \S
                    - anyOf:
                        - type: object
                          required:
                            - customer
                          properties:
                            customer:
                              type: object
                              required:
                                - firstName
                              properties:
                                firstName:
                                  type: string
                                  minLength: 1
                                  pattern: \S
                        - allOf:
                            - not:
                                type: object
                                required:
                                  - customer
                                properties:
                                  customer:
                                    type: object
                                    required:
                                      - firstName
                                    properties:
                                      firstName: {}
                            - type: object
                              required:
                                - firstName
                              properties:
                                firstName:
                                  type: string
                                  minLength: 1
                                  pattern: \S
                    - anyOf:
                        - type: object
                          required:
                            - customer
                          properties:
                            customer:
                              type: object
                              required:
                                - lastName
                              properties:
                                lastName:
                                  type: string
                                  minLength: 1
                                  pattern: \S
                        - allOf:
                            - not:
                                type: object
                                required:
                                  - customer
                                properties:
                                  customer:
                                    type: object
                                    required:
                                      - lastName
                                    properties:
                                      lastName: {}
                            - type: object
                              required:
                                - lastName
                              properties:
                                lastName:
                                  type: string
                                  minLength: 1
                                  pattern: \S
                    - anyOf:
                        - type: object
                          required:
                            - customer
                          properties:
                            customer:
                              type: object
                              required:
                                - emailAddress
                              properties:
                                emailAddress:
                                  type: string
                                  minLength: 1
                                  pattern: \S
                        - allOf:
                            - not:
                                type: object
                                required:
                                  - customer
                                properties:
                                  customer:
                                    type: object
                                    required:
                                      - emailAddress
                                    properties:
                                      emailAddress: {}
                            - type: object
                              required:
                                - emailAddress
                              properties:
                                emailAddress:
                                  type: string
                                  minLength: 1
                                  pattern: \S
          - not:
              oneOf:
                - allOf:
                    - anyOf:
                        - type: object
                          required:
                            - plan
                          properties:
                            plan:
                              type: object
                              required:
                                - template_id
                              properties:
                                template_id:
                                  type: string
                                  minLength: 1
                                  pattern: \S
                        - allOf:
                            - not:
                                type: object
                                required:
                                  - plan
                                properties:
                                  plan:
                                    type: object
                                    required:
                                      - template_id
                                    properties:
                                      template_id: {}
                            - type: object
                              required:
                                - template_id
                              properties:
                                template_id:
                                  type: string
                                  minLength: 1
                                  pattern: \S
                    - not:
                        anyOf:
                          - type: object
                            required:
                              - plan
                            properties:
                              plan:
                                type: object
                                required:
                                  - schedule
                                properties:
                                  schedule:
                                    type: object
                                    minProperties: 1
                          - allOf:
                              - not:
                                  type: object
                                  required:
                                    - plan
                                  properties:
                                    plan:
                                      type: object
                                      required:
                                        - schedule
                                      properties:
                                        schedule: {}
                              - type: object
                                required:
                                  - schedule
                                properties:
                                  schedule:
                                    type: object
                                    minProperties: 1
                    - not:
                        anyOf:
                          - anyOf:
                              - type: object
                                required:
                                  - plan
                                properties:
                                  plan:
                                    type: object
                                    required:
                                      - recurring
                                    properties:
                                      recurring:
                                        type: object
                                        minProperties: 1
                              - allOf:
                                  - not:
                                      type: object
                                      required:
                                        - plan
                                      properties:
                                        plan:
                                          type: object
                                          required:
                                            - recurring
                                          properties:
                                            recurring: {}
                                  - type: object
                                    required:
                                      - recurring
                                    properties:
                                      recurring:
                                        type: object
                                        minProperties: 1
                          - anyOf:
                              - type: object
                                required:
                                  - plan
                                properties:
                                  plan:
                                    type: object
                                    required:
                                      - one_off
                                    properties:
                                      one_off:
                                        type: object
                                        minProperties: 1
                              - allOf:
                                  - not:
                                      type: object
                                      required:
                                        - plan
                                      properties:
                                        plan:
                                          type: object
                                          required:
                                            - one_off
                                          properties:
                                            one_off: {}
                                  - type: object
                                    required:
                                      - one_off
                                    properties:
                                      one_off:
                                        type: object
                                        minProperties: 1
                - allOf:
                    - anyOf:
                        - type: object
                          required:
                            - plan
                          properties:
                            plan:
                              type: object
                              required:
                                - schedule
                              properties:
                                schedule:
                                  type: object
                                  minProperties: 1
                        - allOf:
                            - not:
                                type: object
                                required:
                                  - plan
                                properties:
                                  plan:
                                    type: object
                                    required:
                                      - schedule
                                    properties:
                                      schedule: {}
                            - type: object
                              required:
                                - schedule
                              properties:
                                schedule:
                                  type: object
                                  minProperties: 1
                    - not:
                        anyOf:
                          - type: object
                            required:
                              - plan
                            properties:
                              plan:
                                type: object
                                required:
                                  - template_id
                                properties:
                                  template_id:
                                    type: string
                                    minLength: 1
                                    pattern: \S
                          - allOf:
                              - not:
                                  type: object
                                  required:
                                    - plan
                                  properties:
                                    plan:
                                      type: object
                                      required:
                                        - template_id
                                      properties:
                                        template_id: {}
                              - type: object
                                required:
                                  - template_id
                                properties:
                                  template_id:
                                    type: string
                                    minLength: 1
                                    pattern: \S
                    - not:
                        anyOf:
                          - anyOf:
                              - type: object
                                required:
                                  - plan
                                properties:
                                  plan:
                                    type: object
                                    required:
                                      - recurring
                                    properties:
                                      recurring:
                                        type: object
                                        minProperties: 1
                              - allOf:
                                  - not:
                                      type: object
                                      required:
                                        - plan
                                      properties:
                                        plan:
                                          type: object
                                          required:
                                            - recurring
                                          properties:
                                            recurring: {}
                                  - type: object
                                    required:
                                      - recurring
                                    properties:
                                      recurring:
                                        type: object
                                        minProperties: 1
                          - anyOf:
                              - type: object
                                required:
                                  - plan
                                properties:
                                  plan:
                                    type: object
                                    required:
                                      - one_off
                                    properties:
                                      one_off:
                                        type: object
                                        minProperties: 1
                              - allOf:
                                  - not:
                                      type: object
                                      required:
                                        - plan
                                      properties:
                                        plan:
                                          type: object
                                          required:
                                            - one_off
                                          properties:
                                            one_off: {}
                                  - type: object
                                    required:
                                      - one_off
                                    properties:
                                      one_off:
                                        type: object
                                        minProperties: 1
                - allOf:
                    - anyOf:
                        - anyOf:
                            - type: object
                              required:
                                - plan
                              properties:
                                plan:
                                  type: object
                                  required:
                                    - recurring
                                  properties:
                                    recurring:
                                      type: object
                                      minProperties: 1
                            - allOf:
                                - not:
                                    type: object
                                    required:
                                      - plan
                                    properties:
                                      plan:
                                        type: object
                                        required:
                                          - recurring
                                        properties:
                                          recurring: {}
                                - type: object
                                  required:
                                    - recurring
                                  properties:
                                    recurring:
                                      type: object
                                      minProperties: 1
                        - anyOf:
                            - type: object
                              required:
                                - plan
                              properties:
                                plan:
                                  type: object
                                  required:
                                    - one_off
                                  properties:
                                    one_off:
                                      type: object
                                      minProperties: 1
                            - allOf:
                                - not:
                                    type: object
                                    required:
                                      - plan
                                    properties:
                                      plan:
                                        type: object
                                        required:
                                          - one_off
                                        properties:
                                          one_off: {}
                                - type: object
                                  required:
                                    - one_off
                                  properties:
                                    one_off:
                                      type: object
                                      minProperties: 1
                    - not:
                        anyOf:
                          - type: object
                            required:
                              - plan
                            properties:
                              plan:
                                type: object
                                required:
                                  - template_id
                                properties:
                                  template_id:
                                    type: string
                                    minLength: 1
                                    pattern: \S
                          - allOf:
                              - not:
                                  type: object
                                  required:
                                    - plan
                                  properties:
                                    plan:
                                      type: object
                                      required:
                                        - template_id
                                      properties:
                                        template_id: {}
                              - type: object
                                required:
                                  - template_id
                                properties:
                                  template_id:
                                    type: string
                                    minLength: 1
                                    pattern: \S
                    - not:
                        anyOf:
                          - type: object
                            required:
                              - plan
                            properties:
                              plan:
                                type: object
                                required:
                                  - schedule
                                properties:
                                  schedule:
                                    type: object
                                    minProperties: 1
                          - allOf:
                              - not:
                                  type: object
                                  required:
                                    - plan
                                  properties:
                                    plan:
                                      type: object
                                      required:
                                        - schedule
                                      properties:
                                        schedule: {}
                              - type: object
                                required:
                                  - schedule
                                properties:
                                  schedule:
                                    type: object
                                    minProperties: 1
                    - anyOf:
                        - anyOf:
                            - type: object
                              required:
                                - plan
                              properties:
                                plan:
                                  type: object
                                  required:
                                    - recurring
                                  properties:
                                    recurring:
                                      type: object
                                      required:
                                        - enabled
                                      properties:
                                        enabled:
                                          enum:
                                            - true
                                            - 1
                                            - '1'
                            - allOf:
                                - not:
                                    type: object
                                    required:
                                      - plan
                                    properties:
                                      plan:
                                        type: object
                                        required:
                                          - recurring
                                        properties:
                                          recurring: {}
                                - type: object
                                  required:
                                    - recurring
                                  properties:
                                    recurring:
                                      type: object
                                      required:
                                        - enabled
                                      properties:
                                        enabled:
                                          enum:
                                            - true
                                            - 1
                                            - '1'
                        - anyOf:
                            - type: object
                              required:
                                - plan
                              properties:
                                plan:
                                  type: object
                                  required:
                                    - one_off
                                  properties:
                                    one_off:
                                      type: object
                                      required:
                                        - enabled
                                      properties:
                                        enabled:
                                          enum:
                                            - true
                                            - 1
                                            - '1'
                            - allOf:
                                - not:
                                    type: object
                                    required:
                                      - plan
                                    properties:
                                      plan:
                                        type: object
                                        required:
                                          - one_off
                                        properties:
                                          one_off: {}
                                - type: object
                                  required:
                                    - one_off
                                  properties:
                                    one_off:
                                      type: object
                                      required:
                                        - enabled
                                      properties:
                                        enabled:
                                          enum:
                                            - true
                                            - 1
                                            - '1'
          - allOf:
              - allOf:
                  - not:
                      type: object
                      required:
                        - plan
                      properties:
                        plan:
                          type: object
                          required:
                            - template_id
                          properties:
                            template_id:
                              type: string
                              minLength: 1
                              pattern: \S
                  - anyOf:
                      - type: object
                        required:
                          - plan
                        properties:
                          plan:
                            type: object
                            required:
                              - template_id
                            properties:
                              template_id: {}
                      - not:
                          type: object
                          required:
                            - template_id
                          properties:
                            template_id:
                              type: string
                              minLength: 1
                              pattern: \S
              - allOf:
                  - not:
                      type: object
                      required:
                        - plan
                      properties:
                        plan:
                          type: object
                          required:
                            - plan_name
                          properties:
                            plan_name:
                              type: string
                              minLength: 1
                              pattern: \S
                  - anyOf:
                      - type: object
                        required:
                          - plan
                        properties:
                          plan:
                            type: object
                            required:
                              - plan_name
                            properties:
                              plan_name: {}
                      - not:
                          type: object
                          required:
                            - plan_name
                          properties:
                            plan_name:
                              type: string
                              minLength: 1
                              pattern: \S
          - allOf:
              - anyOf:
                  - type: object
                    required:
                      - plan
                    properties:
                      plan:
                        type: object
                        required:
                          - template_id
                        properties:
                          template_id:
                            type: string
                            minLength: 1
                            pattern: \S
                  - allOf:
                      - not:
                          type: object
                          required:
                            - plan
                          properties:
                            plan:
                              type: object
                              required:
                                - template_id
                              properties:
                                template_id: {}
                      - type: object
                        required:
                          - template_id
                        properties:
                          template_id:
                            type: string
                            minLength: 1
                            pattern: \S
              - allOf:
                  - not:
                      type: object
                      required:
                        - plan
                      properties:
                        plan:
                          type: object
                          required:
                            - start_date
                          properties:
                            start_date:
                              type: string
                              minLength: 1
                              pattern: \S
                  - anyOf:
                      - type: object
                        required:
                          - plan
                        properties:
                          plan:
                            type: object
                            required:
                              - start_date
                            properties:
                              start_date: {}
                      - not:
                          type: object
                          required:
                            - start_date
                          properties:
                            start_date:
                              type: string
                              minLength: 1
                              pattern: \S
              - allOf:
                  - not:
                      type: object
                      required:
                        - plan
                      properties:
                        plan:
                          type: object
                          required:
                            - payment_date
                          properties:
                            payment_date:
                              type: string
                              minLength: 1
                              pattern: \S
                  - anyOf:
                      - type: object
                        required:
                          - plan
                        properties:
                          plan:
                            type: object
                            required:
                              - payment_date
                            properties:
                              payment_date: {}
                      - not:
                          type: object
                          required:
                            - payment_date
                          properties:
                            payment_date:
                              type: string
                              minLength: 1
                              pattern: \S
    SubscriptionCreateResponse:
      type: object
      description: 成功响应。
      properties:
        code:
          type: integer
          description: 0 表示订阅已创建，尚待客户激活。
          enum:
            - 0
        message:
          type: string
          description: 响应说明。
        data:
          type: object
          description: 已创建但尚未激活的 DDR。
          properties:
            customer_id:
              type: string
              description: BillBuddy 客户标识。
            plan_id:
              type: string
              description: DDR 订阅 ID。
            status:
              type: integer
              description: >-
                DDR 状态：1 Active、2 Inactive、3 Processing、4 Processed、5 On Hold、6
                Completed、7 Cancelled、8 Paused、9 Expired。
              enum:
                - 2
            is_by_agreement:
              type: boolean
              description: 仅 By agreement 模式返回 true。
            activation_url:
              type: string
              description: >-
                托管激活页完整 URL，原样用于浏览器跳转或 iframe 的
                src，无需自行拼接地址或解析凭证。成功响应可能不含本字段；如缺少，请联系 BillBuddy 核对已创建订阅后再处理。 周期及
                By agreement 请在 start_date 当天之前使用；纯一次性须在创建后 30 天内提交激活。
              format: uri
            activation_html:
              type: string
              description: >-
                可用于 iframe srcdoc 的 HTML，引导客户打开 activation_url。成功响应可能不含本字段；处理方式同
                activation_url。
          required:
            - customer_id
            - plan_id
            - status
      required:
        - code
        - message
        - data
    BusinessError:
      type: object
      description: 业务错误。HTTP 通常仍为 200。
      properties:
        code:
          type: integer
          description: 业务失败、认证失败为 1；Request 参数及文件属性校验失败为 400。
          enum:
            - 1
            - 400
        message:
          type: string
          description: 错误原因，具体文案会因失败原因不同而变化。
        data:
          oneOf:
            - type: object
              description: 空对象或错误上下文。
              properties:
                customer_id:
                  type: string
                  description: createDdr 后续步骤失败时，可能返回已创建并保留的客户 ID；重试可使用此 ID。
            - type: array
              items: {}
              description: 通常为空数组。
          description: 错误数据，可能为 []、{} 或包含 customer_id 的客户上下文。
      required:
        - code
        - message
        - data
  securitySchemes:
    oauth2:
      type: oauth2
      description: OAuth2 client_credentials 授权。access_token 到期后重新获取。
      flows:
        clientCredentials:
          tokenUrl: /oauth/token
          scopes: {}

````

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