美文网首页PHP经验分享PHP实战程序猿的进阶屋
复盘微信支付金额不正确问题解决过程——PHP浮点型计算

复盘微信支付金额不正确问题解决过程——PHP浮点型计算

作者: fourn熊能 | 来源:发表于2018-06-19 16:04 被阅读91次

    问题

    2017年9月份,商城项目在运行过程中,购买某商品时如果在下单时没有完成付款,而是稍后再从“个人中心-我的订单”发起付款,则无法调起微信支付界面

    思路

    • 其他商品正常,说明导致问题的原因大概率是商品本身
    • 只有从会员中心发起的付款存在此问题,说明大概率是会员中心的代码存在问题
    • 需要先观察问题出现时“统一下单”是否能够成功,检查是否是参数问题导致订单无法在微信端创建

    观察统一下单返回值

    result_code=FAIL
    err_code=OUT_TRADE_NO_USED
    err_code_des=商户订单号重复
    

    微信官方对于此问题的描述如下:

    image.png

    出现这个问题的时候建议核查订单号是否重复提交,但实际上在这个使用场景下,我们是“故意”重复提交订单号的。因为从会员中心发起支付的时候订单已经创建了,系统会再次请求微信统一下单接口,即便如此,我们也没有必要每一次请求支付都创建一个新的订单号。

    那为什么返回了这个错误

    我先给出结论再描述排错过程:

    所谓的同一笔交易不能多次提交,实际上指的是在商品描述、标价金额不相同的情况下,用同一个订单号访问了统一下单接口。

    image.png

    这里的错误实际上是因为:从会员中心发起支付时“标价金额”与提交订单时的不相同。

    PHP浮点型运算

    以下是某位程序员写的微信支付代码:

    $total_fee = (int)($order_total * 100);
    

    微信要求金额的单位必须为分,而数据库中订单金额单位是元,所以使用订单金额*100是正确的做法。
    订单支付金额的计算非常复杂,所以单位转化为分之后再转化为整型,可以保证微信支付参数不出错,也是正确的做法。

    但这里面隐藏了一个问题,还记得我们问题发生的条件必须是“购买某商品时”吗?如果单独购买这个商品的话,订单的金额是19.9。我们可以尝试:

    echo (int)(19.9 * 100);
    // 结果为1989,而非1990
    

    这就导致了订单创建时给微信的支付数据是1990,而再次支付时却是1980,所以接口返回了“订单号重复”的错误。

    为什么会少了1分钱呢?PHP的官方文档中是这么说:


    image.png

    随后我又实验了很多数字,结果如下:

    echo (int)(19.1 * 100);// 1910
    echo (int)(19.2 * 100);// 1920
    echo (int)(19.3 * 100);// 1930
    echo (int)(19.4 * 100);// 1939 注意这里出现了问题
    echo (int)(19.5 * 100);// 1950
    echo (int)(19.6 * 100);// 1960
    echo (int)(19.7 * 100);// 1970
    echo (int)(19.8 * 100);// 1980
    

    这个问题的产生,似乎存在规律,例如19.4、18.4和17.4转化后是错误的,而8.4转化后返回了正确的结果。
    更有趣的是:

    echo (int)((19.8+0.1) * 100);
    // 1990 注意此时结果是正确的
    var_dump(19.9 == (19.8 + 0.1));
    // false
    

    调试过程

    实际上这种奇怪的问题排查起来没有什么捷径,无非就是打日志追踪变量,最多也就是细心点罢了。

    最终使用了一个比较讨巧的方式解决了这个问题,将代码改为了:

    $total_fee = (int)(($order_total + 0.00001) * 100);
    

    至于更加严谨的浮点数计算方法,今后遇到的时候,再研究吧。

    相关文章

      网友评论

      • codefine:PHP提供了高精度数学计算模块:BCMath

        echo (int)(19.9 * 100); // 1989
        echo bcmul(19.9, 100); // 1990

      本文标题:复盘微信支付金额不正确问题解决过程——PHP浮点型计算

      本文链接:https://www.haomeiwen.com/subject/iazoeftx.html