Comment développer une application de facturation électronique sur Stripe App Marketplace ?

16 octobre, 2025


Développement informatique

 

Cet article donne des clés concernant le développement d'application, il a été rédigé par Pablo Menéndez de la société Invopop.

Compte tenu de l'intérêt pour la facturation électronique en France, voici une traduction en français, merci à Pablo Menéndez et à Invopop. Lien vers l'article en anglais

Développement de l'application Invopop pour Stripe

Lorsque j'ai rejoint Invopop, on m'a confié un défi qui incarne parfaitement l'esprit de l'entreprise : "Développe une application Invopop pour la marketplace Stripe." Chez Invopop, la liberté d'action est grande, et avec elle, la responsabilité de livrer. Sam et Juan m'ont simplement dit : "Nous voulons cette intégration. Nous n'avons jamais rien fait de tel, et nous croyons en toi." En tant que personne qui s'épanouit face aux défis, j'étais partant malgré l'absence totale d'expérience en développement d'applications Stripe, ou même d'un cas d'usage clair en tête.

Ce voyage a été une masterclass d'apprentissage, remplie de tournants inattendus et d'enseignements précieux.

Les vérités qui font mal : il faut se poser plus de questions

Ce projet m'a martelé l'importance de la planification préalable, de la recherche approfondie et d'une conception solide. Par exemple, j'ai initialement négligé de rechercher des applications similaires dans la marketplace Stripe. Rétrospectivement, c'était une erreur de débutant ; ces applications auraient pu servir de références inestimables. J'ai brièvement regardé A-cube et Billit, mais leur UX confuse et leurs exigences de données obscures (comme un "codice fiscale") m'ont rapidement conduit à abandonner. Si j'avais investi plus de temps à comprendre les besoins, définir le cas d'usage et concevoir l'expérience utilisateur, je suis convaincu que j'aurais pu économiser un mois de développement.

Les obstacles techniques principaux

Ce projet était intrinsèquement difficile pour plusieurs raisons :

Intégration externe : Contrairement à nos applications Invopop internes, c'était notre première application faite entièrement en dehors de notre écosystème, mais elle devait communiquer de manière transparente avec Invopop.

Lourd en front-end : En tant que personne moins encline au développement front-end, faire l'interface utilisateur complète de zéro dans l'environnement Stripe était une entreprise importante. Organiser l'UI, la faire correspondre précisément aux designs s'est avéré plus complexe que prévu.

Odyssée de l'authentification : Gérer l'authentification utilisateur directement depuis Stripe vers Invopop via OAuth était une expérience d'apprentissage complexe mais incroyablement enrichissante. Bien que Sam ait posé une grande partie des fondations avec notre système OAuth existant, les subtilités de l'authentification des requêtes externes présentaient une courbe d'apprentissage abrupte.

Limitations de la plateforme Stripe : Nous sommes des fanatiques de l'UX chez Invopop, rêvant d'une expérience où les utilisateurs n'ont jamais à quitter Stripe. Cependant, certaines fonctionnalités essentielles, y compris la connexion, la sélection de workflow et la facturation, nécessitent toujours de se connecter à la console Invopop. Nous travaillons continuellement pour intégrer ces fonctionnalités directement dans Stripe.

Comptes Stripe et espaces de travail Invopop : Un défi majeur était de mapper précisément les comptes Stripe à leurs espaces de travail Invopop correspondants. Cela impliquait non seulement d'identifier quel compte Stripe était lié à quel espace de travail Invopop, mais aussi de discerner l'environnement Stripe (live, test ou sandbox) d'où provenaient les requêtes ou événements.

La conversion GOBL.Stripe : la colonne vertébrale du projet

Le tout premier élément que j'ai fait, et sans doute le cœur du projet entier, était la conversion du format de données Stripe vers notre format GOBL interne. Ce processus permet aux factures de Stripe d'être comprises et traitées de manière transparente par Invopop. Bien que cela semble simple, convertir des données entre formats est toujours délicat. On rencontre de nombreuses décisions, surtout lorsque les champs n'ont pas d'équivalent direct. Par exemple, Stripe n'a pas de champ "fournisseur" dédié ; le fournisseur est le titulaire du compte Stripe. Cela signifiait que je devais extraire les informations du fournisseur de divers champs de compte, et prévoir une logique robuste pour gérer les scénarios où certains champs étaient vides. Cela seul a consommé environ deux semaines.

Note : le code de conversion est disponible en open source : gobl.stripe

L'application Stripe "interne" : une première étape

Pour obtenir quelque chose de fonctionnel rapidement, j'ai initialement développé une application au sein d'Invopop qui pouvait importer des factures depuis Stripe. Cela reflétait notre intégration Chargebee existante : les utilisateurs collaient leur clé API et leur secret webhook dans la configuration de l'application, nous permettant d'écouter les événements. Bien que simple à implémenter, l'UX n'était pas idéale, nécessitant que les utilisateurs créent et copient manuellement les clés.

Rétrospectivement, cela aurait pu être un point d'arrêt viable pour valider la demande avant de s'engager dans une application marketplace Stripe complète. Bien que l'application marketplace offre une UX supérieure, une flexibilité et une présence de marque, les deux mois supplémentaires passés à la développer auraient peut-être été mieux investis ailleurs à ce stade. Cela a renforcé une leçon cruciale : livrer quelque chose d'utile, mais livrer rapidement. En dirigeant le projet, j'aurais dû reconnaître plus tôt cette sur-ingénierie potentielle.

Cette phase a apporté son propre ensemble de défis :

Gestion de la réception des événements : La réception d'événements webhook est très complexe. Les endpoints webhook sont conçus pour des réponses rapides (un rapide 200 OK), ou une erreur pour déclencher une nouvelle tentative. Le défi survient lorsque votre système ne peut pas traiter un grand volume d'événements de manière synchrone. La solution ? Une file d'attente d'événements. Nous mettons l'événement en file, envoyons un 200 OK à Stripe, puis traitons la requête de manière asynchrone. Cependant, cela introduit le problème des "limbes" : que se passe-t-il si le traitement échoue après avoir dit à Stripe que tout va bien ? Notre solution initiale : augmenter la fiabilité en faisant attendre le webhook pour le traitement (même si cela risque des envois en double, que nous gérons avec des ID).

Appel de facture : Quand et comment : Les données de Stripe sont hautement normalisées. Une payload du webhook invoice.finalized contient uniquement des ID pour les objets liés comme les clients, les intentions de paiement et les éléments de ligne. Pour obtenir une facture complète, nous devions faire un appel API supplémentaire avec un champ expand pour obtenir toutes les données liées nécessaires (dans notre cas, il y a six champs clés que nous étendons). La vraie question est devenue : quand faire cet appel ? Étant donné la nécessité de réponses webhook rapides, un appel API immédiat pourrait causer des timeouts. Cependant, notre workflow initial nécessitait une facture complète pour une "entrée de silo" valide. La solution est venue d'un changement de paradigme chez Invopop : nous permettons maintenant des entrées de silo vides ou invalides pour démarrer un workflow. Un mois plus tard, nous pouvions même créer un job sans entrée de silo, en passant l'ID de facture Stripe dans les métadonnées pour récupérer la facture pendant l'exécution du workflow. Cela a découplé la réponse webhook de la récupération complète de la facture.

La grande progression : développer l'application dans Stripe

Début février, moins d'un mois après le début, nous avions une intégration Stripe fonctionnelle (interne à Invopop). Pourquoi alors, deux mois de plus ? Parce que nous avons considérablement sous-estimé la complexité pour faire l'application réelle dans Stripe.

Développer l'application Stripe signifiait concevoir une UX entièrement nouvelle. Les utilisateurs n'interagiraient pas avec la console Invopop ; ils utiliseraient une barre latérale dans Stripe, ce qui présentait un ensemble unique de défis de conception et de développement. Alors que le travail fondamental de Sam rend la construction d'applications dans Invopop relativement simple (gestion de l'authentification, communication inter-éléments), une application externe exige de gérer sa propre authentification et une communication sécurisée avec tous les services Invopop (accès, silo, récupération d'espace de travail, récupération de workflow), toutes les requêtes à notre backend devaient être authentifiées comme provenant de Stripe.

Le flux d'authentification : de OAuth aux inscriptions

Ma première semaine dans cette phase a été consacrée à la recherche sur les architectures d'applications Stripe, les méthodes d'authentification et le processus OAuth. Pour moi, c'était la fonctionnalité UX la plus critique. Permettre aux utilisateurs de se connecter via OAuth signifiait qu'un seul clic pouvait établir la connexion et accorder l'autorisation à leur compte Stripe, éliminant le besoin d'entrée manuelle de clé API et de secret webhook. Bien sûr, fournir une option claire de déconnexion était tout aussi crucial.

Maintenant, avec notre application Stripe connectée à la plateforme Invopop, nous avions besoin d'un moyen pour l'application Stripe externe de communiquer avec l'application Stripe Invopop interne, en partageant des données et en déclenchant des actions comme l'importation de factures. C'est là que les inscriptions sont entrées en jeu. Chaque application Invopop a sa propre inscription, et pour se connecter, vous avez besoin d'un jeton d'inscription. Typiquement, ce jeton est généré lorsque vous cliquez sur "connecter" dans la console Invopop. Mais pour une application externe, nous avions besoin d'une nouvelle approche : un nouveau point de terminaison API pour créer une inscription dans un espace de travail depuis Stripe. Cela permet à l'utilisateur Stripe de simplement sélectionner un espace de travail, et une inscription est automatiquement créée, retournant un jeton pour les futures requêtes authentifiées.

Le dilemme du webhook : mapper les ID de compte aux espaces de travail

Avec l'authentification et l'inscription gérées, l'obstacle suivant était le webhook. Notre application interne précédente utilisait des webhooks personnalisés avec l'ID d'inscription intégré dans l'URL. Mais les applications marketplace de Stripe utilisent un seul point de terminaison webhook unique pour toutes les requêtes. Comment savoir quel compte envoie la requête ? Le champ account_id dans la payload du webhook fournit cela. Le nouveau problème : comment mapper cet account_id à l'inscription (ou l'espace de travail) correct ?

Ma solution initiale était une base de données relationnelle stockant les mappages account_id vers enrollment_id. Bien que fonctionnelle, elle n'était pas idéale pour la maintenance à long terme. Cela a conduit à une solution plus élégante tirant parti des structures Invopop existantes. Lorsqu'un utilisateur sélectionne un espace de travail, nous créons non seulement une inscription mais aussi une partie fournisseur dans Invopop contenant des informations de Stripe. Ce fournisseur a un champ de métadonnées stockant le account_id Stripe. Donc, lorsqu'un webhook arrive, nous recherchons le fournisseur avec cet account_id pour récupérer l'ID d'espace de travail et l'inscription associés.

Cela a présenté de nouveaux cas limites. Et si deux inscriptions pointent vers le même compte Stripe (par exemple, environnements live et sandbox) ? L'account_id de Stripe est le même pour les deux. Notre solution : imposer que les comptes Stripe live ne peuvent se connecter qu'aux espaces de travail Invopop live, et les comptes Stripe test/sandbox se connectent aux espaces de travail Invopop sandbox. Lorsqu'un utilisateur se déconnecte, nous devons également supprimer ces métadonnées pour éviter les conflits.

Le flux utilisateur et l'évolution UX

Maintenant, le flux utilisateur devenait clair (et nécessitait une quantité importante de codage UI, rappelez-vous mon aversion pour le front-end !) :

  1. L'utilisateur installe l'application.
  2. L'utilisateur s'authentifie avec Invopop via OAuth.
  3. L'utilisateur sélectionne l'espace de travail Invopop à connecter.
  4. L'utilisateur sélectionne le workflow pour envoyer les factures.
  5. Terminé, prêt à partir !

Chaque fois qu'une facture ou note de crédit Stripe est finalisée, un job est exécuté pour le workflow sélectionné, et la facture apparaît magiquement dans Invopop. Succès ! Ou du moins je le pensais. L'adage "les derniers 20% du travail prennent 80% du temps" s'est avéré vrai.

Début mars, l'application pouvait synchroniser les factures, mais l'UX était, franchement, médiocre. Il n'y avait aucune information sur l'état de la facture, l'exécution du workflow, ou même les options de téléchargement. Les boutons d'onboarding étaient confus. Cela a conduit à une conversation cruciale avec notre designer, Mark, le "magicien UX". Ses designs étaient transformateurs, non seulement pour le front end, mais aussi pour le backend. Un parcours utilisateur clair clarifie comment tous les éléments du système doivent interagir.

Le projet a également gagné en complexité avec de nombreux cas limites apparaissant lors des tests, conduisant à une avalanche de corrections de bugs. Nous devions également prendre en charge différents environnements (live, test, sandbox), chacun nécessitant des clés API et des points de terminaison webhook distincts—un fardeau considérable à gérer.

Pendant des semaines, je me suis concentré sur le raffinement du front end, le rendant visuellement attrayant sans casser la fonctionnalité existante. Initialement, nous jugions la page de paramètres de l'application inutile. Cependant, il est rapidement devenu évident que les utilisateurs y étaient redirigés lors de l'installation. Cela a nécessité la construction d'une interface utilisateur de page de paramètres robuste également, un processus facilité par des designs plus clairs.

Un changement significatif impliquait le stockage des informations d'exécution de job. Les utilisateurs devaient voir l'état de leurs factures dans Stripe. Cela nécessitait de stocker des détails de job minimaux (ID de compte, ID de job) dans une base de données et de récupérer les informations complètes du job depuis Invopop à la demande. L'interface utilisateur affichait alors les états d'exécution, les étapes, les factures Invopop liées, et même les pièces jointes—une énorme amélioration UX. Cette fonctionnalité particulière, finie fin mars, a renforcé l'importance de ne stocker que les données essentielles et de récupérer le reste via des appels API.

La ligne d'arrivée

Cette première version "propre" a été terminée le 11 avril, environ trois mois après mon début. Rétrospectivement, nous aurions pu livrer une version antérieure, moins polie (comme l'application interne Invopop) pour recueillir les retours utilisateurs plus tôt. Cela aurait fourni des informations précieuses sur l'utilisation réelle et les problèmes potentiels, guidant notre développement plus efficacement.

Un problème particulièrement intéressant a émergé du nouvel environnement sandbox de Stripe : il n'y avait aucun moyen de détecter si un appel API front-end provenait de sandbox vs test. Mon "hack" initial impliquait d'essayer une requête vers test et, si elle échouait avec une erreur spécifique, de réessayer en sandbox. Cependant, ce qui m'a vraiment impressionné était la réponse de Stripe lorsque j'ai partagé ce retour (avec d'autres suggestions d'expérience développeur comme la gestion de plusieurs clés). Ils ont non seulement été d'accord mais ont développé et livré la fonctionnalité pour identifier les événements sandbox en deux semaines, m'informant personnellement ! C'était une démonstration fantastique du dévouement d'une grande entreprise à l'expérience développeur.

Une fois l'application développée, créer la page de listing sur la Marketplace Stripe et passer par le processus de révision rapide (un jour !) étaient les dernières étapes. Rédiger un texte convaincant pour le listing était crucial pour attirer les utilisateurs intéressés par la facturation électronique.

Conclusion : les leçons de ce développement

Développer l'application Invopop Stripe a été une expérience d'apprentissage immense, tant techniquement que commercialement. Bien qu'il y ait eu des moments où j'ai senti que le temps aurait pu être mal dépensé sur certaines complexités, le recul est toujours 20/20.

C'était une excellente expérience, remplie d'apprentissages précieux, même si j'ai parfois eu le sentiment que j'aurais pu passer le temps sur des choses plus "rentables". Mais c'est la nature de l'innovation.

Mes principaux enseignements personnels et de développement produit de ce voyage sont clairs :

N'ayez pas peur de demander (tôt et souvent) : Mon instinct initial de "me débrouiller seul" m'a souvent ralenti. Ne sous-estimez pas le pouvoir de la connaissance collective.

Priorisez la recherche et la conception préalables : Plonger directement dans le codage semblait productif, mais c'était une fausse économie.

Livrez rapidement, même si ce n'est pas parfait : Sortez une version fonctionnelle, recueillez les retours et itérez rapidement.

La règle 80/20 est réelle (surtout avec l'UX) : Atteindre 80% de fonctionnalité est souvent rapide ; les derniers 20% de peaufinage, cas limites et raffinement de l'expérience utilisateur peuvent prendre la grande majorité du temps.

Adoptez les changements de paradigme : La volonté d'Invopop de repenser les workflows de base (par exemple, permettre des entrées de silo vides pour les jobs) a permis des solutions élégantes à des problèmes difficiles.

D'un point de vue technique, le projet a fourni des leçons inestimables :

Complexité des intégrations externes : Développer une application qui est en dehors de votre plateforme principale mais interagit profondément avec elle introduit une toute nouvelle couche de défis architecturaux et de sécurité (authentification, communication inter-services). Anticipez ces complexités et concevez pour elles dès le départ.

Les webhooks sont plus difficiles qu'ils n'y paraissent : Gérer la réception d'événements, en particulier avec des temps de traitement variables et des besoins de fiabilité, est beaucoup plus complexe que simplement configurer un endpoint. Des considérations comme la mise en file d'attente, le traitement asynchrone et une gestion robuste des erreurs pour les états de "limbes" sont critiques pour un système résilient.

L'authentification est complexe (mais vaut la peine d'être domptée) : Naviguer dans les flux OAuth, gérer l'authentification externe et lier en toute sécurité les comptes externes aux entités internes (comme nos inscriptions d'application) était l'une des parties les plus complexes mais finalement les plus enrichissantes. Un système d'authentification bien conçu est fondamental pour une bonne expérience utilisateur et la sécurité du système.

Gérer les bases de données est délicat : Même pour des mappages de données apparemment simples (comme account_id vers enrollment_id), choisir la bonne solution de stockage, gérer les cas limites (comme plusieurs inscriptions pour le même compte dans différents environnements) et assurer la cohérence des données ajoute une complexité significative.

J'espère que partager mon voyage fournit des informations précieuses et va inspirer d'autres à embrasser les défis du monde passionnant du développement de produits.

Si vous souhaitez en savoir plus pour pouvoir connecter Stripe et émettre des factures électroniques avec une plateforme agréée, consultez cet article de FactureInfo