Solde et paiements
Lisez le solde du portefeuille et gérez le 402 Payment Required avec son lien de rechargement signé.
Fleeex est prépayé : deux choses comptent donc pour une intégration, connaître le solde, et gérer proprement le moment où il s'épuise.
Lire le solde
const { balanceMicros, currency } = await client.getBalance();
// balanceMicros est en micro-unités EUR : 1_000_000 = 1,00 €getBalance() renvoie un BalanceResponse. Les
montants sont en micro-unités pour éviter les dérives de virgule flottante : divisez par
1 000 000 pour obtenir l'unité principale.
const eur = balanceMicros / 1_000_000; // ex. 4_250_000 → 4,25Comme un appel relayé, getBalance() lève PaymentRequiredError si le couple
(app, utilisateur) n'a pas de portefeuille approvisionné et autorisé.
Les montants sont hors taxes
Le portefeuille contient des crédits hors taxes : la TVA est calculée et encaissée en sus au moment du Checkout, et le solde est crédité du montant hors taxes. Un utilisateur qui a payé 12,00 € voit donc un solde inférieur, et la différence n'est pas de l'argent disparu. Si vous affichez ce chiffre, présentez-le comme du crédit IA plutôt que comme un montant payé. Ses reçus, eux, vivent dans son compte Fleeex.
C'est le solde du portefeuille, pas celui de votre application
Un seul solde finance toutes les applications que l'utilisateur a connectées :
balanceMicros peut donc bouger sans que votre application n'y soit pour rien. Ce qui
borne la part de votre application, c'est le plafond mensuel que l'utilisateur lui
fixe, et c'est pourquoi l'atteindre a son propre code 402, ci-dessous.
Gérer le 402 Payment Required
Quand le portefeuille n'est pas approvisionné ou pas autorisé, le proxy comme
getBalance() lèvent une PaymentRequiredError typée, porteuse le plus souvent d'un lien
de rechargement signé et de courte durée. Envoyez-y l'utilisateur final pour approvisionner
le portefeuille :
import { PaymentRequiredError } from "@fleeex/sdk";
try {
await client.chat.completions.create({ model, messages });
} catch (err) {
if (err instanceof PaymentRequiredError) {
if (err.topupUrl) redirect(err.topupUrl); // envoyer l'utilisateur vers le Checkout
else showSupportMessage(); // un portefeuille suspendu n'a pas de lien, voir plus bas
} else {
throw err;
}
}PaymentRequiredError expose topupUrl, code, correlationId et status (toujours
402). Le message est volontairement générique, car Fleeex ne révèle jamais l'existence
d'un portefeuille : les parties exploitables sont donc code et topupUrl.
Le 402 recouvre trois situations différentes
Faites vos branchements sur code. Envoyer quelqu'un au Checkout quand payer ne peut pas
l'aider est pire que de ne rien dire :
code | Signification | topupUrl |
|---|---|---|
PAYMENT_REQUIRED | Pas de portefeuille autorisé, ou solde insuffisant. | toujours |
APP_SPEND_CAP_EXCEEDED | Votre application a atteint le plafond mensuel que l'utilisateur lui a fixé. Son portefeuille peut être encore plein : le message à afficher est « relevez le plafond », pas « ajoutez des fonds ». | le plus souvent |
WALLET_SUSPENDED | Le portefeuille est bloqué après qu'un remboursement ou une opposition a repris l'argent. L'approvisionner ne lève pas le blocage : orientez l'utilisateur vers le support. | jamais |
switch (err.code) {
case "WALLET_SUSPENDED":
return showSupportMessage();
case "APP_SPEND_CAP_EXCEEDED":
return showCapReached(err.topupUrl); // peut être undefined
default:
return err.topupUrl ? redirect(err.topupUrl) : showGenericMessage();
}topupUrl est optionnel sur n'importe quel 402 : ne le déréférencez jamais sans
vérification. Un refus lié au plafond de dépense qui perd la course avec l'écriture de la
facturation arrive sans lien.
Voir la référence des erreurs pour le contrat complet, et le Bac à sable pour reproduire les trois cas à la demande plutôt que de les attendre en production.
Le gérer en un seul endroit
Répéter ce try/catch partout est fastidieux. Enregistrez une fois le hook
onPaymentRequired : il se déclenche chaque fois qu'un 402 est traduit (depuis un appel
relayé ou depuis getBalance()), juste avant que l'erreur ne soit levée.
const client = new FleeexClient({
apiKey,
userId,
onPaymentRequired: (err) => {
if (err.topupUrl) redirect(err.topupUrl);
},
});Le hook s'exécute d'abord ; l'appel échoue quand même avec la PaymentRequiredError, si
bien que tout try/catch local continue de fonctionner.
L'argent est crédité sur le paiement vérifié, pas sur le retour
Le lien fait passer l'utilisateur final par un Checkout hébergé. Son solde est crédité
quand le prestataire de paiement confirme l'opération, jamais quand il revient sur votre
redirectUri : un getBalance() juste après le retour peut donc encore lire l'ancien
chiffre. Ne conditionnez pas votre interface à la seule redirection : interrogez
périodiquement, ou abonnez-vous à topup.completed pour être prévenu.
Éviter complètement le 402
Pour vérifier en amont, avant un premier appel de chat, qu'un utilisateur est connecté et
approvisionné, utilisez getConnection(), qui renvoie l'état
sans jamais lever de 402.