Iam having a very weird problem. After making the call to the creditcard processor (CCBILL) with a payment token , they send back a subscription id .For example
0124058201000005323 should get returned. however what gets back is
124058201000005330. As you can see it has been rounded.
I also released that JSON.stringify(0124058201000005323) = ‘124058201000005330’
Now the weird thing is that when i use postman the response is not rounded and is as expected. am i not making the correct request
I am using node latest v 20
my function is as follows
function chargeCCbill(authToken, sendData){
return fetch(`${ccBillBaseUrl}/transactions/payment-tokens/${sendData.paymentTokenId}`,{
method:'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${authToken}`,
},
body:JSON.stringify(sendData)
})
.then((subData) => subData.json())
.then((json)=>{
if(json.subscriptionId !== null && json.declineCode === null && json.approved === true ){
// the json.subscriptionId is rounded here
return {status:'success',subscriptionData:json}
}
else{
return {status:'fail',subscriptionData:json}
}
})
I hope you guys can see what i might be doing wrong
I hope you guys can see what i might be doing wrong.
Also there was a option to add the following header
application/vnd.mcn.transaction-service.api.v.1+json
i dont know if that has any bearing. because i dont add that in postman and still gets the correct result. am i making the wrong call?
2
Answers
This is because of floating point precision having lower resolution in high numbers. The details of how floating point numbers are represented in binary are complex enough that I don’t want to get into a full-blown explanation in this answer, but it’s a format that has very high resolution around 0, allowing very small numbers to be represented, but its resolution gets lower and lower as the number gets further from 0.
When you attempt to construct a large number
124058201000005323
(side note, it’s best not to use leading 0s with number primitives as this can be ambiguously interpreted as meaning the number is in base 8, depending on whether or not the number contains the digits8
or9
) it will end up being represented by the closest available "slot" that can be represented by JavaScript. For this particular number, that slot is the number124058201000005330
.If you need to represent large integers specifically, you should use the
BigInt
type instead of regular numbers. This changes the way the number is represented to allow for a higher resolution when representing large integers. The shorthand for constructingBigInt
literals is affixing the number with the lettern
, e.g.124058201000005323n
, or you can use theBigInt
function with a string e.g.BigInt('124058201000005323')
.Since you’re having this problem when reading JSON, you could either fix it when serialising your data so it uses
BigInt
there, or update the way you parse this JSON. TheJSON.parse
function takes arguments including a "reviver", which allows you to specify a custom transformation to make when parsing a JSON blob. You could, for example, tell it to use theBigInt
function when parsing these numbers to ensure they don’t get modified.Just be aware that you can’t "mix"
BigInt
numbers with regular numbers. There are some more details about this in the MDN documentation I’ve linked to.Another option that may be available to you, depending on how these values are used, is just using a string instead. If really this is just a unique label, and it never needs to be treated as a number (e.g. have its size compared with another number) then a string will give you all the precision you could ever need.
Note: Since the question relates to node.js, there will be/is a better way to do this as this TC39 proposal is available in node.js v21.6.2 (maybe a little earlier, but not in v20.11.1)
There is a decent library called json-bigint
You can do something like
Or, if you’re using CommonJS it’s just
Then in your current code
Now any numbers that are outside the range of "SAFE" integers will be returned as strings
You could also use
{useNativeBigInt: true}
instead of{storeAsString: true}
and have the number parsed as aBigInt
Example (OK browser example but the code is the same in nodejs)