138 comments

[ 3.1 ms ] story [ 204 ms ] thread
Hey HN,

My name is Svapnil Ankolkar and I've recently built iso20022.js, a library for creating ISO20022 payments in Typescript.

The goal of this project is to be the easiest way to create, and eventually ingest, files in the ISO20022 standard, the defacto XML standard for bank payments.

I'd love to know what you think and importantly know about any improvements you'd like us to make!

Since you do say it in HN tagline, what are the "3 lines of code"? I can only see 43 LoC in the landing page. A tool to help massively with payments is always welcome of course, but if this is really 43 lines instead of 3 it'd feel like shady marketing (no judgement until there's a reply/clarification).
I mean, it's 3 statements with like 40 lines of JSON options. Debatable what counts as a LOC, but it's still pretty impressive.
I imagine they are counting object literals as one line of code, I'm inclined to agree. Maybe they could have said in "3 statements of code".

> import { ISO20022 } from 'iso20022.js'

> const iso20022 = new ISO20022({ /.../ });

> const creditPaymentInitiation = iso20022.createSWIFTCreditPaymentInitiation({ /.../});

> console.log(creditPaymentInitiation.toString());

Now are we counting the import and not counting the log statement, or are we counting the log statement and not the import?

I'm not too worried about it.

It's the import, the "const iso20022 = new ISO20022" line, and the "const creditPaymentInitiation = iso20022.createSWIFTCreditPaymentInitiation". The big data chunks are being counted as one line.

I will admit I'm usually very skeptical about "Do X in Y line" claims because usually you can do anything in one line by shoving the entire codebase behind a "DoIt()" call. But I'll actually give them this one. I'm not even worried about the data taking up so many lines as clearly the data is necessary. If they were full of mandatory function calls or something I'd ding 'em too, but they're not, it's just the data you need.

If it was free text content I'd agree, but this being configuration code where you need to be very precise on the key names and values, possibly reading some docs and consulting multiple sources per line, I would not count this as 3 lines... 3 simple steps sure, but calling that 3 lines is not being honest IMHO.
You import the library, initialise an object and call its method. I think it's fair enough to call that example three lines of code.

The bigger question of course is what do you end up with. Clearly that method call isn't going to actually send an instruction to your bank to debit your account and credit somebody else's. I guess `creditPaymentInitiation` is an object that has can be converted to a properly formatted message that can then be sent to your bank using an external tool?

It'd be more accurate to say 3 steps, but I see where they're coming from:

    // 1. Import
    import { ISO20022 } from 'iso20022.js'
    // 2. Instantiate the sender
    const iso20022 = new ISO20022(initiatingPartyInfo);
    // 3. Send
    iso20022.createSWIFTCreditPaymentInitiation(paymentInstructions);
Thanks for checking out what I'm working on Francisco - you're right - expanding all the data models here increases the LoC. The objective of iso20022.js is not to be overly simplified, but rather offer the best way to interface with the ISO20022 standard. That's a big reason for the large payloads - no obfuscation.
(comment deleted)
Nothing shady with that at all and extremely obvious what 3 LoC refers to.
Support SEPA and its national variants.

Support bank statements.

Would love to! It's next up on the roadmap for us - would love for you to check it out
Three lines with payloads minified?
(comment deleted)
haha, yes.

fiat bank payments require a lot of information to be send correctly, for better or for worse. this library conveniently exposes the elements needed to send a payment, as per the most widely used standard available (iso20022)

as an average joe, I'm curious about how all this works / what else you need to make it work (what's a banking partner, etc?)
thanks for sharing - this is really helpful context.

ISO20022 is the XML standard to send payment instructions, and this library conveniently exposes it's models in Typescript format. It seems like understanding what ISO20022 is is super important, so I'll spend some effort educating people on what this is

thanks for the feedback. companies that need to programmatically send payments via bank need a corporate bank account that allows them to send programmatic payment instructions. i'll make this more clear in the docs!
It's basically a collection of API standards for common messages to and between financial institutions. If you need to communicate a lot with your bank or banks generally, then you'll probably automate it through this standard, at least in Europe, I'm not sure about adoption elsewhere. Things like reading accounts, registering transactions, stuff like that. You can, for example, build a background job that does monthly consolidation of accounts against a pile of payment providers your customers use.

If you enjoy SOAP you'd feel right at home and likely have better tooling foundations already, if you don't you probably want a library like this one, at least when they've implemented SEPA support.

It’s a shame it’s probably going to take another 10 years before any of the banks actually migrate to ISO20022 (if it ever happens) SwiftMT conversion software is currently the big sell so they don’t have to rewrite / build everything they already have.
In 2018 I implemented a software client that sent SEPA pain messages to the Luxembourg branch of a swedish bank. I don't know where you live, but at least in Europe this standard seems somewhat broadly adopted.
SEPA has always been ISO20022, SWIFT (US) is now following
As of early next year, the Fed (US) will not accept anything other than ISO20022 messages on their networks. So any banks that are using Fed networks are required to use ISO20022.
I wouldn’t hold your breath
I work for a large financial institution and we've been migrating our Fed network processing to ISO20022. All financial institutions we work with, and vendors who process payments for fraud and such, are also doing so. I'm highly skeptical of the date, but it is going to happen.
How about receive payment notification via webhook ? Is there standard ?
Thanks for asking revskill, payment notifications are actually a different payment file, called a Payment Status Report file (pain.002). This is the industry standard

Instead of webhooks, a developer would poll an SFTP directory for an unique PSR file to ingest. Eventually, iso20022.js should support ingesting PSR files and provide an interface for using them.

in what scenario is iso20022 payments used? would this be for forexample automating payroll?
> in what scenario is iso20022 payments used

I wanted to know this as well - specifically sms based payments if its so easy to wrap payment calls... found this:

---

ISO 20022 is a global standard for electronic data interchange between financial institutions, enabling efficient and secure exchange of financial messages. It is widely used in various payment scenarios, including:

* Cross-border payments:

ISO 20022 facilitates international transactions by providing a common language for financial institutions to communicate.

High-value payments: It is used for large-value transactions, such as wholesale payments, securities settlements, and treasury transactions.

Retail payments: ISO 20022 is also used for retail payments, including credit transfers, direct debits, and e-payments.

Real-time payments: It supports real-time payment systems, enabling fast and efficient transactions.

* SMS-based payment initiation:

An SMS can be used to initiate a payment, with the payment details (e.g., amount, recipient) encoded in an ISO 20022 message. The message would then be sent to a payment processor or financial institution for processing.

Mobile payment apps:

Mobile payment apps can use ISO 20022 for payment processing in the background, while the user interacts with the app via SMS or a graphical user interface.

I would've thought some sort of cryptographic signature would be used here, though maybe that's something done in a wrapper around this XML documents?

Neat none-the-less! I always like this sort of actually-it's-simpler-than-you-think peek behind the curtain.

(comment deleted)
What is an API/service we can use this encoder/serializer with? This seems like a part of the puzzle.
Super stupid question, but this must have some kind of authentication, right? Where is that part?
(comment deleted)
That code sample is basically how ISO20022 works, you create a file with instructions to move money from one account to another.

You would need to upload that XML to a bank to actually process the transaction.

There are other formats like NACHA (US only), where you have to generate a text file with a pretty specific format, and upload that, often via sFTP to the Bank's server to process.

as some other comment has pointed out, this is not a payment library at all. It's simply an XML document builder for a specific payment description format, and doesn't actually facilitate a transaction.
This is a nice looking website, and I can imagine this being a really useful hook into consulting work.

Some thoughts:

- replace strings with enums where possible. E.g. "USD" should be from an enum

- this sort of data-interop library could maybe be written in a configuration language that it uses to generate libraries for different languages

- a glossary would be really helpful

- a list of banks who've adopted this ISO standard might be interesting

This is one place where interop between JS and TS gets prickly. If you envision this as a Typescript library, strings are much more ergonomic, so long as you’ve got a union type for all the variants. But if you want it to be decent for non-TS, you pretty much have to concede that compromise and use an enum/object.

Note how this is a typescript project and leverages a type for currency that comes from a common library, and therefore doesn’t have to do any of the work! https://github.com/Svapnil/iso20022.js/blob/main/src/lib/typ...

That's a fair point. The thing I'd be more thinking about is: can I get a list of currencies? I get I have type safety, but if I want to present the user with a list of valid currencies, can I do that from the type system? Enums can make that sort of thing very straightforward.
Yeah. The way I handle this is to make an array (or set) of those strings and then define a union type of that array.

Which is basically just an enum of my own invention. But after years of trying different things, I find this is the most practical. Objects are uncomfortable. TS enums really suck, IMO.

So maybe we’re actually in agreement. And to an extent can just have both: autocompletable, legible strings without another import or object. And also an iterable collection of the variants.

(comment deleted)
Thanks for your feedback Robert!

I think payments is an industry in general that is starving for the right information, so a free and open source package like this should have an abundance of information. I'll make sure I add it in.

The configuration language sounds really interesting. What I think is special about this library is that it comes with all the Typescript furnishings one would need to have a powerful dev experience. The goal is to make it incredibly easy for a developer to use iso20022, not just another middle layer that obfuscates it

It's a pleasure! To be clear, I meant that you could have a repository that contains all of the information in some sort of configuration language (or maybe in Typescript; I suppose that doesn't really matter) but you use that to generate multiple libraries; one for each language you might want to support. So a single codebase (or configuration base) could generate iso20022.js, iso20022.ts, iso20022.py, etc etc.

It's not simple; it might just be an interesting distant goal to keep in mind.

> - replace strings with enums where possible. E.g. "USD" should be from an enum

Believe it or not, many bank use custom/non-standard currency code internally.

I agree enum can be used for SWIFT, but it need to be string to be used internally in many bank.

Slightly stretching the concept of "three lines of code". But it looks clean and self-documenting to this non-js developer. Nice.
(comment deleted)
The problem with this standard is all the free text and bank specific fields that banks will use instead of the standard. One bank I integrated with had the equivalent of "Our fee is 5.65" in a text field which you had to parse, instead of the field for fees. Of course, the language of that string could also change. Fun times
That does sound really really fun.. What's great about XML is that free text / bank specific fields can be handled elegantly with XML's extensible structure. That is why I think ISO20022 is here to stay.

That said, this library is made to be extensible. One day I think it will even be able to encapsulate any type of bank. For example, imagine bofaISO20022.createACHPaymentInitation or something

What XML has to do with bank-specific messages that have to be parsed and processed? It’s just a markup format.
Banks add their own features to the spec - imagine they want to add a new "Bank only" attribute that makes their XML schema differentiated and better in some way.

ISO20022 / XML allows this to be possible without breaking anything. In the past payment formats used to be fixed width text files - impossible to change or improve functionality for

Custom schema means nothing against improper implementation
This has me a little dumbfounded as either really profound or slightly misguided. How do you mean?

As I am reading this you think a custom schema wont effect an implementation, but how do you expect to implement an external service (API for example) without the required defined schema. That's kind of the definition of a schema in this scenario.

Extending the schema might be another thing. But implementation can't work without adhering to the defined schema of the provider? Right?

(comment deleted)
> impossible to change or improve functionality for

You have to think inside the box!

What if, say, instead of lastname "SMITH" we used "SMITH,FEE: 5.65"

I wish this example was exaggerated.
I saw an airport running with stuff like this, too. A mixture of brilliant and insane engineering in that place…
Excellent example clearly from a fellow soldier from the trenches!

As somebody who has built several instances of both payments- and travel booking systems, I have seen things in systems that "adhere to published schemas" (often because the schemas were beastly, design-by-committee hellscapes of extensibility) that defy belief.

While there is a strong argument to be made that strict type systems in programming langues like Haskell and Rust make it very difficult to play outside of the rules, this is unfortunately not the case in practice when it comes to APIs - both at present where you have a JSON Schema or Open API spec if you are lucky, and in the past (XML Schema, SOAP).

I wish that the ability to express a tight, fit-for-purpose schema or API actually resulted in this commonly being done. But look at the average API of any number of card payment gateways, hotels, or airlines, and you enter into a world where each integration with a new provider is a significant engineering undertaking to map the semantics - and the errors, oh the weird and wonderful errors... to the semantics of your system.

I am glad to work in the space-adjacent industry now, where things are ever so slightly better.

(Note the lack of sarcastic emphasis - it really is only _slightly_ better!)

Yes, my son is really named BOB,FEE: -999999999.99
It's the extensible nature of XML that gives it an advantage. You can add custom elements and attributes whilst conforming to the base schema.

Granted, XML isn't the only format where this is possible. You can sort of achieve it with JSON, though XML's namespace system helps deal with name collisions. Adding bank-specific messages wouldn't be possible (or would be difficult) with fixed-column formats, for example, unless they had been specifically designed to be extended.

Iso200022 are tagged as well.
You can have extensible structure and fields with JSON Schema, gRPC, Cap’n Proto, etc. There’s nothing XML-specific about that.

The only thing XML gives you over any of those formats is unstructured mixing of text and data, which is more a foot-gun than anything. Oh, and of course, being significantly more verbose.

A niche thing maybe, but XML has comments, which I appreciate.
Why do you need comments in machine generated files meant for other machines?
It can be very helpful when trying to figure out why one machine won't understand another, for instance.

You can put meta-data in for debugging without compromising anything, schema wise.

Or in the case of config files, there can be detailed instructions on what fields are what they should contain.

The thing about XML is that it strikes a sweet-spot between machine readable files and human readable files. (I can't believe I'm coming out as an XML apologist!)

If it were only "by machines for machines", we wouldn't consider JSON, YaML or XML as much, we'd all go for Protocol Buffers or Parquet or something.

Would be nice if people just adopted ethereum
Other than bank specific custom extensions, another problem with this standard is its scope and, consequently, its size – it is vast. ISO 20022 breaks down into over 700 what they call «messages» that cover pretty much everything, from the interbank settlements to bank-to-customer account statements.

Another challenge is that different banks may use slightly different versions of the standard messages that are enunciated via the implementation specific concrete XML namespace in the xmlns attribute of the message envelope.

Overall, ISO 20022 is an improvement over MT940/MT942 and friends, although it is not easy to use.

I'd love to learn more about what you mean about the different xmlns's banks use - haven't seen that one yet.

The must know a lot about ISO20022 payments! I'd love to get in touch with you, if you can please shoot me an email @ svapnil@woodside.sh :)

Same thing happens with ISO8583. Plenty of firms have an ISO8583-compatible spec, except anything remotely interesting happens in vendor-specific fields with a galaxy of different architectures.
I work in banking in the EU, we process SEPA messages only (not SWIFT) and the standards for interbank communication are very strict and top-down. I.e. (fictional), if you want to charge a fee when you return money after you received an investigation, it MUST be put in "field xyz" and if you do so, "field abc" MUST contain the code "ABC1" or "DEF2" etc.

The times when the standards are expanded or updated are fun (https://www.europeanpaymentscouncil.eu/what-we-do/epc-paymen...), translating hundreds of pages of PDF into working code and then have hundreds of banks implement those changes in the same nightly hour during a weekend...but once it is working, there is no ambiguity or (horror) manual intervention in payment messages. Either you as a bank send valid messages and they are processed, or you don't and they get rejected.

In my experience at the frontlines (banks allowing users to submit SEPA XMLs), the situation is a lot messier. I ended up building an exporter from Xero (globally renowned cloud accounting software) to SEPA for both payments and direct debits, and we have several bespoke export templates for a handful of banks that want it this-not-that way.

I wrote a bit about the tool and my experience with SEPA standards at https://evrim.zone/blog/projects/batch2sepa, check it out!

Are you planning on generating all (or a subset) of 20022 messages? That’s what we did for our Go package using 20022 for RTP. We added helpers for generating IDs and the signature sign/validate.

https://github.com/moov-io/rtp20022

Hi Adam, thanks for asking. Eventually, this library should be extensible to generate all types of ISO20022 messages, but I'm starting out with PAIN (Payment Initation!) messages. Would be super keen to add RTP - will reach out to you!
The difficult thing about payments is not necessarily formatting payment instructions to ISO20022, but rather setting up the underlying infrastructure like:

- Shelving a Windows box in some authorized datacenter.

- Going through a years long process of getting certified to send payment instructions to the scheme (e.g. SWIFT network).

- Receiving a couple of USB sticks that contain certificates and signing keys.

- Connecting to the schemes VPN.

- Having all the legals in place.

- And probably many more things I was never exposed to.

(comment deleted)
Can you go into a bit more detail here. Seems like there are a lot of hurdles in place to preventing any random from just moving money around?
There are a lot of hurdles. I'm not exactly sure why an end-user would even want to use ISO20022. Some end-user system may use ISO20022 behind the scenes, but the UI's are very simplified. ISO20022 as far as I have used it (20+ years in finance) is for payment networks. And a random person can't just send messages on any payment networks that I'm aware of. These payment networks generally exist as message queues that are interconnected and they use ISO20022 as the data format.

Not to mention that the flows of ISO20022 messages are full duplex. If you send a message you need to be ready to handle a response (acceptance or rejection of a credit/debit for example).

Thanks for your comment phillippta, I agree with you there are a few big challenges in setting up payments companies need to deal with:

1. Security Adding vendors is a very scary thing for companies, rightfully so. Open source is great for this, because an NPM package is not a vendor.

2. Signing / Sending a Transaction iso20022.js doesn't deal with message sending yet. Best case there is a straightforward SFTP setup, which I've given instructions for here: https://docs.iso20022js.com/quickstart#step-4-send-the-payme...

3. Getting certified to send payment instructions. This should be the work of the module!

>> I'd love to know what you think and importantly know about any improvements you'd like us to make!

I think it's scary to do any finance related stuff using NPM dependencies. How large is your dependency tree?

That’s what fintech is, one large dependency tree
With internal infosec, secinfra teams, compliance, and serious business contracts.
With 500 MB of unversioned, arbitrary JavaScript?
Why NPM in particular? Is there any package manager where you don't have to audit your dependencies?
Yes, any repository that uses maintainers and doesn't let anyone upload random stuff to it, like Debian stable. Package maintainers are the missing piece.
NPM is particularly nasty because they've solved the issue of transitive dependencies having mismatched versions. This removes one of the biggest immediate pain points caused by having very large dependency graphs so you only feel the pain later on. Because of that you get this emergent behavior of unusually large dependency graphs in a lot of NPM packages.
This looks pretty interesting, but my only experience with payments are black-box payment processors that expose an API, and services like Stripe that handle all of the institutional interconnection.

Could you help me understand who the target devs are for this library? I doubt it's someone like me, who would try to use it as a replacement for stripe before realizing all of the stuff I have to do outside of that and giving up on it. But maybe this is more for people who are doing heavy financial management anyway? Or am I just completely thinking about what this is wrongly?

Hey catapart, thanks for your comment.

You're right. Accepting credit card payments over the internet is usually done handled by black-box third party payment processors.

Bank payments, like ACH, SWIFT, and others are usually built in-house by companies that move money at scale, like payroll providers, insurance companies, and other old school institutions.

As companies that do heavy financial management refresh their architecture and move to modern web servers and the like, they'll likely need to rebuild it from scratch. This is what iso20022.js plays a small part in - if they're building on Node this is a convenient library for them to use.

At the risk of sounding like an a-hole, there's a very important word there... "if".
Okay, gotcha. That makes sense! I was getting stuck on where I should send the end result, but I'm guessing if I were doing financial work, I would already have an endpoint from my partner financial institution that would expect this kind of data.

Thanks for the explanation!

> As companies that do heavy financial management refresh their architecture and move to modern web servers and the like, they'll likely need to rebuild it from scratch. This is what iso20022.js plays a small part in - if they're building on Node this is a convenient library for them to use.

Are companies do heavy financial management refreshing their architecture in node, javascript and typescript? And do they then rely on a library with one sole contributor?

Sorry if this sounds dismissive. I am actually afraid by the answer, because, having worked in fintech, I wouldn't be surprised if the answer is "yes, certainly some".

When I worked at the international (global markets, cross border payments) arm of a bank, we had to build SWIFT messages through code. Such a library is quite useful, especially since most places have legacy code that manipulates payment messages manually.

Black box payment processors and Stripe need libraries too.

XML Schema Definitions for all ISO20022 messages are public: https://www.iso20022.org/iso-20022-message-definitions

There are tools that can turn the schema files into POJO/dataclasses/structs/etc in your language of choice, sometimes with proper data validation. Not sure about Typescript, but Java/Python/Golang definitely have those.

It may very well be lost knowledge in certain ecosystems, but generating a valid XML based on the given schema is generally a solved problem. Not sure if the projects adds anything beyond that.

Thanks for your comment mkuznets. I appreciate your input regarding XSD to class conversion - I agree that this is a super useful concept for sending ISO20022 files.

I think there's always some ergonomic gap between these XML Schema generated classes and SDKs that developers are comfortable using. My intention for this library is for a non-payment developer to be able to interact with the ISO20022 schemas, while being as true to the underlying models as possible.

I am currently implementing an ISO20022 integration at a large financial institution. The way these generally work is that whatever network you're connecting with will require a certain standard number of an ISO20022 message. When you send the generated messages, they must be valid as per the specific schema in question. If not, they will be rejected. I'm speaking specifically about payment networks, not consumer level payments.

Generating classes from the XSD's is fairly trivial in most languages. The hard part is being able to read the XSD's and then being able to create the ISO20022 message with all of the required elements (given all the possible combinations of valid elements).

I guess creating a code-time library where it was not possible to create an ISO20022 message that invalid would be interesting. But being able to create invalid ISO20022 is fairly easy to do for free.

Yeah exactly, with Go this should literally just be the type definition and that's it. You can do XML marshal, byte reader and http request with the standard library
So regarding ISO 20022 it is just a format for what is sent and read so each party have a common format to use (this is only partially true as institutions like making their own variant of it thats valid ISO 20022, think yaml vs json). Sending it is a completely different story, different financial institutions use different technologies for authentication and transport of these messages (SFTP, SWIFT, exotic custom stuff, etc..). My experience has not been in using ISO 20022 to send payments so maybe the payments space all use SWIFT or something.
That's true, ISO20022 was designed to be extensible, meaning banks add their own crazy variants to it all the time. Currently iso20022.js can create a minimum viable working ISO20022 file, and in the future I'd love to support as many specs as we reasonably can
This makes sense to be used with Open Banking regimes, that is being mandated by government legislation.
Government mandates are almost necessary to guide standard adoption. I saw this when I worked on UPI at WhatsApp
Nice work, can I ask how you tested this with SWIFT for example? Is there a development portal you need access to?
It isnt sending a payment in 3 LoC at all. Its generating an XML doc.
Do you have any plans to support ACH/NACHA payments?
Yes, ACH/NACHA payments work through ISO20022 aswell and are next up on the roadmap. I'd love to let you know when it's shipped - please shoot me an email at svapnil@woodside.sh so we can stay in touch!
Hey this seems really interesting. But I have questions about this statement “.

In order to transmit bank payments programatically, you must have direct transmission enabled with your banking partner. If you have any questions about this, don’t hesitate to reach out to us.

So do we need to get this enabled on a per bank basis or a per account basis?

Hey ab_testing, this is done on a per bank basis. Perhaps bank by bank has different rules, so this is something they'd be best equip to answer.

If you have more questions, I'd love to help you out - please shoot me an email @ svapnil@woodside.sh

(comment deleted)