周期扣款签约后支付的订单调用退款接口提示参数无效
结论
周期扣款订单退款时提示“参数无效”,通常不是退款接口缺少必传参数,而是退款请求中的订单标识与实际支付交易不匹配。
签约、周期扣款和退款属于不同环节。调用退款接口时,必须使用扣款成功后生成的支付订单号,例如支付接口返回的 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 文档和完整响应报文为准。
备注:内容仅供参考。
