ISO 8583-1987 message packing, decoding and validation for MoonBit
IsoMessage
│ 字段规格校验 / 业务模板校验 / 领域一致性校验
▼
MTI + Bitmap + Data Elements
│ WireProfile: ASCII / BCD / Binary
▼
ISO 8583 payload
│ FrameHeader: uint16-be 或 ASCII4
▼
TCP framelet packager = @iso.iso1987_packager()
let request = @iso.message("0200").unwrap()
request.set_field(2, "6222021234567890").unwrap()
request.set_field(3, "000000").unwrap()
request.set_field(4, "000000001500").unwrap()
request.set_field(7, "0916123456").unwrap()
request.set_field(11, "123456").unwrap()
request.set_field(22, "051").unwrap()
request.set_field(41, "TERM0001").unwrap()
request.set_field(49, "156").unwrap()
let issues = @iso.validate_message_template(
request,
@iso.financial_request_template(),
)
assert_eq(issues.length(), 0)
let payload = @iso.pack_message(
packager,
@iso.ascii_binary_profile(),
request,
).unwrap()import {
"Han-Wentao/mooniso8583" @iso,
}let received = @iso.unpack_message(
packager,
@iso.ascii_binary_profile(),
payload,
).unwrap()
let response = @iso.response_skeleton(received, "00").unwrap()
response.set_field(38, "A12345").unwrap()
assert_true(@iso.response_correlates(received, response))let decoder = @iso.stream_decoder(
@iso.BinaryBigEndian16,
8192,
).unwrap()
let frames = decoder.feed(socket_chunk).unwrap()
for frame in frames {
let message = @iso.unpack_message(
packager,
@iso.ascii_binary_profile(),
frame,
).unwrap()
// 处理一条完整报文
}moon run examples/authorizationmoon run examples/reversalmoon run examples/network_management| API | MTI | Bitmap | 典型用途 |
|---|---|---|---|
| ascii_binary_profile() | 4 字节 ASCII | 8/16 字节二进制 | 常见 TCP 私有网络 |
| ascii_hex_profile() | 4 字节 ASCII | 16/32 字节 ASCII Hex | 文本网关、联调日志 |
| bcd_binary_profile() | 2 字节 BCD | 8/16 字节二进制 | 带宽敏感或历史终端协议 |
let nodes = @iso.parse_de55(
"9F260811223344556677889F270180",
).unwrap()
let cryptogram = @iso.find_tlv(nodes, "9F26").unwrap()
assert_eq(cryptogram.value.length(), 8)println(@iso.safe_message_dump(message, packager))| 字段 | 日志行为 |
|---|---|
| DE2 | 前六后四,其余替换为 * |
| DE35 / DE45 | 隐藏有效期、服务码和全部轨道数据 |
| DE52 | 完全隐藏 PIN Data |
| DE53 / DE96 | 隐藏安全控制和密钥管理数据 |
| DE55 | 仅显示字节数和 Tag 列表 |
| DE64 / DE128 | 完全隐藏 MAC |
| 项目/类型 | 主要定位 | MoonISO8583 的取舍 |
|---|---|---|
| jPOS | Java 支付平台,覆盖 Channel、MUX、交易管理等完整基础设施 | 不做交换平台,只提供 MoonBit 原生编解码、校验和传输帧 |
| j8583 | Java ISO 8583 报文库 | 提供 MoonBit 类型和多后端构建,增加严格 BER-TLV、DE90 关联与安全诊断 |
| pyiso8583 | Python 配置驱动编解码 | 保留可配置字段模型,同时面向静态编译和 Wasm/JS/Native 目标 |
| 手写字符串拼接 | 快速但难以处理次位图、变长头和截断错误 | 字段规格、位图和错误偏移统一处理,并用 round-trip 测试覆盖 |
moon fmt
moon check --target wasm-gc --deny-warn
moon check --target wasm --deny-warn
moon check --target js --deny-warn
moon test --target wasm-gc
moon test --target js
moon run examples/authorization
moon run examples/reversal
moon run examples/network_management
python tools/count_effective_moonbit.py --check-core 3000| 分类 | 文件 | 物理行 | 有效行 |
|---|---|---|---|
| 核心算法(不含规格表) | 20 | 5288 | 4396 |
| ISO 8583-1987 字段规格表 | 1 | 302 | 282 |
| 测试 | 18 | 1042 | 886 |
| 三个示例 | 3 | 105 | 102 |
pub(all) struct DecodeResult {
message : IsoMessage
consumed : Int
bitmap_bytes : Int
} derive(Debug)pub(all) struct FieldSpec {
number : Int
name : String
length_kind : LengthKind
max_length : Int
min_length : Int
length_encoding : LengthEncoding
content_encoding : ContentEncoding
data_kind : DataKind
pad_direction : PadDirection
pad_char : UInt16
} derive(Eq, Debug)pub(all) enum IsoError {
InvalidMti(String)
InvalidFieldNumber(Int)
DuplicateField(Int)
MissingField(Int)
UnknownField(Int)
InvalidLength(Int, Int, Int)
InvalidCharacter(Int, Int, String)
InvalidNumeric(Int, String)
InvalidHex(String)
InvalidBcd(Int, Int)
InvalidBitmapLength(Int)
BitmapFieldOneReserved
Truncated(String, Int, Int)
LengthPrefixOverflow(Int, Int)
FieldTooLong(Int, Int, Int)
FieldTooShort(Int, Int, Int)
InvalidSpec(Int, String)
InvalidTrack2(String)
InvalidProcessingCode(String)
InvalidAmount(String)
InvalidDateTime(String)
InvalidPan(String)
InvalidCurrency(String)
InvalidTlv(String)
InvalidOriginalData(String)
InvalidStreamFrame(String)
TrailingBytes(Int)
TemplateViolation(String)
} derive(Eq, Debug)pub(all) struct MessageTemplate {
name : String
mtis : Array[String]
rules : Array[TemplateFieldRule]
require_pan_source : Bool
} derive(Debug)pub(all) struct Mti {
text : String
version : MtiVersion
message_class : MessageClass
function : MessageFunction
origin : MessageOrigin
} derive(Eq, Debug)pub(all) struct SafeFieldDiagnostic {
field : Int
name : String
logical_length : Int
sensitivity : FieldSensitivity
display : String
} derive(Eq, Debug)pub(all) struct StreamDecoder {
header : FrameHeader
max_frame : Int
buffer : Array[Byte]
} derive(Debug)pub(all) struct TemplateFieldRule {
field : Int
presence : FieldPresence
purpose : String
} derive(Eq, Debug)pub(all) struct WireProfile {
name : String
mti_encoding : MtiEncoding
bitmap_encoding : BitmapEncoding
reject_trailing : Bool
} derive(Eq, Debug)fn additional_amount(account_type_code : String, amount_type_code : String, currency : String, debit : Bool, minor_units : Int64) -> Result[AdditionalAmount, IsoError]fn bcd_pack_digits(field : Int, digits : String, pad_left : Bool, pad_nibble : Int) -> Result[Bytes, IsoError]fn bcd_unpack_digits(field : Int, data : Bytes, digit_count : Int, pad_left : Bool, pad_nibble : Int) -> Result[String, IsoError]fn bytes_equal(left : Bytes, right : Bytes) -> Boolfn concat_bytes(left : Bytes, right : Bytes) -> Bytesfn decode_selected_fields(packager : Packager, bitmap : Bitmap, data : Bytes, start : Int) -> Result[(Array[FieldEntry], Int), IsoError]fn diagnostic_contains_raw_value(diagnostic : String, field : Int, raw_value : String) -> Boolfn find_additional_amount(values : Array[AdditionalAmount], amount_type : String) -> AdditionalAmount?fn iso1987_variable_field_count() -> Intfn left_pad(text : String, width : Int, fill : UInt16) -> Stringfn mask_pan(value : String) -> Stringfn message_layout(packager : Packager, profile : WireProfile, message : IsoMessage) -> Result[Array[String], IsoError]fn original_data(original_mti : String, original_stan : String, original_transmission_datetime : String, acquiring_institution_id : String, forwarding_institution_id : String) -> Result[OriginalDataElements, IsoError]fn pack_framed_message(packager : Packager, profile : WireProfile, header : FrameHeader, message : IsoMessage) -> Result[Bytes, IsoError]fn pack_message(packager : Packager, profile : WireProfile, message : IsoMessage) -> Result[Bytes, IsoError]fn pack_message_hex(packager : Packager, profile : WireProfile, message : IsoMessage) -> Result[String, IsoError]fn pan_equal(left : String, right : String) -> Boolfn pan_luhn_valid(value : String) -> Boolfn parse_minor_amount(field : Int, value : String, width : Int, scale : Int) -> Result[MinorAmount, IsoError]fn processing_code(transaction_type : String, from_account : String, to_account : String) -> Result[String, IsoError]fn reversal_skeleton(original : IsoMessage, reversal_stan : String, reversal_transmission_datetime : String) -> Result[IsoMessage, IsoError]fn right_pad(text : String, width : Int, fill : UInt16) -> Stringfn safe_field_display(field : Int, value : String) -> Stringfn safe_layout_dump(message : IsoMessage, packager : Packager, profile : WireProfile) -> Result[String, IsoError]fn trim_left_char(text : String, fill : UInt16) -> Stringfn trim_right_char(text : String, fill : UInt16) -> Stringfn unpack_framed_message(packager : Packager, profile : WireProfile, header : FrameHeader, frame : Bytes) -> Result[IsoMessage, IsoError]fn unpack_message(packager : Packager, profile : WireProfile, data : Bytes) -> Result[IsoMessage, IsoError]fn unpack_message_hex(packager : Packager, profile : WireProfile, fixture : String) -> Result[IsoMessage, IsoError]fn unpack_message_with_result(packager : Packager, profile : WireProfile, data : Bytes) -> Result[DecodeResult, IsoError]fn validate_alphanumeric_text(field : Int, value : String, allow_special : Bool) -> Result[Unit, IsoError]fn validate_known_message_template(message : IsoMessage) -> Result[Array[ValidationIssue], IsoError]fn validate_message_template(message : IsoMessage, template : MessageTemplate) -> Array[ValidationIssue]fn validate_response_correlation(request : IsoMessage, response : IsoMessage) -> Array[ValidationIssue]fn validate_reversal_correlation(original : IsoMessage, reversal : IsoMessage) -> Array[ValidationIssue]Install
Download zipISO 8583-1987 message packing, decoding and validation for MoonBit