客服反馈操作订单退款失败,用户的消费记录显示,该订单并没有用余额扣款。这排除了因为实际支付金额与订单金额不一致导致的退款失败情况。

根据订单号查询日志,发现了这个报错 订单金额或退款金额与之前请求不一致,请核实后再试

{
    "success": false,
    "error_message": "wechat pay refund failed.",
    "payInfo": {
        "code": "INVALID_REQUEST",
        "message": "订单金额或退款金额与之前请求不一致,请核实后再试"
    }
}

查询微信支付分订单,参数支付金额 total_amount230,参数是在支付金额上乘以 100,与实际的支付金额 2.3 一致。这也表示支付金额没有问题。

查询日志中提交的支付参数,发现 total229,而非预期中的 230。错误找到了:

'amount' => [
    'refund' =>  intval(round($refund_money, 2) * 100)),
    'total'  =>  intval(round($total_money, 2) * 100)),
    'currency' => "CNY"
]

本地 php -a 进入 php shell 中输出 intval(round(2.3, 2) * 100),得到的结果确实是 229。这意味着之前的修正精度的操作是失败的。之前遇到过这个问题,也百度过 php 高精度计算问题。可以通过 PHP 的 BC 高精度函数解决,但之前似乎有其他的想法可以解决,但并没有奏效。

百度同时发现了另外一种比较简洁的方法 round(2.3 * 100),可以直接解决问题。round 还有两个默认的参数,一个是精度,默认为 0,一个为舍入模式,默认为 PHP_ROUND_HALF_UP,即四舍五入。

如果直接 2.3 * 100 也能拿到预期的 230,那么使用函数 intval 来取整看起来不太明智的方法。就比如那个经典的示例 0.58 * 100 也是在 intval 取整的时候出现问题的,那么不取整似乎也没有问题。主要是因为该字段在取出到 PHP 内存中可能表示为 2.2999...(众所周知的 php 精度问题)。所以取出来乘以 100 之后需要对结果进行取整,而 intval 取整结果也看到了,就是 229。所以需要使用 round 或者 bcmul 处理才行。

最终的写法为 intval(round($refund_money * 100)) 或者 round($refund_money * 100)