2022-03-01 更新

通过最近收到的反馈,分析得到了支付宝信用借支付报错 ACQ.DUP_OUT_ORDER 外部订单号重复 的原因:商户订单号(外部订单号)相同而支付参数 subject 不相同,导致支付宝将后面提交的支付参数当成了新的请求,而新的请求商户订单号验证失败了。

支付宝统一收单交易支付接口 甚至都没有显示这个错误码,显然这个错误相当罕见。

结合收到的反馈,自动售出时出现用户账户余额不足、扣款失败的情况,在后台重新发起支付(支付宝支付接口重复提交)时多次出现这种 [外部订单号重复] 的提示信息。而正常的订单借用待支付重新发起支付就没有这种提示信息,猜测两种情况下存在参数差异导致报错,而非订单号本身重复的问题。

对比发现:正常订单借用支付参数中实例参数 $this->body 值与重新支付时参数一致,与自动售出支付参数不相同:

$request->setBizContent("{" .
    "\"auth_no\":\"{$auth_no}\"," .
    "\"out_trade_no\":\"{$out_order_no}\"," .
    "\"product_code\":\"PRE_AUTH_ONLINE\"," .
    "\"total_amount\":{$this->alipayMoney}," .
    "\"buyer_id\":\"{$this->user_id}\"," .
    "\"seller_id\":\"{$seller_id}\"," .
    "\"subject\":\"{$this->body}\"," .
    "\"body\":\"{$this->body}\"," .
    "\"auth_confirm_mode\":\"COMPLETE\"" .
    "}");

对比 API 接口请求参数,body 不需要,需要的是 subject 订单标题。所以需要确保支付时的 subject 与重新支付时的参数保持一致。可以将 subject 作为新增字段放到 [支付宝信用单] 表中,之后重新支付时取出填充。但个人感觉不需要那么麻烦,直接将 subject 设置成固定值即可。

修改后的 bizContent

$this->subject = 'xxxx';
$request->setBizContent("{" .
    "\"auth_no\":\"{$auth_no}\"," .
    "\"out_trade_no\":\"{$out_order_no}\"," .
    "\"product_code\":\"PRE_AUTH_ONLINE\"," .
    "\"total_amount\":{$this->alipayMoney}," .
    "\"buyer_id\":\"{$this->user_id}\"," .
    "\"seller_id\":\"{$seller_id}\"," .
    "\"subject\":\"{$this->subject}\"," .
    "\"auth_confirm_mode\":\"COMPLETE\"" .
    "}");

在整理历史支付中的订单时,对现有已授权(Authorized)信用单再次支付报错:外部订单号重复

这可能是之前测试提交过相同的订单,即当前提交的 out_order_no 已经存在了。这个也好解决,存在了换一个就是了,修改当前订单号在末尾加上一位自定义字符即可。

还遇到了 交易已存在且与本次请求的订单金额不一致。这种是第一次提交支付失败了,第二次再次提交,提交的金额与第一次尝试支付的金额不一致导致的。这种情况一般不会出现,这边测试提交金额是否可以修改手动更改的。如果实在不记得第一次的支付金额,也可以尝试解冻订单或撤销,未测试是否可以成功。