PAYATHON 2026

alipay.trade.order.settle 返回 ACQ.INVALID_PARAMETER,如何正确传递 ro

支付老李

结论

royalty_parameters 的类型传错了。

在 biz_content 中,它应当是一个 JSON 数组,每个数组元素是一条分账明细。当前请求却把整个数组再次编码成了字符串:

"royalty_parameters":"[{\"trans_in\":\"2088441108413251\",\"amount\":0.1}]"

支付宝解析这个字段后得到的是字符串,而不是分账明细数组,所以返回 ACQ.INVALID_PARAMETER。正确结构如下:

"royalty_parameters":[
  {
    "trans_in":"2088441108413251",
    "amount":"0.10",
    "desc":"系统利润分账"
  }
]

问题原因

当前代码很可能先对分账明细执行了 json_encode(),之后又对整个 biz_content 执行了一次 json_encode()。

下面的写法就会造成双重 JSON 编码:

$bizContent = [
    'royalty_parameters' => json_encode($royaltyParameters),
];

$bizContentJson = json_encode($bizContent);

这样一来,royalty_parameters 就会变成带引号和转义符的 JSON 字符串。URL 解码无法解决这个问题,因为错误出在 biz_content 的数据结构中,而不是 URL 编码环节。

将该字段清空后仍然报错也很正常,因为空字符串同样不是合法的分账明细数组。没有分账明细时,应直接省略这个参数;需要分账时,则必须传入至少一条有效明细。

正确的 PHP 写法

先将分账明细保留为 PHP 数组,最后再统一编码整个 biz_content:

<?php

$royaltyParameters = [
    [
        'trans_in' => '2088441108413251',
        'amount'   => '0.10',
        'desc'     => '系统利润分账',
    ],
];

$bizContent = [
    'out_request_no'   => 'settle20230310100142342351963',
    'trade_no'         => '2023031022001455411432403300',
    'royalty_parameters' => $royaltyParameters,
    'extend_params'    => [
        'royalty_finish' => true,
    ],
    'royalty_mode'     => 'async',
];

$bizContentJson = json_encode(
    $bizContent,
    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);

if ($bizContentJson === false) {
    throw new RuntimeException(json_last_error_msg());
}

生成的 biz_content 应类似下面这样:

{
  "out_request_no": "settle20230310100142342351963",
  "trade_no": "2023031022001455411432403300",
  "royalty_parameters": [
    {
      "trans_in": "2088441108413251",
      "amount": "0.10",
      "desc": "系统利润分账"
    }
  ],
  "extend_params": {
    "royalty_finish": true
  },
  "royalty_mode": "async"
}

如果使用支付宝官方 SDK,直接将这段 JSON 设置为 biz_content,不要再单独编码 royalty_parameters:

$request = new AlipayTradeOrderSettleRequest();
$request->setBizContent($bizContentJson);

$result = $aopClient->execute($request);

具体类名可能因 PHP SDK 版本而异,但处理方式相同:业务对象组装完成后,只对整个 biz_content 编码一次。

如果必须自行拼接请求

自行构造网关请求时,按以下顺序处理:

  1. 使用原始参数值组装待签名字符串。
  2. 对参数签名。
  3. 对参数名和参数值进行 URL 编码。
  4. 发送请求。

不要先进行 URL 编码再签名,也不要让 URL 编码后的 biz_content 重新参与签名。使用官方 SDK 通常更稳妥,可以避免参数排序、字符集、空值过滤和签名编码不一致等问题。

还需要检查的事项

修正 JSON 类型后,如果接口仍然返回参数错误,可以逐项检查:

  • royalty_parameters 必须是数组,不能是字符串、空字符串或对象。
  • 每条分账明细都应包含有效的 trans_in 和 amount。
  • 金额建议使用字符串形式,例如 "0.10",以免浮点数在程序处理过程中产生精度问题。
  • trans_in 必须是符合当前分账产品要求的有效收入方账号,并满足相应的签约、绑定或分账关系要求。
  • 不需要的 operator_id、空的 app_auth_token 等参数应省略,不要传入 null 或空字符串。
  • out_request_no 应保持幂等。同一次分账重试使用同一个请求号,新的分账请求使用新的请求号。
  • 分账金额、交易状态、可分账余额以及 royalty_finish 的使用条件,还要以当前接入产品和接口文档为准。

发送请求前直接查看 biz_content,就能判断是否已经修正。如果 royalty_parameters 后面紧跟着 [,说明结构正确;如果后面是带引号的 "[{\"...,说明仍然存在重复编码。

备注:内容仅供参考。