Café Verdé
Tout ce dont un restaurant a besoin pour fonctionner une journée — du client qui compose sa salade sur son téléphone à la TVA du soir — écrit d'un seul tenant.
Mon rôle
36 000 lignes, une seule main. Le restaurant tourne dessus tous les jours de service. J'ai conçu et écrit l'intégralité : les douze collections de la base, l'API et ses dix validateurs, les six applications qui s'y branchent, la couche d'analytique, la conformité RGPD, puis la conteneurisation, le déploiement et l'exploitation. C'est moi qu'on appelle quand la caisse ne prend plus la carte un samedi midi.

La demande
Il n'y avait pas de système à réparer. Le client voulait un site complet, où tout se trouve au même endroit : commander en ligne, encaisser, envoyer en cuisine, tenir la fidélité, sortir la TVA — une seule application plutôt qu'un assemblage d'outils. Le projet est né comme un configurateur de salades et de bubble teas, puis a pris le périmètre entier du restaurant : plats chauds, petits déjeuners, goûters, caisse, comptabilité. La difficulté n'était donc pas de résoudre un problème existant, c'était de tenir cette centralisation à mesure qu'elle grandissait, sans laisser les six applications diverger les unes des autres.
Ce que j'ai livré
- Boutique client : vitrine, carte organisée en arbre de catégories à profondeur libre, page de plat avec accompagnements et menu, panier, paiement carte avec 3-D Secure, ou règlement sur place.
- Configurateur de salade guidé par les limites de la formule, avec construction visuelle en direct, compteur de calories, suggestions de composition, retour haptique, et annulation-rétablissement des vingt derniers gestes.
- Espace client : suivi de commande en temps réel, historique, points de fidélité, création ou changement de mot de passe, envoi du ticket par email ou SMS.
- Conformité RGPD écrite dans le code : le client exporte lui-même ses données et supprime son compte, y compris son identité chez le fournisseur d'authentification.
- Caisse : parcours en trois temps, composition guidée de tous les articles, tickets ouverts mis de côté sans partir en cuisine, remise commerciale, points, quatre moyens de paiement combinables, et séparation d'addition en parts égales, à montant libre ou par article.
- Terminal de paiement Stripe piloté depuis le serveur : le montant part de l'application et s'affiche sur le lecteur, il n'est jamais ressaisi.
- Écran cuisine alimenté par websocket : minuteur et alerte de retard par ticket, regroupement des articles identiques, séparation des commandes en ligne et sur place, mise en attente découplée de l'envoi en production.
- Espace gérant : neuf onglets de catalogue — ingrédients, formules, menus, plats, produits, arbre de catégories, bubble teas, milkshakes — chacun avec recherche, tri, pagination, deux modes d'affichage, images compressées automatiquement et réorganisation par glisser-déposer.
- Espace finance : chiffre d'affaires net des remboursements, réconciliation du brut au net, ventilation TVA, répartition par moyen de paiement et par type, pourboires du terminal reconstitués depuis Stripe, export CSV, impression — le tout rechargé en temps réel.
- Moteur de TVA configurable : deux taux sur place et à emporter, surchargeables par produit ou par catégorie et hérités en remontant l'arbre, ventilés au prorata du net encaissé.
- Analytique comportementale maison : entonnoir de conversion, taux d'abandon, carte de chaleur des clics, profondeur de défilement, appareils, ingrédients les plus manipulés, visiteurs actifs, et flux d'activité en direct.
- Espace développeur : gestion de comptes de tous rôles, sauvegarde et restauration non destructive, et récupération d'un paiement resté autorisé chez Stripe.
- Authentification par SMS à usage unique ou par email, rôles portés par des revendications signées faisant autorité côté serveur, et un rôle développeur invisible des listes, des promotions et des statistiques.
- Ticket rendu à l'identique par quatre canaux : impression 80 mm, email, SMS et modale d'historique — avec pour chacun une variante sans le détail des articles.
Les points durs
Un refus de carte n'est pas un échec de paiement
Le terminal redemande la carte avec saisie du code après un sans-contact refusé — un incident banal, fréquent par cumul de paiements. Ma boucle sortait sur cette erreur : écran d'échec, lecteur bloqué, et une autorisation orpheline à capturer à la main dans Stripe. J'ai reconstruit le suivi autour d'une règle — seul l'état du lecteur fait foi — puis traduit les dix-sept codes de refus en consignes pour la caissière, en les indexant sur le code et jamais sur le message, qui peut être reformulé par Stripe. Elle sait maintenant s'il faut attendre ou proposer un autre moyen. J'ai enfin fermé le trou restant : un paiement resté autorisé se retrouve, se capture et se reconstruit en commande depuis l'espace développeur.
Une part d'addition qui se ré-ouvre
Séparer une addition n'est pas répartir un total : quelqu'un commande un café après avoir payé. J'ai donc modélisé une part non comme « payée ou non » mais comme un dû assorti de l'historique de ses règlements. Un article attribué après coup rouvre la part, un article partagé se répartit sur toutes en gardant la trace de chaque quote-part, et chaque convive peut porter son propre client et ses propres points. À la validation, les règlements multiples d'une même part sont aplatis en autant d'écritures — la comptabilité, la fidélité par client et les remboursements Stripe n'ont ainsi rien à connaître de cette complexité.
Une TVA qui tombe juste au centime
Les prix sont affichés TTC, les remises portent sur le panier et non sur la ligne, et le taux peut être forcé sur un produit ou hérité d'une catégorie parente en remontant l'arbre. Je ventile au prorata du net encaissé, taux par taux, sous un invariant que je tiens : la somme des HT plus la somme des TVA égale exactement ce qui a été payé. Une commande antérieure à la mise en place de la TVA est recomposée à la volée depuis le catalogue actuel, et toutes les bornes de période sont ancrées au fuseau de la Guadeloupe — sans quoi la période glissait d'un jour en production.
Six applications, une seule vérité
Un article n'a pas la même forme selon qu'il arrive de la caisse ou de la boutique, et le même ticket doit se rendre à l'imprimante, en email, en SMS et dans l'historique. Chaque fois que j'ai laissé un chemin reconstruire sa propre version des données, elle a dérivé en silence — jamais avec une erreur, toujours avec un chiffre faux. J'en ai fait une règle : un constructeur unique par objet métier, et toute évolution se propage à tous les rendus ou elle n'est pas livrée.
Faire entendre une commande dans le bruit du service
Une commande arrivée du site doit s'entendre depuis la cuisine. Plutôt qu'embarquer un fichier son, je génère l'alarme en JavaScript : trois bips deux-tons en onde carrée, encodés à la main dans un fichier WAV, avec un fondu de cinq millisecondes qui supprime le claquement de début. La salve se répète toutes les dix secondes jusqu'à ce que la cuisine prenne la commande en charge, l'acquittement survit à un rechargement de page, et si le navigateur refuse de jouer le son, un diagnostic explique quoi faire plutôt que de laisser croire au silence.
Mesurer sans monter une usine
Je voulais l'entonnoir de conversion, le taux d'abandon et une carte de chaleur des clics sans service d'analytique tiers ni index composite à créer à la main pour chaque nouvelle question. J'ai posé la limite franchement : une seule requête filtrée sur le temps, toutes les agrégations en mémoire, et j'ai écrit dans le code le seuil au-delà duquel ce choix ne tient plus. Une décision bornée et documentée vaut mieux qu'une architecture prématurée.
Aperçus


