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

# 批量直连支付

> ### 每笔交易的参数规则

顶层提供 `merchant_id` 和 `transactions`，每批 1–100 笔。每笔交易均须提供 `trans_func`、`amount`、`merchant_order_no` 和 `currency`，再按下表填写。

| trans_func | 操作 | 每笔额外必填参数 |
| --- | --- | --- |
| 2 | Token 支付 | `card_token`、`first_name`、`last_name` |
| 3 | 卡号支付 | `card.card_number`、`card.expiration_month`、`card.expiration_year`、`card.security_code`、`first_name`、`last_name` |
| 5 | 银行账户支付 | `bank_account.bsb`、`bank_account.account_no`、`bank_account.account_name`；本笔使用银行账户，`card` 不用于支付，请省略 |

批量不支持创建 Token、收银台或 3DS；`is3ds`、`return_url` 不是支持的行参数。`process_date` 可选，填写时须严格晚于业务时区的今天。坏行返回失败原因，好行可被受理；受理不代表扣款成功。

每次提交 1 至 100 笔，逐笔返回受理或拒绝结果。accepted 表示请求已受理，将异步处理，不代表支付成功。仅支持 trans_func=2/3/5，不支持创建 Token、收银台或 3DS。已存在订单号、批内重复订单号及无效参数会导致对应交易 rejected，不影响其他有效交易受理。即使全部 rejected，顶层仍可能 code=0；请检查逐笔结果并核对最终支付状态。

逐笔结果用于处理正常完成的批次；请求结构异常或处理异常可能导致整批失败。未收到完整逐笔结果时，请先按订单查询状态，再决定是否重试。

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



## OpenAPI

````yaml /openapi/billbuddy-v2-openapi.yaml post /api/v2/trans/batchDirectPay
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/trans/batchDirectPay:
    post:
      tags:
        - 交易
      summary: 批量直连支付
      description: >-
        ### 每笔交易的参数规则


        顶层提供 `merchant_id` 和 `transactions`，每批 1–100 笔。每笔交易均须提供
        `trans_func`、`amount`、`merchant_order_no` 和 `currency`，再按下表填写。


        | trans_func | 操作 | 每笔额外必填参数 |

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

        | 2 | Token 支付 | `card_token`、`first_name`、`last_name` |

        | 3 | 卡号支付 |
        `card.card_number`、`card.expiration_month`、`card.expiration_year`、`card.security_code`、`first_name`、`last_name`
        |

        | 5 | 银行账户支付 |
        `bank_account.bsb`、`bank_account.account_no`、`bank_account.account_name`；本笔使用银行账户，`card`
        不用于支付，请省略 |


        批量不支持创建 Token、收银台或 3DS；`is3ds`、`return_url` 不是支持的行参数。`process_date`
        可选，填写时须严格晚于业务时区的今天。坏行返回失败原因，好行可被受理；受理不代表扣款成功。


        每次提交 1 至 100 笔，逐笔返回受理或拒绝结果。accepted 表示请求已受理，将异步处理，不代表支付成功。仅支持
        trans_func=2/3/5，不支持创建 Token、收银台或 3DS。已存在订单号、批内重复订单号及无效参数会导致对应交易
        rejected，不影响其他有效交易受理。即使全部 rejected，顶层仍可能 code=0；请检查逐笔结果并核对最终支付状态。


        逐笔结果用于处理正常完成的批次；请求结构异常或处理异常可能导致整批失败。未收到完整逐笔结果时，请先按订单查询状态，再决定是否重试。


        所有示例均为格式示例，客户、订单、卡及账户数据须替换为测试环境获准使用的数据。BASE_URL 为服务根域名，不附加
        /api/v2；ACCESS_TOKEN 为 /oauth/token 返回的 access_token。日期示例为 2030
        年，执行时请改为满足当前日期限制的实际日期。
      operationId: v2BatchDirectPay
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchDirectPayRequest'
            examples:
              batch:
                summary: 批量支付请求
                value:
                  merchant_id: '48290713'
                  transactions:
                    - amount: '25.50'
                      merchant_order_no: BB-BATCH-20300101-001
                      currency: AUD
                      trans_func: 3
                      first_name: Emily
                      last_name: Carter
                      card:
                        card_number: '4111111111111111'
                        expiration_month: '12'
                        expiration_year: '2030'
                        security_code: '123'
                      notify_email: false
                      notify_sms: false
                    - trans_func: 5
                      amount: '12.00'
                      merchant_order_no: BB-BATCH-20300101-002
                      currency: AUD
                      bank_account:
                        bsb: 062-000
                        account_no: '618472950'
                        account_name: Emily Carter
                      process_date: '2030-01-15'
      responses:
        '200':
          description: HTTP 200。成功、业务失败、参数校验失败或业务鉴权失败以响应体 code 区分。
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/BatchDirectPayResponse'
                  - $ref: '#/components/schemas/BusinessError'
              examples:
                partial:
                  summary: 部分成功
                  value:
                    code: 0
                    message: accepted
                    data:
                      total: 2
                      accepted: 1
                      rejected: 1
                      items:
                        - merchant_order_no: BB-BATCH-20300101-001
                          result: accepted
                        - merchant_order_no: BB-BATCH-20300101-002
                          result: rejected
                          reason: Order already exists
                allAccepted:
                  summary: 全部已受理
                  value:
                    code: 0
                    message: accepted
                    data:
                      total: 2
                      accepted: 2
                      rejected: 0
                      items:
                        - merchant_order_no: BB-BATCH-20300101-001
                          result: accepted
                        - merchant_order_no: BB-BATCH-20300101-002
                          result: accepted
                validation:
                  summary: 顶层结构不合法
                  value:
                    code: 400
                    message: The transactions field is required.
                    data: {}
                auth:
                  summary: OAuth 失败
                  value:
                    code: 1
                    message: Unauthenticated, invalid or expired access token
                    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/trans/batchDirectPay" \
              -H "Authorization: Bearer ${ACCESS_TOKEN}" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data-raw '{
              "merchant_id": "48290713",
              "transactions": [
                {
                  "amount": "25.50",
                  "merchant_order_no": "BB-BATCH-20300101-001",
                  "currency": "AUD",
                  "trans_func": 3,
                  "first_name": "Emily",
                  "last_name": "Carter",
                  "card": {
                    "card_number": "4111111111111111",
                    "expiration_month": "12",
                    "expiration_year": "2030",
                    "security_code": "123"
                  },
                  "notify_email": false,
                  "notify_sms": false
                },
                {
                  "trans_func": 5,
                  "amount": "12.00",
                  "merchant_order_no": "BB-BATCH-20300101-002",
                  "currency": "AUD",
                  "bank_account": {
                    "bsb": "062-000",
                    "account_no": "618472950",
                    "account_name": "Emily Carter"
                  },
                  "process_date": "2030-01-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/trans/batchDirectPay"

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

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

            payload = json.loads(r'''{
              "merchant_id": "48290713",
              "transactions": [
                {
                  "amount": "25.50",
                  "merchant_order_no": "BB-BATCH-20300101-001",
                  "currency": "AUD",
                  "trans_func": 3,
                  "first_name": "Emily",
                  "last_name": "Carter",
                  "card": {
                    "card_number": "4111111111111111",
                    "expiration_month": "12",
                    "expiration_year": "2030",
                    "security_code": "123"
                  },
                  "notify_email": false,
                  "notify_sms": false
                },
                {
                  "trans_func": 5,
                  "amount": "12.00",
                  "merchant_order_no": "BB-BATCH-20300101-002",
                  "currency": "AUD",
                  "bank_account": {
                    "bsb": "062-000",
                    "account_no": "618472950",
                    "account_name": "Emily Carter"
                  },
                  "process_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: 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/trans/batchDirectPay");
            const headers = { Accept: "application/json" };
            headers.Authorization = `Bearer ${env("ACCESS_TOKEN")}`;
            const payload = {
              "merchant_id": "48290713",
              "transactions": [
                {
                  "amount": "25.50",
                  "merchant_order_no": "BB-BATCH-20300101-001",
                  "currency": "AUD",
                  "trans_func": 3,
                  "first_name": "Emily",
                  "last_name": "Carter",
                  "card": {
                    "card_number": "4111111111111111",
                    "expiration_month": "12",
                    "expiration_year": "2030",
                    "security_code": "123"
                  },
                  "notify_email": false,
                  "notify_sms": false
                },
                {
                  "trans_func": 5,
                  "amount": "12.00",
                  "merchant_order_no": "BB-BATCH-20300101-002",
                  "currency": "AUD",
                  "bank_account": {
                    "bsb": "062-000",
                    "account_no": "618472950",
                    "account_name": "Emily Carter"
                  },
                  "process_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: 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/trans/batchDirectPay";
            $headers = ['Accept: application/json'];
            $headers[] = 'Authorization: Bearer ' . $env('ACCESS_TOKEN');
            $body = <<<'JSON'
            {
              "merchant_id": "48290713",
              "transactions": [
                {
                  "amount": "25.50",
                  "merchant_order_no": "BB-BATCH-20300101-001",
                  "currency": "AUD",
                  "trans_func": 3,
                  "first_name": "Emily",
                  "last_name": "Carter",
                  "card": {
                    "card_number": "4111111111111111",
                    "expiration_month": "12",
                    "expiration_year": "2030",
                    "security_code": "123"
                  },
                  "notify_email": false,
                  "notify_sms": false
                },
                {
                  "trans_func": 5,
                  "amount": "12.00",
                  "merchant_order_no": "BB-BATCH-20300101-002",
                  "currency": "AUD",
                  "bank_account": {
                    "bsb": "062-000",
                    "account_no": "618472950",
                    "account_name": "Emily Carter"
                  },
                  "process_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: 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/trans/batchDirectPay"
                payload := `{
              "merchant_id": "48290713",
              "transactions": [
                {
                  "amount": "25.50",
                  "merchant_order_no": "BB-BATCH-20300101-001",
                  "currency": "AUD",
                  "trans_func": 3,
                  "first_name": "Emily",
                  "last_name": "Carter",
                  "card": {
                    "card_number": "4111111111111111",
                    "expiration_month": "12",
                    "expiration_year": "2030",
                    "security_code": "123"
                  },
                  "notify_email": false,
                  "notify_sms": false
                },
                {
                  "trans_func": 5,
                  "amount": "12.00",
                  "merchant_order_no": "BB-BATCH-20300101-002",
                  "currency": "AUD",
                  "bank_account": {
                    "bsb": "062-000",
                    "account_no": "618472950",
                    "account_name": "Emily Carter"
                  },
                  "process_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: 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/trans/batchDirectPay";
                    String payload =
                        "{\n" +
                        "  \"merchant_id\": \"48290713\",\n" +
                        "  \"transactions\": [\n" +
                        "    {\n" +
                        "      \"amount\": \"25.50\",\n" +
                        "      \"merchant_order_no\": \"BB-BATCH-20300101-001\",\n" +
                        "      \"currency\": \"AUD\",\n" +
                        "      \"trans_func\": 3,\n" +
                        "      \"first_name\": \"Emily\",\n" +
                        "      \"last_name\": \"Carter\",\n" +
                        "      \"card\": {\n" +
                        "        \"card_number\": \"4111111111111111\",\n" +
                        "        \"expiration_month\": \"12\",\n" +
                        "        \"expiration_year\": \"2030\",\n" +
                        "        \"security_code\": \"123\"\n" +
                        "      },\n" +
                        "      \"notify_email\": false,\n" +
                        "      \"notify_sms\": false\n" +
                        "    },\n" +
                        "    {\n" +
                        "      \"trans_func\": 5,\n" +
                        "      \"amount\": \"12.00\",\n" +
                        "      \"merchant_order_no\": \"BB-BATCH-20300101-002\",\n" +
                        "      \"currency\": \"AUD\",\n" +
                        "      \"bank_account\": {\n" +
                        "        \"bsb\": \"062-000\",\n" +
                        "        \"account_no\": \"618472950\",\n" +
                        "        \"account_name\": \"Emily Carter\"\n" +
                        "      },\n" +
                        "      \"process_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: Ruby
          label: 批量支付请求
          source: |
            require "net/http"
            require "uri"

            base_url = ENV.fetch("BASE_URL").sub(%r{/+$}, "")
            uri = URI(base_url + "/api/v2/trans/batchDirectPay")
            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",
              "transactions": [
                {
                  "amount": "25.50",
                  "merchant_order_no": "BB-BATCH-20300101-001",
                  "currency": "AUD",
                  "trans_func": 3,
                  "first_name": "Emily",
                  "last_name": "Carter",
                  "card": {
                    "card_number": "4111111111111111",
                    "expiration_month": "12",
                    "expiration_year": "2030",
                    "security_code": "123"
                  },
                  "notify_email": false,
                  "notify_sms": false
                },
                {
                  "trans_func": 5,
                  "amount": "12.00",
                  "merchant_order_no": "BB-BATCH-20300101-002",
                  "currency": "AUD",
                  "bank_account": {
                    "bsb": "062-000",
                    "account_no": "618472950",
                    "account_name": "Emily Carter"
                  },
                  "process_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
components:
  schemas:
    BatchDirectPayRequest:
      type: object
      description: 请求参数。
      properties:
        merchant_id:
          type: string
          description: 业务标识，由 BillBuddy 分配。
          maxLength: 20
          minLength: 1
        transactions:
          type: array
          items:
            $ref: '#/components/schemas/BatchTransaction'
          description: 交易列表，1–100条。正常逐笔校验失败不影响其他有效交易受理；异常结构或处理异常可能导致整批失败。
          minItems: 1
          maxItems: 100
      required:
        - merchant_id
        - transactions
    BatchDirectPayResponse:
      type: object
      description: 成功响应。
      properties:
        code:
          type: integer
          description: 0 表示批量请求处理完成；请检查逐笔 accepted/rejected，accepted 不表示支付成功。
          enum:
            - 0
        message:
          type: string
          description: 响应说明。
        data:
          type: object
          description: 处理汇总。
          properties:
            total:
              type: integer
              description: 本次交易条数。
            accepted:
              type: integer
              description: 已受理条数，将异步处理，不代表支付成功。
            rejected:
              type: integer
              description: 未受理条数。
            items:
              type: array
              items:
                type: object
                description: 逐笔处理结果。
                properties:
                  merchant_order_no:
                    type: string
                    description: 商户订单号。
                  result:
                    type: string
                    description: 逐笔结果：accepted 表示已受理，将异步处理；rejected 表示未受理。
                    enum:
                      - accepted
                      - rejected
                  reason:
                    type: string
                    description: 仅 rejected 时返回拒绝原因。
                required:
                  - merchant_order_no
                  - result
              description: 逐笔清单，顺序与提交顺序一致。
          required:
            - total
            - accepted
            - rejected
            - items
      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
    BatchTransaction:
      type: object
      description: >-
        支付请求。客户标识均不传为无客户模式；同时传 customer_id 和 external_customer_id 时以 customer_id
        为准。卡支付只支持 Visa/Mastercard/Amex。金额、姓名、卡号等字段要求仍适用于已有客户；预约只允许 2/3/5。 is3ds
        与 return_url 不属于批量接口支持参数；批量强制关闭 3DS。
      properties:
        trans_func:
          type: integer
          description: 交易操作：2 Token 支付；3 卡号支付；5 银行账户支付。批量不支持 1、4 或 3DS。
          enum:
            - 2
            - 3
            - 5
        amount:
          oneOf:
            - type: number
              multipleOf: 0.01
              minimum: 0.01
              maximum: 999999.99
            - type: string
              pattern: ^\d+(\.\d{1,2})?$
              allOf:
                - pattern: ^0*(?:[1-9]\d{0,5}(?:\.\d{1,2})?|0\.(?:0[1-9]|[1-9]\d?))$
          description: >-
            支付金额。 金额以主货币单位计，例如 10.50 表示 AUD 10.50，最多两位小数。接受数值或数字字符串，最小 0.01、最大
            999999.99，两种类型遵守相同范围限制。
        merchant_order_no:
          type: string
          description: 商户订单号。同一商户范围内必须唯一；重复调用不会重新返回已有成功订单。
          maxLength: 60
          minLength: 1
        currency:
          type: string
          description: 货币代码，如 AUD。支付服务能否处理该币种还取决于商户渠道配置。
          minLength: 3
          maxLength: 3
        process_date:
          type: string
          description: >-
            预约处理日期，YYYY-MM-DD，必须严格晚于服务业务时区的当天；只支持 trans_func 2、3、5，且不能与 is3ds=1
            一起使用。 建议使用 YYYY-MM-DD。 必须晚于服务业务时区的今天。
          nullable: true
          format: date
        customer_id:
          type: string
          description: BillBuddy 客户标识，须为当前商户及业务范围内的有效客户。
          nullable: true
          maxLength: 32
        external_customer_id:
          type: string
          description: 商户系统的客户引用编号；未传 customer_id 时用于查询客户。同时传两个标识时以 customer_id 为准。
          nullable: true
          maxLength: 64
        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'
        card:
          type: object
          description: 信用卡信息。
          properties:
            card_number:
              type: string
              description: >-
                信用卡号，使用 Visa、Mastercard 或 American Express；最终能否使用取决于品牌识别及渠道配置。
                trans_func=2 时忽略本字段。 trans_func=3 时必填，且不能为 null。
                请提供有效卡信息，支付结果以实际响应为准。
              maxLength: 20
            expiration_month:
              type: string
              description: >-
                卡有效期月份，建议两位数字，如 12。 trans_func=2 时忽略本字段。 trans_func=3 时必填，且不能为
                null。 请提供有效卡信息，支付结果以实际响应为准。
              minLength: 2
              maxLength: 2
            expiration_year:
              type: string
              description: >-
                卡有效期年份，建议四位数字，如 2030。 trans_func=2 时忽略本字段。 trans_func=3 时必填，且不能为
                null。 请提供有效卡信息，支付结果以实际响应为准。
              minLength: 4
              maxLength: 4
            security_code:
              type: string
              description: >-
                卡安全码 CVV，不使用数字类型以免丢失前导零。 trans_func=2 时忽略本字段。 trans_func=3
                时必填，且不能为 null。 请提供有效卡信息，支付结果以实际响应为准。
              maxLength: 4
        card_token:
          type: string
          description: BillBuddy 返回的支付 Token。有客户时 Token 必须属于该客户。 trans_func=2 时必填。
          nullable: true
          maxLength: 255
        bank_account:
          type: object
          description: 银行账户信息。
          properties:
            bsb:
              type: string
              description: trans_func=5 时必填。填写有效的六位 BSB。允许空格及连字符，不计入六字符长度。
              nullable: true
              pattern: ^(?:[\s-]*[^\s-]){6}[\s-]*$
            account_no:
              type: string
              description: >-
                trans_func=5 时必填。填写有效银行账号字符串，保留前导零。允许空格及连字符；去除这些分隔符后须为 1 至 9
                个字符，不能只填写分隔符。
              nullable: true
              pattern: ^(?:[\s-]*[^\s-]){0,9}[\s-]*$
            account_name:
              type: string
              description: 银行账户名。 当 trans_func 为 5 时必填，且不能为 null。
              nullable: true
              maxLength: 100
        first_name:
          type: string
          description: 名字。trans_func=2/3 时必填且不能为 null，最长 50 字符。
          nullable: true
          maxLength: 50
        last_name:
          type: string
          description: 姓氏。trans_func=2/3 时必填且不能为 null，最长 50 字符。
          nullable: true
          maxLength: 50
        email:
          type: string
          description: 邮箱。
          nullable: true
          format: email
          maxLength: 100
        phone:
          type: string
          description: 联系电话。
          nullable: true
          maxLength: 50
        address:
          type: string
          description: 地址。
          nullable: true
          maxLength: 100
        city:
          type: string
          description: 城市。
          nullable: true
          maxLength: 50
        state:
          type: string
          description: 州或省。
          nullable: true
          maxLength: 100
        postcode:
          type: string
          description: 邮编。
          nullable: true
          maxLength: 8
        country_iso2:
          type: string
          description: 国家两字母代码，如 AU。
          nullable: true
          minLength: 2
          maxLength: 2
      required:
        - trans_func
        - amount
        - merchant_order_no
        - currency
      not:
        anyOf:
          - allOf:
              - required:
                  - trans_func
                properties:
                  trans_func:
                    enum:
                      - 2
              - not:
                  required:
                    - card_token
                    - first_name
                    - last_name
                  properties:
                    card_token:
                      not:
                        enum:
                          - null
                      minLength: 1
                    first_name:
                      not:
                        enum:
                          - null
                      minLength: 1
                    last_name:
                      not:
                        enum:
                          - null
                      minLength: 1
          - allOf:
              - required:
                  - trans_func
                properties:
                  trans_func:
                    enum:
                      - 3
              - not:
                  required:
                    - card
                    - first_name
                    - last_name
                  properties:
                    card:
                      not:
                        enum:
                          - null
                    first_name:
                      not:
                        enum:
                          - null
                      minLength: 1
                    last_name:
                      not:
                        enum:
                          - null
                      minLength: 1
          - allOf:
              - required:
                  - trans_func
                properties:
                  trans_func:
                    enum:
                      - 5
              - not:
                  required:
                    - bank_account
                  properties:
                    bank_account:
                      not:
                        enum:
                          - null
          - allOf:
              - required:
                  - trans_func
                properties:
                  trans_func:
                    enum:
                      - 3
              - not:
                  required:
                    - card
                  properties:
                    card:
                      not:
                        enum:
                          - null
                      minProperties: 1
                      required:
                        - card_number
                        - expiration_month
                        - expiration_year
                        - security_code
                      properties:
                        card_number:
                          not:
                            enum:
                              - null
                          minLength: 1
                        expiration_month:
                          not:
                            enum:
                              - null
                          minLength: 1
                        expiration_year:
                          not:
                            enum:
                              - null
                          minLength: 1
                        security_code:
                          not:
                            enum:
                              - null
                          minLength: 1
          - allOf:
              - required:
                  - trans_func
                properties:
                  trans_func:
                    enum:
                      - 5
              - not:
                  required:
                    - bank_account
                  properties:
                    bank_account:
                      not:
                        enum:
                          - null
                      minProperties: 1
                      required:
                        - bsb
                        - account_no
                        - account_name
                      properties:
                        bsb:
                          not:
                            enum:
                              - null
                          minLength: 1
                        account_no:
                          not:
                            enum:
                              - null
                          minLength: 1
                          pattern: '[^\s-]'
                        account_name:
                          not:
                            enum:
                              - null
                          minLength: 1
          - allOf:
              - required:
                  - trans_func
                properties:
                  trans_func:
                    enum:
                      - 2
              - not:
                  required:
                    - card_token
                    - first_name
                    - last_name
                  properties:
                    card_token:
                      not:
                        enum:
                          - null
                      minLength: 1
                    first_name:
                      not:
                        enum:
                          - null
                      minLength: 1
                    last_name:
                      not:
                        enum:
                          - null
                      minLength: 1
          - allOf:
              - required:
                  - trans_func
                properties:
                  trans_func:
                    enum:
                      - 3
              - not:
                  required:
                    - first_name
                    - last_name
                  properties:
                    first_name:
                      not:
                        enum:
                          - null
                      minLength: 1
                    last_name:
                      not:
                        enum:
                          - null
                      minLength: 1
  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.