报文里的 DE49 放的是数字码不是字母码,而小数位决定了金额怎么读。下面还有个换算器。
| 字母码 | 数字码 DE49 | 小数位 | 名称 |
|---|---|---|---|
JPY | 392 | 0 | 日元 |
KRW | 410 | 0 | 韩元 |
VND | 704 | 0 | 越南盾 |
CLP | 152 | 0 | 智利比索 |
ISK | 352 | 0 | 冰岛克朗 |
PYG | 600 | 0 | 巴拉圭瓜拉尼 |
RWF | 646 | 0 | 卢旺达法郎 |
UGX | 800 | 0 | 乌干达先令 |
XAF | 950 | 0 | 中非法郎 |
XOF | 952 | 0 | 西非法郎 |
XPF | 953 | 0 | 太平洋法郎 |
BIF | 108 | 0 | 布隆迪法郎 |
DJF | 262 | 0 | 吉布提法郎 |
GNF | 324 | 0 | 几内亚法郎 |
KMF | 174 | 0 | 科摩罗法郎 |
VUV | 548 | 0 | 瓦努阿图瓦图 |
BHD | 048 | 3 | 巴林第纳尔 |
IQD | 368 | 3 | 伊拉克第纳尔 |
JOD | 400 | 3 | 约旦第纳尔 |
KWD | 414 | 3 | 科威特第纳尔 |
LYD | 434 | 3 | 利比亚第纳尔 |
OMR | 512 | 3 | 阿曼里亚尔 |
TND | 788 | 3 | 突尼斯第纳尔 |
USD | 840 | 2 | 美元 |
EUR | 978 | 2 | 欧元 |
CNY | 156 | 2 | 人民币 |
GBP | 826 | 2 | 英镑 |
HKD | 344 | 2 | 港元 |
MOP | 446 | 2 | 澳门元 |
TWD | 901 | 2 | 新台币 |
SGD | 702 | 2 | 新加坡元 |
AUD | 036 | 2 | 澳元 |
NZD | 554 | 2 | 新西兰元 |
CAD | 124 | 2 | 加元 |
CHF | 756 | 2 | 瑞士法郎 |
SEK | 752 | 2 | 瑞典克朗 |
NOK | 578 | 2 | 挪威克朗 |
DKK | 208 | 2 | 丹麦克朗 |
PLN | 985 | 2 | 波兰兹罗提 |
CZK | 203 | 2 | 捷克克朗 |
HUF | 348 | 2 | 匈牙利福林 |
RON | 946 | 2 | 罗马尼亚列伊 |
BGN | 975 | 2 | 保加利亚列弗 |
RUB | 643 | 2 | 俄罗斯卢布 |
UAH | 980 | 2 | 乌克兰格里夫纳 |
TRY | 949 | 2 | 土耳其里拉 |
ILS | 376 | 2 | 以色列新谢克尔 |
AED | 784 | 2 | 阿联酋迪拉姆 |
SAR | 682 | 2 | 沙特里亚尔 |
QAR | 634 | 2 | 卡塔尔里亚尔 |
EGP | 818 | 2 | 埃及镑 |
ZAR | 710 | 2 | 南非兰特 |
NGN | 566 | 2 | 尼日利亚奈拉 |
KES | 404 | 2 | 肯尼亚先令 |
INR | 356 | 2 | 印度卢比 |
PKR | 586 | 2 | 巴基斯坦卢比 |
BDT | 050 | 2 | 孟加拉塔卡 |
LKR | 144 | 2 | 斯里兰卡卢比 |
NPR | 524 | 2 | 尼泊尔卢比 |
THB | 764 | 2 | 泰铢 |
MYR | 458 | 2 | 马来西亚林吉特 |
IDR | 360 | 2 | 印尼盾 |
PHP | 608 | 2 | 菲律宾比索 |
KHR | 116 | 2 | 柬埔寨瑞尔 |
LAK | 418 | 2 | 老挝基普 |
MMK | 104 | 2 | 缅甸元 |
MNT | 496 | 2 | 蒙古图格里克 |
KZT | 398 | 2 | 哈萨克坚戈 |
BRL | 986 | 2 | 巴西雷亚尔 |
MXN | 484 | 2 | 墨西哥比索 |
ARS | 032 | 2 | 阿根廷比索 |
COP | 170 | 2 | 哥伦比亚比索 |
PEN | 604 | 2 | 秘鲁索尔 |
UYU | 858 | 2 | 乌拉圭比索 |
ALL | 008 | 2 | 阿尔巴尼亚列克 |
DZD | 012 | 2 | 阿尔及利亚第纳尔 |
BSD | 044 | 2 | 巴哈马元 |
AMD | 051 | 2 | 亚美尼亚德拉姆 |
BBD | 052 | 2 | 巴巴多斯元 |
BMD | 060 | 2 | 百慕大元 |
BTN | 064 | 2 | 不丹努尔特鲁姆 |
BOB | 068 | 2 | 玻利维亚诺 |
BWP | 072 | 2 | 博茨瓦纳普拉 |
BZD | 084 | 2 | 伯利兹元 |
SBD | 090 | 2 | 所罗门群岛元 |
CVE | 132 | 2 | 佛得角埃斯库多 |
KYD | 136 | 2 | 开曼群岛元 |
CRC | 188 | 2 | 哥斯达黎加科朗 |
CUP | 192 | 2 | 古巴比索 |
DOP | 214 | 2 | 多米尼加比索 |
ETB | 230 | 2 | 埃塞俄比亚比尔 |
FKP | 238 | 2 | 福克兰群岛镑 |
GMD | 270 | 2 | 冈比亚达拉西 |
GIP | 292 | 2 | 直布罗陀镑 |
GTQ | 320 | 2 | 危地马拉格查尔 |
GYD | 328 | 2 | 圭亚那元 |
HTG | 332 | 2 | 海地古德 |
HNL | 340 | 2 | 洪都拉斯伦皮拉 |
IRR | 364 | 2 | 伊朗里亚尔 |
JMD | 388 | 2 | 牙买加元 |
KGS | 417 | 2 | 吉尔吉斯斯坦索姆 |
LBP | 422 | 2 | 黎巴嫩镑 |
LSL | 426 | 2 | 莱索托洛蒂 |
LRD | 430 | 2 | 利比里亚元 |
MWK | 454 | 2 | 马拉维克瓦查 |
MVR | 462 | 2 | 马尔代夫拉菲亚 |
MUR | 480 | 2 | 毛里求斯卢比 |
MDL | 498 | 2 | 摩尔多瓦列伊 |
MAD | 504 | 2 | 摩洛哥迪拉姆 |
NAD | 516 | 2 | 纳米比亚元 |
AWG | 533 | 2 | 阿鲁巴弗罗林 |
NIO | 558 | 2 | 尼加拉瓜科多巴 |
PAB | 590 | 2 | 巴拿马巴波亚 |
PGK | 598 | 2 | 巴布亚新几内亚基那 |
SHP | 654 | 2 | 圣赫勒拿镑 |
SCR | 690 | 2 | 塞舌尔卢比 |
SOS | 706 | 2 | 索马里先令 |
SZL | 748 | 2 | 斯威士兰里兰吉尼 |
SYP | 760 | 2 | 叙利亚镑 |
TOP | 776 | 2 | 汤加潘加 |
TTD | 780 | 2 | 特立尼达和多巴哥元 |
MKD | 807 | 2 | 北马其顿代纳尔 |
TZS | 834 | 2 | 坦桑尼亚先令 |
UZS | 860 | 2 | 乌兹别克斯坦苏姆 |
WST | 882 | 2 | 萨摩亚塔拉 |
YER | 886 | 2 | 也门里亚尔 |
SLE | 925 | 2 | 塞拉利昂利昂 |
VES | 928 | 2 | 委内瑞拉玻利瓦尔 |
MRU | 929 | 2 | 毛里塔尼亚乌吉亚 |
STN | 930 | 2 | 圣多美和普林西比多布拉 |
BYN | 933 | 2 | 白俄罗斯卢布 |
TMT | 934 | 2 | 土库曼斯坦马纳特 |
GHS | 936 | 2 | 加纳塞地 |
RSD | 941 | 2 | 塞尔维亚第纳尔 |
MZN | 943 | 2 | 莫桑比克梅蒂卡尔 |
AZN | 944 | 2 | 阿塞拜疆马纳特 |
XCD | 951 | 2 | 东加勒比元 |
ZMW | 967 | 2 | 赞比亚克瓦查 |
SRD | 968 | 2 | 苏里南元 |
MGA | 969 | 2 | 马达加斯加阿里亚里 |
AFN | 971 | 2 | 阿富汗尼 |
TJS | 972 | 2 | 塔吉克斯坦索莫尼 |
AOA | 973 | 2 | 安哥拉宽扎 |
CDF | 976 | 2 | 刚果法郎 |
BAM | 977 | 2 | 波黑可兑换马克 |
GEL | 981 | 2 | 格鲁吉亚拉里 |
BND | 096 | 2 | 文莱元 |
FJD | 242 | 2 | 斐济元 |
KPW | 408 | 2 | 朝鲜元 |
SDG | 938 | 2 | 苏丹镑 |
第一,DE49 放的是数字码。报文里写的是 392 不是 JPY,是 840 不是 USD。做内部系统映射时把两者搞混,会导致币种识别错误,而这种错误往往要到对账才暴露。
第二,金额是最小货币单位的整数。报文里的 DE4 没有小数点,000000012345 是多少取决于币种:美元是 123.45,日元是 12345,科威特第纳尔是 12.345。
金额 × 100 写死。一笔 5,000 日元这么发出去,字段里是 500000,而日元零小数位——那就是 50 万日元,多了整整 100 倍,而且它会正常授权通过。反方向同样糟:科威特第纳尔除以 100,会少收 10 倍。两种错都是静默的,不报错、不触发校验,只有对账时才发现。维护一张「币种 → 小数位」的表,所有换算都走这张表,不要让字面量 100 出现在任何金额附近。上面的换算器可以用来核对你的实现算得对不对。
DE5(结算金额)、DE6(持卡人入账金额)以及 EMV 的 9F02 标签都遵循同一规则。而且跨境交易里,这三个金额的币种可能各不相同——分别看 DE49、DE50、DE51,别默认它们一致。
ISO 8583 的金额域不带小数点。那个数字是「最小货币单位」的整数,而多少个最小单位等于一个单位 是由币种决定的,不是由报文决定的。默认到处都是两位小数,你会在两个方向上都出错。
日元的小数位是 0:一笔日元交易里的 000000001000 是 1000 日元,不是 10.00。
科威特第纳尔、巴林第纳尔、约旦第纳尔的小数位是 3:同样这串数字是 1.000 第纳尔。
一个写死的除以 100,会把一千日元的咖啡变成十日元,把一第纳尔的车费变成一千第纳尔 ——
而这两种错在日志里都不像错。
还有两个上面那张表显示不出来的坑。数字码和字母码是独立分配的,所以不能从一个推出另一个, 也不能按一个排序然后指望另一个跟着有序。以及 ISO 4217 里有卡组织压根不支持的码 —— 包括资金码和贵金属码 —— 所以「是合法的 ISO 币种码」和「能路由」不是一回事。 这套算术连带算例在金额与最小货币单位。