支付回调传的 amount 是 19.999,字段是 decimal(10,2),入库后变成 20.00,和渠道账单的 19.99 对不上,日终差了 0.01 元,对账任务报了 37 条差异。第一次遇到会往复杂处想,其实漏掉的多是个小前提。

SELECT id, amount FROM payment
WHERE amount * 100 <> ROUND(amount * 100);
SHOW CREATE TABLE payment;

校验只判断了大于 0,没有限制小数位。数据库按列精度四舍五入,多出来的第三位被进位,金额被悄悄改大。回调金额和订单金额的比较又在入库后做,比的已经是改过的值。这一点先确认清楚,后面就不用反复试。

碰到「金额小数位校验」这类问题,我习惯先把当前配置原样备份一份,改坏了立刻换回来,不用现场推。把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。

改动写完就同步给同事,下一个人踩同一个坑,成本还是你的。

具体做法是,在 DTO 上给 amount 加 @Digits(integer=10, fraction=2);比价放在入库前 用原始字符串转 BigDecimal 后 setScale 2 ROUND_DOWN;渠道回调按分做整数比较 避免小数位再出问题;把 37 条差异单重放一遍确认都能对上。

改完照着核一遍,19.999 的回调返回 400 不再入库 重放差异单金额与渠道一致。

作者 9823189