WalleoPay WalleoPay
FR
Mon compte
Se connecter Ouvrir un compte Mot de passe oublié Documentation

Ouverture gratuite. Vos clés de test sont disponibles immédiatement.

Le franc CFA n'a pas de centimes, et ça change tout

1 500 francs se stockent « 1500 », jamais « 150000 » : le franc CFA a un exposant décimal de zéro. Ce détail invalide la plupart des réflexes hérités des devises à deux décimales.

L'équipe WalleoPay 9 septembre 2026 6 min de lecture 3 lectures

Un développeur qui a déjà intégré un paiement en euros ou en dollars arrive avec un réflexe : « les montants se manipulent en centimes ». Ce réflexe est bon. Mais appliqué au franc CFA, il produit des factures cent fois trop élevées.

L'exposant décimal, en une ligne

Chaque devise a un exposant : le nombre de décimales de sa sous-unité. L'euro et le dollar valent 2 — un euro vaut cent centimes. Le franc CFA, lui, vaut 0. Il n'a pas de sous-unité. Il n'y a pas de « centime de franc CFA ».

Conséquence directe : 1500 XAF se stocke 1500. Pas 150000. La plus petite unité du franc CFA, c'est le franc lui-même.

La même chose vaut pour le franc CFA d'Afrique de l'Ouest. Les deux sont à exposant zéro.

Pourquoi on stocke quand même en entiers

Si le franc n'a pas de sous-unité, pourquoi parler d'« unités mineures » ? Parce que la règle qui compte n'est pas « multiplier par cent », c'est ne jamais représenter de l'argent par un nombre à virgule flottante.

Un flottant ne stocke pas les décimales exactement. En PHP comme ailleurs, 0.1 + 0.2 ne vaut pas 0.3. Sur une transaction isolée l'écart est invisible ; sur un registre comptable additionné des milliers de fois, il devient un solde qui ne tombe jamais juste et qu'on n'arrive plus à expliquer.

D'où le principe : les montants circulent partout en entiers, dans la plus petite unité de leur devise. Pour le franc CFA, cela revient à manipuler des francs entiers. Pour l'euro, des centimes. La conversion se fait à deux endroits seulement : à l'entrée, quand un humain saisit une valeur, et à la sortie, quand on l'affiche ou qu'on la transmet à une API tierce.

Money::toMinor('1500', 'XAF');   // 1500   — exposant 0
Money::toMinor('15.50', 'EUR');  // 1550   — exposant 2
Money::toDecimal(1500, 'XAF');   // « 1500 »
Money::toDecimal(1550, 'EUR');   // « 15.50 »
Money::format(1500, 'XAF');      // « 1 500 FCFA »

Notez que la conversion vers une valeur décimale existe pour une raison précise : certaines API d'opérateurs attendent un montant formaté, pas un entier. Il faut alors respecter leur format exactement, et surtout ne pas fabriquer 1500.00 pour une devise qui n'a pas de décimales.

Le piège du bac à sable en euros

Un cas concret qui mord régulièrement : le bac à sable de MTN n'accepte que l'euro comme devise, quelle que soit la devise réelle de la transaction, alors que la production travaille en francs CFA.

Si votre code calcule le montant transmis à partir de la devise déclarée par l'opérateur, vous ne testerez jamais le chemin réel — et le jour du passage en production, la conversion changera d'exposant sous vos pieds. La parade est simple : que la devise utilisée pour formater le montant vienne de la configuration du pilote, jamais d'une valeur codée en dur, et que le calcul soit le même dans les deux modes.

Les arrondis, et de quel côté ils tombent

Une commission en pourcentage produit presque toujours un résultat à virgule. Sur 1 500 francs à 3 %, la commission vaut 45 francs pile ; sur 1 250 francs, elle vaut 37,5 francs. Or le franc CFA ne connaît pas les demi-francs.

Il faut donc décider, une fois pour toutes, de quel côté l'arrondi tombe — et documenter ce choix. Chez nous, la commission est arrondie à l'unité supérieure, puis la part fixe est ajoutée :

Money::fee(1250, 3.0, 0);   // ceil(37.5) = 38

Arrondir vers le haut n'est pas de la gourmandise, c'est de la prévisibilité : le tarif annoncé est un minimum garanti, et jamais un marchand ne se retrouve à payer plus que ce qu'il a lu, ni la plateforme à encaisser moins qu'elle n'a annoncé. Le point important n'est pas le sens de l'arrondi, c'est qu'il soit unique, appliqué au même endroit, et jamais recalculé ailleurs. Deux endroits qui arrondissent le même montant, c'est un écart comptable garanti.

Corollaire : une commission se calcule une fois, à la création du paiement, et se stocke avec lui. On ne recalcule pas une commission à l'affichage — sinon un changement de tarif réécrit l'histoire.

L'affichage

Un montant lisible au Cameroun s'écrit 1 500 FCFA : séparateur de milliers, pas de décimales, symbole après le nombre. Le séparateur est une espace, pas une virgule ni un point — 1,500 se lit « mille cinq cents » pour un anglophone et « un virgule cinq » pour un francophone.

Et ce formatage n'appartient qu'à la couche d'affichage. Une chaîne formatée ne doit jamais redescendre dans un calcul : le jour où quelqu'un tente d'additionner "1 500 FCFA", il obtient 1.

Les erreurs qu'on rencontre vraiment

Multiplier par 100 « par habitude ». Une facture de 25 000 francs devient 2 500 000. Le client refuse, et c'est heureux.

Diviser par 100 à l'affichage. Le symétrique : 25 000 francs affichés « 250 FCFA ». Celui-là passe inaperçu bien plus longtemps, parce que personne ne se plaint d'un prix trop bas.

Envoyer 1500.00 à une API en XAF. Selon l'implémentation d'en face, le montant est rejeté, tronqué ou réinterprété. Aucune de ces trois issues n'est souhaitable.

Utiliser un flottant en base. Une colonne decimal s'en sort, une colonne float finit par produire des soldes qui ne se réconcilient pas.

Additionner des devises. Un solde n'a de sens que par devise. Additionner des XAF et des EUR dans la même colonne n'a aucune signification, même si les deux sont des entiers.

La liste de contrôle

  1. Stocker tous les montants en entiers, dans la plus petite unité de leur devise.
  2. Transporter la devise avec le montant, toujours — un entier seul ne veut rien dire.
  3. Ne convertir qu'aux frontières : saisie humaine et affichage.
  4. Vérifier l'exposant plutôt que de le supposer : 0 pour le XAF et le XOF, 2 pour l'euro et le dollar.
  5. Arrondir à un seul endroit, dans un sens décidé et documenté.
  6. Stocker la commission calculée avec le paiement, ne jamais la recalculer après coup.
  7. Ne jamais faire descendre une chaîne formatée dans un calcul.

Ces sept règles tiennent en une centaine de lignes de code. Elles évitent la catégorie de bogue la plus coûteuse à corriger : celle qu'on découvre des mois plus tard, en essayant d'expliquer un solde.

Cet article peut servir à quelqu'un
Sujets xaf montants arrondi api

Vous voulez essayer par vous-même ?

Ouvrez un compte WalleoPay : vos clés de test sont disponibles immédiatement, et le code écrit en test est celui qui encaissera en réel.