退款接口提示 ACQ.INVALID_PARAMETER 如何解决?
结论
ACQ.INVALID_PARAMETER 表示退款请求缺少原交易标识,也就是 out_trade_no(商户订单号)和 trade_no(支付宝交易号)都为空。
调用退款接口时,至少要传入其中一个参数:
out_trade_no:商户创建原支付订单时生成的订单号。trade_no:原订单支付成功后,支付宝返回的交易号。
接口通过这两个参数定位原支付交易。两者不能同时缺失,也不能传空字符串。接入时通常选择其中一个即可,以免两个编号指向不同的交易。
问题原因
出现该错误,通常是以下情况造成的:
- 请求中没有设置
out_trade_no和trade_no。 - 参数值为
null、空字符串或纯空格。 - 参数名称拼写错误,例如写成了
out_refund_no。 - 参数位置不对,没有写入退款接口要求的业务参数。
- 对象序列化时过滤了空值字段,最终请求中没有交易标识。
- 把退款请求号
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,两者必须指向同一笔交易。一般传入一个即可,以降低数据不一致的风险。 - 修复交易标识后,如果接口继续提示其他参数无效,请根据新的错误信息检查退款金额、退款请求号和接口要求的其他字段。
备注:内容仅供参考。
