PAYATHON 2026

周期扣款签约后支付的订单调用退款接口提示参数无效

支付老李

结论

周期扣款订单退款时提示“参数无效”,通常不是退款接口缺少必传参数,而是退款请求中的订单标识与实际支付交易不匹配。

签约、周期扣款和退款属于不同环节。调用退款接口时,必须使用扣款成功后生成的支付订单号,例如支付接口返回的 trade_no,或该笔支付对应的 out_trade_no。签约号、协议号、签约请求号及其他周期扣款业务编号不能用于定位退款交易。

确认订单标识无误后,再检查退款金额格式、应用与商户环境、字符编码和签名参数。

原因分析

周期扣款一般涉及以下几类编号:

  • 签约请求号
  • 协议号或签约号
  • 商户支付订单号 out_trade_no
  • 支付平台交易号 trade_no
  • 退款请求号 out_request_no

每类编号都有自己的用途。签约号用于管理扣款协议,不能直接作为退款订单号。

以 alipay.trade.refund 为例,退款请求需要通过以下任一参数定位原支付交易:

  • trade_no:支付成功后返回的支付宝交易号。
  • out_trade_no:调用支付接口时提交的商户订单号。

如果传入签约请求号、协议号或业务系统内部的其他订单号,即使必传参数齐全,接口仍可能返回参数无效、交易不存在等错误。

解决步骤

1. 找到实际支付流水

查看周期扣款支付成功时保存的同步响应或异步通知,确认以下字段:

trade_no
out_trade_no
trade_status
total_amount

退款时建议优先使用支付成功结果中的 trade_no。如果使用 out_trade_no,它必须与发起该笔扣款时提交的值完全一致。

2. 不要混用签约参数

不要把以下值传给退款请求中的 trade_no 或 out_trade_no:

external_agreement_no
agreement_no
personal_product_code
sign_scene
签约请求号
业务合同号

协议仍然有效,不影响已支付交易的退款。退款针对的是某一笔支付流水,不是扣款协议本身。

3. 检查退款金额

refund_amount 需要满足以下条件:

  • 使用十进制金额字符串,例如 "10.00"。
  • 不含人民币符号、千位分隔符或空格。
  • 不超过该交易当前的可退金额。
  • 多次退款时,累计退款金额不能超过实付金额。

金额计算不建议直接使用浮点数。业务系统可以用整数形式的“分”来存储金额,提交接口前再转换成保留两位小数的字符串。

4. 检查应用和环境是否一致

退款请求必须与原支付交易使用相同的环境和商户关系,包括:

  • 同一个应用 app_id
  • 同一个收款商户身份
  • 相同的沙箱或生产环境
  • 正确的网关地址
  • 与当前应用匹配的私钥、平台公钥或证书

生产环境产生的交易不能在沙箱环境中退款。服务商模式下,还要确认退款请求使用了原交易所属商户的授权关系,不能只使用服务商自身身份。

5. 查看完整错误响应

不要只记录“参数无效”这一层提示,还要保存以下字段:

code
msg
sub_code
sub_msg
trace_id

具体字段名称以接口的实际响应为准。sub_code 和 sub_msg 通常可以帮助区分订单号错误、金额错误、权限问题和签名问题。

请求示例

下面是 alipay.trade.refund 的核心业务参数。网关公共参数和签名方式应以当前接入方式为准:

{
  "trade_no": "支付成功后返回的交易号",
  "refund_amount": "10.00",
  "out_request_no": "REFUND_202609140001",
  "refund_reason": "用户申请退款"
}

也可以使用原支付请求中的商户订单号定位交易:

{
  "out_trade_no": "PAY_202609140001",
  "refund_amount": "10.00",
  "out_request_no": "REFUND_202609140001"
}

重试同一个退款请求时,应保持 out_request_no 不变,以实现幂等处理。如果发起新的部分退款,则应使用新的退款请求号。

Java 示例:

AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

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

request.setBizModel(model);

AlipayTradeRefundResponse response = alipayClient.execute(request);

if (!response.isSuccess()) {
    log.error(
        "refund failed: code={}, msg={}, subCode={}, subMsg={}",
        response.getCode(),
        response.getMsg(),
        response.getSubCode(),
        response.getSubMsg()
    );
}

注意事项

  • 周期扣款订单的退款规则通常与普通支付交易相同,签约协议号不能作为退款凭证。
  • 接口调用成功不等于退款已经成功,还需要核对退款响应和退款查询结果。
  • 网络超时后,不要立即生成新的 out_request_no 再次退款。应先使用原退款请求号重试或查询退款结果。
  • 如果订单号、金额和环境都正确,仍需结合 sub_code 和 sub_msg 继续排查。仅凭“参数无效”无法判断唯一原因。
  • 不同支付产品和接入模式可能存在差异,最终以实际调用的退款 API 文档和完整响应报文为准。

备注:内容仅供参考。