Unable to set subscription price to $19.99

  • using Discourse-Subscriptions
  • trying to add a pricing plan to a product
  • Enter 19.99 in the price box (this is already hard because the input behaves very weird)
  • Try to submit
  • Get error “Invalid integer 1998.9999999999998”

I’ve only tested this on stable 3.5.3 so far but the code is the same on beta.

Code

  @computed("unit_amount")
  get amountDollars() {
    return parseFloat(this.get("unit_amount") / 100).toFixed(2);
  }

  set amountDollars(value) {
    const decimal = parseFloat(value) * 100;
    this.set("unit_amount", decimal);
  }

Javascript / IEEE-754 float nastyness:

-> parseFloat("19.99")*100
<- 1998.9999999999998 

Suggested fix: const decimal = Math.round(Number(value) * 100)

My biggest question: why didn’t anyone notice? Didn’t anyone set the price to $19.99 before?

Oh, by the way, I also frequently get NaN, for instance when I make the input field completely empty.

1 Like

I knew right away. One more thing I learned about computers 40+ years ago that’s not changed.

Or use 1-cent integers? I think that’s what Stripe does, actually. (But your solution requires changing much less code, so I guess it wins.)

2 Likes

Yes, that’s what it does, unit_amount are cents and the amountDollars is an ‘XX.XX’ input value.
The “invalid integer” error even comes from the Stripe API.

I guess we’re both old enough to know about the FDIV bug :wink:

2 Likes

honestly I think storing this in a float is misguided this column should be an int and stored in cents

2 Likes

It is, the parseFloat is used to convert from/to input field which is “currency with two decimals”. The unit_amount object property is obviously typeless though, hence my suggestion to use Math.round()

It is not a column server side, it is passed to the Stripe API.

For currency BCD floating point is generally better than binary floating point.