← Tous les projets

Web3 · API · FastAPI

Une plateforme d'abonnement avec avantages NFT et jetons soulbound

Un backend complet permettant aux utilisateurs de souscrire un abonnement et de recevoir un lot d'avantages on-chain, dont des NFT soulbound non transférables. Le système garantit une émission unique par paiement et sécurise le passage entre paiement fiat et blockchain irréversible.

Période
2025
Rôle
Consultant backend et web3
Stack
Python, FastAPI, SQLAlchemy, PostgreSQL, web3.py, BSC, Stripe

Contexte

La plateforme devait permettre aux utilisateurs de souscrire un abonnement et de recevoir automatiquement les avantages associés on-chain. J'ai conçu et développé l'ensemble du backend : API FastAPI, authentification, paiement, gestion de l'abonnement, émission on-chain des NFT et métadonnées associées.

Le problème

Émettre des NFT à la suite d'un paiement fiat revient à synchroniser deux systèmes aux propriétés opposées : un système de paiement réversible et une blockchain irréversible.

  • Le mint est irréversible et consomme du gas : un double mint ou une transaction perdue est inacceptable.
  • Les paiements peuvent être contestés ou remboursés après autorisation ; les actifs ne doivent donc être émis qu'une fois les fonds effectivement réglés.
  • Certains NFT doivent être non transférables (soulbound), d'autres transférables.
  • Le frontend a besoin d'une API claire, typée et prévisible pour s'intégrer proprement.

Objectifs

  • Un backend complet : authentification, paiement, gestion de l'abonnement, émission on-chain et métadonnées des NFT.
  • Minter le bon lot de NFT, une seule fois, pour chaque abonnement payé.
  • Gérer à la fois les NFT soulbound et transférables.
  • Résister aux doubles mints, aux transactions perdues, aux interruptions de service, aux litiges et aux remboursements.
  • Une API claire, documentée et entièrement typée.

Mon approche

J'ai construit un backend FastAPI en couches, adossé à PostgreSQL. L'identité utilisateur repose sur le wallet, les paiements Stripe suivent une règle stricte d'attente du règlement effectif, et l'émission on-chain est pilotée par une machine à états idempotente basée sur web3.py. L'accès à la blockchain passe par un client interchangeable, remplaçable par un stub pour les tests. Le flux complet :

auth · achat paiement · règlement mint NFT livrés au wallet Stripe paiements · hold-until-settled Wallet utilisateur identité on-chain Backend FastAPI auth · checkout · moteur de mint · métadonnées BSC · ERC-721 soulbound + transférable
Le parcours complet : le backend authentifie le wallet, encaisse via Stripe, et seulement après règlement minte le lot ERC-721 on-chain, vers le wallet de l'utilisateur.

L'API (FastAPI)

Le backend est une application FastAPI en couches et entièrement typée. Chaque requête et réponse dispose d'un schéma Pydantic, tandis que les modèles SQLAlchemy sont typés et vérifiés avec mypy. Les routes sont organisées par domaine (authentification, abonnement, checkout, métadonnées, webhooks et administration) et utilisent l'injection de dépendances : l'utilisateur courant et la session de base de données sont injectés plutôt que récupérés au cas par cas.

Le wallet constitue l'identité utilisateur. Lors de la connexion, un token signé est vérifié en ES256 à partir du JWKS du fournisseur, puis échangé contre un JWT de session, et le wallet intégré reste l'identité on-chain ; les endpoints sensibles sont soumis à une limitation de débit. Les paiements passent par Stripe Checkout et des webhooks idempotents. Stripe reste la source de vérité pour le règlement : un paiement ne peut déclencher l'émission des NFT qu'une fois les fonds réellement réglés, sans litige ni remboursement. Une synthèse quotidienne signale les abonnements prêts à être émis. Enfin, un endpoint de métadonnées ERC-721 sert les métadonnées de chaque NFT, avec un statut (à venir, actif ou expiré) calculé dynamiquement à partir des dates de validité de l'abonnement, plutôt que stocké en base.

Émission on-chain (NFT & soulbound)

Chaque abonnement payé déclenche le mint d'un lot de NFT ERC-721 sur BSC. Les familles de jetons sont séparées entre NFT soulbound, non transférables, et NFT transférables. Ainsi, un NFT d'identité ou d'abonnement ne peut pas être transféré, tandis que les avantages peuvent l'être. Les opérations de mint et de burn sont réservées au propriétaire du contrat. Elles sont envoyées depuis un wallet dédié, financé uniquement en gas, dont la clé est stockée comme secret chiffré et surveillée. Un mécanisme de burn d'urgence, réservé à l'administration, permet de traiter les cas exceptionnels.

L'accès à la blockchain est encapsulé derrière un protocole unique, décliné en deux implémentations : un stub déterministe, instantané et sans interaction avec la blockchain, pour les tests et le développement local, et une implémentation web3.py qui signe et diffuse les transactions on-chain. Une variable d'environnement permet de choisir l'implémentation utilisée. L'ensemble du flux peut ainsi être testé sans interaction avec une blockchain réelle.

Au centre du système, une machine à états idempotente pilote l'émission des NFT, sans worker d'arrière-plan. Une seule opération advance fait progresser le lot et peut être appelée plusieurs fois sans risque, que ce soit depuis le frontend ou depuis une relance administrative. Ses garanties principales :

  • Les identifiants de NFT ne peuvent pas entrer en collision : chaque abonnement reçoit un identifiant issu d'une séquence atomique PostgreSQL, jamais réinitialisée, et protégé par une contrainte d'unicité sur (collection, id on-chain).
  • Pas d'envoi concurrent en double : advance prend un verrou consultatif par paiement. Un second appelant retourne simplement l'état courant au lieu de relancer une émission.
  • Aucune transaction perdue : chaque transition d'état est immédiatement enregistrée en base, ce qui empêche un crash de faire oublier une transaction déjà envoyée.
  • Pas de double émission après erreur : un job en échec vérifie d'abord l'état de sa transaction avant toute relance. Une erreur temporaire de confirmation ne peut donc pas déclencher une seconde émission.
  • Pas de lot dupliqué : la création des tâches est idempotente et ignore toute collection déjà associée à l'abonnement.
mint confirmations erreur re-vérif tx en attente tx diffusée confirmée échec
Chaque mint du lot est un job : validé en base à chaque étape, verrouillé par paiement, et re-vérifié avant toute relance, pour qu'une transaction ne soit jamais perdue ni envoyée deux fois.

Résultats

  • Un backend de production complet opérant l'ensemble de la plateforme d'abonnement : authentification, paiement, gestion des abonnements, émission on-chain et métadonnées.
  • Des avantages on-chain émis une seule fois par abonnement payé, sans double mint ni transaction perdue.
  • Les NFT soulbound et transférables tous deux pris en charge.
  • Les actifs on-chain ne sont émis qu'après règlement effectif des paiements, ce qui évite toute fuite de valeur liée aux litiges ou aux remboursements.
  • Le système est entièrement testable sans blockchain réelle grâce au client stub, typé avec mypy, versionné avec Alembic et déployé en production.

Ce que j'en retiens

Relier une blockchain irréversible à un système de paiement réversible est avant tout un problème d'idempotence et de source de vérité : l'émission ne doit dépendre que du règlement effectif. Une API claire, typée, et un accès blockchain interchangeable avec un stub ont rendu ce système distribué à la fois sûr à exploiter et simple à tester.

Un défi similaire ? Me contacter →