Manipulating monetary amounts is a common computing chore. However, no mainstream language has a first-class data type for representing money, it’s up to programmers to code abstractions for it. This isn’t an issue per se until dealing with rounding issues from operations like installment payments (e.g., buy now, pay later), foreign exchange, or even simple things like fee processing and tax collection.

Inspired by my days at N26 dealing with these challenges, I introduce Money: a Kotlin library that makes monetary calculations and allocations easy:

val price = 100 money "USD"                     // USD 100.00
val shipping = 5 money "USD"                    // USD 5.00
val subtotal = price + shipping                 // USD 105.00
val discount = 10.percent()                     // 10%
val total = subtotal decreaseBy discount        // USD 94.50

val ratios = listOf(60.percent(), 40.percent()) // [60%, 40%]

total allocate 2                                // [USD 47.25, USD 47.25]
total allocate ratios                           // [USD 56.70, USD 37.80]

The library supports mathematical operations with monetary amounts, calculations with percentages, and allocation, making it simple to model use cases like those mentioned. Cryptocurrencies are also fully supported out of the box:

val price = 0.01607580 money "BTC"           // BTC 0.01607580
val transactionFee = 1.25.percent()          // 1.25%
val total = price increaseBy transactionFee  // BTC 0.01627675
val installments = total allocate 3          // [BTC 0.00542559, BTC 0.00542558, BTC 0.00542558]

val rate = 62_555.60 money "USD"             // USD 62555.60
val totalInUsd = total exchange rate         // USD 1005.63

Allocation

One of the nicest features of the library is its allocation capability. Allocation allows the distribution of a monetary amount into parts while guaranteeing that the sum of the parts equals the original value. For example, a retailer may accept purchases by credit card installments or by buy now, pay later (BNPL). What happens when a customer makes a purchase totaling USD 100.00 to be paid in three installments?

val price = 100 money "USD"
val number = 3
val installment = price / number
val installments = List(number) { installment } // [USD 33.33, USD 33.33, USD 33.33]
val total = installments.sum()                  // USD 99.99

As you noticed, there is a loss of USD 0.01. A penny here and there may seem a slight loss, but it may be costly over time. But there are other complications as well, such as overcharging a customer (which can be an infringement of consumer rights in several countries) due to rounding issues. The library provides a handy allocate() method that guarantees the result won’t differ from the original amount:

val price = 100 money "USD"
val installments = price allocate 3          // [USD 33.34, USD 33.33, USD 33.33]
val total = installments.allocations().sum() // USD 100.00

To allocate in proportional parts, pass a list of Percentage values to the method:

val amount = 2345.89 money "USD"
val result = dueAmount allocate listOf(50.percent(), 30.percent(), 20.percent())
val allocations = result.allocations() // [USD 1172.94, USD 703.77, USD 469.18]
val total = allocations.sum()          // USD 2345.89

As you can see in the previous examples, both results totaled up to the original monetary amount. No cent was lost or gained. By default, the library automatically allocates the difference. But you can tweak how the difference is allocated in the allocations list. For example, suppose your company requires the difference to be always allocated to the last item. You can do it by creating the allocator object directly with the desired allocation strategy:

val price = 100 money "USD"
val allocator = EvenAllocator(OnLast)
val installments = allocator.allocate(price, 3) // [USD 33.33, USD 33.33, USD 33.34]
val total = installments.allocations().sum()    // USD 100.00

Wrapping up

This post is just a glimpse of the library’s capabilities. I intend to keep the library’s API concise and to expand its capabilities gradually, including supporting Android development and out of the box persistence and serialization. Nevertheless, I hope it’s useful in its current version for people manipulating monetary amounts in Kotlin projects.

Refer to the usage guide on how to work with Money. The library has built-in support for 306 circulating currencies and 2283 cryptocurrencies. The installation procedures are explained in the project’s README. Give it a shot!

References