Quand un client envoie un montant hors tolérance — trop peu ou trop — le paiement devient irrégulier. La part hors plage n’est ni acquise ni rendue : elle attend un arbitrage.
L’arbitrage se fait depuis le tableau de bord, pas par API. Encaisser ou rembourser engage une revue humaine : la décision n’est pas automatisable. Votre intégration détecte le cas et réconcilie le résultat ; la décision, elle, se prend dans Litiges.

Le piège à connaître d’abord

Un sous-paiement hors tolérance produit un intent au statut expired — alors que de l’argent a bien été reçu.Si votre intégration écoute seulement payment_intent.completed et traite expired comme un abandon, vous ignorerez de l’argent réellement encaissable. Le statut ne suffit pas : lisez irregularStatus.
paymentResult précise la nature : underpaid_accepted, underpaid_rejected, overpaid_accepted, overpaid_rejected. Les variantes _accepted sont dans la tolérance — elles se règlent seules, sans arbitrage.

1. Détecter — webhook

Abonnez-vous à dispute.created :
  • amountRejected — la part brute hors tolérance.
  • amountToRefund — ce qui serait effectivement remboursé, frais déduits.
Ajoutez aussi une garde sur payment_intent.expired : testez irregularStatus avant de conclure à un abandon.

2. Consulter — API

Les champs d’irrégularité sont exposés sur le payment intent. Lister ceux en écart :
Lire un cas précis :
La réponse porte irregularStatus, paymentResult, amountRejected et amountToRefund — de quoi afficher l’état dans votre back-office et suivre ce qui reste à arbitrer. Scope requis : payments:read.

3. Arbitrer — tableau de bord

Encaisser ou rembourser se fait dans Litiges. Pour un remboursement, l’adresse du client est reprise de celle qu’il a laissée sur la page de paiement ; elle peut être corrigée au moment de la décision.

4. Réconcilier — webhook

Le montant crédité est data.object.amountEncashed, net de frais — pas amountRejected, qui est le brut.
Après un encaissement, l’intent reste expired et aucun payment_intent.completed n’est émis. C’est volontaire : le paiement n’a jamais atteint le montant demandé. Votre comptabilité doit se fonder sur dispute.encashed, sinon cet argent restera invisible dans votre système alors qu’il est bien sur votre solde.
Pour un remboursement, payoutId permet de suivre le transfert on-chain via payout.confirmed ou GET /v1/payouts/{id}.

Récapitulatif

  1. Détecterdispute.created, et garde sur irregularStatus pour tout payment_intent.expired.
  2. ConsulterGET /v1/payment-intents?status=irregular.
  3. Arbitrer — dans le tableau de bord.
  4. Réconcilier — sur dispute.encashed / dispute.refunded, jamais sur le statut de l’intent.

Voir aussi

Litiges au tableau de bord

Où se prend la décision.

Événements webhook

Le format exact de dispute.*.