Stéphane.
WebArchivé

FoodQard — la carte du restaurant derrière un QR code

Plateforme de carte de restaurant sans contact déployée à l'été 2020 : QR codes à jetons imprimés sur sous-verre, carte multilingue avec allergènes, et un canal SMS pour qui n'a pas de smartphone.

Technologies employées

  • PHP
  • MySQL
  • JavaScript
  • Composer
  • endroid/qr-code
  • Python 3
  • PyMySQL
  • FFmpeg
  • RaspiSMS
  • Raspberry Pi

Démonstration

Résumé

À la réouverture des restaurants, en 2020, la carte papier est devenue un objet à désinfecter entre deux clients. FoodQard remplaçait ce papier par un QR code imprimé sur un sous-verre : le client scanne, la carte s’affiche sur son téléphone, avec les photos, les vidéos des plats, les allergènes et la traduction. Le restaurateur la met à jour depuis un back-office, sans appeler personne. Et le client qui n’a pas de smartphone envoie un SMS pour recevoir la même carte en texte. Mis en production en août 2020.

Contexte

L’été 2020 impose aux restaurants un protocole sanitaire dont la carte papier est le point faible : elle passe de main en main, et la désinfecter entre chaque service n’est pas tenable. La réponse immédiate du secteur a été le PDF derrière un QR code, collé sur la table.

Les restaurateurs visés n’avaient ni service informatique, ni budget, ni l’intention d’apprendre un outil de plus au milieu d’une réouverture.

Problème

Le PDF derrière un QR code règle la question sanitaire et rien d’autre. Il oblige à zoomer sur un document conçu pour du A4, ne se met à jour qu’en regénérant un fichier, ne dit rien des allergènes autrement qu’en petits caractères, n’existe que dans une langue, et laisse sur le carreau le client qui n’a pas de smartphone — les personnes âgées, en premier lieu, qui sont aussi une part importante de la clientèle d’un restaurant de quartier.

Objectifs

  • Une carte lisible sur un téléphone, pas un document A4 réduit.
  • Une mise à jour par le restaurateur lui-même, en quelques minutes.
  • Les allergènes et les régimes visibles d’un coup d’œil, pas en note de bas de page.
  • Un accès pour qui n’a ni smartphone, ni application, ni forfait data.

Contraintes

  • Le support est imprimé. Un QR code sur un sous-verre ne se corrige pas : ce qu’il encode doit rester valable des mois.
  • Le réseau en terrasse est mauvais. Une carte qui charge en trois secondes n’est pas consultée.
  • L’utilisateur final n’est pas technicien, ni du côté du restaurateur, ni du côté du client.
  • Aucune application à installer : personne ne télécharge une application pour lire une carte une fois.

Approche

Un même contenu, trois portes d’entrée, choisies selon ce dont le client dispose réellement au moment où il s’assoit :

CanalCe qu’il faut au clientCe qu’il reçoitLimite assumée
QR sur sous-verreUn appareil photoLa carte complète : photos, vidéos, allergènes, traductionSuppose un smartphone
QR Wi-FiUn appareil photoLa connexion au Wi-Fi de la salle, sans saisir la cléUtile en complément, pas seul
SMSN’importe quel téléphoneLa carte en texte, découpée en messagesNi photo, ni mise en forme

Le canal SMS n’est pas un repli dégradé qu’on ajoute pour la forme : c’est le seul des trois qui fonctionne sans smartphone, sans application et sans connexion de données.

Phases

PériodeObjectifTravail réaliséRésultat
Juin 2020Valider l’idée du jetonGénérateur de QR codes, essais d’insertion massive de jetons, premières vues clientLe modèle d’URL est figé avant toute impression
Juillet 2020Rendre la carte administrableBack-office : établissement, catégories, sous-catégories, produits, menu du jour, profilsLe restaurateur devient autonome
Août 2020Mise en productionSous-verres imprimés, QR Wi-Fi, canal SMS, traitements de nuitService ouvert en clientèle réelle
Octobre 2020Aller au-delà de la cartePlan de salle et réservationsLa table devient une entité, pas seulement un support de QR

Architecture

Les trois accès à la carte FoodQardTrois voies parallèles aboutissent à la même base MySQL. Au comptoir, le client scanne un QR code imprimé sur un sous-verre ; l’URL porte un jeton de soixante-quatre signes qui désigne la carte et affiche une page HTML avec photos et vidéos. Sans smartphone, le client envoie un SMS : un Raspberry Pi équipé d’une carte SIM interroge la même carte au format JSON et renvoie la réponse découpée en messages de cent soixante caractères. La nuit, un traitement ré-encode les vidéos déposées par le restaurateur puis supprime les dossiers que la base ne référence plus.Au comptoirsous-verreQR imprimé/?code=…jeton de 64 signescarte HTMLphotos, vidéosSans smartphoneSMS du clientnuméro affichéRaspberry PiRaspiSMS + SIMréponse SMSdécoupée à 160La nuituploadsdu restaurateurffmpeg x265ré-encodagepurgedossiers orphelinsMySQL — carte, jetons, médias
Trois chemins, une seule source. Le canal SMS n’est pas un repli dégradé : c’est le seul qui fonctionne sans smartphone, sans application et sans connexion de données.

Une seule base porte tout : la carte, ses catégories, ses produits, les jetons et les références de médias. Les trois voies y aboutissent, ce qui garantit qu’aucune ne peut afficher une carte périmée pendant qu’une autre affiche la bonne.

Les médias sont servis depuis un domaine distinct : les photos et les vidéos de plats ne partagent ni le cycle de vie ni le profil de charge des pages applicatives.

Un jeton, pas un identifiant

L’URL derrière le QR code ne contient jamais l’identifiant de la carte. Elle porte un jeton de 64 signes hexadécimaux, tiré au sort et stocké dans une table dédiée qui associe le jeton à la carte. La page ne lit que les 64 derniers caractères de ce qu’elle reçoit et cherche la correspondance ; sans elle, elle n’affiche rien.

Le stock de jetons a été pré-généré — deux cent mille d’un coup, par lots — plutôt que produit à la demande. C’était le bon choix : au moment d’imprimer une série de sous-verres, on tire des jetons déjà disponibles, sans dépendre de la disponibilité d’une machine, et la génération lente n’est jamais dans le chemin d’un client.

Le canal SMS

Un Raspberry Pi équipé d’une carte SIM et de RaspiSMS reçoit les messages. Un programme Python interroge en boucle les messages arrivés dans l’heure, récupère la carte de l’établissement au format JSON auprès de l’application, puis découpe la réponse en messages de 160 caractères — sans jamais couper un mot au milieu. Les numéros déjà servis sont mémorisés le temps d’un cycle, pour qu’un client qui envoie deux fois le même SMS ne reçoive pas deux fois la carte.

Réalisation

QR code vert foncé avec, en son centre, le pictogramme d’un carnet de menu portant fourchette et couteau, et sous le code la mention « Scannez la carte ! ».
Le QR code de la carte, tel que le générait la plateforme : logo au centre, libellé sous le code, et la couleur de l’enseigne.

Le logo au centre et le libellé sous le code ne sont pas décoratifs. Un QR code nu sur une table ne dit pas ce qu’il fait, et personne ne scanne un carré noir sans savoir ce qu’il déclenche. Les deux ajoutés, la correction d’erreur a été poussée à son niveau maximal : le logo masque une partie des modules, et le support finira taché, rayé ou mouillé — c’est un sous-verre.

QR code vert portant en son centre le logo « Free WiFi » et, sous le code, la mention « Scannez le code WIFI ! ».
Le même générateur produisait le QR code du Wi-Fi de la salle : le client se connecte sans qu’on lui épelle la clé.

Côté restaurateur, le back-office couvre l’établissement, les catégories et sous-catégories, les produits — avec description, ingrédients, prix, photo, vidéo, et les marqueurs végétalien et sans gluten —, le menu du jour daté, les sous-verres, le QR Wi-Fi, puis le plan de salle et les réservations. Un mode dégradé était prévu pour ceux qui ne voulaient rien saisir : une simple image de leur carte existante, servie derrière le même QR code.

La vidéo, le coût caché

Laisser un restaurateur téléverser une vidéo de son plat depuis son téléphone, c’est accepter des fichiers de plusieurs centaines de mégaoctets filmés en .mov. Sur une terrasse mal couverte, cette vidéo ne s’ouvrira jamais.

Deux traitements de nuit s’en chargent. Le premier ré-encode ce qui a été déposé dans la journée, en H.265 à qualité constante, après conversion préalable des formats issus des téléphones. Le second compare les dossiers présents sur le disque aux médias réellement référencés en base, et supprime les orphelins — ceux qu’un produit supprimé ou remplacé a laissés derrière lui.

Résultats

  • Mise en production en août 2020, en clientèle réelle, avec une démonstration vidéo publiée à l’appui.
  • Trois canaux d’accès à la même carte, dont un qui ne demande aucun smartphone.
  • Un stock de deux cent mille jetons, soit de quoi imprimer des séries de sous-verres pendant des années sans jamais réutiliser une URL.
  • Des vidéos de plats consultables en terrasse, grâce au ré-encodage de nuit.
  • Le périmètre étendu en octobre 2020 au plan de salle et aux réservations, sans toucher au modèle des jetons — signe que la fondation tenait.

Enseignements

C’est un projet de 2020, écrit vite, pendant une réouverture. Avec cinq ans de recul, la partie conception a bien vieilli ; la partie mise en œuvre, beaucoup moins.

Choix de 2020Ce que je ferais aujourd’huiPourquoi
Identifiants de base dans les fichiers du dépôtVariables d’environnement, secrets hors dépôtUn secret commité reste dans l’historique, même effacé
Requêtes SQL construites par concaténationRequêtes préparées, systématiquementLa seule protection qui ne dépend pas de la vigilance du développeur
Traitements de nuit lancés par os.systemAppels de processus explicites, sans passer par un shellUn nom de fichier hostile ne doit pas pouvoir devenir une commande
Aucun test automatiséAu moins les fonctions pures : découpage SMS, résolution de jetonCe sont celles dont la panne est silencieuse

Ce qui, en revanche, mérite d’être gardé : le jeton plutôt que l’identifiant, la pré-génération du stock, la purge écrite en même temps que l’envoi, et surtout le canal SMS — la seule fonctionnalité que personne ne demandait et que les clients âgés ont réellement utilisée.

Conclusion

  1. Le problème n’était pas d’afficher une carte, mais de la rendre atteignable par tout le monde, y compris sans smartphone.
  2. Le support imprimé impose ses contraintes à l’URL : jeton stable, stock pré-généré, correction d’erreur maximale.
  3. Un média téléversé coûte deux fois : à l’envoi, puis chaque fois qu’on oublie de le supprimer.
  4. Les décisions de conception ont mieux vieilli que la mise en œuvre : ce sont les secrets en dur et le SQL concaténé que je reprendrais d’abord.