Le problème du dimanche soir : automatiser l'accès Discord après un paiement Stripe

Paiements6 min de lectureEnglish version

Deux personnes enregistrent une vidéo à un bureau, anneau lumineux et caméra face à elles.

Certains problèmes se présentent sous forme de tableur. Celui-là s'est présenté sous forme de dimanche soir.

Deux fondateurs vendent une formation en ligne. Chaque acheteur reçoit un rôle VIP sur un Discord privé, et c'est là que vit la vraie valeur du produit : les questions, les retours, les autres membres qui ont un mois d'avance sur vous. Le paiement fonctionnait très bien. L'accès, lui, c'était deux humains, à la main, un acheteur à la fois.

Ce qui est parfaitement tenable à dix clients. À quelques centaines, ça veut dire que l'un des deux ouvre Stripe un dimanche soir parce que quelqu'un a payé il y a deux heures et attend, très poliment, en message privé. Retrouver le paiement. Retrouver le pseudo Discord. Attribuer le rôle. Espérer qu'il l'a écrit pareil qu'au moment de l'achat.

Ils voulaient que tout ça disparaisse. Bon objectif. Faisons-le disparaître.

La forme de la solution

Deux workflows n8n, et aucun serveur à surveiller.

Le premier écoute Stripe. Un paiement aboutit, et le workflow enregistre le client et le paiement dans Airtable, génère un jeton à usage unique, puis envoie à l'acheteur un lien d'invitation personnalisé.

Le second attrape le retour OAuth de Discord. L'acheteur clique, autorise l'application, et le workflow valide le jeton avant d'appeler l'API Discord pour l'ajouter au serveur avec le rôle VIP déjà en place.

Voilà toute l'architecture. Elle tient en deux paragraphes, ce qui est en général bon signe.

Y arriver a demandé trois détours, et ce sont les détours qui sont intéressants.

Détour 1 : Stripe a plusieurs façons de dire "il a payé"

L'événement évident, c'est checkout.session.completed. Quelqu'un a terminé un tunnel d'achat, c'est votre client, terminé.

Sauf que cette formation se vend aussi en trois fois. Or checkout.session.completed ne se déclenche qu'une fois, au premier paiement. Les mois deux et trois passent dans un silence complet. Toute personne en paiement échelonné existerait dans le système pendant un mois, puis cesserait discrètement d'exister.

Alors invoice.payment_succeeded, qui couvre bien les échéances. Sauf qu'il couvre aussi les périodes d'essai gratuites que la plateforme de formation crée automatiquement à 0 euro. Écouter cet événement, c'est offrir un rôle VIP à tous ceux qui ont pris un essai sans jamais payer un centime. Moyennement idéal pour une communauté payante.

Celui qui décrit vraiment ce qui nous intéresse, c'est charge.succeeded. Il se déclenche quand l'argent bouge. Les essais gratuits ne font bouger aucun argent, donc ils ne le déclenchent jamais et s'excluent tout seuls, sans liste d'exceptions à maintenir ni condition maligne dont il faudra se souvenir six mois plus tard.

Petite décision. C'est aussi la différence entre une intégration qui fonctionne et une intégration qui distribue des abonnements gratuits en silence.

Détour 2 : le raccourci qui aurait mal vieilli

Une fois les paiements qui remontaient, il fallait distinguer un achat comptant d'un paiement en trois fois, parce que la suite du traitement n'est pas la même.

La première version le faisait au montant. Au-dessus d'un certain seuil, c'était du comptant, en dessous de l'échelonné. Ça a marché tout de suite, ce qui est précisément ce qui rend ce genre de raccourci dangereux : rien ne vous signale qu'il est fragile tant qu'il fonctionne.

Mais le jour où les fondateurs lancent une promotion, augmentent leurs prix ou ajoutent une offre moins chère, chaque nouvel enregistrement est mal catégorisé. Et rien ne casse. Pas d'erreur, pas d'alerte, rien de rouge nulle part. Juste un tas de données fausses qui grossit doucement, et que quelqu'un découvre trois mois plus tard en essayant de comprendre ses propres chiffres.

Le payload Stripe répond déjà honnêtement à la question. Un paiement rattaché à une facture appartient à un abonnement. Un paiement sans facture est un achat unique. Le test se moque du prix, donc il survit à tous les changements tarifaires à venir.

La règle sur laquelle je retombe toujours : si votre logique casse quand un nombre change, ce n'est pas une conception, c'est un contournement déguisé. Livrer le contournement est parfois le bon choix. Il faut juste l'écrire noir sur blanc, et le remplacer avant de l'oublier.

Détour 3 : celui qui m'a fait redresser la tête

La première version marchait. Les clients payaient, recevaient leur email, cliquaient, atterrissaient sur Discord avec leur rôle VIP. Tout le monde était content, moi compris.

Puis j'ai regardé le lien d'invitation une fois de plus.

Pour reconnaître l'acheteur quand Discord le renvoie chez nous, le workflow faisait transiter son adresse email dans le paramètre OAuth state. Discord renvoie state tel quel, le workflow lit l'email, retrouve le client, attribue le rôle. Propre. Lisible. Grand ouvert.

state est une valeur contrôlée par l'utilisateur. Elle est là, dans l'URL. N'importe quel acheteur, ou n'importe qui à qui il aurait transféré l'email, pouvait y lire sa propre adresse, la remplacer par une autre, et se voir attribuer un accès VIP à une communauté payante sans avoir payé. Aucun piratage là-dedans. De la curiosité et un éditeur de texte.

Le correctif a consisté à ne plus mettre aucun sens dans ce paramètre. Au moment du paiement, le workflow génère désormais un jeton aléatoire, le stocke de notre côté en le rattachant à cette commande précise, et ne met que ce jeton dans le lien. Il ne dit rien de qui vous êtes. Il prouve seulement que ce lien exact a été émis pour une commande payée, qu'il n'a pas encore servi, et qu'il a moins de 48 heures.

Coût du correctif : une colonne de plus en base, une étape de validation dans le workflow. Une après-midi, à peu près.

Et ça dépasse largement Discord. Toute valeur qui part dans le navigateur de l'utilisateur et qui vous revient est une valeur que l'utilisateur peut modifier. Si votre contrôle d'accès lui fait confiance, vous n'avez pas un contrôle d'accès, vous avez une suggestion.

Où ça en est

Les deux workflows tournent en production. Quelqu'un achète la formation à deux heures du matin, et trente secondes plus tard il est dans la communauté avec le bon rôle. Aucun humain dans la boucle, aucun dimanche soir passé dans le tableau de bord Stripe.

Les fondateurs peuvent retourner à ce qu'ils font vraiment bien, enseigner, et qui est précisément ce que leurs clients viennent acheter.

C'est en général le vrai retour sur ce type de travail. Pas les heures économisées sur un tableur, mais l'attention rendue aux deux ou trois choses que personne d'autre ne peut faire à leur place.

Si ça vous parle

Les plateformes de paiement, les outils communautaires et les logiciels internes se parlent rarement d'eux-mêmes. Et l'écart entre les deux finit toujours par être comblé par une personne qui refait les mêmes cinq clics, plusieurs fois par jour, indéfiniment.

Combler cet écart, c'est mon métier. Si vous en avez un, racontez-le moi et je vous dirai franchement s'il vaut le coup d'être automatisé.

Autres notes

Construisons quelque chose

Un projet IA à lancer ? Un système à intégrer ? Des workflows à automatiser ? Écrivez-moi : je réponds généralement sous 24 h.

Démarrer un projet hello@wolfdevlabs.com