2 minutes
How a JS edge case broke our payments
Ah! the wonderland of JavaScript, where anything is possible. Where logic meets the ground beneath, and likes to stay there. Just kidding, logic doesn’t exist in this world.
No, I don’t hate JavaScript, I really like it, the ease of programming and little to no time to get your application running. But along with so many pros comes few cons too. But this blog is not to list all of them, but to pinpoint one.
What happened?!
We have integrated with many payment gateways, for different payment modes. We use Stripe as one of the payment gateways.
Not to go in exact details, we are supposed to pass the amount to deduct while initiating the payment, and post deduction, we validated the deducted amount. But the catch with Stripe is, it takes amount in paises not rupees. Yeah, but that’s not a blocker, you simply multiply your amount by 100, and voila, you have it in paises.
So let’s try to imagine it in Node.
const rupeeToPaises = (amount: number) => {
// this should give us the right output
let amountInPaise = amount * 100;
// and to remove any trailing decimals if any
return parseInt(amountInPaise);
// though TS will throw error on this,
// as parseInt in TS expects string input
}
This worked really well. Everything was well and good, life was merry. But happiness is a myth.
The Edge Case!!!!
An issue got reported that few payments weren’t going through. Just like any wise developer, we blamed it on third-party integrations. But that also couldn’t buy us time with our happiness. So we sat for a debugging session, and we picked one case which was failing -
Amount - 522.56
and Stripe trying to charge - 522.55
🤨
Then we looked up to our function to convert rupee to paise, and that was the culprit. But how? Because if you do 522.56 * 100
its 52256
.
Eeh! you are wrong 👎lol you thought math is easy!
Go, and start a node console, and do 522.56 * 100
. Yes, its 52255.99999999999
, and if you do parseInt(52255.99999999999)
its 52255
🤦♂️.
Solution
toFixed
was the way to go, where it seemed to get the us the desired result. So final solution:
const rupeeToPaises = (amount: number) => {
let amountInPaise = amount * 100;
return Number(amountInPaise.toFixed(0));
// output of toFixed is a string
}
Thoughts
I love working with JavaScript, it always keeps things exciting and unpredictable. But do let me know if you know why did it behave the way it did.