🎓 FormationAcceptation & monétiqueIntermédiaire⏱ 60 min
🧩
Intégrer un PSP de A à Z. 6 chapitres et un QCM final.
Le projet d'intégration complet, côté marchand. Choisir son prestataire de paiement, apprivoiser la sandbox et les clés API, créer un paiement, fiabiliser les webhooks avec l'idempotence, modéliser la machine à états, gérer capture et remboursement. Puis passer en production avec une checklist de recette digne d'une équipe monétique.
Comparer les PSP sur des critères objectifs (tarification, couverture, API, réversibilité) et éviter le lock-in
Manipuler une sandbox, sécuriser les clés API et distinguer clé publiable, clé secrète et secret de webhook
Créer un paiement de bout en bout : intention, montant en centimes, 3-D Secure, autorisation
Construire un consommateur de webhooks robuste : signature, réponse rapide, idempotence, réconciliation
Chapitre 1. Cadrer le projet et choisir son PSP.
Intégrer un prestataire de services de paiement (PSP) est un chantier qui engage la trésorerie, la conformité et l'expérience client de l'entreprise pour plusieurs années, bien au-delà du branchement d'une API. Le cadrage du besoin précède le code. Il porte sur les pays servis, les méthodes de paiement attendues, le volume prévu et le modèle de vente (vente directe, abonnement, marketplace). Ce cadrage conditionne le choix du prestataire bien plus que la beauté de sa documentation.
Ce que fait (vraiment) un PSP
Collecte les données de paiement dans un environnement conforme PCI DSS, pour que le marchand n'ait pas à le faire
Route les transactions vers un ou plusieurs acquéreurs et les réseaux (CB, Visa, Mastercard…)
Orchestre l'authentification 3-D Secure imposée par la DSP2 en Europe
Agrège les méthodes de paiement locales : wallets, virement, BNPL, iDEAL, Pix, Wero…
Restitue : dashboard, rapports de règlement, exports de réconciliation, gestion des litiges
Reverse les fonds (payouts) sur le compte du marchand, nets de commissions
Les critères qui comptent
🌍
Couverture
Pays d'encaissement, devises de règlement, méthodes locales. Un PSP excellent en France peut être moyen au Brésil ou en Asie.
💶
Tarification
Blended ou interchange++, frais de remboursement, de litige, de change. Exiger une simulation sur votre mix de transactions réel.
📈
Taux d'autorisation
1 point d'autorisation gagné rapporte souvent davantage que 0,1 % de commission en moins. Demander des chiffres par pays et par type de carte.
🛠️
Qualité API et docs
Sandbox complète, SDK maintenus, webhooks signés, journal des changements, versioning d'API. Vos équipes vivront avec pendant des années.
🏛️
Conformité et agrément
Statut d'établissement de paiement ou de monnaie électronique (en France, agrément ACPR), cantonnement des fonds, localisation des données.
🔓
Réversibilité
Portabilité des tokens de cartes, export des mandats et des abonnés, préavis contractuel. Le coût de sortie se négocie à l'entrée.
Modèle
Principe
Pour qui
Vigilance
Blended
Taux unique tout compris, ex. 1,4 % + 0,25 € par transaction européenne
TPE/PME, volumes modestes, besoin de lisibilité
Le PSP conserve la marge quand l'interchange baisse ; peu transparent
Volumes importants, équipes capables d'auditer les relevés
Relevés complexes ; comparer la ligne « ++ » entre offres
Forfait / plateforme
Abonnement + prix unitaire dégressif, souvent lié à une offre logicielle
Verticales (restauration, SaaS) où le paiement est embarqué
Bien isoler le coût du paiement du coût du logiciel
Les trois grands modèles de tarification PSP
0,2 % / 0,3 %
plafonds d'interchange UE (débit/crédit), la base du modèle interchange++
Règlement (UE) 2015/751
100+
méthodes de paiement proposées par les grands PSP internationaux
Documentation Stripe, 2026
2 à 6 semaines
durée typique du chantier d'intégration côté marchand, recette comprise
Retours d'expérience intégrateurs, 2025
Quelques PSP et réseaux que vous croiserez dans l'appel d'offresStripeAdyenPayPalVisaMastercardCBCB
🔑
Pensez réversibilité dès le premier jour
Exigez contractuellement la portabilité des tokens de cartes (migration vers un autre PSP ou vers les network tokens) et l'export des mandats SEPA. Sans elle, migrer impose de faire re-saisir sa carte à chaque abonné, et le marchand y perd typiquement 5 à 15 % de sa base au passage, ce que coûte exactement le lock-in.
🎯 Question éclair
En tarification interchange++, que paie précisément le marchand ?
Chapitre 2. Sandbox, clés API et sécurité des secrets.
Tout PSP sérieux fournit une sandbox, environnement de test isolé, sans argent réel, qui reproduit l'API de production et où votre équipe vivra les premières semaines. On commence par créer le compte de test et générer les clés API. Puis on provoque volontairement tous les scénarios (succès, refus, 3-D Secure, expiration) avant même de penser au parcours nominal.
Cartes de test : 4242 4242 4242 4242 (succès chez Stripe) et ses variantes qui simulent refus, fonds insuffisants, carte volée, échec 3DS
3-D Secure simulé : pages de challenge factices pour tester frictionless et challenge sans vraie banque
Webhooks de test : rejouables à la demande depuis le dashboard, indispensables pour le chapitre 4
Données factices : IBAN de test, montants magiques déclenchant des comportements précis (ex. un montant qui force un refus)
Dashboard sandbox : mêmes écrans qu'en production, pour former le support et la comptabilité avant le go-live
Premier appel : créer un paiement en sandbox (clé de test)
Initialiser le composant de paiement, tokeniser une carte, jamais lire ni débiter
Rarement nécessaire, faible sensibilité
Secrète (sk_…)
Serveur uniquement, injectée par un coffre-fort de secrets
Créer, capturer, rembourser : tous les pouvoirs sur le compte
Immédiate en cas de fuite ; planifiée sinon (ex. semestrielle)
Secret de webhook (whsec_…)
Serveur, un secret par endpoint
Vérifier la signature HMAC des notifications entrantes
À chaque changement d'endpoint ou suspicion de fuite
Les trois familles de clés et leur exposition autorisée
⚠️
La clé secrète ne quitte JAMAIS le serveur
Une clé sk_live_… qui traîne dans un dépôt Git, un bundle JavaScript ou un log applicatif donne à un attaquant le pouvoir de rembourser vos ventes vers d'autres cartes ou d'aspirer vos données clients. Stockez-la dans un gestionnaire de secrets (Vault, AWS Secrets Manager…) et préférez des clés restreintes par permission quand le PSP le propose. La CI, elle, embarque une détection de secrets.
Dernier point de méthode, la sandbox se traite comme un environnement à part entière, avec sa propre configuration versionnée. Les équipes qui réussissent leur go-live maintiennent une parité stricte entre test et production : mêmes versions d'API, mêmes webhooks abonnés, mêmes devises, à une seule différence près, le préfixe des clés (sk_test_ contre sk_live_).
🎯 Question éclair
Où une clé API secrète (sk_…) a-t-elle le droit d'apparaître ?
Chapitre 3. Créer un paiement : de l'intention à l'autorisation.
Les API modernes de paiement reposent sur le patron de l'intention de paiement (payment intent). Le serveur marchand annonce d'abord le montant qu'il veut encaisser, et le PSP renvoie un identifiant et un secret client. Le navigateur confirme ensuite le paiement avec les données de carte, qui ne transitent jamais par le serveur du marchand. Ce découpage en deux temps absorbe l'authentification 3-D Secure et les méthodes asynchrones.
Cycle de vie d'un paiement par intention
Serveur marchand
Crée l'intention de paiement
POST /payments, montant recalculé côté serveur, devise, clé d'idempotence
➜
PSP
Renvoie l'identifiant et le client_secret
Statut initial : created / requires_payment_method
➜
Navigateur client
Confirme avec les données de carte tokenisées
Le PAN va directement au PSP via le composant front
➜
Émetteur (banque du client)
Authentifie via 3-D Secure si requis
Frictionless ou challenge (SMS, app bancaire), exigence DSP2
➜
PSP
Demande l'autorisation au réseau
Réponse en 1 à 2 secondes : approved ou declined + code motif
➜
Serveur marchand
Reçoit le webhook de confirmation
payment.authorized / payment.captured, la source de vérité
⚠️
Les montants sont en centimes
La quasi-totalité des API de paiement attendent le montant dans la plus petite unité de la devise, si bien qu'on écrit 4990 pour 49,90 €. L'erreur du facteur 100, qui consiste à débiter 4 990 € au lieu de 49,90 €, est un classique des mises en production ratées. Ajoutez un test automatisé qui refuse tout montant aberrant. Attention aux devises sans décimales comme le yen : ¥500 s'écrit 500, pas 50000.
Clé d'idempotence à la création : envoyez un en-tête Idempotency-Key unique par commande ; en cas de timeout réseau, rejouer la requête avec la même clé ne créera pas de second paiement
Montant recalculé côté serveur : jamais de montant venu du navigateur, qu'un client outillé peut modifier
Devise en ISO 4217 (eur, usd, jpy) et cohérente avec le compte de règlement
`metadata.order_id` systématique : c'est le fil qui reliera paiement, commande et réconciliation comptable
`return_url` propre : la page de retour affiche « paiement en cours de confirmation », elle ne valide rien elle-même
🎯 Question éclair
Votre API PSP attend les montants en plus petite unité de devise. Que faut-il envoyer pour encaisser 49,90 € ?
Chapitre 4. Webhooks : l'asynchrone fiable et l'idempotence obligatoire.
Un paiement moderne est asynchrone par nature : 3-D Secure peut prendre deux minutes, un virement deux jours, un litige deux mois. Le PSP vous notifie chaque changement d'état par webhook, une requête HTTP POST vers votre serveur. Ce canal, qui porte toute l'intégration, voit aussi naître la plupart des incidents de production : webhooks perdus, traités deux fois, ou arrivés dans le désordre.
Réception d'un webhook, dans les règles de l'art
PSP
POST /webhooks/psp
Corps JSON + signature HMAC dans l'en-tête
➜
Serveur marchand
Vérifie la signature
HMAC-SHA256 sur le corps brut, secret whsec_…
➜
Serveur marchand
Déduplique par event.id
Insertion en base avec contrainte d'unicité
➜
Serveur marchand
Répond 200 immédiatement
En moins de 5 secondes, avant tout traitement lourd
➜
File de traitement
Traite l'événement
Mise à jour commande, e-mail, comptabilité, le tout en asynchrone
Les cinq règles d'or du consommateur de webhooks
Vérifier la signature sur le corps brut de la requête (avant tout parsing JSON) : sans cela, n'importe qui peut vous envoyer un faux payment.captured
Répondre 2xx vite (< 5 s) : les PSP considèrent tout autre code ou timeout comme un échec et re-livrent la notification
Traiter en file asynchrone : accuser réception d'abord, travailler ensuite, jamais d'envoi d'e-mail ou d'appel ERP dans le handler HTTP
Dédupliquer par identifiant d'événement : la livraison est garantie au moins une fois, donc les doublons sont normaux, pas exceptionnels
Tolérer le désordre et réconcilier : un payment.captured peut arriver avant le payment.authorized ; en cas de doute, interroger l'API (GET /payments/{id}) qui reste la source de vérité
Handler de webhook idempotent (Node.js, pseudo-code)
app.post("/webhooks/psp", async (req, res) => {
// 1. Signature sur le corps brut : rejeter sans traiter si invalide
const signature = req.headers["psp-signature"];
if (!verifyHmacSha256(req.rawBody, signature, WEBHOOK_SECRET)) {
return res.status(400).send("invalid signature");
}
const event = JSON.parse(req.rawBody);
// 2. Idempotence : event.id est UNIQUE en base.
// Si l'insertion echoue, l'evenement a deja ete recu.
const inserted = await db.webhookEvents.insertIfAbsent({
id: event.id,
type: event.type,
receivedAt: new Date(),
});
if (!inserted) {
return res.status(200).send("duplicate ignored"); // deja traite : 200 quand meme
}
// 3. Accuser reception vite, traiter en asynchrone
await queue.enqueue(event);
return res.status(200).send("ok");
});
🔑
Idempotence : la définition à retenir
Un traitement est idempotent quand le recevoir une fois ou dix fois produit exactement le même résultat : une seule commande expédiée, un seul e-mail envoyé, une seule écriture comptable. Côté sortant, la clé Idempotency-Key protège vos créations de paiement, tandis que côté entrant, la déduplication par event.id protège vos traitements de webhooks. Les deux sont non négociables.
3 jours
durée maximale pendant laquelle Stripe rejoue un webhook non acquitté (backoff exponentiel)
Documentation Stripe, 2026
< 5 s
délai de réponse au-delà duquel la plupart des PSP comptent la livraison en échec
Documentations Stripe / Adyen, 2026
≥ 1 fois
garantie de livraison des webhooks : au moins une fois, jamais exactement une fois
Modèle standard de l'industrie
⚠️
Le retour navigateur ne prouve rien
Ne déclenchez jamais la livraison ou l'activation d'un service sur la seule arrivée du client sur la return_url, puisque l'utilisateur peut fermer l'onglet avant, ou forger l'URL. La commande passe à « payée » uniquement sur réception du webhook signé (ou d'un GET /payments/{id} confirmant le statut). Le retour navigateur ne sert qu'à afficher un écran d'attente aimable.
🎯 Question éclair
Votre serveur reçoit pour la deuxième fois le même événement webhook (même event.id, déjà traité). Que doit-il faire ?
Chapitre 5. Machine à états, capture et remboursement.
Un paiement traverse une machine à états, alors qu'un simple booléen « payé / pas payé » n'en distinguerait que deux positions. La modéliser explicitement dans votre code, avec la liste des transitions autorisées, reste le meilleur antidote contre les bugs d'intégration. Impossible alors de rembourser un paiement jamais capturé, ou d'expédier une commande encore en attente de 3DS.
Machine à états d'un paiement (TypeScript)
type PaymentStatus =
| "created" // intention creee, en attente du client
| "requires_action" // 3-D Secure ou redirection en cours
| "authorized" // fonds reserves chez l'emetteur, rien n'est encaisse
| "captured" // capture demandee : l'encaissement est enclenche
| "settled" // fonds regles au marchand (J+1 a J+3)
| "failed" // refus emetteur, echec 3DS, expiration
| "canceled" // annulation (void) avant capture
| "refunded"; // rembourse, totalement ou partiellement
const transitions: Record<PaymentStatus, PaymentStatus[]> = {
created: ["requires_action", "authorized", "failed"],
requires_action: ["authorized", "failed", "canceled"],
authorized: ["captured", "canceled"],
captured: ["settled", "refunded"],
settled: ["refunded"],
failed: [],
canceled: [],
refunded: [],
};
function assertTransition(from: PaymentStatus, to: PaymentStatus): void {
if (!transitions[from].includes(to)) {
throw new Error("Transition interdite : " + from + " -> " + to);
}
}
Capturer : tout de suite ou plus tard ?
Capture immédiate
Capture différée
Principe
Autorisation et capture dans le même appel
Autorisation seule, capture déclenchée plus tard (expédition, check-out hôtel)
Cas d'usage
Biens numériques, services instantanés, e-commerce standard
Expédition différée, location, hôtellerie, montant final incertain
Contrainte
Remboursement obligatoire en cas d'annulation
L'autorisation expire : 10 jours chez Visa pour un paiement à distance initié par le client, 5 jours pour un paiement initié par le marchand (plus long pour certains secteurs)
Souplesse
Aucune : le montant capturé est le montant autorisé
Capture partielle possible (expédition en plusieurs colis) selon le PSP
Capture immédiate vs capture différée
Annuler n'est pas rembourser
Tant que le paiement n'est qu'autorisé, on peut l'annuler (void), opération gratuite et quasi instantanée qui lève la réservation de fonds chez l'émetteur sans que rien ait bougé. Une fois capturé, il faut rembourser (refund). Un flux financier inverse part vers la carte du client, met 3 à 10 jours à apparaître sur son relevé, et les commissions initiales ne sont généralement pas restituées par le PSP.
ℹ️
Void d'abord, refund ensuite
À l'annulation d'une commande, on vérifie d'abord l'état du paiement. authorized → void (gratuit, instantané) ; captured ou settled → refund (payant, différé). Un remboursement est lui-même asynchrone, passant par un état pending et pouvant échouer (carte expirée), d'où un webhook refund.failed à gérer aussi.
Remboursement partiel : plusieurs refunds successifs possibles, dans la limite du montant capturé, jamais au-delà
Litige après remboursement : un client peut ouvrir un chargeback malgré un refund déjà émis ; conservez les preuves du remboursement pour la représentation
Traçabilité : chaque refund a son propre identifiant, à raccrocher à l'order_id pour la réconciliation comptable
🎯 Question éclair
Une commande est annulée alors que le paiement est au statut « authorized » (non capturé). Quelle est la bonne opération ?
Chapitre 6. Go-live et checklist de recette.
Le passage en production d'une intégration de paiement se prépare comme un déménagement, où tout ce qui n'a pas été testé se cassera pendant le transport. Le go-live n'est pas un événement, c'est un processus. Il se déroule par bascule progressive, sous surveillance renforcée, avec une capacité de retour arrière à chaque étape.
J-30
Recette fonctionnelle complète en sandbox
Tous les scénarios de la checklist joués et documentés, y compris les cas d'échec.
J-15
Tests bout en bout et revue sécurité
Webhooks en conditions réelles (rejeu, désordre, doublons), revue des secrets, tests de charge sur l'endpoint de webhooks.
J-7
Configuration production
Clés live dans le coffre-fort, endpoints webhooks de production déclarés et vérifiés, questionnaire PCI (SAQ) signé.
J-0
Go-live progressif
5 à 10 % du trafic via feature flag, transactions réelles de faible montant vérifiées de bout en bout (jusqu'au relevé de règlement).
J+7
Généralisation et bilan
100 % du trafic, comparaison des taux d'autorisation avec les prévisions, première réconciliation comptable complète.
La checklist de recette
✅ Paiement nominal carte : autorisation, capture, webhook, e-mail de confirmation, écriture comptable
✅ 3-D Secure : parcours frictionless ET challenge, plus un abandon en plein challenge
✅ Refus émetteur : message client compréhensible, pas de commande créée, possibilité de réessayer
✅ Timeout réseau à la création : rejeu avec la même clé d'idempotence → un seul paiement
✅ Webhooks : signature invalide rejetée, doublon ignoré proprement, événements dans le désordre absorbés
✅ Remboursement total et partiel, y compris l'échec de remboursement
✅ Montants : centimes vérifiés, devise sans décimales (JPY) si concernée, montant serveur ≠ montant client refusé
✅ Machine à états : chaque transition interdite lève bien une erreur
✅ Monitoring : alertes sur chute du taux d'autorisation, sur échecs de webhooks, sur file de traitement qui s'allonge
✅ Réconciliation : le rapport de règlement du PSP se rapproche automatiquement des commandes
✅
Le go-live progressif, votre meilleure assurance
Basculer 5 % du trafic derrière un feature flag révèle en une heure ce qu'un big bang aurait transformé en crise, qu'il s'agisse d'un mauvais compte de règlement, d'un webhook de production non déclaré ou d'un montant multiplié par 100. Gardez l'ancien parcours actif et un bouton de retour arrière tant que la première réconciliation complète n'a pas été validée.
5-10 %
part de trafic recommandée pour la première vague de go-live
Pratique courante des équipes paiement, 2026
5 à 10 jours
validité typique d'une autorisation carte (Visa : 10 jours en ligne, 5 jours en magasin ou pour un paiement initié par le marchand) : à surveiller si votre capture est différée
Règles Visa, 2025
J+1 à J+3
délai usuel de règlement des fonds par le PSP après capture
Conditions standard PSP européens, 2026
🔑
La recette ne s'arrête pas au go-live
Une intégration de paiement se juge sur la durée, avec une réconciliation quotidienne automatisée, une revue mensuelle des taux d'autorisation et des motifs de refus, une veille sur les changements d'API du PSP. Les équipes qui traitent le paiement comme un produit vivant, et non comme un chantier terminé, ne découvrent pas leurs écarts de caisse six mois plus tard.
🎯 Question éclair
Quel événement doit déclencher l'expédition d'une commande payée par carte ?