Sirocco

Stripe Connect : architecture pour un app marketplace de chauffeurs

application_fee_amount, transfer_data, facturation par course, abonnement chauffeur 2€/mois.

4 min de lecture
Stripe Connect : architecture pour un app marketplace de chauffeurs

Les marketplaces de mobilité — VTC, covoiturage, livraison à la demande — combinent une structure de facturation que peu d'autres modèles supportent : un prélèvement à la course avec partage automatique entre la plateforme et le chauffeur, un abonnement mensuel récurrent côté chauffeur, des dépôts de garantie remboursables, et un flux de remboursements en cas de litige. Stripe Connect est l'outil canonique pour ce besoin, mais sa documentation est dispersée entre trois grands modes (Standard, Express, Custom) et une vingtaine d'objets API. Cet article détaille l'architecture qui marche en production sur des dizaines de milliers de chauffeurs, avec les pièges qu'on a personnellement payés. La première décision est le type de compte connecté. Standard Account convient quand le chauffeur a déjà ou veut avoir une relation directe avec Stripe (dashboard, support, paiements multi-plateformes) — c'est typiquement le cas pour les VTC indépendants en France. Express Account est l'entre-deux : Stripe fournit un onboarding hosted et un dashboard léger, mais la plateforme garde le contrôle de l'expérience. Custom Account met toute la responsabilité KYC côté plateforme et n'est viable que pour des opérateurs avec une équipe compliance dédiée. Pour 90% des marketplaces que nous voyons, Express est le bon choix : friction minimale au onboarding, conformité KYC déléguée à Stripe, et continuité d'expérience dans l'app. Le flux d'une course payante repose sur le modèle « destination charges » de Stripe. Le client paie sur le compte de la plateforme (le money flow passe par la plateforme), puis un transfer est immédiatement déclenché vers le compte connecté du chauffeur, en soustrayant l'application_fee_amount. Concrètement, un seul appel PaymentIntent avec `transfer_data: { destination: stripe_user_id, amount: net_amount }` et `application_fee_amount: platform_fee` règle les trois opérations atomiquement. La plateforme touche son fee, le chauffeur touche son net, et Stripe gère le reversement vers la banque du chauffeur selon la fréquence configurée sur le compte connecté. Les abonnements chauffeur — typiquement 2 à 5 € par mois pour l'accès à la plateforme — se créent directement sur le Connected Account via l'API Subscriptions, en utilisant `Stripe-Account: acct_xxx` dans le header de la requête. Le chauffeur voit la facture sur son propre dashboard Stripe (cohérent avec son statut d'auto-entrepreneur), et la plateforme reçoit `application_fee_amount` sur chaque renouvellement si elle est configurée. Le piège classique est de créer les abonnements sur le compte plateforme par défaut — cela fonctionne techniquement mais brouille la comptabilité du chauffeur et complique les audits TVA. Les webhooks sont le système nerveux de l'intégration. Au minimum, écouter `payment_intent.succeeded` (course confirmée, déclencher le calcul de commission), `payment_intent.payment_failed` (carte refusée, alerter passager), `charge.refunded` (annulation, recalculer le fee), `invoice.payment_failed` (abonnement chauffeur échoué, envoyer un FCM pour mettre à jour la carte), `account.updated` (changement KYC, peut bloquer les transfers), `account.application.deauthorized` (chauffeur a déconnecté Stripe). Chaque webhook doit être idempotent — Stripe peut retenter — et signé via `Stripe-Signature` avec un secret stocké en variable d'environnement. Un endpoint qui logge l'event_id en base et refuse de retraiter un id déjà vu est la première ligne de défense. Les nuances opérationnelles dévorent du temps de développement et sont rarement bien documentées. Premier sujet : le taux d'application variable. Une plateforme qui distingue uber-like et covoiturage applique typiquement 20% sur uber-like et 5% sur covoiturage. Cela se gère côté code en calculant `application_fee_amount` au moment du PaymentIntent ; pas de configuration globale possible côté Stripe. Deuxième sujet : la gestion des remboursements partiels. Si une course est annulée à mi-parcours avec un remboursement de 40%, le fee de la plateforme doit être recalculé au prorata. Stripe ne le fait pas automatiquement ; il faut passer un `refund_application_fee` explicite dans l'appel `refunds.create`. Troisième sujet souvent négligé : la TVA. Sur un trajet en France, la plateforme facture sa commission HT au chauffeur (qui la déduit de son CA), et la plateforme doit collecter et reverser la TVA sur sa propre marge. Stripe Tax automatise une partie de cette logique, mais l'intégration demande de paramétrer correctement les `tax_behavior` sur les Prices et les `tax_rates` sur les line items. Sur l'international, le passage par Stripe Tax est presque obligatoire — la complexité OSS et IOSS dépasse rapidement les capacités d'une petite équipe finance. Un dernier piège, vécu sur un projet réel : les payouts automatiques vers le compte bancaire du chauffeur peuvent être bloqués si le KYC de Stripe trouve un problème sur la pièce d'identité ou le justificatif de domicile. L'évènement `account.updated` arrive avec `payouts_enabled: false` et `requirements.currently_due` qui liste les pièces manquantes. La plateforme doit afficher un bandeau explicatif au chauffeur (avec lien direct vers la complétion KYC hosted par Stripe) plutôt que de laisser les transfers s'accumuler en attente, ce qui génère systématiquement des tickets support et de la défiance. Le code production complet tient typiquement dans un service FastAPI dédié de 800 à 1500 lignes : modèle de données pour les comptes connectés et les abonnements, handlers webhook idempotents, helpers pour les PaymentIntents avec destination charges, jobs de réconciliation horaire qui croisent les transfers Stripe avec la base interne. C'est moins que ce qu'on imagine au début, et plus que ce que les tutoriels Stripe laissent entendre — la complexité est dans les cas limites et la rigueur de la réconciliation.