A Go library for reading, writing, and manipulating SWIFT MT (FIN) messages. Ported from and API-compatible with prowide-core.
- 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
go get github.com/Built-by-Sign/swiftRequires Go 1.21 or later.
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"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)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 -> SwiftMessageimport 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 XMLmsg.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 anymsg.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")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 + referencemodel.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 messagemsg.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)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 typebuilder := 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,434f := 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"}block4 := msg.Block4
sub := block4.GetSubBlock("TRADDET") // 16R/16S delimited
tag := block4.GetFieldByNumberAndQualifier(98, "SETT") // :SETT//20230515
subs := block4.GetSubBlocks("FIN") // all matching sub-blocksresult := model.CompareMessages(msg1, msg2, true) // ignoreCR=true
if !result.Equal {
for _, diff := range result.Differences {
fmt.Println(diff)
}
}jsonBytes, _ := msg.ToJSON()
msg, _ = model.SwiftMessageFromJSON(jsonBytes)model.CalculateChecksum(fin) // MD5 of FIN string
model.MD5Hash(text) // generic MD5In 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 deviationXML 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 | 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 |
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/See ARCHITECTURE.md for design details including block model, parser/writer design, and message flow.
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) |
See CONTRIBUTING.md for guidelines.
See SECURITY.md for reporting vulnerabilities.
Apache License 2.0 — see LICENSE.