PAYATHON 2026

批次下单接口 order_title 长度及特殊字符限制

支付小周

明确结论

仅凭接口文档中的 string(64),只能确认 order_title 的最大长度为 64。文档没有说明这个 64 按字符数、UTF-8 字节数还是其他编码口径计算,因此不能直接理解为“最多 64 个汉字”。

为减少验签成功后仍被接口拒绝的情况,生产环境可以采用更严格的校验:

  • order_title 经 UTF-8 编码后不得超过 64 字节。
  • 常用汉字在 UTF-8 中通常占 3 字节。如果标题全部由常用汉字组成,建议最多填写 21 个。
  • 英文字母、数字和半角符号通常占 1 字节。
  • Emoji 和部分生僻汉字通常占 4 字节。
  • 文档没有提供特殊字符黑名单,不能据此断言某个字符一定被官方禁止。生产系统可以使用白名单,只允许汉字、字母、数字、空格和少量常用标点。
  • 批量付款的支付密码通常在支付宝收银台输入,不应通过批次下单接口传递。密码格式、连续输错限制和风控校验应以收银台的实际提示为准。

为什么不能把 string(64) 直接理解为 64 个汉字

接口字段中的 string(64) 通常表示字符串的最大长度,但不同系统的计算方式可能不同:

  1. 按 Unicode 字符数量计算;
  2. 按 UTF-8 编码后的字节数计算;
  3. 按 Java UTF-16 代码单元计算;
  4. 接口层按字符数校验,存储层或下游系统再按字节数校验。

例如,“付款”包含 2 个 Unicode 字符,采用 UTF-8 编码后则占 6 字节:

付:3 字节
款:3 字节
合计:6 字节

几类常见内容的长度对比如下:

内容字符数UTF-8 字节数
ABC12366
批量付款412
付款-2026711
😀14

如果接口最终按字节数校验,64 个常用汉字大约占 192 字节,会明显超过限制。官方文档没有进一步说明时,按 UTF-8 字节数限制更稳妥。

推荐的校验步骤

提交批次下单请求前,可以按以下顺序处理 order_title:

  1. 检查字段是否为空;
  2. 去除首尾空白字符;
  3. 拒绝换行符、制表符和不可见控制字符;
  4. 根据业务需要限制允许使用的字符;
  5. 计算 UTF-8 字节数,确认不超过 64;
  6. 使用生产环境的实际编码完成序列化和签名,签名后不要再修改标题。

可以优先允许以下字符:

  • 中文;
  • 英文字母;
  • 数字;
  • 普通空格;
  • -、_、(、)、(、) 等业务确实需要的常用标点。

以下内容建议谨慎使用或直接拦截:

  • \r、\n、\t 等控制字符;
  • Emoji;
  • 零宽空格等不可见字符;
  • HTML 标签;
  • URL 编码片段;
  • 可能影响日志、CSV 或页面展示的符号组合。

这些拦截措施是调用方的防御性校验,并不代表相关字符都在支付宝官方黑名单中。如果业务必须使用某个符号,应通过沙箱或小额生产请求进行验证,并以接口返回结果为准。

Java 校验示例

下面的实现将 UTF-8 字节数限制为 64,并拒绝控制字符:

import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;

public final class OrderTitleValidator {

    private static final int MAX_UTF8_BYTES = 64;
    private static final Pattern CONTROL_CHARS =
            Pattern.compile("[\\p{Cc}\\p{Cf}]");

    public static String validate(String orderTitle) {
        if (orderTitle == null || orderTitle.trim().isEmpty()) {
            throw new IllegalArgumentException("order_title 不能为空");
        }

        String normalized = orderTitle.trim();

        if (CONTROL_CHARS.matcher(normalized).find()) {
            throw new IllegalArgumentException(
                    "order_title 不能包含控制字符或不可见格式字符"
            );
        }

        int byteLength =
                normalized.getBytes(StandardCharsets.UTF_8).length;

        if (byteLength > MAX_UTF8_BYTES) {
            throw new IllegalArgumentException(
                    "order_title 的 UTF-8 编码长度不能超过 64 字节,当前为 "
                            + byteLength + " 字节"
            );
        }

        return normalized;
    }
}

如果业务需要使用字符白名单,可以增加以下校验:

private static final Pattern ALLOWED_CHARS =
        Pattern.compile("^[\\p{IsHan}A-Za-z0-9 _\\-()()]+$");

if (!ALLOWED_CHARS.matcher(normalized).matches()) {
    throw new IllegalArgumentException(
            "order_title 包含不支持的字符"
    );
}

白名单需要根据实际业务调整。在没有官方依据的情况下,不要把示例正则视为支付宝完整的字符规则。

JavaScript 校验示例

在浏览器或 Node.js 中,可以使用 TextEncoder 计算 UTF-8 字节数:

function validateOrderTitle(value) {
  if (typeof value !== "string" || value.trim() === "") {
    throw new Error("order_title 不能为空");
  }

  const orderTitle = value.trim();

  // 拒绝常见控制字符和不可见格式字符
  if (/[\p{Cc}\p{Cf}]/u.test(orderTitle)) {
    throw new Error("order_title 不能包含控制字符或不可见格式字符");
  }

  const byteLength = new TextEncoder().encode(orderTitle).length;

  if (byteLength > 64) {
    throw new Error(
      `order_title 的 UTF-8 编码长度不能超过 64 字节,当前为 ${byteLength} 字节`
    );
  }

  return orderTitle;
}

不要使用下面的写法计算 UTF-8 字节数:

orderTitle.length

JavaScript 的 length 计算的是 UTF-16 代码单元。遇到汉字、Emoji 或组合字符时,其结果可能与 UTF-8 字节数不同。

关于批量付款的支付密码

order_title 只是批次下单请求中的转账标题,不是支付密码字段,也不应包含密码、验证码或其他敏感信息。

批量付款进入支付宝收银台后,支付密码一般由用户在支付宝提供的页面中输入。处理时应注意:

  • 不要通过接口参数、日志、数据库或前端埋点采集支付密码;
  • 不要把支付密码拼接到 order_title、备注或扩展字段中;
  • 密码长度、字符组成、错误次数和账户风控限制由支付宝收银台控制;
  • 是否支持免密、证书、复核或其他安全能力,取决于签约产品、账户状态和当时的收银台规则;
  • 如果支付页面提示标题含有非法字符或长度超限,应记录支付宝返回的错误码和原始错误信息,再据此调整校验规则。

在字段说明不够明确时,建议将 order_title 控制在 64 个 UTF-8 字节以内,并通过业务白名单限制字符。特殊字符和支付密码规则不能根据 string(64) 自行推断,应以对应产品文档、接口错误码和收银台的实际校验结果为准。

备注:内容仅供参考。