PAYATHON 2026

支付宝小程序资金预授权调用my.tradePay报错ALIN1018031

支付小周

结论

ALIN1018031 通常出现在 my.tradePay 拉起支付宝收银台,以及解析或校验资金预授权订单时。它并非资金冻结成功后返回的业务错误码,更可能表示客户端无法使用传入的订单信息。

建议先检查以下三类问题:

  1. 传给 my.tradePay 的内容并非由 alipay.fund.auth.order.app.freeze 通过 SDK sdkExecute 生成的完整签名订单串,而是普通接口 JSON、经过二次序列化的字符串,或订单中的某个字段。
  2. 订单串中的 app_id、签名私钥、支付宝公钥或证书、网关环境与当前小程序不一致。
  3. 小程序虽然开通了相关产品,但资金预授权能力、签约主体、商户身份或支付主体未与生成订单的应用建立有效关联。

公开文档未必会稳定披露 ALIN1018031 的准确中文定义,最终应以支付宝开放平台后台日志或技术支持对该笔请求的查询结果为准。排查时不要只看这个客户端错误码,还要定位服务端生成订单时的 request_id、app_id、订单号和原始订单串。

首先确认传入的 Body 是否正确

在资金预授权的 App 或小程序唤起模式中,服务端一般需要调用:

alipay.fund.auth.order.app.freeze

服务端使用支付宝 SDK 的 sdkExecute 一类方法生成供客户端使用的签名订单串,再把这个字符串原样返回给小程序。

这里要区分两种内容:

  • SDK 生成且包含签名信息的订单串,可以传给 my.tradePay.orderStr。
  • 支付宝网关返回的 JSON 响应报文,不能直接作为 orderStr 使用。

正确的订单串通常是 URL 参数组合,例如:

app_id=...&biz_content=...&charset=utf-8&format=json&method=alipay.fund.auth.order.app.freeze&sign=...&sign_type=RSA2&timestamp=...&version=1.0

如果 Body 是下面这种 JSON,通常表示调用方式有误:

{
  "alipay_fund_auth_order_app_freeze_response": {
    "code": "10000",
    "msg": "Success"
  },
  "sign": "..."
}

服务端变量即使名为 body,也要确认它是否真正来自 sdkExecute,不能只看变量名。

服务端生成订单串示例

下面的 Java 示例只保留了关键调用。具体 SDK 类名和构造参数应以项目当前使用的支付宝 SDK 为准。

AlipayFundAuthOrderAppFreezeRequest request =
        new AlipayFundAuthOrderAppFreezeRequest();

request.setBizContent("""
{
  "out_order_no": "AUTH_ORDER_202609090001",
  "out_request_no": "AUTH_REQ_202609090001",
  "order_title": "服务资金预授权",
  "amount": "100.00",
  "product_code": "PRE_AUTH_ONLINE"
}
""");

// 关键点:使用 sdkExecute 生成供客户端拉起的签名订单串
AlipayFundAuthOrderAppFreezeResponse response =
        alipayClient.sdkExecute(request);

String orderStr = response.getBody();

// 将 orderStr 原样返回给小程序,不要解析、拼接或再次编码

不同的签约产品和接入模式,对必填参数的要求可能不同,应以当前应用对应的接口文档为准。示例中未出现某个业务字段,不代表现有代码可以直接删除该字段。

服务端可以用 JSON 包装返回结果,但必须保持订单串本身不变:

{
  "orderStr": "app_id=...&biz_content=...&sign=..."
}

不要对订单串执行以下操作:

encodeURIComponent(orderStr);
JSON.stringify(orderStr);
orderStr.replace(/\+/g, ' ');

这些操作可能破坏签名或参数格式。

小程序端调用示例

my.request({
  url: 'https://example.com/api/auth-order',
  method: 'POST',
  data: {
    amount: '100.00'
  },
  success(res) {
    const orderStr = res.data && res.data.orderStr;

    if (typeof orderStr !== 'string' || !orderStr) {
      console.error('服务端未返回有效的 orderStr', res.data);
      return;
    }

    my.tradePay({
      orderStr,
      success(result) {
        console.log('tradePay success:', result);
      },
      fail(error) {
        console.error('tradePay fail:', error);
      }
    });
  },
  fail(error) {
    console.error('创建预授权订单失败:', error);
  }
});

不要同时传入 orderStr 和 tradeNO:

my.tradePay({
  orderStr: serverOrderStr
});

tradeNO 通常用于已有支付宝交易号的场景,不能随意填入商户订单号、预授权订单号或 auth_no。

逐项检查应用、签名和环境

1. 核对 app_id

打印订单串中的 app_id,确认它:

  • 与当前运行的小程序应用一致;
  • 与开放平台中开通资金预授权产品的应用一致;
  • 与服务端 SDK 配置的应用一致;
  • 不是其他 App、生活号或历史测试应用的 app_id。

商户账号相同,并不意味着不同应用之间可以直接共用相关能力。

2. 核对签名配置

检查服务端生成订单串时是否满足以下条件:

  • 使用了该 app_id 对应的应用私钥;
  • sign_type 与平台配置一致,通常为 RSA2;
  • 没有混用普通公钥模式和证书模式;
  • 更换密钥或证书后,服务没有继续读取旧配置;
  • 订单串生成后,未被网关、JSON 转换或前端代码改写。

可以暂时在服务端记录以下信息,但不要记录私钥:

app_id
method
sign_type
timestamp
out_order_no
out_request_no
orderStr 长度
支付宝接口 request_id(如果存在)

完整的 orderStr 含有业务数据和签名,不适合长期以明文保存在生产日志中。

3. 核对正式环境与沙箱环境

以下配置必须属于同一环境:

  • 支付宝网关;
  • 应用 app_id;
  • 应用密钥或证书;
  • 测试账号;
  • 小程序版本;
  • 产品签约状态。

沙箱生成的订单串不能直接用于正式环境的小程序,正式应用生成的订单串也不能通过沙箱配置验证。

4. 核对订单参数

重点检查以下内容:

  • product_code 是否为当前资金预授权产品要求的值;
  • amount 是否为合法的金额字符串,且不含货币符号;
  • out_order_no 和 out_request_no 是否满足长度、字符和唯一性要求;
  • 必填的业务场景、类目、收款方等参数是否完整;
  • JSON 中是否出现 undefined、空对象或错误的数据类型;
  • 订单标题中是否含有不可见控制字符;
  • 同一个外部订单号是否曾用不同金额或业务参数发起请求。

金额应写成:

{
  "amount": "100.00"
}

不建议直接使用浮点运算结果:

const amount = 0.1 + 0.2; // 可能得到 0.30000000000000004

金额应在服务端使用十进制定点类型处理。

核对产品开通主体

后台显示“已开通”,不等于当前订单已经具备调用条件。还要确认:

  • 产品开通在生成订单串所用的应用下;
  • 签约商户主体与应用主体或授权关系符合要求;
  • 当前账号具备对应资金预授权场景的权限;
  • 如果由第三方应用代商户调用,应用授权、商户签约和 app_auth_token 均有效;
  • 如果传入 payee_user_id、商户 PID 等身份字段,其归属与签约信息一致。

在第三方应用模式下,还要确认生成订单串时是否正确携带商户授权信息。只为服务商应用本身开通产品,通常不能替代被代调用商户的签约和授权。

推荐的定位顺序

  1. 在服务端打印 response.getBody() 的类型和前几十个非敏感字符,确认它是参数形式的签名订单串,而非 JSON 网关响应。
  2. 确认调用的是 sdkExecute,不是面向同步业务查询的 execute。
  3. 分别记录订单串在服务端和小程序端的长度与摘要,逐段比对,排除传输过程中被修改的情况。

可以只比较摘要,避免在日志中暴露完整内容:

String digest = DigestUtils.sha256Hex(orderStr);
log.info("orderStr length={}, sha256={}", orderStr.length(), digest);
  1. 从订单串中核对 app_id、method、sign_type 和环境。
  2. 使用新的 out_order_no、out_request_no 创建最小参数订单,排除旧订单冲突。
  3. 在支付宝开放平台调用日志中,按照请求时间、接口名称、应用和订单号查找对应记录。
  4. 如果平台侧没有任何接口记录,应重点检查客户端收到的内容以及签名订单串格式。
  5. 如果平台侧存在记录,应根据平台返回的业务子错误码处理,不能只凭 ALIN1018031 判断原因。

仍无法解决时需要提交的信息

向支付宝技术支持反馈时,建议提供以下信息:

  • 小程序 app_id;
  • 接口名称 alipay.fund.auth.order.app.freeze;
  • 调用发生的准确时间,精确到秒;
  • out_order_no 和 out_request_no;
  • 支付宝侧 request_id,前提是服务端响应中可以取得;
  • my.tradePay 的完整失败回调对象;
  • SDK 名称及版本;
  • 使用普通公钥模式还是证书模式;
  • 使用正式环境还是沙箱环境;
  • 脱敏后的订单串参数列表和订单串长度。

不要提交应用私钥、AES 密钥或未脱敏的用户信息,也不要把完整签名订单串发布到公开社区。

排查这类问题时,应先确认是否误把普通响应 JSON 当成 orderStr,调用的是否为 sdkExecute,订单串是否经过二次编码,以及订单串中的应用和签名配置是否与当前小程序完全一致。

备注:内容仅供参考。