Project Icon

iso8583

Go语言实现的ISO 8583金融交易报文处理库

iso8583是一个Go语言实现的ISO 8583报文处理库,支持自定义规范、构建解析报文和检查字段。该项目经过实际环境验证,可靠稳定。它简化了银行交易处理功能的开发和集成。这个高性能、可扩展且易用的库适用于处理卡交易、转账、查询等多种金融交易报文。

Moov 横幅标志

项目文档 · 社区 · 博客

GoDoc 构建状态 覆盖状态 Go Report Card 仓库大小 Apache 2 License Slack 频道 GitHub Stars Twitter

moov-io/iso8583

Moov的使命是为开发者提供一种简单的方式来创建和集成银行处理到他们自己的软件产品中。我们的开源项目每一个都专注于解决金融服务中的单一职责,并围绕着性能、可扩展性和易用性进行设计。

ISO8583 在 Go 中实现了 ISO 8583 消息读取和写入。ISO 8583 是一种用于银行卡交易消息的国际标准,它定义了消息格式和通信流程。它被全球主要的银行卡网络(如Visa、Mastercard和Verve)所使用。该标准支持银行卡购买、取款、存款、退款、冲正、余额查询、账户之间转账、管理消息、安全密钥交换等各种交易。

目录

项目状态

Moov ISO8583 是一个 Go 包,经过了 彻底的现实世界测试和信任。该项目在真实世界的高风险场景中已经证明了其可靠性和健壮性。如果您发现任何缺失的功能/错误/文档不清晰,请通过 提交 issue 告知我们。谢谢!

Go 模块

本项目使用 Go Modules。请参考 Golang 的安装说明 来设置 Go 环境。您可以下载源代码,我们也提供了 已标记和发布的版本。我们强烈建议您在生产环境中使用已发布的标记版本。

Go 版本支持政策

永远保持最新,绝不落下

尽管我们努力拥抱最新的语言增强功能,但我们也理解需要一定程度的向后兼容性。我们知道并非所有人都可以立即升级到最新版本。我们的理念是向前发展,拥抱新事物,但不会让任何人立即落下。

我们现在支持哪些版本?

截至目前,我们正在支持以下版本,如 setup-go action step 所引用:

  • stable (指向当前 Go 版本)
  • oldstable (指向上一个 Go 版本)

setup-go action 自动管理版本控制,使我们能够始终与最新和前一个 Go 版本保持一致。

这对您意味着什么?

每当 Go 的新版本发布时,我们都会更新我们的系统,并确保我们的项目完全兼容。与此同时,我们将继续支持前一个版本。但是,一旦新版本发布,"前前一个"版本将不再受官方支持。

持续集成

为了确保我们对这些版本的支持承诺,我们已经配置了 GitHub CI 操作,使用当前版本和前一版本的 Go 来测试我们的代码。这意味着,如果您正在使用这两个版本中的任何一个,您都可以放心地使用该项目。

安装

go get github.com/moov-io/iso8583

如何做

定义您的规范

目前,我们已经定义了以下 ISO 8583 规范:

  • Spec87ASCII - 1987 版本的规范,使用 ASCII 编码
  • Spec87Hex - 1987 版本的规范,使用十六进制编码

Spec87ASCII 适用于大多数用例。只需使用 specs.Spec87ASCII 实例化一个新消息:

isomessage := iso8583.NewMessage(specs.Spec87ASCII)

如果此规范不满足您的需求,我们鼓励您修改它或使用以下信息创建自己的规范。 首先,您需要定义在ISO8583规范中描述的消息字段的格式。每个数据字段都有一种类型及其自身的规范。您可以创建一个"NewBitmap"、"NewString"或"NewNumeric"字段。每个单独的字段规范由几个元素组成:

元素注释示例
长度最大字段长度(字节、字符或数字),对于定长和变长都适用。10
描述描述数据字段的内容。"主账号"
编码设置编码类型(ASCII, Hex, Binary, BCD, LBCD, EBCDIC)。encoding.ASCII
前缀设置字段长度和类型的编码(ASCII, Hex, Binary, BCD, EBCDIC)为定长或变长(Fixed, L, LL, LLL, LLLL)。'L'的数量对应于变长中的数字位数。prefix.ASCII.Fixed
填充(可选)设置填充方向和类型。padding.Left('0')

某些ISO8583规范可能没有0号和1号字段,但我们会使用它们来表示MTI和Bitmap。因为从技术上讲,它们只是普通的字段。我们也使用字段规范来描述MTI和Bitmap。我们目前使用String字段来表示MTI,同时我们有一个单独的Bitmap字段来表示位图。

以下示例创建了一个包含三个单独字段(不包括MTI和Bitmap)的完整规范:

spec := &iso8583.MessageSpec{
    Fields: map[int]field.Field{
        0: field.NewString(&field.Spec{
            Length:      4,
            Description: "消息类型标识符",
            Enc:         encoding.ASCII,
            Pref:        prefix.ASCII.Fixed,
        }),
        1: field.NewBitmap(&field.Spec{
            Description: "位图",
            Enc:         encoding.Hex,
            Pref:        prefix.Hex.Fixed,
        }),

        // 消息字段:
        2: field.NewString(&field.Spec{
            Length:      19,
            Description: "主账号",
            Enc:         encoding.ASCII,
            Pref:        prefix.ASCII.LL,
        }),
        3: field.NewNumeric(&field.Spec{
            Length:      6,
            Description: "处理码",
            Enc:         encoding.ASCII,
            Pref:        prefix.ASCII.Fixed,
            Pad:         padding.Left('0'),
        }),
        4: field.NewString(&field.Spec{
            Length:      12,
            Description: "交易金额",
            Enc:         encoding.ASCII,
            Pref:        prefix.ASCII.Fixed,
            Pad:         padding.Left('0'),
        }),
    },
}

以下示例创建了一个包含三个单独字段(不包括MTI和Bitmap)的完整规范。它与上面的示例不同,通过展示位图字段的可扩展性。这对于定义主位图和次位图的规范很有用。

spec := &iso8583.MessageSpec{
    Fields: map[int]field.Field{
        0: field.NewString(&field.Spec{
            Length:      4,
            Description: "消息类型标识符",
            Enc:         encoding.ASCII,
            Pref:        prefix.ASCII.Fixed,
        }),
        1: field.NewBitmap(&field.Spec{
            Description: "位图",
            Enc:         encoding.Hex,
            Pref:        prefix.Hex.Fixed,
        }),

        // 消息字段:
        2: field.NewString(&field.Spec{
            Length:      19,
            Description: "主账号",
            Enc:         encoding.ASCII,
            Pref:        prefix.ASCII.LL,
        }),
        3: field.NewNumeric(&field.Spec{
            Length:      6,
            Description: "处理码",
            Enc:         encoding.ASCII,
            Pref:        prefix.ASCII.Fixed,
            Pad:         padding.Left('0'),
        }),
        4: field.NewString(&field.Spec{
            Length:      12,
            Description: "交易金额",
            Enc:         encoding.ASCII,
            Pref:        prefix.ASCII.Fixed,
            Pad:         padding.Left('0'),
        }),
        // 从1993年的规范中提取
        67: field.NewNumeric(&field.Spec{
            Length: 2,
            Description: "扩展付款数据",
            Enc: encoding.ASCII,
            Pref: prefix.ASCII.Fixed,
            Pad: padding.Left('0'),
        }),
    },
}

构建和打包消息

定义了规范后,您可以构建一条消息。根据提供的规范,将消息的二进制表示打包,可以直接将其发送到支付系统!

请注意,在下面的示例中,您不需要手动设置位图值,因为它会在打包过程中自动生成。

设置单个字段的值

如果您只需要设置几个字段,可以使用message.Field(id, string)message.BinaryField(id, []byte)轻松设置,如下所示:

// 使用定义的规范创建消息
message := NewMessage(spec)

// 在字段0设置消息类型标识符
message.MTI("0100")

// 根据需要将所有消息字段设置为字符串

err := message.Field(2, "4242424242424242")
// 处理错误

err = message.Field(3, "123456")
// 处理错误

err = message.Field(4, "100")
// 处理错误

// 生成消息的二进制表示rawMessage
rawMessage, err := message.Pack()

// 现在您可以通过网络发送rawMessage

使用单个字段的工作方式仅限于两种类型:string[]byte。底层字段会将输入转换为自己的类型。如果失败,则会返回错误。

使用数据结构设置值

当您需要访问很多字段,并且想要使用字段类型工作时,使用带有message.Marshal(data)的结构体会更加方便。

首先,您需要定义一个包含要设置的字段的结构体。字段应该对应于规范字段类型。以下是一个示例:

// 列出您想要设置的字段,使用`index`标签添加字段索引或标签(对于复合子字段)
// 使用与消息规范相同的类型
type NetworkManagementRequest struct {
    MTI                  *field.String `index:"0"`
    TransmissionDateTime *field.String `index:"7"`
    STAN                 *field.String `index:"11"`
    InformationCode      *field.String `index:"70"`
}

message := NewMessage(spec)

现在, 将有字段的数据传递到消息中 err := message.Marshal(&NetworkManagementRequest{ MTI: field.NewStringValue("0800"), TransmissionDateTime: field.NewStringValue(time.Now().UTC().Format("060102150405")), STAN: field.NewStringValue("000001"), InformationCode: field.NewStringValue("001"), })

// 对消息进行打包并发送给您的提供商 requestMessage, err := message.Pack()

解析消息并访问数据

当您有一个二进制(打包)消息并知道它遵循的规范时, 您可以解包它并访问数据。 再次, 您有两种数据访问选择: 访问单个字段或使用消息字段值填充结构体。

获取单个字段的值

您可以使用message.GetString(id), message.GetBytes(id)来访问单个字段的值, 如下所示:

message := NewMessage(spec)
message.Unpack(rawMessage)

mti, err := message.GetMTI() // MTI: 0100
// 处理错误

pan, err := message.GetString(2) // 卡号: 4242424242424242
// 处理错误

processingCode, err := message.GetString(3) // 处理码: 123456
// 处理错误

amount, err := message.GetString(4) // 交易金额: 100
// 处理错误

当您获取单个字段的值时, 您仍然限于string[]byte类型。

使用数据结构获取值

要获取多个字段的值及其类型, 只需将指向您想要的数据的结构体指针传递给message.Unmarshal(data)即可, 如下所示:

// 列出您想要设置的字段, 添加带有字段索引或标签的`index`标签(对于复合子字段)
// 使用与消息规范相同的类型
type NetworkManagementRequest struct {
	MTI                  *field.String `index:"0"`
	TransmissionDateTime *field.String `index:"7"`
	STAN                 *field.String `index:"11"`
	InformationCode      *field.String `index:"70"`
}

message := NewMessage(spec)
// 让我们解包二进制消息
err := message.Unpack(rawMessage)
// 处理错误

// 创建指向空结构体的指针
data := &NetworkManagementRequest{}

// 将字段值获取到数据结构中
err = message.Unmarshal(data)
// 处理错误

// 现在你可以访问字段值
data.MTI.Value() // "0100"
data.TransmissionDateTime.Value() // "220102103212"
data.STAN.Value() // "000001"
data.InformationCode.Value() // "001"

有关完整的代码示例, 请查看./message_test.go

检查消息字段

该软件包中有一个Describe函数, 它以人类可读的方式显示所有消息字段。 以下是如何将消息字段及其值打印到标准输出的示例:

// 将消息打印到os.Stdout
iso8583.Describe(message, os.Stdout)

它将产生以下输出:

MTI........................................: 0100
Bitmap.....................................: 000000000000000000000000000000000000000000000000
Bitmap bits................................: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
F000 Message Type Indicator................: 0100
F002 Primary Account Number................: 4242****4242
F003 Processing Code.......................: 123456
F004 Transaction Amount....................: 100
F020 PAN Extended Country Code.............: 4242****4242
F035 Track 2 Data..........................: 4000****0506=2512111123400001230
F036 Track 3 Data..........................: 011234****3445=724724000000000****00300XXXX020200099010=********************==1=100000000000000000**
F045 Track 1 Data..........................: B4815****1896^YATES/EUGENE L^^^356858      00998000000
F052 PIN Data..............................: 12****78
F055 ICC Data – EMV Having Multiple Tags...: ICC  ... Tags

默认情况下, 我们应用iso8583.DefaultFilters来掩盖具有敏感数据的字段的值。 您可以定义自己的过滤器函数并屏蔽特定字段, 如下所示:

filterAll = func(in string, data field.Field) string {
	runesInString := utf8.RuneCountInString(in)

	return strings.Repeat("*", runesInString)
}

// 仅过滤字段2的值
iso8583.Describe(message, os.Stdout, filterAll(2, filterAll))

// 输出:
// F002 Primary Account Number................: ************

如果您想查看未过滤的值, 可以使用我们定义的无操作过滤器iso8583.DoNotFilterFields:

// 显示未过滤的字段值
iso8583.Describe(message, os.Stdout, DoNotFilterFields()...)

JSON编码

您可以将消息序列化为JSON格式:

message := iso8583.NewMessage(spec)
message.MTI("0100")
message.Field(2, "4242424242424242")
message.Field(3, "123456")
message.Field(4, "100")

jsonMessage, err := json.Marshal(message)

它将产生以下JSON(位图未包括, 因为它只用于从二进制表示中解包消息):

{
   "0":"0100",
   "2":"4242424242424242",
   "3":123456,
   "4":"100"
}

您也可以将JSON解组到iso8583.Message中:

input := `{"0":"0100","2":"4242424242424242","4":"100"}`

message := NewMessage(spec)
if err := json.Unmarshal([]byte(input), message); err != nil {
    // 处理错误
}

// 访问单个字段或使用结构体

网络头

客户端/服务器(ISO主机和端点)之间的所有消息都有一个消息长度头。它可以是4字节ASCII或2字节BCD编码的长度。我们提供一个network.Header接口来简化网络头的读写。

支持以下网络头:

  • Binary2Bytes - 消息长度编码为2个字节, 例如, {0x00 0x73}表示115个字节的消息
  • ASCII4Bytes - 消息长度编码为4个字节ASCII, 例如, 0115表示115个字节的消息
  • BCD2Bytes - 消息长度编码为2个字节BCD, 例如, {0x01, 0x15}表示115个字节的消息
  • VMLH (Visa Message Length Header) - 消息长度编码为2个字节+2个保留字节

您可以从网络连接中读取网络头, 如下所示:

header := network.NewBCD2BytesHeader()
_, err := header.ReadFrom(conn)
if err != nil {
	// 处理错误
}

以下是英文到中文的翻译:

// 创建一个缓冲区来保存消息 buf := make([]byte, header.Length()) // 将传入的消息读取到缓冲区中。 read, err := io.ReadFull(conn, buf) if err != nil { // 处理错误 } if reqLen != header.Length() { // 处理错误 }

message := iso8583.NewMessage(specs.Spec87ASCII) message.Unpack(buf)

以下是如何将网络报头写入网络连接的示例:

header := network.NewBCD2BytesHeader() packed, err := message.Pack() if err != nil { // 处理错误 } header.SetLength(len(packed)) _, err = header.WriteTo(conn) if err != nil { // 处理错误 } n, err := conn.Write(packed) if err != nil { // 处理错误 }

CLI

CLI支持以下命令:

  • display 以人类可读的格式显示ISO8583消息

安装

iso8583 CLI可以从发布页面下载MacOS、Windows和Linux的可执行文件。

以下是安装MacOS版本的示例:

wget -O ./iso8583 https://github.com/moov-io/iso8583/releases/download/v0.4.6/iso8583_0.4.6_darwin_amd64 && chmod +x ./iso8583

现在您可以运行CLI:

➜ ./iso8583
从命令行无缝使用ISO 8583。

使用方法:
  iso8583 <命令> [标志]

可用命令:
  describe: 以人类可读的格式显示ISO 8583文件

显示

以人类可读的格式显示ISO8583消息

示例:

➜ ./bin/iso8583 describe msg.bin
MTI........................................: 0100
Bitmap.....................................: 000000000000000000000000000000000000000000000000
Bitmap bits................................: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
F000 Message Type Indicator................: 0100
F002 Primary Account Number................: 4242****4242
F003 Processing Code.......................: 123456
F004 Transaction Amount....................: 100
F020 PAN Extended Country Code.............: 4242****4242
F035 Track 2 Data..........................: 4000****0506=2512111123400001230
F036 Track 3 Data..........................: 011234****3445=724724000000000****00300XXXX020200099010=********************==1=100000000000000000**
F045 Track 1 Data..........................: B4815****1896^YATES/EUGENE L^^^356858      00998000000
F052 PIN Data..............................: 12****78
F055 ICC Data – EMV Having Multiple Tags...: ICC  ... Tags

您可以通过spec标志指定使用哪个内置规范来描述消息:

➜ ./bin/iso8583 describe -spec spec87ascii msg.bin

您还可以以JSON格式定义您自己的规范,并使用spec-file标志使用该规范文件来描述消息:

➜ ./bin/iso8583 describe -spec-file ./examples/specs/spec87ascii.json msg.bin

请查看JSON规范文件spec87ascii.json的示例。

  • Moov Metro2提供了一种简单的方法来读取、创建和验证Metro 2格式,这是美国信用局用于消费者信用历史报告的格式。

许可证

Apache 许可证 2.0 - 详见 许可证

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号