skip to Main Content

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


  1. 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 digits 8 or 9) 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 number 124058201000005330.

    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 constructing BigInt literals is affixing the number with the letter n, e.g. 124058201000005323n, or you can use the BigInt 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. The JSON.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 the BigInt 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.

    Login or Signup to reply.
  2. 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

    import JSONbig from "json-bigint";
    const JSONbigString = JSONbig({storeAsString: true});
    

    Or, if you’re using CommonJS it’s just

    var JSONbigString = require('json-bigint')({ storeAsString: true });
    

    Then in your current code

    .then((subData) => subData.text())
    .then(json => JSONbigString(json))
    .then((json)=>{
    

    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 a BigInt

    Example (OK browser example but the code is the same in nodejs)

    <script type="module">
      import JSONbig from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm';
      const JSONbigString = JSONbig({storeAsString:true});
      var json = '{ "value" : 124058201000005323, "v2": 123 }';
      console.log(JSONbigString.parse(json));
    </script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search