Skip to content

ARTARNA/ex_iso20022

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ex_iso20022

Hex.pm Docs CI License: MIT

ISO 20022 message parsing for Elixir. Currently covers camt.053 (Bank to Customer Statement), the highest-demand message type. More message types are planned.

Installation

# mix.exs
def deps do
  [
    {:ex_iso20022, "~> 0.1"}
  ]
end

Quick start

xml = File.read!("statement.xml")

case ISO20022.Camt053.parse(xml) do
  {:ok, doc} ->
    Enum.each(doc.statements, fn stmt ->
      IO.puts("Account IBAN : #{stmt.account.iban}")
      IO.puts("Currency     : #{stmt.account.currency}")

      closing = Enum.find(stmt.balances, &(&1.type == :closing_booked))
      IO.puts("Closing bal  : #{closing.amount} #{closing.currency} (#{closing.credit_debit})")

      Enum.each(stmt.entries, fn entry ->
        IO.puts("  #{entry.ref}  #{entry.credit_debit}  #{entry.amount} #{entry.currency}")
      end)
    end)

  {:error, reason} ->
    IO.inspect(reason, label: "parse error")
end

Using the bang variant when you are confident the input is valid:

doc = ISO20022.Camt053.parse!(xml)

Supported message types

Module Message Versions
ISO20022.Camt053 Bank to Customer Statement camt.053.001.02 – 014

More message types (camt.052, camt.054, pain.001, pacs.008, …) will be added in subsequent releases. The top-level ISO20022.parse/1 dispatcher is already in place and will route to the right module automatically once each type is implemented.

Struct reference

ISO20022.Camt053.Document

The top-level struct returned by parse/1.

Field Type Description
group_header GroupHeader Message-level metadata
statements [Statement] One entry per account per period

ISO20022.Camt053.GroupHeader

Field Type Description
message_id String Unique message identifier (max 35 chars)
created_at DateTime UTC creation timestamp
pagination map | nil %{page_number: String, last_page: boolean}

ISO20022.Camt053.Statement

Field Type Description
id String Statement identifier
electronic_seq_number integer | nil Sequence number for gap detection
legal_seq_number integer | nil Legal sequence number (present alongside electronic seq in some setups e.g. Goldman Sachs)
created_at DateTime | nil Statement generation time
from_to_date map | nil %{from: DateTime, to: DateTime}
account Account Account identification
balances [Balance] At least opening and closing booked balances
transactions_summary map | nil %{total_entries, total_credit_entries, total_debit_entries} (from <TxsSummry> when present)
entries [Entry] Booked transactions

ISO20022.Camt053.Account

Field Type Notes
iban String | nil Present when account is IBAN-identified
other_id String | nil Present when account uses a non-IBAN scheme (common in US)
other_scheme String | nil Scheme code, e.g. "BBAN"
type String | nil Proprietary account type, e.g. "VIRTUAL", "VIRTUAL_SUSPENSE", "IBDA_DDA"
currency String | nil ISO 4217 alpha code, e.g. "EUR"
servicer_bic String | nil BIC of the account-servicing institution
servicer_name String | nil Name of the account-servicing institution
name String | nil Account name

ISO20022.Camt053.Balance

amount is always a positive Decimal. The sign is expressed through credit_debit.

Field Type Description
type atom See balance types below
amount Decimal Positive amount
currency String ISO 4217 alpha code
credit_debit :credit | :debit :debit means overdraft
date Date Balance reference date

Balance type atoms:

Atom ISO code Meaning
:opening_booked OPBD Opening booked (mandatory)
:opening_available OPAV Opening available (common in US/virtual account setups)
:closing_booked CLBD Closing booked (mandatory)
:closing_available CLAV Closing available
:interim_booked ITBD Interim booked
:interim_available ITAV Interim available
:forward_available FWAV Forward available
{:other, code} any Unrecognised code

ISO20022.Camt053.Entry

Field Type Description
ref String Bank-assigned entry reference
amount Decimal Positive amount
currency String ISO 4217 alpha code
credit_debit :credit | :debit Direction
reversal boolean true if this cancels a prior entry
status :booked Always :booked in camt.053
booking_date Date | nil Date posted to account
value_date Date | nil Value date (may differ from booking date)
account_servicer_ref String | nil Bank's own reference
bank_transaction_code BankTxCode | nil ISO 20022 transaction classification
additional_info String | nil Free-text entry description
details [EntryDetails] Transaction-level detail blocks (batch entries)

ISO20022.Camt053.BankTxCode

Field Type Example
domain String | nil "PMNT"
family String | nil "RCDT", "ICDT", "RDDT"
sub_family String | nil "XBCT", "ESCT", "SALA"
proprietary_code String | nil Bank-specific code
proprietary_issuer String | nil Issuer of the proprietary code

ISO20022.Camt053.EntryDetails

Present when an entry groups multiple underlying transactions (batch payments).

Field Type Description
batch map | nil %{message_id, payment_info_id, number_of_transactions, total_amount}
transaction_details [TransactionDetails] Individual transaction records

ISO20022.Camt053.TransactionDetails

Field Type Description
refs map | nil %{message_id, end_to_end_id, uetr, …}
amount Decimal | nil Individual transaction amount
currency String | nil ISO 4217
credit_debit :credit | :debit | nil Direction
related_parties map | nil %{debtor, creditor, ultimate_debtor, ultimate_creditor}
related_agents map | nil %{debtor_agent, creditor_agent}
remittance_info tuple / nil {:unstructured, text} or {:structured, %{ref, ref_type, …}}
purpose String | nil ISO 20022 purpose code

Error handling

{:ok, %ISO20022.Camt053.Document{}}

# Malformed XML
{:error, {:parse_error, reason}}

# Namespace not recognised as a camt.053 variant
{:error, {:unsupported_version, "urn:iso:std:iso:20022:tech:xsd:pain.001.001.09"}}

# Mandatory field absent from an otherwise valid document
{:error, {:missing_required_field, [:group_header, :message_id]}}
{:error, {:missing_required_field, [:statements, 0, :id]}}

# Field present but value could not be parsed
{:error, {:invalid_amount, "N/A", [:statements, 0, :entries, 1, :amount]}}
{:error, {:invalid_date, "32-01-2024"}}

The path in missing_required_field and invalid_* errors follows the struct hierarchy so it is straightforward to pinpoint the problematic node.

Multi-version support

Real-world bank files use a wide range of schema versions:

urn:iso:std:iso:20022:tech:xsd:camt.053.001.02   # many European banks
urn:iso:std:iso:20022:tech:xsd:camt.053.001.04   # UK, SEPA migration era
urn:iso:std:iso:20022:tech:xsd:camt.053.001.08   # current SWIFT / TARGET2
urn:iso:std:iso:20022:tech:xsd:camt.053.001.11   # newer implementations
urn:iso:std:iso:20022:tech:xsd:camt.053.001.14   # latest ISO (2026)

ex_iso20022 detects the version from the root element's xmlns attribute and normalises to the same struct regardless of input version. All versions 02 – 14 are accepted. Documents lacking a namespace (some older senders) are also accepted.

License

MIT

About

ISO 20022 message parsing library for Elixir.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages