L’intégration des paiements
Pourquoi ajouter plusieurs moyens de paiement ?
Quand un utilisateur arrive à l’étape du checkout et ne trouve pas son moyen de paiement habituel, il repart. C’est aussi simple que ça. Pas de message d’erreur, pas de tentative avec une autre carte : il ferme l’app. C’est le scénario que personne ne veut voir dans ses métriques, et pourtant il se produit bien plus souvent qu’on ne le croit, uniquement parce que l’application ne proposait qu’une seule option.
Les habitudes de paiement varient énormément d’un pays à l’autre, et même d’un utilisateur à l’autre dans la même région. Certains ne jurent que par PayPal parce qu’ils n’ont pas envie de saisir leurs coordonnées bancaires sur un site qu’ils ne connaissent pas. D’autres utilisent Apple Pay pour la rapidité, sans avoir à déverrouiller leur portefeuille. D’autres encore préfèrent payer directement par carte. Multiplier les options, c’est simplement réduire les frictions au moment où l’intention d’achat est la plus forte.
Cas d’usage mobile les plus courants
Il n’y a pas un seul type d’application de paiement mobile. Une boutique e-commerce qui traite des commandes uniques n’a pas les mêmes contraintes qu’une application SaaS avec des abonnements mensuels, ni qu’une marketplace où l’argent transite entre plusieurs parties. Ces différences ont un impact direct sur l’architecture à choisir.
Pour un achat ponctuel, le flux est relativement linéaire : l’utilisateur choisit un produit, paie, reçoit une confirmation. Pour un abonnement, il faut gérer les renouvellements automatiques, les échecs de paiement et les relances. Pour une marketplace, s’ajoute la question des reversements vers les vendeurs, que Stripe Connect ou PayPal Payouts gèrent différemment. Il est utile d’identifier clairement son cas d’usage avant de commencer, parce que ça conditionne directement quels endpoints API utiliser et quelle logique implémenter côté serveur.
Choisir la bonne architecture
Paiement natif ou redirection vers une page externe
La première décision à prendre avant d’écrire la moindre ligne de code, c’est de savoir si le paiement se fait entièrement dans l’application ou si l’utilisateur est redirigé vers une page web externe pour finaliser sa transaction.
La redirection, c’est l’option la plus simple à mettre en place. On ouvre un navigateur intégré ou externe, l’utilisateur paie sur une page hébergée par Stripe ou PayPal, puis est renvoyé vers l’application via un deep link ou un scheme d’URL. Côté développement, c’est peu de travail. Côté expérience, c’est une sortie de l’application que beaucoup d’utilisateurs perçoivent comme une rupture, et parfois comme un signal de méfiance.
Le paiement natif, lui, garde l’utilisateur dans l’application. Le formulaire de paiement est rendu directement dans l’interface via un SDK. C’est plus engageant, mais ça demande plus de travail d’intégration et une attention plus rigoureuse à la sécurité, puisqu’on traite des données sensibles directement dans l’app.
Rôle du backend dans le flux de paiement
Quel que soit le choix fait pour l’interface, une règle ne souffre aucune exception : la logique de paiement critique ne vit pas dans le client mobile. Le mobile fait des requêtes à votre propre serveur, le serveur parle aux APIs de Stripe ou PayPal, et c’est tout.
Le raisonnement est simple. Le code d’une application mobile peut être décompilé, analysé, modifié. Si les clés secrètes ou la logique de création des paiements se trouvent dans le client, elles sont exposées. Le backend est le seul endroit où l’on peut stocker des clés secrètes, valider les montants, vérifier l’identité de l’utilisateur et confirmer que le paiement a réellement abouti avant de mettre à jour une commande.
En pratique, le flux ressemble à ceci : l’application envoie une requête au backend avec l’identifiant de la commande, le backend demande à Stripe de créer un Payment Intent pour le bon montant, renvoie le client secret à l’application, l’application l’utilise pour afficher le formulaire de paiement et finalise la transaction côté SDK. La confirmation du statut final, elle, passe par un webhook côté serveur.
Gestion des utilisateurs, commandes et statuts de paiement
Un paiement ne vit jamais seul dans une base de données. Il est lié à un utilisateur, à une ou plusieurs commandes, à un statut qui évolue dans le temps. Penser ce modèle de données en amont évite beaucoup de problèmes.
Une commande devrait avoir au minimum un identifiant interne, un identifiant de paiement externe (celui renvoyé par Stripe ou PayPal), un statut parmi une liste fermée de valeurs, et les montants en centimes plutôt qu’en décimales pour éviter les erreurs d’arrondi. Les statuts doivent couvrir tous les cas : en attente, confirmé, échoué, remboursé, en litige. Chaque transition de statut devrait être loguée avec un horodatage, parce que les réclamations clients ou les audits arrivent toujours au mauvais moment.
Préparer l’application
Créer les comptes marchands nécessaires
Avant d’écrire quoi que ce soit, il faut créer les comptes sur les plateformes que l’on souhaite intégrer. Pour Stripe, l’inscription est rapide et l’accès au mode test est immédiat. Pour PayPal, il faut un compte Business et accéder séparément au portail développeur sur developer.paypal.com pour créer une application sandbox. Pour Apple Pay, il faut un compte Apple Developer actif, qui est payant et nécessite quelques jours de validation si c’est un nouveau compte.
Créer ces comptes en amont est important parce que certaines étapes, comme la vérification d’identité chez Stripe pour le passage en production ou la déclaration de domaines pour Apple Pay, prennent du temps et ne peuvent pas être réalisées à la dernière minute.
Configurer les clés API et environnements de test
Chaque solution de paiement fournit deux jeux de clés : un pour l’environnement de test (sandbox) et un pour la production. Ces clés ne doivent jamais se retrouver dans le code source versionné. On les stocke dans des variables d’environnement côté serveur, et on s’assure que le client mobile ne reçoit que la clé publique, jamais la clé secrète.
La clé publique de Stripe (elle commence par pk_test_ en sandbox) peut être intégrée dans l’application mobile sans risque, parce qu’elle sert uniquement à tokeniser les informations de carte dans le SDK. La clé secrète (sk_test_), elle, ne sort pas du serveur. Mettre en place ce cloisonnement correctement dès le début évite d’avoir à restructurer toute l’architecture plus tard.
Sécuriser les échanges entre mobile et serveur
Toutes les communications entre l’application mobile et le backend doivent transiter en HTTPS, sans exception. Mais HTTPS seul ne suffit pas. Il faut aussi authentifier les requêtes pour s’assurer qu’elles viennent bien d’un utilisateur légitime de l’application et pas de quelqu’un qui aurait intercepté les endpoints.
Concrètement, cela signifie que chaque requête vers le backend doit porter un token d’authentification (JWT ou token de session), que le backend valide avant de créer quoi que ce soit chez Stripe ou PayPal. Sans ça, n’importe qui qui connaît l’URL de votre endpoint peut déclencher des créations de payment intents, ce qui n’est pas idéal. On peut aussi envisager une couche de certificate pinning dans l’application mobile pour les contextes particulièrement sensibles, même si cette approche a ses propres inconvénients en termes de maintenance.
Intégrer Stripe dans une application mobile
Quand utiliser Stripe
Stripe est le choix par défaut pour la plupart des applications qui visent les marchés occidentaux, et il y a des raisons concrètes à ça. La documentation est exhaustive, les SDKs mobile (stripe-react-native, stripe-ios, stripe-android) sont bien maintenus, et les éléments d’interface fournis par Stripe ont une qualité d’intégration qu’il serait difficile de reproduire soi-même. Stripe gère nativement les cartes, les virements, Apple Pay, Google Pay, et une longue liste de moyens de paiement locaux.
Là où Stripe est moins adapté, c’est pour les utilisateurs qui n’ont pas de carte bancaire et préfèrent payer depuis un portefeuille en ligne déjà approvisionné comme PayPal. Les deux solutions sont complémentaires bien plus que concurrentes.
Création d’un Payment Intent
Le Payment Intent est l’objet central du flux de paiement Stripe. Il représente une intention de payer un montant donné dans une devise donnée, et son statut évolue tout au long du processus. On le crée côté serveur avec la clé secrète Stripe, en spécifiant le montant en centimes, la devise, et éventuellement des métadonnées comme l’identifiant de commande interne.
La réponse de l’API Stripe contient un champ client_secret. C’est lui que le serveur renvoie à l’application mobile. Ce client secret ne doit être utilisé que pour ce paiement précis et par cet utilisateur. C’est le mécanisme qui permet au SDK mobile de compléter la transaction sans avoir besoin de la clé secrète complète. Si quelqu’un intercepte le client secret, il peut tenter de confirmer ce paiement précis, mais il ne peut rien faire d’autre avec votre compte Stripe.
Affichage du formulaire de paiement
Le SDK Stripe fournit des composants d’interface prêts à l’emploi : PaymentSheet, CardField, ou des éléments plus granulaires selon le niveau de personnalisation souhaité. PaymentSheet est le plus simple à intégrer : il ouvre une feuille modale qui gère la saisie de carte, Apple Pay et Google Pay en une seule interface, et s’adapte automatiquement à la locale de l’utilisateur.
Pour l’utiliser, on initialise le SDK avec la clé publique, on configure la PaymentSheet avec le client secret récupéré du serveur, et on l’affiche au moment du checkout. La PaymentSheet gère elle-même la validation des numéros de carte, les animations de chargement et les messages d’erreur. C’est un gain de temps considérable par rapport à une implémentation de zéro.
Confirmation et gestion des erreurs
Quand l’utilisateur valide le paiement dans la PaymentSheet, le SDK renvoie un résultat avec un statut : completed, canceled ou failed. Le statut completed ne signifie pas que l’argent a été capturé, seulement que la confirmation côté client a réussi. La vérification définitive doit toujours venir du webhook côté serveur.
Pour les erreurs, Stripe renvoie des codes d’erreur structurés. Les erreurs de carte (card_declined, insufficient_funds, expired_card) doivent afficher un message compréhensible à l’utilisateur, pas le code brut de l’API. Les erreurs réseau doivent proposer de réessayer. Les erreurs d’authentification forte (requires_action) déclenchent un flux 3D Secure que le SDK gère automatiquement si on a correctement configuré le return_url.
Gestion des webhooks Stripe
Les webhooks sont le mécanisme par lequel Stripe informe votre serveur des événements qui se produisent sur votre compte, y compris les confirmations de paiement, les remboursements et les litiges. Ignorer les webhooks est l’une des erreurs les plus courantes dans les intégrations Stripe, et elle conduit à des situations où une commande reste en statut « en attente » alors que le paiement a été confirmé, ou inversement.
Pour recevoir les webhooks, on crée un endpoint sur le serveur, on l’enregistre dans le dashboard Stripe, et Stripe envoie des requêtes POST signées avec un secret de webhook. Il faut vérifier cette signature pour s’assurer que la requête vient bien de Stripe et pas d’un tiers. Les événements les plus importants à traiter sont payment_intent.succeeded, payment_intent.payment_failed, et charge.dispute.created.
Intégrer PayPal dans une application mobile
Quand utiliser PayPal
PayPal reste pertinent pour plusieurs raisons. Une part importante des acheteurs en ligne, particulièrement dans les tranches d’âge au-dessus de 40 ans ou dans les pays où la confiance envers les paiements par carte directe est plus faible, préfère PayPal parce qu’ils n’ont pas à saisir leurs coordonnées bancaires sur chaque site. L’argent vient de leur solde PayPal ou de leur compte bancaire déjà lié, et ils se sentent protégés par le programme de protection des achats.
Pour les applications qui vendent des biens physiques ou des services à forte valeur, proposer PayPal peut réduire significativement les abandons de panier chez ces profils d’utilisateurs. Pour une application qui vise principalement des utilisateurs jeunes très à l’aise avec les paiements par carte, l’impact sera moindre.
Création du compte développeur
L’intégration PayPal commence sur developer.paypal.com. On y crée une application sandbox, qui génère un Client ID et un Secret pour les tests. L’environnement sandbox inclut des comptes PayPal fictifs (acheteur et vendeur) que l’on peut utiliser pour simuler des transactions sans argent réel.
Le SDK mobile de PayPal s’appelle PayPal Mobile Checkout SDK et est disponible pour iOS et Android. Pour React Native, il existe des packages communautaires, mais l’approche la plus fiable reste d’utiliser l’approche WebView avec le JavaScript SDK de PayPal ou de passer par le SDK natif via un module bridge. Il faut évaluer l’option qui correspond le mieux à sa stack technique et à ses contraintes de maintenance.
Connexion du bouton PayPal dans l’app
PayPal fournit un bouton avec un style particulier qui ne peut pas être modifié librement, et c’est voulu : PayPal exige que le bouton soit reconnaissable pour que l’utilisateur sache exactement ce qu’il déclenche. Le SDK gère l’affichage de ce bouton et l’ouverture du flux de connexion PayPal.
Quand l’utilisateur appuie sur le bouton, une vue s’ouvre pour lui permettre de se connecter à son compte PayPal et d’approuver le paiement. Cette vue peut être une WebView intégrée ou une ouverture de l’application PayPal native si elle est installée. À l’issue de cette étape, le SDK renvoie un identifiant de commande PayPal (order ID) à l’application.
Validation du paiement côté serveur
Recevoir un order ID côté client ne signifie pas que le paiement est validé. L’application mobile envoie cet identifiant au backend, qui appelle l’API PayPal avec la clé secrète pour capturer le paiement (endpoint /v2/checkout/orders/{order_id}/capture). C’est cette capture qui déclenche le débit réel et renvoie la confirmation définitive.
Cette étape côté serveur est indispensable. Si on se contente de faire confiance à l’identifiant renvoyé par le client sans le valider côté serveur, n’importe qui peut envoyer un faux identifiant de commande et prétendre avoir payé. La validation serveur protège contre cette attaque basique.
Traitement des retours utilisateur
L’expérience PayPal implique une sortie temporaire du flux principal de l’application (vers la vue de connexion PayPal), puis un retour. Il faut gérer proprement les différents cas de retour : l’utilisateur a validé le paiement, l’utilisateur a annulé, ou l’utilisateur a fermé la vue sans rien faire.
Le SDK PayPal fournit des callbacks pour chacun de ces cas. Pour l’annulation, on remet l’interface dans son état initial sans montrer de message d’erreur alarmiste, puisque c’est l’utilisateur qui a choisi d’annuler. Pour un retour sans action, on traite ça comme une annulation. Pour une validation, on procède à la capture côté serveur comme décrit précédemment, puis on affiche la confirmation à l’utilisateur.
Intégrer Apple Pay dans une application mobile
Conditions d’éligibilité
Apple Pay est réservé aux applications iOS et ne peut être utilisé que sur des appareils Apple équipés d’un Secure Element (iPhone, iPad avec Touch ID ou Face ID, Apple Watch). Il faut aussi que l’utilisateur ait une carte ajoutée à Wallet sur son appareil. Si ces conditions ne sont pas réunies, le bouton Apple Pay ne doit pas apparaître dans l’interface, et une alternative doit être proposée.
Côté développeur, il faut un abonnement Apple Developer Program actif. Apple Pay n’est pas disponible dans les environnements de développement génériques : il faut un vrai appareil physique et un certificat de paiement valide pour tester dans des conditions proches de la production. Le simulateur iOS ne peut pas traiter de vrais paiements Apple Pay.
Activation dans l’Apple Developer Account
La configuration d’Apple Pay passe par plusieurs étapes dans le portail Apple Developer. Il faut d’abord créer un Merchant ID, qui identifie votre application comme marchand Apple Pay. Ce Merchant ID doit ensuite être associé à votre App ID dans la section Identifiers. Il faut également créer un certificat de traitement des paiements, que vous fournirez à votre processeur de paiement (Stripe, par exemple) pour qu’il puisse décrypter les tokens Apple Pay.
Dans Xcode, on active la capability Apple Pay dans les paramètres du projet et on associe le Merchant ID. Sans cette configuration, le code d’intégration Apple Pay ne compilera pas correctement et les appels au framework PassKit échoueront.
Affichage du bouton Apple Pay
Le bouton Apple Pay ne peut pas être un bouton ordinaire stylé en noir avec une pomme. Apple impose d’utiliser PKPaymentButton du framework PassKit, qui garantit que le bouton respecte les Human Interface Guidelines d’Apple et est immédiatement reconnaissable par les utilisateurs. Remplacer ce bouton par un élément personnalisé viole les guidelines et peut entraîner un rejet lors de la revue App Store.
Avant d’afficher le bouton, on vérifie que PKPaymentAuthorizationController.canMakePayments(usingNetworks:) retourne true, ce qui confirme que l’appareil supporte Apple Pay et que l’utilisateur a une carte compatible configurée. Si le résultat est false, on affiche un autre moyen de paiement à la place.
Validation du paiement via le processeur choisi
Apple Pay ne traite pas les paiements directement. Il génère un token de paiement chiffré (PKPaymentToken) que votre processeur de paiement (Stripe, Adyen, Braintree, etc.) déchiffre et utilise pour débiter la carte sous-jacente. C’est pourquoi la configuration du certificat de traitement est nécessaire : Apple chiffre le token avec la clé publique du processeur, et seul le processeur peut le déchiffrer avec sa clé privée.
Avec Stripe, l’intégration est particulièrement simple parce que le SDK Stripe pour iOS gère nativement la conversion du PKPaymentToken en PaymentMethod Stripe. On crée un STPPaymentHandler, on initie la demande de paiement Apple Pay, et le SDK se charge du reste. Le flux se termine par la même confirmation de Payment Intent que pour un paiement par carte classique.
Gestion des appareils compatibles
Afficher le bouton Apple Pay sur un appareil Android ou sur un iPhone dont l’utilisateur n’a pas configuré de carte dans Wallet serait une erreur d’expérience utilisateur. La règle est simple : on n’affiche le bouton Apple Pay que si le paiement est réellement possible sur l’appareil en question.
Il faut aussi prévoir le cas des utilisateurs qui ont un appareil compatible mais qui n’ont pas encore configuré Apple Pay. Dans ce cas, PKPaymentAuthorizationController.canMakePaymentsUsingNetworks retourne false, mais PKPaymentAuthorizationController.canMakePayments retourne true. On peut alors afficher le bouton « Configurer Apple Pay » qui redirige vers Wallet, ou simplement masquer le bouton et proposer les alternatives classiques.
Gérer plusieurs moyens de paiement
Ajouter un sélecteur de paiement dans le checkout
Proposer plusieurs moyens de paiement implique de concevoir une interface de sélection qui soit claire sans être encombrée. L’approche la plus commune est une liste de boutons ou de tuiles avec le logo de chaque option, un bouton de confirmation commun, et une zone de formulaire qui change selon le choix de l’utilisateur.
Il faut éviter de montrer toutes les options en même temps avec leurs formulaires respectifs dépliés : ça crée une interface longue et confuse. L’utilisateur sélectionne une option, le formulaire correspondant apparaît, et il valide. Simple à décrire, un peu moins à implémenter proprement, mais l’effort vaut le résultat en termes de taux de conversion.
Prioriser le moyen le plus adapté selon l’utilisateur
L’ordre dans lequel on présente les options compte. Afficher Apple Pay en premier sur iOS, Google Pay en premier sur Android, et replier les autres options derrière un « Autres moyens de paiement » est une pratique qui améliore la vitesse de checkout pour la majorité des utilisateurs. Ces méthodes sont les plus rapides à utiliser sur leur plateforme respective, et les placer en tête réduit les étapes pour les utilisateurs qui peuvent s’en servir.
On peut aussi mémoriser le dernier moyen de paiement utilisé par un utilisateur et le présélectionner lors de la prochaine visite. C’est une amélioration relativement simple à implémenter côté backend, et elle a un impact réel sur l’expérience des utilisateurs récurrents.
Uniformiser les statuts de paiement en base de données
Stripe, PayPal et Apple Pay ont chacun leur propre terminologie pour les statuts de paiement. Stripe parle de payment_intent.succeeded, PayPal de COMPLETED, et Apple Pay délègue le statut à son processeur. Si on stocke ces statuts bruts sans normalisation, on se retrouve avec une base de données hétérogène difficile à interroger et à maintenir.
La solution est de définir un vocabulaire interne à l’application (pending, confirmed, failed, refunded, disputed) et de faire la traduction entre les statuts de chaque provider et ce vocabulaire interne au niveau de la couche de service du backend. Toute la logique métier travaille alors avec ce vocabulaire normalisé, quel que soit le provider utilisé pour le paiement.
Sécurité et conformité
Éviter de stocker les données bancaires
Stocker des numéros de carte, des CVV ou des dates d’expiration directement dans sa base de données est à la fois risqué et inutile. C’est risqué parce qu’une fuite de données devient immédiatement un problème majeur pour les utilisateurs concernés. C’est inutile parce que Stripe et PayPal proposent des mécanismes pour sauvegarder les moyens de paiement côté eux, de façon sécurisée, et de les réutiliser via des identifiants opaques.
Côté Stripe, un Customer object permet d’associer des PaymentMethods à un utilisateur. On stocke l’identifiant Stripe du client dans sa propre base, et on appelle l’API Stripe pour lister ou utiliser ses cartes sauvegardées. Aucune donnée bancaire ne transit par son propre serveur au-delà du token éphémère qui sert à la première tokenisation dans le SDK mobile.
Utiliser des jetons et identifiants sécurisés
Toutes les communications entre le client mobile et le serveur autour des paiements doivent utiliser des identifiants opaques et à durée de vie limitée. Le client secret d’un Payment Intent Stripe expire une fois utilisé. Le token Apple Pay est à usage unique. L’order ID PayPal ne peut être capturé qu’une seule fois.
Ces identifiants ne doivent jamais être exposés dans des logs applicatifs, des rapports d’erreur ou des réponses d’API non chiffrées. Il est courant de voir des client secrets Stripe apparaître dans des logs Sentry ou Datadog parce que quelqu’un a loggué la réponse entière de l’API sans filtrage. C’est une faute de sécurité qui peut être difficile à détecter et coûteuse à corriger.
Gérer les paiements 3D Secure et les validations fortes
En Europe, la directive PSD2 impose l’authentification forte du client (SCA) pour la plupart des paiements en ligne. Concrètement, cela signifie que beaucoup de transactions déclencheront un challenge 3D Secure : l’utilisateur doit valider le paiement via son application bancaire ou un code SMS avant que le paiement soit confirmé.
Stripe gère le 3DS automatiquement quand on utilise la PaymentSheet : si la banque de l’utilisateur exige une authentification forte, Stripe affiche le flow d’authentification et attend la validation avant de confirmer le Payment Intent. Pour les intégrations plus personnalisées, il faut configurer un return_url que Stripe peut utiliser pour renvoyer l’utilisateur dans l’application après l’authentification dans un navigateur externe. Ne pas gérer ce cas conduit à des paiements bloqués en statut requires_action avec des utilisateurs qui ne comprennent pas pourquoi leur paiement n’aboutit pas.
Respect des bonnes pratiques de conformité
PCI DSS est le référentiel de sécurité pour toute entité qui traite des paiements par carte. Quand on utilise Stripe ou PayPal via leurs SDK officiels sans stocker de données bancaires, on se place dans une zone de conformité simplifiée (SAQ A), ce qui est l’objectif. Si on commence à transférer des données de carte par ses propres serveurs, les exigences augmentent considérablement.
Il est aussi important de documenter son architecture de paiement clairement pour pouvoir répondre à des questions de conformité, que ce soit de la part d’un auditeur, d’un partenaire commercial ou d’un store de distribution. Savoir précisément où les données transitent, où elles sont stockées et qui y a accès n’est pas seulement une bonne pratique : c’est une information que l’on peut avoir à fournir à tout moment.
Tests et mise en production
Tester en mode sandbox
Chaque provider fournit un environnement de test avec des numéros de carte fictifs qui simulent différents comportements. Stripe propose des cartes de test pour les paiements réussis, les refus, les authentifications 3DS et les erreurs spécifiques. PayPal fournit des comptes sandbox avec des soldes fictifs. Ces environnements de test permettent de vérifier tous les cas sans engager d’argent réel.
Une discipline utile est d’écrire les tests d’intégration en ciblant les endpoints sandbox et de les faire tourner en CI. Ça permet de détecter les régressions dans le flux de paiement avant qu’elles n’atteignent la production. Un paiement cassé en production, c’est du chiffre d’affaires perdu et un utilisateur frustré qui ne reviendra peut-être pas.
Vérifier les scénarios d’échec et d’annulation
Il est tentant de tester uniquement le chemin heureux, celui où tout fonctionne. C’est une erreur. Les scénarios qui doivent absolument être testés avant une mise en production incluent : le refus de carte (fonds insuffisants, carte expirée, carte bloquée), l’annulation par l’utilisateur en cours de flux, la perte de connexion réseau pendant la confirmation, le webhook reçu en retard ou en doublon, et le cas où l’utilisateur ferme l’application pendant le traitement du paiement.
Ce dernier cas est particulièrement piégeux. Si l’utilisateur ferme l’application au moment où le Payment Intent est en cours de confirmation, l’application ne reçoit pas le résultat du SDK. La prochaine fois qu’il ouvre l’application, il faut être capable de récupérer l’état réel de sa commande en interrogeant le backend, qui lui-même a reçu (ou recevra via webhook) la confirmation du statut final.
Passer en production sans casser le tunnel de paiement
Le passage de sandbox à production implique de remplacer les clés de test par les clés de production, d’activer le compte marchand sur chaque plateforme (ce qui peut nécessiter une vérification d’identité ou une revue de compte), et de vérifier que les webhooks pointent vers les bons endpoints de production.
Un point souvent négligé : les numéros de version des API. Stripe et PayPal gèrent des versions d’API, et il faut s’assurer que la version utilisée en production est la même qu’en sandbox. Une différence de version peut introduire des comportements inattendus dans les réponses. Il vaut mieux épingler la version d’API explicitement plutôt que d’utiliser la version « latest », ce qui peut changer sans préavis.
Erreurs fréquentes à éviter
Confondre logique front et logique serveur
C’est probablement l’erreur la plus répandue dans les premières intégrations de paiement. On voit des implémentations où le montant à débiter est calculé côté client et envoyé au serveur pour créer le Payment Intent, ce qui permet à quelqu’un d’intercepter la requête et de modifier le montant. Le serveur doit toujours recalculer le montant lui-même à partir des données de la commande en base de données, sans faire confiance au montant envoyé par le client.
De la même façon, décider qu’une commande est confirmée uniquement parce que le SDK mobile a renvoyé un statut « completed » sans attendre la confirmation du webhook est une erreur. Le SDK peut mentir, ou plus prosaïquement, la connexion peut être coupée avant que le statut final soit stable. Le webhook, lui, est déclenché directement par le provider et peut être considéré comme la source de vérité.
Oublier la gestion des webhooks
Intégrer le flow de paiement côté client sans mettre en place les webhooks côté serveur, c’est construire une maison sans fondations. Tout fonctionne dans les cas simples, jusqu’au jour où un paiement est confirmé quelques secondes après que l’utilisateur a fermé l’application, ou jusqu’à ce qu’un remboursement soit initié depuis le dashboard Stripe et que la commande ne soit jamais mise à jour dans la base de données.
Les webhooks doivent être idempotents : si le même événement est reçu deux fois (ce qui peut arriver), traiter le doublon ne doit pas produire d’effets indésirables. On stocke l’identifiant de l’événement reçu et on vérifie qu’il n’a pas déjà été traité avant de l’exécuter.
Ne pas prévoir les cas de paiement refusé
Un paiement refusé n’est pas une exception rare : c’est un cas normal que l’interface doit gérer élégamment. L’utilisateur doit comprendre pourquoi son paiement a échoué (carte refusée, fonds insuffisants, carte expirée) et savoir quoi faire ensuite (essayer une autre carte, contacter sa banque). Un message d’erreur générique du type « une erreur s’est produite » est insuffisant et frustrant.
Il faut aussi gérer le cas où le paiement est refusé après l’écran de confirmation de l’application : l’utilisateur pense avoir payé, mais le paiement n’a pas abouti. Sans notification claire et sans mécanisme de reprise, la commande reste en attente côté marchand et l’utilisateur n’a aucun moyen de savoir ce qui s’est passé.
Multiplier les intégrations sans stratégie claire
Vouloir intégrer Stripe, PayPal, Apple Pay, Google Pay, Klarna, Lydia et dix autres solutions en même temps parce que « certains utilisateurs pourraient les préférer » est une source de complexité disproportionnée. Chaque intégration ajoute du code à maintenir, des cas de test à couvrir, des webhooks à gérer et des APIs susceptibles d’évoluer.
Une approche plus raisonnée consiste à analyser où se trouvent les utilisateurs cibles, quels moyens de paiement sont dominants dans ces marchés, et à commencer par les deux ou trois options qui couvrent 80% des besoins. On ajoute les autres selon les retours des utilisateurs et les données d’abandon de panier, pas par anticipation de cas hypothétiques.
Choisir la solution adaptée à son application
Il n’y a pas de réponse universelle à la question du meilleur système de paiement. Stripe est le choix le plus polyvalent pour une application qui cible des marchés où les cartes bancaires dominent et où on veut une expérience native de qualité. PayPal est un complément utile pour les utilisateurs qui préfèrent un portefeuille en ligne. Apple Pay est incontournable sur iOS pour les applications où la vitesse de checkout a un impact direct sur la conversion.
Le critère le plus important n’est pas la liste de features de chaque provider, mais la qualité de l’expérience que l’on peut construire que ce soit en interne ou en faisant appel à une agence spéalisée en application mobile. Une intégration Stripe bien faite vaut mieux que cinq intégrations bancales.
Simplifier l’expérience utilisateur
Chaque étape supplémentaire dans un tunnel de paiement est une opportunité pour l’utilisateur de se demander s’il veut vraiment continuer. Simplifier ne veut pas dire supprimer des options : ça veut dire rendre chaque option accessible en un minimum de gestes, afficher des messages d’erreur qui aident plutôt que de décourager, et ne jamais laisser l’utilisateur dans l’incertitude sur le statut de sa transaction.
Un checkout réussi, c’est un utilisateur qui n’a pas eu à réfléchir. Il a choisi, il a confirmé, il a reçu sa confirmation. Tout ce qui rend ce chemin plus court ou plus clair est un investissement qui se traduit directement en revenus.
Prévoir l’évolutivité du système de paiement
Les APIs de paiement changent. Les réglementations évoluent. De nouveaux moyens de paiement apparaissent. Une architecture de paiement bien pensée anticipe ces changements en isolant la logique spécifique à chaque provider derrière une couche d’abstraction. Si demain on veut remplacer PayPal par une autre solution ou ajouter un nouveau provider, le reste du système ne devrait pas avoir à changer.
Ça veut aussi dire documenter les choix faits, les webhooks configurés, les identifiants marchands stockés et les procédures pour renouveler les certificats (particulièrement pour Apple Pay, dont les certificats expirent). Ce type de documentation semble superflu quand on est en train de construire. Il devient indispensable quand quelqu’un d’autre doit reprendre le projet, ou quand un certificat expire le vendredi soir.