Não é possível definir o preço da assinatura para $19.99

  • usando Discourse-Subscriptions
  • tentando adicionar um plano de preços a um produto
  • Inserir 19.99 na caixa de preço (isso já é difícil porque a entrada se comporta de maneira muito estranha)
  • Tentar enviar
  • Receber o erro “Inteiro inválido 1998.9999999999998”

Eu só testei isso na versão estável 3.5.3 até agora, mas o código é o mesmo na versão beta.

Código

  @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);
  }

Aspereza de ponto flutuante Javascript / IEEE-754:

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

Correção sugerida: const decimal = Math.round(Number(value) * 100)

Minha maior dúvida: por que ninguém notou? Ninguém definiu o preço como US$ 19,99 antes?

2 curtidas

Eu soube na hora. Mais uma coisa que aprendi sobre computadores há mais de 40 anos e que não mudou.

Ou usar inteiros de 1 centavo? Acho que é o que o Stripe faz, na verdade. (Mas sua solução exige mudar muito menos código, então acho que ela vence.)

2 curtidas

Sim, é isso que ele faz, unit_amount são centavos e o amountDollars é um valor de entrada ‘XX.XX’.
O erro “inteiro inválido” vem até da API do Stripe.

Acho que ambos somos velhos o suficiente para saber sobre o bug FDIV :wink:

3 curtidas

honestamente, acho que armazenar isso em um float é equivocado, esta coluna deveria ser um int e armazenada em centavos

3 curtidas

É, o parseFloat é usado para converter de/para o campo de entrada que é “moeda com duas casas decimais”. A propriedade do objeto unit_amount é obviamente sem tipo, daí minha sugestão de usar Math.round()

Não é uma coluna no lado do servidor, é passada para a API do Stripe.

1 curtida

Para a moeda BCD, ponto flutuante é geralmente melhor que ponto flutuante binário.