Etsy Icon>

Code as Craft

Making The Indian Rupee Work For Humans and Databases main image

Making The Indian Rupee Work For Humans and Databases

  image

As a global marketplace, Etsy offers support for a few dozen international currencies. Recently we decided to add the Indian rupee (₹ - INR) to the list. Used by more than 1.5 billion Indians, the rupee turns out to present quite a tricky case for digital transactions. How so? We'll walk you through it, and then discuss Etsy's approach.

Some background on how currencies work in databases

The best way to store monetary values in a database is in the smallest denomination of whatever currency you're working with. In the US, though prices are customarily quoted in dollars (USD), that’s the penny. The same is true for the Canadian dollar (CAD). In Japan, on the other hand, there's no smaller denomination than the yen (JPY). So in a row where amounts are in USD or CAD, "100" represents one dollar; in JPY that amount would represent a hundred yen.

Transaction table

We choose this smallest-denomination approach to avoid well-known problems involved with floating-point arithmetic. Many common fractions have infinitely long decimal representations (e.g., ⅓ = 0.3333…), and storing them in fixed-width database fields makes rounding and truncation errors a constant issue. While you'll never entirely eliminate dealing with floating-point errors, storing amounts as integers the way we do here can avoid many headaches.

Some background on the rupee

Technically, the rupee is like the US or Canadian dollar: it has a fractional denomination equivalent to the penny, called a paisa. But paisa are so low in value (around a hundredth of a cent USD, at this writing) that they're no longer in circulation. No one uses them in price calculations. All transactions in India, digital or in-person, happen in units of rupees.

In practice, then, the rupee is more like the yen, its own lowest denomination. So what's the right way to handle it in the database? Do as the real world does, and ignore the paisa? Or maintain the consistency of our technical practice and retain it?

Option 1: Thinking in rupees

Why worry about a coin no longer in circulation? Etsy's Indian users think in rupees, so we should too.

There’s a huge catch though. Due to the paisa’s former use in circulation, all financial institutions and partners (shipping, tax, payment processors, etc.) denominate the rupee in paisa. And if we don't? Say Etsy wants to send ₹100 to a seller. But if we tell their bank to credit them with 100 INR, and the bank denominates in paisa, then our seller will only end up with a single rupee. Etsy and the bank have incompatible ideas of what "100 INR" means. To say that’s an issue is an understatement.

Quick solution: just multiply your amounts by 100 before every call and divide by 100 on every response. On a small scale, that works. But it turns the rupee into an exception requiring special handling, something every future external integration touching INR would need to remember and replicate. If someone misses it, your values are going to be 100x off. That's a high-stakes kind of error, and not one you want to open yourself up to.

Option 2: Paisa, then

So we do what the banks do, and denominate INR in paisa. Any integration we want to build, the currency is already in its standard form and we’re good to go. But with that approach comes a different issue.

We’ve optimized our database for integration, but what about users? Indian buyers and sellers don't expect to see prices quoted in fractional rupees. Since most of Etsy's listings are originally priced in other currencies, like USD, we have to apply an exchange-rate calculation to show them in rupees. For our Indian users, that means that almost every price on our site would be an "ugly" one, trailing with it some vestigial amount of paisa.

Amount with paisa
Listing from CuzyCo

It seems like we're faced with an impossible choice between technical integrity and user experience. So what do we do?

Option 3: Do both

The approach we landed on was to store all values in paisa BUT round the paisas away. We already round off insignificant digits (fractions below the smallest denomination–think of them as sub-cents) every time we do an exchange-rate calculation. We can apply a similar idea to the rupee.

How we round

We transparently do all our INR calculations in paisa. But when the amount enters a “final” state to store or present to the user we treat the paisa as a sub-cent, and round to the real-world value, the closest rupee.

This requires more work but it lets us have the best of both worlds. For our database and partners our data is stored in the commonly accepted format. For our engineers, future integrations have no special logic to remember, no 100x issues to worry about. And for our users, paisa always shows as zero.

Amount with paisas rounded

Of course, a price ending in ".00" still looks wrong to an Indian user, even if it's technically correct. That problem is easily handled: a CSS class on the front end hides the trailing zeroes, and our users see what they expect to see, a price quoted in units of rupees.

Amount with no paisa

We expect all the various roundings up and down we're performing to come out in the wash, more or less. Given how tiny the value of the paisa is, we're not at risk of exposing ourselves to any extra costs.

Supporting the rupee required that we make some very sensitive changes, and we had to audit our whole codebase to make sure the money class was adopted consistently. But the result is a seamless, technically solid solution that we're proud of, that meets the expectations of our Indian users, and is live today!