Analyse détaillée de la stack technique, de l'architecture backend, du flux de paiement et de la sécurité biométrique.
Une architecture moderne, sans serveur dédié
SmartLocker est conçue comme une Progressive Web App serverless dans son esprit : un backend Flask léger orchestrant des services managés (Firebase, Stripe), un frontend vanilla optimisé mobile, et l'API WebAuthn native pour la biométrie. Cette approche permet une scalabilité immédiate sans gestion d'infrastructure complexe.
Chaque brique a été choisie pour sa fiabilité et sa documentation
Python 3.10 / Flask, hébergé sur PythonAnywhere (free tier). Routes REST, sessions Flask, webhook Stripe idempotent.
Firebase Realtime Database (projet smartlocker-94e11). Synchronisation temps réel des états casiers, sessions et paired_lockers.
Firebase Auth (email/mot de passe + Google OAuth). Stockage de idToken en session pour les appels API authentifiés.
Stripe Checkout. Webhook checkout.session.completed avec idempotency key pour éviter les doubles paiements.
API WebAuthn native. Compatible iOS Safari (Passkey / iCloud Keychain) et Android Chrome (empreinte capteur).
HTML5, CSS3, JS ES6 vanilla. Chart.js pour les statistiques. Service Worker v5 + Web App Manifest pour l'installation.
Organisation modulaire du projet Flask
Backend découpé en modules selon les conventions Flask : flask_app.py (point d'entrée) enregistre des blueprints par domaine (routes_auth, routes_sport, routes_school, routes_admin, routes_api…), avec extensions.py (Firebase/Stripe) et helpers.py (utilitaires + @require_admin).
Toutes les clés API isolées (Firebase, Stripe). Ignoré par git via .gitignore. Jamais versionné.
7 templates : index.html, settings.html, locker_school.html, locker_sport.html, history.html, admin.html, payment_success.html.
Assets, Service Worker v5, Manifest PWA, icônes, CSS. Cache stratégique pour le mode hors-ligne.
Protège config.py, *.env, __pycache__ et tout fichier sensible.
Documentation complète : installation, configuration Firebase, configuration Stripe, déploiement PythonAnywhere.
Du paiement au déverrouillage
L'utilisateur sélectionne un créneau dans le calendrier. Flask crée une Stripe Checkout Session et redirige vers la page de paiement.
Au succès du paiement, Stripe POST sur /stripe/webhook. Flask vérifie la signature, crée la session dans Firebase, passe le casier en reserved.
À l'arrivée du créneau, le casier passe automatiquement de reserved à unlocked. L'utilisateur peut alors déverrouiller via WebAuthn.
Le frontend déclenche des notifications navigateur 5 min et 1 min avant l'expiration via l'API Notification.
À l'expiration, le casier repasse en locked automatiquement. La session est archivée dans /sessions/{uid}.
L'utilisateur consulte ses sessions passées et exporte le tout en CSV via /history/export.
Structure hiérarchique JSON
État live de chaque casier : state (locked / reserved / unlocked), current_user, expires_at, updated_at.
Profil utilisateur : email, photo, is_premium, paired_lockers (dict de casiers appairés), date d'inscription.
Historique des sessions : locker_id, started_at, ended_at, amount_paid, stripe_session_id.
Credential IDs WebAuthn enregistrés par utilisateur. Permet la vérification serveur de la signature biométrique.
Idempotency key des événements Stripe. Empêche le double-traitement d'un même webhook en cas de retry.
Liste des emails admin (case-insensitive). Contrôle l'accès au tableau de bord via le décorateur @require_admin.
Détection par email avec décorateur @require_admin
État des deux casiers en temps réel, auto-refresh toutes les 60 secondes via JavaScript setInterval.
Sessions totales, revenus Stripe, nombre d'utilisateurs, taux d'occupation hebdomadaire.
Activation/désactivation du statut Premium des utilisateurs via /admin/toggle_premium.
Liste complète avec statut Premium, nombre de sessions, dernière activité. Tri et filtre côté client.
Possibilité de forcer le state=locked d'un casier en cas de problème, sans intervention physique.
Visualisation des erreurs Stripe (paiements échoués) et WebAuthn (tentatives biométriques refusées).
Sept couches de protection
Toutes les clés API (Firebase, Stripe) dans config.py, ignoré par git via .gitignore.
Vérification de la signature Stripe sur chaque webhook pour empêcher les requêtes forgées.
Authentification sans mot de passe, biométrie locale uniquement (la clé privée ne quitte jamais l'appareil).
Chaque appel à la base de données est authentifié par un idToken court-vivant (1h), pas par l'uid.
PythonAnywhere force HTTPS sur tous les endpoints. WebAuthn refuse de fonctionner en HTTP.
Les webhooks Stripe sont déduplicaqués via leur event_id stocké en base. Pas de double paiement possible.
Les bugs qui ont fait progresser le projet
Safari traite isUserVerifyingPlatformAuthenticatorAvailable() comme consommant le geste utilisateur, bloquant l'appel WebAuthn suivant. Résolu en supprimant la pré-vérification.
L'option residentKey: 'preferred' provoquait un NotReadableError sur Chrome Android. Résolu en la supprimant pour utiliser le capteur en mode non-discoverable.
Le bug original stockait l'uid au lieu de l'idToken, cassant tous les appels Firebase suivants. Résolu en récupérant credential.idToken du flow OAuth.
Sans idempotency key, un retry réseau créait deux paiements. Résolu en stockant les event_id Stripe dans Firebase et en filtrant les doublons.
Le timer de locker_school.html déclenchait un reload infini à T=0. Résolu en ajoutant une condition d'arrêt et un cleanup de l'setInterval.
La détection admin était case-sensitive : Admin@inraci.be ne matchait pas. Résolu avec .lower() sur les deux côtés de la comparaison.
Justification des technologies retenues
Léger, lisible, idéal pour un projet pédagogique. Documentation excellente, intégration native avec Jinja2, déploiement trivial sur PythonAnywhere.
Auth + DB + Storage en un seul service. Synchronisation temps réel native, SDK web maintenu par Google, free tier généreux.
Standard de l'industrie pour les paiements en ligne. Documentation impeccable, mode test illimité, webhook fiable, conformité PCI gérée.
Standard W3C, supporté nativement par Safari et Chrome. Pas de SDK propriétaire, pas de coût, sécurité cryptographique éprouvée.
Une seule base de code pour iOS et Android. Pas de validation par les stores, déploiement instantané, mises à jour automatiques.
Gratuit, Python natif, configuration WSGI ultra-simple, console SSH incluse, HTTPS gratuit. Parfait pour un TFE.