BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Author Q+A: finprim is a Case for One Financial Validation Library in TypeScript

Author Q+A: finprim is a Case for One Financial Validation Library in TypeScript

Listen to this article -  0:00

finprim, which is an MIT-licensed TypeScript library that puts IBAN, BIC, card, sort code, VAT, routing number and loan calculations behind one API, has published its fifth release, 0.1.4. The repository has 81 stars.

InfoQ asked author Oluwatosin Adelaja what pushed him to build it, and where he wants it to stop.

InfoQ: What pushed you to build finprim?

Adelaja: Honestly, it came from doing the same work too many times. At Sprint Technology Solutions we had several products, and each one ended up with its own copy of the same financial validation, sort code checks in one place, IBAN formatting in another, card validation somewhere else, all written slightly differently by whoever got there first.

The moment it really landed for me was on a payout flow. We had account and sort code validation on the web side, and then the mobile app needed the same thing. Rather than share it, the validation got rewritten for mobile, and the two copies drifted. One of them was a little stricter than the other, so a bank detail that the web form accepted would get rejected on mobile, and we had people re-entering details that were actually fine. It took longer than it should have to track down, because nobody thinks to check whether two "identical" validators are actually identical. The fix was easy once we found it, but the real problem was that there were two of them at all.

I hit the same shape of thing again later building a daily pay feature for NHS workers, where the same rules needed to hold on web and on mobile. That is when I stopped treating it as a one-off chore and started thinking it should just be one library, written once and tested once, that any product could pull in and trust.

InfoQ: What was missing that justified a unified library rather than assembling existing single-purpose packages?

Adelaja: You can absolutely wire together a pile of single-purpose packages, and plenty of teams do. The problem is what you are left maintaining. Each package has its own API shape, its own types, its own idea of what a valid result looks like, and often its own dependencies. So you end up writing a layer of glue code just to make them agree with each other, and that glue becomes its own thing to test and keep working.

finprim's bet is that financial validation is coherent enough to live behind one consistent API. The same shape of input, the same shape of result, and the same typing approach across IBAN, sort code, card, VAT, all of it. And zero runtime dependencies, so there is no tree of other people's packages to audit or keep patched. For something sitting in the path of a payment, I wanted fewer moving parts, not more.

InfoQ: What concrete class of bugs do branded types prevent, and why not rely on plain runtime validation?

Adelaja: The bug class is the one where a string that was never validated gets used as if it had been. To the compiler, a validated IBAN and a random string are both just a string, so nothing stops you passing the wrong one into a function that assumes the value is good. It compiles fine, and then it fails at runtime, which in this domain means it fails in the middle of a live transaction.

Branded types close that gap. A validated IBAN comes back as its own distinct type, not a plain string, so if you try to use an unchecked value where a validated one is required, the compiler rejects it before the code ever runs. The runtime validation still happens, that does not go away. The branding just guarantees it actually happened and that nobody quietly skipped it on some code path. It moves a whole class of "we forgot to validate this one route" mistakes from production back to compile time.

InfoQ: For something handling payment data, how do you think about trust, what can you guarantee, and where do you draw the line?

Adelaja: The honest answer starts with being clear about what finprim is and is not. It validates and formats financial data. It does not store anything, it does not send anything over the network, and it does not process payments. No database, no telemetry, no outbound calls.

So the guarantees I can actually make are narrow and specific. It checks values against published standards, IBAN structure, checksum rules, card network ranges, and tells you whether they are well formed, deterministically, without logging the sensitive values it is handling. That last part is deliberate. You do not want account numbers or card numbers turning up in logs, so the library does not put them there.

Where I draw the line matters just as much. finprim does not tell you a payment is safe to make, it is not a compliance tool, and it is not a replacement for the checks your payment provider runs. It confirms the data is shaped correctly before it goes anywhere. Treating a validation library as more than that would be the real mistake.

InfoQ: How do you source and maintain the correctness of 80+ IBAN formats, VAT quirks and card ranges, and how is it tested?

Adelaja: The rules come from the published standards rather than from me guessing. IBAN is checked against the ISO 13616 country formats, card numbers use Luhn plus the published network ranges, VAT uses the documented per-country formats, and so on. Where there is an authoritative spec, that is the source of truth.

On testing, each validator runs against known-good and known-bad values taken from those reference sources, so the tests check behaviour against the standard, not just against themselves. Overall coverage is around 91% of lines and 88% of statements. The core validation modules are the most heavily covered, the IBAN, card, VAT, routing, BIC and sort code logic sits around 94% of lines with full function coverage, and the thinner coverage is in the optional React bindings rather than the validation itself. I would rather be straight about that split than quote one headline number, because the part that actually handles financial data is the part that is tested hardest.

Keeping it correct over time is mostly manual, I update the formats as the standards change, and this is one place the outside contributors have genuinely helped, because people using it in different countries spot edge cases I would never hit on my own.

InfoQ: SEPA and ACH generation is a big jump from validation into transaction generation. Where do you want finprim to end up?

Adelaja: That is a fair thing to push on, because generation is a genuinely different job from validation, and I do not want to pretend otherwise. Validating an IBAN is self-contained. Producing a SEPA credit transfer file or an ACH batch means taking on formatting rules, sequencing and a lot more responsibility for getting something exactly right that is going to be submitted to a bank.

Where I want finprim to sit is as a validation and formatting toolkit first. I am deliberately cautious about the generation side. If it does move into producing standard file formats, I would want that to be safe, well-tested generation of documented formats, not finprim turning into a transaction engine or a payments platform. I would rather it did one layer really well than spread itself thin doing a half version of what a payment provider already gives you.

Narrower tools exist, such as ibankit for IBAN and BIC alone, and on DEV a commenter called the Dinero.js approach solid while warning that teams get burned when a library's precision assumptions diverge from the processor's.

Adelaja is a senior full stack engineer. All five releases have stayed within 0.1.x, so upgrading has meant patch releases rather than code changes, and there is no dedicated migration guide, so the README and release notes are the reference.

About the Author

Rate this Article

Adoption
Style

BT