Why precision matters in payments ledgering

You’ll never hear an accountant (or an engineer, for that matter) say, “That’s probably close enough.”

When you’re dealing with financial transactions—or building a system to record and handle them—precision matters. If you keep an eye on Moov’s changelog—where we post new products, features, and improvements—you might’ve noticed updates to the precision of our ledger. We upgraded transfers, wallet transactions, and wallet endpoints to handle additional decimal places: up to nine decimal places to be exact (because, again, precision matters).

To this end, one of our priorities is to provide transparency in the money movement process, specifically around money visibility. This makes reconciliation easier for our customers. Here’s an overview of our approach to providing transparency, as well as a roundup of some recent advancements to Moov’s platform.

Why does precision matter?

You may be asking, what’s the point of accounting for that many decimal places? After all, U.S. currency is based on a hundred pennies adding up to dollars. Shouldn’t two decimal places provide enough precision?

When building financial software, engineers have to make decisions about how money is represented in their platform—including the kind of currency and the degree of precision they’ll represent with decimal places. A conventional strategy for the latter is to avoid decimals altogether by making the penny the base unit of currency. For example, a dollar would be 100 pennies; $78 would be 7,800 pennies, and so on. One of the difficulties with this approach is that no one really thinks about currency in that way, and either the developer or the analyst will have to eventually add decimals back in for accounting and reporting purposes.

There are also pretty common examples of costs and fees that are fractions of pennies. If you’ve ever bought gasoline, for example, you’ve probably noticed that the prices per gallon are calculated down to tenths of cents (some details on the reasoning behind this, if you’re interested). So, even with pennies as your base units, you’ve got to figure out a way to handle those fractions of a penny. This brings us right back to our need for supporting more than two decimal places.

Both the payments ecosystem and the larger financial landscape contain specific use cases for higher precision. So, if you’re building fintech, be sure your product can manage the precision required by the following:

1. Passthrough fees

Swiping a card or clicking “BUY NOW” makes payments seem simple on the surface, but underneath, the process is much more complex. Every transaction touches multiple platforms, providers, and processes. For a typical credit card transaction, the merchant pays a set of fees to the providers involved. There are a variety of pricing structures underlying these fees, based on the provider, the amount of the transaction, etc. Some providers charge flat fees per transaction, others charge percentages of transactions, some combine the two or add on monthly service fees, and so on.

A ledgering system that needs to integrate and account for this variety of fee structures has to be accurate and precise. For example, some of the fees that Moov deals with are as low as $0.0025. You just can’t record those accurately with two decimal places.

2. The variety of currency

There are close to 300 different kinds of currency used globally. Not every country follows the two-decimal-place standard that we use in the U.S. Some have no decimal places (the Korean Won, for example) and some have three (like the Bahraini, Iraqi, Kuwaiti, and Libyan Dinars). If you’re interested, here’s a comprehensive list of global currency names and their decimal places, as well as their ISO4217 codes—the recognized standard codes used to ensure clarity in international transactions. While we’re on the subject, I’d like to give a quick shout out here to Moov’s open source ISO4217 library, which is designed to support currency information and validation for our community of fintech developers.

Currently, most of what we do here at Moov is in dollars, but it’s important that we future-proof our infrastructure. We want to be able to handle international currencies with as much precision as we handle U.S. dollars. We also want to be ready to handle other kinds of emergent currency. Bitcoin, for example, is divisible to 100 millionths of a unit, or eight decimal places. That requires a lot of precision!

How we represent precision in money

In order to support high-precision values in our ledgering platform, we reworked our internal tooling for how we represent money in our system. It took a fair amount of time and effort. Without diving too deep into the weeds, I’ll walk you through a simple example of what our money model looked like before and after.

Our representation of money in our system before our precision project looked like this:

 type Money {
	 Value   int64
  	 Currency string

For non-developers, that first line, Value int, represents Money’s amount in integers (whole numbers), or in cents. The Currency string line represents the 3-letter abbreviated code for a given currency. Prior to our upgrade, this field was defaulted to “USD” as our system was exclusively operating on U.S. dollars. This design, however, creates an issue if we change the currency to another one that isn’t based on two decimal places, i.e., we have no way of swapping out how cents are represented in the Value field.

Now, following our upgrade, our money model looks like this:

 type Money {
	 Value	  decimal.Decimal
  	 Currency iso4217.CurrencyCode

Even if you’re not an engineer, two big changes probably jump out at you. The Value has been changed from integers (int)to decimals (decimal.Decimal). So now, rather than storing a value as an integer, we can store it in the conventional decimal format. This is powered by Spring Engineering’s decimal open source library, so big thanks to them.

The decimal library gives us some useful tools right out of the box, like performing basic arithmetic on values with different decimal points, for example, 0.001 + 0.0001. This may seem trivial, but implementing the logic for it isn’t. This library allows us to safely handle the math behind the scenes, which is crucial for maintaining accuracy and correctness.

Also, you’ll notice the second line in the money model above now uses our ISO4217 open source library I mentioned earlier. This allows us to represent money in any country’s currency with the specific decimal place format of that currency.

Ready for new use cases

Moov’s platform enables our customers to do virtually anything related to money movement—accept, send, store, and spend. We also support payments through (and have direct connections to) all the major card brands, The Fed, and The Clearing House. This makes us different from other payment platforms; we can handle a wide variety of transactions, and it opens up more possibilities for monetizing money movement and supporting new use cases in the payments space.

This also means we have to be future-proof and ready to handle payments with precision in a complex ecosystem. Arguably, the most important piece of everything we do is our ledgering system. A ledger has to be the single source of truth that sits at the center of all things. It always needs to be 100% accurate, precise, and up-to-date in real time.

If you’re a fintech builder and you’d like to learn more about the above (and other) open source solutions, take a look at our OSS libraries. Moov started as open source, and these libraries still play a big part in what we do and how we openly share knowledge with the fintech community. The Moov Slack community is full of talented and collaborative developers; it’s 100% worth checking out if you’re building in the fintech space!

Next up
How fintech_devcon creates community and opportunity
Company • 7m