Skip to content

Built-by-Sign/swift

swift

A Go library for reading, writing, and manipulating SWIFT MT (FIN) messages. Ported from and API-compatible with prowide-core.

Go Reference CI Go Report Card License

Features

  • Full MT message model — Block 1-5, user blocks, tags, and typed fields
  • Parser — Lenient and strict FIN message parsing
  • Writer — FIN serialization with correct TEXT/TAG block 4 formatting
  • 900+ field types — Auto-generated typed fields with component access (Field20, Field32A, Field103, ...)
  • 270+ MT definitions — MT101 through MT999 with field mappings
  • Conversion service — Bidirectional FIN/XML/SwiftMessage conversion
  • Batch formats — RJE, PPC, and internal XML readers/writers
  • Message utilities — Sender/receiver extraction, value date, amount, checksum, variant detection, ACK/NAK, GPI, fragments

Installation

go get github.com/Built-by-Sign/swift

Requires Go 1.21 or later.

Quick Start

Parse a SWIFT FIN message

p := parser.NewSwiftParser()
msg, err := p.Parse(fin)
if err != nil {
    log.Fatal(err)
}

fmt.Println(msg.GetType())        // "103"
fmt.Println(msg.GetSender())      // "BANKBEBBAXXX"
fmt.Println(msg.GetReceiver())    // "BANKDEFFXXXX"
fmt.Println(msg.Field("20").Value) // "REFERENCE"

Build and write a message

msg := model.NewSwiftMessage()
msg.Block1 = model.NewSwiftBlock1()
msg.Block1.LogicalTerminal = "BANKBEBBAXXX"

b2 := model.NewSwiftBlock2Input()
b2.MessageType = "103"
b2.ReceiverAddress = "BANKDEFFXXXX"
msg.Block2 = b2

msg.Block4 = model.NewSwiftBlock4()
msg.Block4.Append(model.NewTag("20", "REF-001"))
msg.Block4.Append(model.NewTag("23B", "CRED"))
msg.Block4.Append(model.NewTag("32A", "230101EUR1000,00"))

fin := writer.WriteMessageToFIN(msg)

Use the conversion service

svc := convert.NewConversionService()

msg, _ := svc.GetMessage(fin)       // FIN -> SwiftMessage
fin  := svc.GetFIN(msg)             // SwiftMessage -> FIN
xml, _ := svc.GetXML(msg)           // SwiftMessage -> XML
msg, _ = svc.GetMessageFromXML(xml) // XML -> SwiftMessage

Read batch files

import swiftio "github.com/Built-by-Sign/swift/io"

messages, errs := swiftio.ReadRJE(data) // RJE ($ separated)
messages, errs := swiftio.ReadPPC(data) // PPC (512-byte sectors)
msg, err := swiftio.ParseXML(xmlData)   // Internal XML

Message Inspection

Variant and type detection

msg.IsCOV()               // block3 tag 119 == "COV"
msg.IsSTP()               // block3 tag 119 == "STP"
msg.IsGpi()               // MT103, 199, 299, 192, 196, or 202/205+COV
msg.GetVariant()           // *MTVariant (COV, STP, REMIT, or nil)
msg.GetMtId().Id()         // "fin.103.STP"
msg.GetCategory()          // *MtCategory (0-9)
msg.IsTypes(103, 202, 205) // true if message type matches any

ACK/NAK and service messages

msg.IsServiceMessage21()  // service ID == "21"
msg.IsAck()               // service 21 + tag 451 == "0"
msg.IsNack()              // service 21 + tag 451 == "1"
msg.IsServiceMessage()    // service ID != "01"
msg.IsSystemMessage()     // category 0 (type starts with "0")

Sender, receiver, and references

msg.GetSender()              // direction-aware sender BIC
msg.GetReceiver()            // direction-aware receiver BIC
msg.GetCorrespondentBIC()    // outgoing→receiver, incoming→sender
model.Reference(msg)         // field 20 / 20C:SEME / MUR fallback
msg.GetUUID()                // "I" + BIC11 + type + reference

Financial data extraction

model.ValueDateString(msg)  // value date for 60+ MT types
model.TradeDateString(msg)  // trade date (field 30T / 98:TRAD)
model.MoneyFromMessage(msg) // *Money{Currency, Amount} for 60+ MT types
model.CurrencyStrings(msg)  // all currency codes in message

Fragment and trailer handling

msg.IsFragment()           // tags 202 + 203 both present
msg.FragmentNumber()        // tag 202 value
msg.FragmentCount()         // tag 203 value
msg.GetPDE()               // Possible Duplicate Emission (block5)
msg.GetPDM()               // Possible Duplicate Message (block5)

UETR and GPI

msg.GetUETR()                         // block3 tag 121
msg.SetUETR("eb6305c2-...")           // set UETR
msg.GenerateUETR()                    // generate UUID v4 and set
msg.GetServiceTypeIdentifier()        // block3 tag 111
msg.SetServiceTypeIdentifier("001")   // set service type

Block3 builder

builder := model.NewSwiftBlock3Builder(msg.Block3)
builder.SetField108(model.NewTag("108", "MUR-REF"))
builder.SetField119(model.NewTag("119", "STP"))
builder.SetField121(model.NewTag("121", uetr))
// Fields are always emitted in canonical order: 103,113,108,119,423,106,424,111,121,115,165,433,434

Typed field access

f := field.NewField32A("230101EUR1000,00")
f.GetComponent(1) // "230101" (date)
f.GetComponent(2) // "EUR"    (currency)
f.GetComponent(3) // "1000,00" (amount)

f.GetDateAsTime()  // time.Time
f.GetAmountAsRat() // *big.Rat

tag := f.Tag() // *model.Tag{Name:"32A", Value:"230101EUR1000,00"}

Sub-block queries (securities messages)

block4 := msg.Block4
sub := block4.GetSubBlock("TRADDET")                       // 16R/16S delimited
tag := block4.GetFieldByNumberAndQualifier(98, "SETT")     // :SETT//20230515
subs := block4.GetSubBlocks("FIN")                          // all matching sub-blocks

Message comparison

result := model.CompareMessages(msg1, msg2, true) // ignoreCR=true
if !result.Equal {
    for _, diff := range result.Differences {
        fmt.Println(diff)
    }
}

JSON serialization

jsonBytes, _ := msg.ToJSON()
msg, _ = model.SwiftMessageFromJSON(jsonBytes)

Checksum

model.CalculateChecksum(fin)  // MD5 of FIN string
model.MD5Hash(text)           // generic MD5

Error Handling

Parser errors

In lenient mode (default), the parser returns a best-effort result and collects non-fatal warnings:

p := parser.NewSwiftParser()
msg, err := p.Parse(fin)
if err != nil {
    // Severe error — message could not be parsed at all
}
// Check non-fatal warnings
for _, warn := range p.GetErrors() {
    log.Printf("parse warning: %v", warn)
}

In strict mode, the first error stops parsing:

p := parser.NewSwiftParserWithConfig(parser.ParserConfig{Lenient: false})
msg, err := p.Parse(fin)
// err is non-nil for any format deviation

Conversion errors

XML conversion methods return errors for malformed input:

msg, err := svc.GetMessageFromXML(xml)
if err != nil {
    // Invalid XML structure
}

FIN conversion is lenient by default and rarely errors.

Package Structure

Package Description
model Core types: SwiftMessage, Block1-5, Tag, BIC, MIR, enums
parser FIN message parser (lenient/strict modes)
writer FIN message serializer
field 900+ auto-generated typed field implementations
mt 270+ message type definitions (MT101-MT999)
convert Conversion service (FIN/XML/SwiftMessage)
io Batch format readers/writers (RJE, PPC, XML)
utils Date/time formatting, character set validation

Code Generation

Field types (field/*_gen.go) and MT definitions (mt/*_gen.go) are auto-generated from cmd/swiftgen/field_meta.json. See cmd/swiftgen/README.md for details.

go run ./cmd/swiftgen -meta cmd/swiftgen/field_meta.json -field-out field/ -mt-out mt/

Architecture

See ARCHITECTURE.md for design details including block model, parser/writer design, and message flow.

Compatibility

This library is a Go port of prowide-core (Java). The API follows Go conventions while remaining familiar to prowide-core users:

Java Go
msg.getSender() msg.GetSender()
msg.isType(103, 202) msg.IsTypes(103, 202)
new MIR(value) model.NewMIR(value)
SwiftMessageUtils.reference(msg) model.Reference(msg)
msg.isCOV() msg.IsCOV()
Field32A.getComponent(1) field.NewField32A(v).GetComponent(1)

Contributing

See CONTRIBUTING.md for guidelines.

Security

See SECURITY.md for reporting vulnerabilities.

License

Apache License 2.0 — see LICENSE.

About

No description, website, or topics provided.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors