金额格式化必须用 decimal.StringFixed,全程使用 shopspring/decimal.Decimal 类型存储计算,结合 ISO 4217 动态小数位、Webhook 安全解析及 x/text/message 本地化展示,确保精度贯穿全链路。
金额格式化必须用
,别碰
Go 原生
无法安全表示 0.1、0.01 等金额,直接格式化会引入不可控误差。比如
在某些边界值下可能输出
或
—— 这不是 bug,是 IEEE 754 浮点规则决定的。
正确做法是全程使用
类型存储和计算,最后用
输出:
是四舍五入,不是截断;传
就一定保证两位小数,不足补零
不要先转
再格式化,那等于把精度问题又引回来了
前端传来的金额字符串(如
)应直接用
解析,别用
不同币种的小数位数得动态控制,不能硬写
日元(
)、韩元(
)等货币法定无小数位,欧元(
)、美元(
)强制两位,而有些加密货币支持 8 位。硬编码
会导致日元显示成
,违反当地金融规范。
推荐做法:查 ISO 4217 标准,建一张映射表:
立即学习
“
go语言免费学习笔记(深入)
”;
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
从支付网关或用户 locale 获取币种代码(如
),再查表取
查不到时 fallback 到 2,但要打 warning 日志,避免静默错误
注意 PayPal 和 Stripe 都要求金额以「最小单位整数」传入(如 USD 用分),返回时才按币种还原小数位
Webhook 回调里的金额字段必须用
解析,别信 JSON 中的数字
Stripe 和 PayPal 的 webhook payload 里,金额字段都是整数(如
),但有些第三方支付网关会返回
—— 这是非法的,但真实存在。如果用
直接解到
字段,就又掉进浮点陷阱。
定义 webhook struct 时,金额字段必须是
类型
实现自定义
方法,优先尝试解析整数字符串(
),再 fallback 到浮点字符串(
),最后才解析数字字面量
PayPal v1 SDK 返回的
字段名大小写不固定(有时是
),建议用
先取原始值,再转
本地化显示货币符号和分隔符要用
只管小数位,不管 ¥ 还是 $,也不管千分位是逗号还是空格。这些必须交给
处理,它能根据
自动适配。
别拼接符号字符串(如
),欧元区用户看到
是对的,但巴西用户需要
,符号位置和小数点符号都不同
千分位分隔符不能靠
手动加,
会按 locale 自动处理
或
如果用
做多语言文案,货币格式化仍要单独走
,它俩职责不同:一个管文案翻译,一个管数字/日期/货币格式
真正难的不是写对一行
,而是让金额在计算、传输、存储、展示四个环节始终用同一套精度规则贯穿。任何一环退回到
或字符串拼接,都会在某个深夜报警里重现。
decimal.StringFixedfmt.Printffloat64fmt.Sprintf("%.2f", 19.99)"19.98""20.00"shopspring/decimal.DecimalStringFixedamount := decimal.NewFromInt(1999).Div(decimal.NewFromInt(100)) // = 19.99
fmt.Println(amount.StringFixed(2)) // ✅ 稳定输出 "19.99"StringFixed2float64"19.99"decimal.NewFromStringstrconv.ParseFloatStringFixed(2)JPYKRWEURUSDStringFixed(2)"10000.00"var currencyScale = map[string]int32{
"USD": 2, "EUR": 2, "GBP": 2,
"JPY": 0, "KRW": 0,
"BTC": 8, "ETH": 18,
}req.Currencyplacesdecimal"amount": 1999"amount": 19.99json.Unmarshalfloat64decimal.DecimalUnmarshalJSON"1999""19.99"Amountamountmap[string]interface{}decimalgolang.org/x/text/messageStringFixedmessage.Printerlanguage.Tagp := message.NewPrinter(language.Japanese)
p.Printf("¥%s", amount.StringFixed(currencyScale["JPY"])) // → ¥10000"$" + amount.StringFixed(2)"€19.99""R$ 19,99"strings.Replacemessage.Printer1000000 → "1,000,000""1.000.000"go-i18nx/text/messageStringFixedfloat64