Intégrer des paiements dans une application web est l'une de ces tâches qui semble simple jusqu'à ce qu'on s'y plonge vraiment. Un bouton de paiement sur une landing page, c'est une chose. Un système de facturation dans un SaaS multi-tenant avec deux passerelles de paiement différentes - une pour les États-Unis, une pour l'Amérique latine - c'est une tout autre affaire.
Chez Nebula, nous l'avons construit dans des projets réels. Voici le modèle que nous utilisons et les problèmes que nous avons appris à éviter.
Le scénario : un SaaS qui vend aux États-Unis et en Amérique latine
Le cas le plus courant que nous rencontrons : un SaaS avec des clients aux États-Unis (qui paient par carte via Stripe) et des clients en Argentine, au Mexique ou au Brésil (qui paient avec Mercado Pago). Le système doit gérer les deux flux sans que l'utilisateur ne sente la différence.
La vraie complexité apparaît quand on ajoute le multi-tenancy : chaque client entreprise du SaaS peut avoir ses propres abonnements, ses propres cycles de facturation et, potentiellement, ses propres configurations de paiement.
L'erreur la plus courante : logique de paiement mélangée avec logique métier
La première erreur que nous voyons dans les systèmes qui ont grandi sans planification : la logique de paiement est mélangée avec la logique métier. Un contrôleur qui appelle directement l'API Stripe, sans aucune couche intermédiaire.
Ça fonctionne quand vous n'avez qu'une seule passerelle. Quand vous voulez en ajouter une deuxième, vous devez réécrire ou dupliquer du code à plusieurs endroits - et espérer ne pas en oublier.
La solution : une couche d'abstraction des paiements
La solution est de créer votre propre couche d'abstraction : une interface de paiement système qui se connecte ensuite à Stripe ou Mercado Pago selon le cas. Le reste du système ne connaît que cette interface, pas la passerelle spécifique.
Dans Laravel, cela s'implémente sous forme de classes qui répondent à une interface commune. Le système appelle processPayment() et l'implémentation concrète décide si la requête va à Stripe ou à Mercado Pago selon la configuration du tenant.
Changer de passerelle, en ajouter une troisième, ou modifier le comportement d'une seule devient un changement localisé dans l'implémentation correspondante, sans toucher au reste du système.
Comment le système décide quelle passerelle utiliser ?
Généralement par combinaison de facteurs :
Cette logique vit dans la couche d'abstraction, pas dispersée dans le code.
Le modèle de données pour le multi-tenancy avec paiements
Les tables minimales dont vous avez besoin :
| Table | À quoi elle sert |
|---|---|
| tenants | Chaque client entreprise du SaaS |
| subscriptions | Abonnement actif par tenant (plan, cycle, statut) |
| payment_methods | Méthodes de paiement enregistrées par tenant |
| invoices | Historique de facturation |
| payment_gateway_configs | Configuration de passerelle par tenant |
Chaque abonnement référence un external_id dans la passerelle correspondante. C'est cet ID que vous utilisez pour gérer les renouvellements, annulations et changements de plan depuis le système.
Les webhooks : le cœur qu'on ne peut pas ignorer
Ni Stripe ni Mercado Pago ne fonctionnent bien sans webhooks. Les paiements asynchrones, les renouvellements automatiques et les événements de litige ou d'échec sont notifiés via webhook. Si vous ne les traitez pas correctement, votre système a des états incohérents - des abonnements qui semblent actifs mais qui ont échoué, des paiements traités mais non enregistrés.
Les erreurs les plus courantes :
Ne pas vérifier la signature du webhook. Stripe et Mercado Pago incluent une signature dans chaque requête qui prouve qu'elle vient d'eux. La vérifier est la première chose à faire. Sinon, n'importe qui peut envoyer des fausses requêtes à votre endpoint.
Traiter le même événement deux fois. Les webhooks peuvent arriver en double. Il faut sauvegarder l'ID de chaque événement traité et vérifier s'il a déjà été traité avant d'agir.
Bloquer le traitement avec une logique lourde. L'endpoint webhook doit répondre 200 OK immédiatement et déléguer le traitement réel à une queue. S'il prend plus de quelques secondes, la passerelle va réessayer l'envoi et vous traiterez le même événement plusieurs fois.
Renouvellements et cycles de facturation
Stripe gère les renouvellements automatiquement si vous utilisez ses abonnements. Mercado Pago a un support pour les paiements récurrents avec plus de variations selon le pays.
Dans les systèmes où nous voulons un contrôle total sur le cycle, nous implémentons la logique de renouvellement directement dans Laravel : un scheduled job qui vérifie chaque jour les abonnements proches de l'expiration et déclenche le prélèvement dans la passerelle configurée pour ce tenant.
Cela donne plus de contrôle et permet de personnaliser le comportement : périodes de grâce, notifications avant expiration, downgrade automatique pour non-paiement, et nouvelles tentatives avec intervalle configurable.
Un apprentissage de projets réels
Dans MyOfficeTaxes, l'intégration de Stripe et Square (pas Mercado Pago, mais le même concept) a été l'un des modules qui a nécessité le plus de temps de conception avant d'écrire une seule ligne de code. Ce temps de conception a été le meilleur investissement du projet.
Quand le client a voulu ajouter un nouveau plan avec une facturation différente, ça a pris des heures - pas des semaines - parce que l'architecture le prévoyait déjà.
Vous construisez un SaaS avec des paiements ou avez besoin d'intégrer une passerelle ?
L'intégration des paiements est l'un des points où la dette technique s'accumule le plus quand elle n'est pas bien conçue dès le départ. Bien le faire économise beaucoup de problèmes quand le business passe à l'échelle.
Si vous avez des questions sur la façon de structurer le système de paiement de votre projet, planifiez un diagnostic. Nous vous donnerons une analyse concrète de ce que votre cas spécifique nécessite.
Partager

Écrit par
Ana Olivia Todesco
CEO @ Nebula Solutions



