Contexte
Le problème
Une boutique en ligne paraît simple jusqu’aux cas réels : deux clients qui achètent la dernière unité au même moment, un paiement confirmé deux fois, un prix modifié dans le navigateur. Je voulais un e-commerce qui résiste à ces cas, et le prouver par des tests.
Pour qui
Recruteurs et responsables techniques qui évaluent des compétences back-end, base de données et sécurité, au-delà de l’interface.
Architecture
- Monolithe modulaire Next.js 16 (App Router, Server Components, Server Actions) : les routes appellent des services de domaine, seuls ces services accèdent à la base.
- PostgreSQL 16 avec Prisma 7, et des contraintes CHECK écrites à la main pour que la base refuse elle-même un total incohérent.
- Paiement par Stripe Checkout ; seul le webhook signé et idempotent peut marquer une commande comme payée.
- Authentification better-auth avec sessions en base ; le rôle n’est jamais lu depuis une entrée utilisateur.
- Image Docker sans privilèges root, en lecture seule, avec Postgres et un service de migration dédié ; déployée sur mon serveur derrière Nginx.
Décisions et compromis
Les montants en centimes entiers
Aucun nombre à virgule pour l’argent. Chaque commande garde une copie des prix, des noms et de l’adresse au moment de l’achat : modifier un produit ne réécrit jamais une commande passée.
Un stock réservé par une seule requête SQL
La réservation est un UPDATE conditionnel (« seulement si le stock suffit »), exécuté par la base de façon atomique. Un test lance 8 acheteurs simultanés sur 3 unités : 3 commandes passent, 5 sont refusées.
Le webhook, seule source de vérité du paiement
Le retour du navigateur après paiement ne prouve rien. La commande n’est marquée payée que par l’événement Stripe vérifié, traité une seule fois même s’il arrive plusieurs fois.
Difficultés rencontrées
- Problème
- Il fallait des images de produits sans montrer les produits d’autres marques sous le nom NEXORA, ce que font les photos de banque d’images.
- Solution
- Les images sont des rendus 3D de modèles procéduraux écrits avec three.js d’après les caractéristiques du catalogue, signalés comme rendus sur le site.
- Problème
- Le moteur de Prisma ne pouvait pas être téléchargé dans l’environnement où les migrations ont été préparées.
- Solution
- Les migrations SQL ont été écrites et vérifiées autrement, et la CI contrôle qu’elles correspondent exactement au schéma (détection de dérive).
État du projet
En place
- 79 tests unitaires et d’intégration : prix, codes promo, panier, commandes, concurrence, webhooks, droits d’accès.
- 20 tests de bout en bout sur le build de production, dont un achat entièrement au clavier, des tests d’IDOR et de redirection ouverte, et des contrôles d’accessibilité axe.
- Back-office : machine à états des commandes, ajustements de stock atomiques, codes promo, modération des avis, journal d’audit.
- CSP, en-têtes de sécurité, limites de débit, validation de toutes les entrées côté serveur.
- Sauvegarde nocturne de la base, avec restauration testée.
Prévu, pas encore fait
- Envoi d’e-mails : vérification d’adresse, réinitialisation du mot de passe en production, confirmations de commande.
- Remboursements exécutés par l’API Stripe (aujourd’hui seulement enregistrés).
Limites connues
- Les paiements fonctionnent en mode test Stripe : aucune commande n’est réellement payée ni expédiée.
- Les images de produits sont des rendus 3D, pas des photographies.
- Une page de produit inexistante affiche la page 404 avec le code HTTP 200 (et noindex).
- La limite de débit est gardée par processus : suffisant pour un seul conteneur.
Captures d’écran





