20 juil. 2025·7 min de lecture

Stocker l'argent en centimes entiers pour éviter les bugs de facturation dans les prototypes

Stockez l'argent en centimes entiers pour éviter les erreurs d'arrondi, définir des règles de devise et prévenir les litiges de facturation lorsque votre prototype devient un vrai produit.

Stocker l'argent en centimes entiers pour éviter les bugs de facturation dans les prototypes

Pourquoi les prototypes se trompent sur l'argent

Un prototype peut sembler parfait à l'écran et facturer quand même le mauvais montant. L'étiquette indique $19.99, le panier montre $39.98, mais la confirmation de paiement revient à $39.97 ou $39.99. Personne ne le remarque pendant les tests, puis des utilisateurs réels le font.

Cela arrive généralement parce que le prototype considère l'argent comme n'importe quel autre nombre. Il additionne, divise et applique des pourcentages en utilisant des décimales qui ne sont pas toujours représentées exactement. L'interface arrondit d'une manière, le backend d'une autre, et le prestataire de paiement peut arrondir différemment encore. Ces petits écarts se transforment en messages « Pourquoi j'ai été facturé en plus ? ».

Un centime paraît anodin, mais ça s'accumule :

  • Les tickets de support et les remboursements prennent du temps
  • Les rétrofacturations entraînent des frais et un risque pour le compte
  • La comptabilité devient confuse quand les totaux ne correspondent pas aux rapports
  • La confiance diminue quand les reçus et les écrans sont en désaccord

L'objectif est la prévisibilité ennuyeuse : les mêmes entrées doivent produire les mêmes totaux à chaque fois, dans le panier, le checkout, le reçu, les factures et les remboursements.

Vous n'avez pas besoin d'un système financier complexe pour y parvenir. Il suffit de quelques choix pratiques : stocker les montants en sécurité (le modèle le plus simple est les centimes entiers), décider où l'on autorise l'arrondi, et s'accorder sur les règles de devise avant la mise en production.

Le problème caché des décimales et des floats

Les prix paraissent simples à l'écran : $9.99, $19.00, $0.50. Le problème est que beaucoup de langages de programmation les stockent comme des nombres à virgule flottante. Le flottant est conçu pour la vitesse, pas pour des calculs monétaires exacts. Certaines valeurs décimales ne peuvent pas être représentées parfaitement en binaire, donc la valeur stockée est légèrement fausse.

C'est ainsi que $9.99 peut devenir silencieusement quelque chose comme 9.9899999997 ou 9.9900000003 dans votre application. Vous ne le remarquez généralement pas quand vous affichez un seul nombre parce que le formatage arrondit pour l'affichage. Mais l'erreur minuscule est toujours là.

Ces erreurs apparaissent dans le travail courant de facturation :

  • Addition de nombreuses lignes
  • Application de taxe (multiplication, puis arrondi)
  • Application de remises en pourcentage
  • Répartition ou proratisation des frais

Un scénario courant : un panier totalise $49.95 (cinq articles à $9.99). L'UI affiche $49.95. Puis vous ajoutez 8.25% de taxe. Si les valeurs sous-jacentes sont légèrement fausses, la taxe peut s'arrondir différemment selon la méthode de calcul (par article vs sur le total). Le client voit un total, mais le processeur de paiement en reçoit un autre, parfois différent d'un centime.

C'est ce décalage qui déclenche les litiges. Les utilisateurs ne se soucient pas que la différence soit « juste de l'arrondi ». Ils se soucient que l'écran de paiement, le reçu et la charge sur la carte ne correspondent pas.

Décidez des règles de devise et d'arrondi avant d'écrire du code

Beaucoup de bugs de facturation ne sont pas causés par une « mauvaise mathématique ». Ils arrivent parce que personne n'a convenu des règles.

Commencez par choisir une devise de tarification. Pour les premiers tests, une seule devise suffit généralement. Vous pouvez toujours afficher un symbole dans l'interface, mais le produit devrait avoir une devise officielle pour les prix stockés et les prélèvements.

Ensuite, décidez quand l'arrondi est autorisé. Arrondir chaque ligne peut produire un résultat différent d'un arrondi uniquement sur le total final, surtout une fois que les remises et les taxes interviennent. Les deux approches fonctionnent, mais il faut choisir une seule approche et l'appliquer partout.

Un petit ensemble de règles évite la plupart des litiges :

  • Une devise de tarification pour les prix stockés
  • Un seul moment d'arrondi (par ligne ou sur le total final)
  • Un ordre fixe d'opérations (par exemple : remises, puis taxe, puis frais)
  • Une promesse claire affichage vs prélèvement (ce que l'utilisateur voit doit correspondre à ce que vous envoyez au processeur)
  • Une source de vérité quand les nombres diffèrent (vos totaux sauvegardés, ou le total du processeur)

Si vous passez ensuite au stockage en centimes entiers, ces règles déterminent ce qu'il faut stocker, ce qu'il faut calculer et ce qu'il faut comparer aux reçus du processeur.

Le modèle le plus sûr : entiers pour les montants, devise en code

Si vous voulez que la tarification se comporte de la même manière dans tous les environnements, le choix le plus sûr est simple : stockez l'argent en unités mineures entières (centimes pour l'USD) et stockez la devise comme un code séparé.

Quand vous enregistrez un montant décimal comme 9.99, beaucoup de systèmes ne peuvent pas le représenter exactement. Même si l'UI affiche « $9.99 », la valeur stockée peut être légèrement au-dessus ou en-dessous. Cette différence peut changer les totaux après taxes, remises ou calculs répétés. Si vous stockez 999 à la place, les calculs restent exacts.

Un modèle de stockage clair comporte deux parties :

  • Montant en unités mineures (entier)
  • Code de devise (comme "USD" ou "EUR")

Utilisez des noms de champs qui rendent les erreurs difficiles à manquer :

  • amount_cents (entier)
  • currency (chaîne)
  • description (optionnel, pour reçus et logs)
  • created_at (pour audits)
  • metadata (optionnel, séparé des champs monétaires)

Gardez les chaînes formatées hors du stockage. « $9.99 » est un choix d'affichage, pas une valeur. Formatez à la frontière UI en utilisant amount + currency.

Prévoyez les négatifs dès le départ. Remboursements, crédits et rétrofacturations sont normaux. Un entier signé permet d'indiquer clairement un remboursement comme -999 dans la même devise.

Étape par étape : implémenter la tarification en centimes entiers dans un prototype

Inherited an AI prototype?
FixMyMess repairs broken AI-generated prototypes from Lovable, Bolt, v0, Cursor, and Replit.

Choisissez une devise de base et notez-la : en quoi vous facturez, quel symbole vous montrez et comment vous arrondissez. Cette décision unique évite beaucoup de surprises du type « on croyait que c'était en USD ».

Ensuite, stockez l'argent en centimes entiers partout où c'est important. Dans votre base de données, conservez amount_cents en entier (999 signifie $9.99) et currency en code court comme USD. Dans votre code, faites transiter les montants en entiers aussi.

Un flux simple qui reste prévisible :

  • Gardez les listes de prix en centimes (plan_price_cents = 999)
  • Multipliez et additionnez en utilisant des entiers (quantité, options, unités d'usage)
  • Appliquez les remises avec des entiers, en utilisant une règle d'arrondi unique
  • Ajoutez taxes et frais avec cette même règle
  • Sauvegardez les résultats comme subtotal_cents, tax_cents, total_cents, plus la devise

N'arrondissez que là où vos règles l'autorisent. Une approche commune est de calculer le sous-total en centimes, calculer la taxe à partir de ce sous-total, puis arrondir la taxe une fois en centimes.

Pour l'auditabilité, enregistrez les entrées (prix, quantités, remise, taux de taxe, devise) et les sorties (subtotal_cents, tax_cents, total_cents). Quand un total semble incorrect, ces logs rendent le problème évident au lieu d'être mystérieux.

Taxes, remises et frais sans surprises d'arrondi

Si vous stockez les prix de base en centimes entiers, la plupart des problèmes apparaissent plus tard : taxes, pourboires, frais et remises en pourcentage. Ces étapes créent des fractions de centime, donc il faut des règles cohérentes.

Choisissez une règle d'arrondi et utilisez-la partout

Choisissez une méthode d'arrondi et tenez-vous-y. Deux choix courants sont half-up (0.5 arrondit vers le haut) et half-even (arrondi bancaire). Les deux peuvent convenir. Les mélanger, c'est ce qui cause les discussions « votre reçu diffère du mien ».

Décidez aussi quand l'arrondi est permis. Une règle pratique : faites les calculs en unités mineures et n'arrondissez que lorsque vous devez convertir un résultat en pourcentage en centimes.

Remises en pourcentage : éviter le piège du centime

Une remise de 10% sur 999 cents donne 99.9 cents. Cette décimale doit aller quelque part, et elle doit aller de la même manière à chaque fois.

Une séquence fiable :

  • Calculez discount_cents à partir du sous-total original
  • Arrondissez discount_cents une fois selon votre méthode choisie
  • Soustrayez discount_cents de subtotal_cents
  • Calculez la taxe à partir du sous-total réduit (si votre politique l'exige)

Cela évite le double-arrondi, où vous arrondissez en cours de route puis encore à la fin.

Rendre les frais explicites

Les frais sont plus faciles à comprendre et à déboguer lorsqu'ils sont des champs séparés, pas noyés dans les totaux. Utilisez des noms clairs comme shipping_cents, service_fee_cents, platform_fee_cents et tip_cents. Votre reçu pourra alors refléter votre base de données.

Si vous pouvez expliquer chaque centime sur la facture, vous évitez généralement les litiges.

Multi-devises : que faire maintenant, que reporter

La plupart des prototypes n'ont pas besoin du vrai multi-devises dès le premier jour. Si vos utilisateurs paient dans un pays et que vous vous réglez dans une devise, restez en devise unique et corrigez les bases. Vous pouvez toujours afficher une estimation convertie, mais traitez-la comme affichage uniquement, pas comme le montant facturé.

Si vous supportez plusieurs devises, chaque montant doit être accompagné d'un code de devise (USD, EUR, GBP). Le modèle entier/unité mineure s'applique toujours, mais les « centimes » ne sont pas universels. Certaines devises n'ont pas d'unités mineures (JPY) et d'autres en ont 3 (KWD). Donc stockez :

  • Montant entier en unités mineures
  • Code de devise
  • La précision en unités mineures de la devise (dérivée du code)

Acceptez aussi que les taux de change ne sont pas réversibles. Convertir USD en EUR puis revenir ne rendra pas forcément le même montant entier. C'est normal. L'erreur est de prétendre que les conversions sont sans perte, puis de se disputer sur le centime manquant.

Si vous supportez plusieurs devises, notez :

  • D'où viennent les taux
  • Combien de temps un taux est valide
  • Ce que vous stockez (montant facturé, taux utilisé, eventuelle référence de conversion)
  • Quand vous convertissez (checkout, facture, règlement)
  • Comment vous arrondissez

Évitez de mélanger les devises dans un même total à moins de définir aussi l'étape de conversion.

Erreurs communes qui mènent à des litiges

Find the real root cause
We pinpoint the exact functions and tables causing billing mismatches and flaky totals.

La plupart des litiges de facturation démarrent petit : un écran affiche $19.99, le reçu affiche $20.00, et la carte est débitée $19.98. Les utilisateurs ne se soucient pas du pourquoi. Ils trouvent que ça semble négligé ou malhonnête.

Une cause fréquente est de stocker des valeurs « jolies » plutôt que les valeurs brutes. Si vous enregistrez « $10.00 » ou « 10.00 » (déjà formaté), différentes parties de l'appli vont le reparser, le réarrondir ou supposer une devise. Un choix d'affichage inoffensif devient un total erroné.

Une autre cause est d'avoir plusieurs endroits qui calculent les totaux. Si le panier arrondit chaque ligne, la facture arrondit le sous-total, et la requête de paiement arrondit le total final, vous pouvez obtenir trois réponses différentes.

Des schémas qui créent souvent des totaux non concordants :

  • Calculer les totaux dans le navigateur et s'y fier sans vérification serveur
  • Générer des e-mails ou factures avec un chemin de calcul différent du checkout
  • Hardcoder un symbole de devise en supposant que toutes les devises se comportent comme l'USD
  • Appliquer remise-ensuite-taxe à un endroit et taxe-ensuite-remise à un autre
  • Ignorer les tests limites (petits montants, beaucoup de lignes, ajout/retrait répété)

Vérifications rapides avant de lancer les paiements

Avant d'accepter de vraies cartes sur un prototype, faites une passe de sanity autour de l'argent. La plupart des bugs de paiement sont de petites incohérences qui créent de gros problèmes de support.

Commencez par le stockage : les montants doivent être des entiers dans l'unité la plus petite pour cette devise, y compris les remboursements et crédits. Si vous voyez des décimales dans les colonnes de montant de la base, considérez-le comme un signal d'alerte.

Vérifiez ensuite la gestion des devises : chaque montant doit circuler avec un code de devise. Si une API retourne amount: 1999 sans currency: "USD", quelqu'un devinera plus tard de travers.

Enfin, choisissez un propriétaire unique des totaux. Une seule fonction ou un seul service calcule le sous-total, les taxes, les remises, les frais et le total général. Tout le monde lit ensuite les résultats sauvegardés. Si la page de checkout et le gestionnaire de webhook recalculent chacun de leur côté, ils finiront par diverger.

Une checklist courte qui attrape la plupart des problèmes :

  • Montants entiers partout là où c'est important (y compris remboursements)
  • Code devise présent sur chaque valeur monétaire
  • Totaux calculés en un seul endroit, sauvegardés et réutilisés
  • Règles d'arrondi documentées et couvertes par des tests
  • Numéros de facture/reçu correspondant au montant débité au centime près

Testez avec de vrais cas limites, pas seulement des exemples à « $1.00 » : remises en pourcentage plus taxe, remboursements partiels après remises, frais ajoutés avant vs après taxe, et paniers avec beaucoup de petits articles.

Un exemple réaliste : où disparaît un centime

Stop one-cent charge errors
We will find where your totals drift and show the exact fixes before you ship payments.

Disons que vous vendez un plan à $9.99, offrez 20% de réduction, et facturez 8.25% de taxe. Le plan est facturé pour 3 sièges sur une facture.

Avec des centimes entiers, chaque siège vaut 999 cents. Le désaccord vient du moment où vous arrondissez.

Deux manières raisonnables d'arrondir

Méthode A : arrondir chaque siège après la remise

La remise par siège est 20% de 999 = 199.8 cents, arrondis à 200 cents. Net par siège = 999 - 200 = 799 cents. Pour 3 sièges : 799 x 3 = 2,397 cents ($23.97). Taxe : 2,397 x 0.0825 = 197.7525 cents, arrondie à 198 cents. Total : 2,397 + 198 = 2,595 cents ($25.95).

Méthode B : arrondir une fois sur le sous-total

Le sous-total est 999 x 3 = 2,997 cents ($29.97). La remise est 20% de 2,997 = 599.4 cents, arrondie à 599 cents. Net : 2,997 - 599 = 2,398 cents ($23.98). Taxe : 2,398 x 0.0825 = 197.835 cents, arrondie à 198 cents. Total : 2,398 + 198 = 2,596 cents ($25.96).

Les deux méthodes sont défendables. Elles diffèrent d'un centime. Si votre UI montre une méthode et que votre backend en charge une autre, vous créez un litige.

Sur la facture, exposez-le en clair pour que le calcul soit facile à suivre. Pour les remboursements, ne recalculer pas. Remboursez les centimes exactement débités, sinon vous pouvez créer un nouveau décalage plus tard.

Consignez suffisamment pour rejouer la décision :

  • Code devise, taux de taxe, taux de remise
  • Règle d'arrondi et où l'arrondi a lieu
  • Lignes, quantités et cents facturés finaux
  • Valeurs intermédiaires clés (avant et après arrondi)
  • IDs du processeur de paiement pour la charge et le remboursement

Étapes suivantes : rendre la gestion de l'argent banale et fiable

L'objectif n'est pas un code de facturation sophistiqué. C'est d'obtenir des totaux qui correspondent toujours : panier, facture, reçu, remboursements et rapports.

Rédigez vos règles monétaires dans un petit document partagé : devises supportées, méthode d'arrondi, où l'arrondi est autorisé et ordre des opérations. Ajoutez ensuite un petit ensemble de tests qui verrouillent ces comportements, en particulier autour des petites remises, des nombreux articles et des remboursements partiels.

Si vous héritez d'un prototype généré par IA (surtout depuis des outils comme Lovable, Bolt, v0, Cursor ou Replit), un nettoyage rapide vaut le coup avant d'avoir de vrais clients. Le float se cache souvent dans des helpers, des fonctions de formatage UI ou des colonnes de base de données avec une précision incohérente.

FixMyMess (fixmymess.ai) aide les équipes à transformer des prototypes « les paiements marchent à peu près » en flux de facturation prêts pour la production en diagnostiquant les maths, en normalisant le stockage de l'argent et en resserrant les règles d'arrondi pour que le montant débité reste cohérent à chaque étape.