PAYATHON 2026

退款接口提示 ACQ.INVALID_PARAMETER 如何解决?

支付小周

结论

ACQ.INVALID_PARAMETER 表示退款请求缺少原交易标识,也就是 out_trade_no(商户订单号)和 trade_no(支付宝交易号)都为空。

调用退款接口时,至少要传入其中一个参数:

  • out_trade_no:商户创建原支付订单时生成的订单号。
  • trade_no:原订单支付成功后,支付宝返回的交易号。

接口通过这两个参数定位原支付交易。两者不能同时缺失,也不能传空字符串。接入时通常选择其中一个即可,以免两个编号指向不同的交易。

问题原因

出现该错误,通常是以下情况造成的:

  1. 请求中没有设置 out_trade_no 和 trade_no。
  2. 参数值为 null、空字符串或纯空格。
  3. 参数名称拼写错误,例如写成了 out_refund_no。
  4. 参数位置不对,没有写入退款接口要求的业务参数。
  5. 对象序列化时过滤了空值字段,最终请求中没有交易标识。
  6. 把退款请求号 out_request_no 当成了原交易订单号。

out_request_no 通常用于标识一笔退款请求,不能替代 out_trade_no 或 trade_no 定位原支付交易。

解决方法

方式一:传入商户订单号 out_trade_no

如果系统保存了创建支付订单时使用的商户订单号,可以直接传入:

{
  "out_trade_no": "ORDER_202609080001",
  "refund_amount": "10.00",
  "refund_reason": "用户申请退款"
}

这里的 out_trade_no 必须与原支付请求中的商户订单号一致,不能临时生成,也不能换成新的退款单号。

方式二:传入支付宝交易号 trade_no

如果系统保存了支付成功后支付宝返回的交易号,也可以传入 trade_no:

{
  "trade_no": "2026090822001000000000000000",
  "refund_amount": "10.00",
  "refund_reason": "用户申请退款"
}

示例中的交易号只用于展示格式。实际请求必须使用原交易对应的真实 trade_no。

Java SDK 示例

以下是 alipay.trade.refund 常见的 Java SDK 调用方式。不同版本的 SDK 在类名和方法上可能略有差异,请以当前项目使用的 SDK 定义为准。

使用商户订单号:

AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

AlipayTradeRefundModel model = new AlipayTradeRefundModel();
model.setOutTradeNo("ORDER_202609080001");
model.setRefundAmount("10.00");
model.setRefundReason("用户申请退款");

request.setBizModel(model);

AlipayTradeRefundResponse response = alipayClient.execute(request);

使用支付宝交易号:

AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

AlipayTradeRefundModel model = new AlipayTradeRefundModel();
model.setTradeNo("2026090822001000000000000000");
model.setRefundAmount("10.00");
model.setRefundReason("用户申请退款");

request.setBizModel(model);

AlipayTradeRefundResponse response = alipayClient.execute(request);

如果同一笔交易可能多次退款,还需要按照所用接口的规范设置退款请求号,例如:

model.setOutRequestNo("REFUND_202609080001");

out_request_no 用来区分不同的退款请求,但不能替代 out_trade_no 或 trade_no。

请求前的参数校验

调用退款接口前,可以先校验参数,避免把空值传入 SDK:

boolean outTradeNoEmpty =
        outTradeNo == null || outTradeNo.trim().isEmpty();

boolean tradeNoEmpty =
        tradeNo == null || tradeNo.trim().isEmpty();

if (outTradeNoEmpty && tradeNoEmpty) {
    throw new IllegalArgumentException(
            "out_trade_no 和 trade_no 不能同时为空"
    );
}

如果业务系统同时保存了两个编号,可以选用来源明确且已绑定原支付记录的一个:

if (tradeNo != null && !tradeNo.trim().isEmpty()) {
    model.setTradeNo(tradeNo.trim());
} else {
    model.setOutTradeNo(outTradeNo.trim());
}

排查最终发送的请求

如果代码里已经赋值,接口仍然返回相同错误,需要检查签名前或发送前的实际业务参数,不能只看代码中的变量值。实际内容应类似:

{
  "biz_content": {
    "out_trade_no": "ORDER_202609080001",
    "refund_amount": "10.00"
  }
}

逐项确认以下内容:

  • 字段名是 out_trade_no 或 trade_no。
  • 字段位于退款接口要求的业务参数中。
  • 参数值不是 null、"" 或纯空格。
  • 序列化后,字段没有被过滤。
  • 传入的是原支付交易编号,不是退款单号、请求流水号或其他业务编号。
  • 当前应用和商户身份有权操作这笔原交易。

记录请求日志时,不要输出签名、密钥等敏感信息。订单号也应按照系统的安全规范进行脱敏。

注意事项

  • out_trade_no 必须与原支付请求中的商户订单号完全一致。
  • trade_no 必须来自原交易的支付宝支付结果或交易查询结果。
  • 退款时不要重新生成 out_trade_no。
  • 不要用 out_request_no、商品订单号或内部退款流水号代替原交易标识。
  • 如果同时传入 out_trade_no 和 trade_no,两者必须指向同一笔交易。一般传入一个即可,以降低数据不一致的风险。
  • 修复交易标识后,如果接口继续提示其他参数无效,请根据新的错误信息检查退款金额、退款请求号和接口要求的其他字段。

备注:内容仅供参考。