微信支付 V2 提示 invalid total_fee,先检查实际发送的金额单位和整数格式。total_fee 使用“分”,不能直接传“元”,也不能让二进制浮点转换和隐式舍入决定应付金额。V2 金额字段
旧记录中的算式需要纠正
2018 年项目把字符串金额转成 Double 再乘 100,随后出现该错误。旧稿将输入 12 的结果写成 12.0,这个算式不成立:12 元对应 1200 分,浮点打印可能是 1200.0,而不是 12.0。
当时用 Math.round 去掉小数部分。这个动作不能解释所有失败:还要看请求实际序列化成什么、金额是否来自可信订单,以及是否发生了不符合业务规则的舍入。
从十进制字符串精确得到整数分
下面示例接受至多两位小数的非负十进制文本,并要求结果大于零、能放入 int。金额上限还需由业务额外限制:
import java.math.BigDecimal;
public final class MoneyInCents {
public static int fromYuan(String amount) {
if (amount == null || !amount.matches("[0-9]{1,9}(\\.[0-9]{1,2})?")) {
throw new IllegalArgumentException("金额格式不正确");
}
int cents = new BigDecimal(amount)
.movePointRight(2)
.intValueExact();
if (cents <= 0) {
throw new IllegalArgumentException("金额必须大于零");
}
return cents;
}
}
new BigDecimal(String) 直接保留十进制输入含义;intValueExact 拒绝不能精确表示的值及溢出,不会悄悄截断。Java 8 BigDecimal
用边界例子检查契约
| 输入 | 本示例应如何处理 |
|---|---|
| 12 | 1200 分 |
| 12.30 | 1230 分 |
| 0.01 | 1 分 |
| 0、负数、空文本 | 拒绝 |
| 1.234 | 因超过两位小数拒绝 |
| 转换后超出 int | 抛出精确转换异常 |
上表是函数设计要求,不是已发起支付的结果。如果折扣产生更细小数,应先确定舍入政策再计算,不要临时用 round 修补。
金额应从服务端订单计算,不能直接信任客户端传来的价格。之后再按接口版本序列化和签名;同样的 total_fee 提示也可能只是上游下单失败的连带现象,见订单重复错误链。