NODE_01
Reputap
Collecte d'avis Google par NFC, multi-établissements, avec anti-abus.
- Rôle
- Fondateur, conception et développement
- Période
- 2026 à aujourd'hui
- Statut
- En production
- Stack
- Next.js 16 · TypeScript · PostgreSQL · Prisma · Auth.js · Zod · GSAP · Resend · Stripe · Vercel
- Liens
- Voir en ligne

Le problème
Un commerce local vit de ses avis Google, mais les obtenir demande un effort au pire moment : le client est en train de payer et de partir. Les solutions existantes mettent un QR code sur la table et s'arrêtent là. Elles ne savent ni motiver le client, ni empêcher que la même personne enchaîne les récompenses, ni donner au staff un geste simple en caisse.
Reputap part d'une contrainte physique : une puce NFC unique par établissement. Il faut être sur place pour scanner. À partir de là, le parcours est guidé : scan, avis, roue de gains, ticket, validation par le commerçant. Le produit se vend aujourd'hui aux commerces des Alpes-Maritimes avec la plaque incluse.
Les contraintes
- Google ne fournit aucune API fiable pour prouver qu'un avis a été déposé. Tout le système devait fonctionner sans cette preuve.
- Multi-établissements dès le premier jour. Un même client SaaS peut avoir plusieurs points de vente, chacun avec ses tags, sa campagne, ses récompenses.
- Zéro friction côté client final. Pas de compte, pas d'application : une URL ouverte par le téléphone au contact de la plaque.
- Un geste en caisse qui tient en cinq secondes pour un staff qui n'a pas le temps de lire une documentation.
Les décisions d'architecture
Déplacer la barrière anti-fraude à l'endroit où un humain est déjà présent
Puisque l'avis ne peut pas être vérifié, la récompense l'est. Le spin produit un ticket ISSUED avec un code unique et une expiration. Rien n'est remis tant que le commerçant ne l'a pas passé à REDEEMED depuis son écran de validations, ou rejeté avec un motif. La fraude devient un problème de caisse, pas un problème de cryptographie.
Tirer la roue côté serveur, animer côté client
Le client ne tire jamais. Il envoie une requête de spin ; le serveur vérifie la session, les cooldowns et les plafonds, tire le lot selon les poids configurés, écrit spin et claim en base, puis renvoie le résultat. L'animation GSAP ne fait que jouer ce résultat. Un client modifié ne peut pas gagner plus souvent.
Isolation par tenant dans la couche d'accès aux données
Chaque requête passe par une couche d'accès qui reçoit le tenant courant et ne peut pas l'oublier. Les rôles super_admin, owner et user sont portés par Auth.js et vérifiés côté serveur sur chaque action. Un propriétaire ne peut pas lire les données d'un autre établissement, même en devinant un identifiant.
Des règles anti-abus configurables par campagne, pas codées en dur
Un spin maximum par session de scan, cooldown de 24 h par appareil, limite douce par adresse IP hachée, plafond de spins par tag et par heure, jetons NFC non devinables avec rotation possible, tickets expirants. Chaque règle est un paramètre de campagne, parce qu'un bar de nuit et une boulangerie n'ont pas le même trafic.
Le parcours, tel qu'il tourne
- Étape 1

- Étape 2

- Étape 3

- La plaque ouvre une URL du type
/s/{publicToken}. Le serveur résout le tag, l'établissement et la campagne active, puis crée unescan_sessionavec ses métadonnées anti-abus et son expiration. - La page affiche le branding de l'établissement et un seul bouton : laisser un avis. Le clic enregistre une intention d'avis et ouvre la fiche Google.
- Au retour, la roue est disponible si la session est valide. Le tirage est serveur, l'animation est client.
- Le ticket s'affiche avec son code. En caisse, le staff le retrouve par code, valide ou rejette.
Architecture
Ce qui n'est pas résolu
Côté exploitation

Une console super-admin liste les tenants, gère les abonnements et génère des liens de connexion magiques pour le support. Un tableau de bord système affiche la latence de la base, l'usage disque et les journaux. Les emails transactionnels partent par Resend, la facturation est branchée sur Stripe, le tout est déployé sur Vercel avec PostgreSQL géré par Supabase.
Stack et conventions
Next.js 16 en App Router avec Server Actions, TypeScript strict, Prisma sur PostgreSQL, Auth.js v5 avec contrôle d'accès par rôle, validation Zod de toutes les entrées, SCSS Modules. Le cadrage impose des fichiers de moins de 300 lignes et une séparation nette entre interface, orchestration, domaine et accès aux données. Ce n'est pas de la coquetterie : c'est ce qui permet d'ajouter un fournisseur d'avis ou une règle anti-abus sans toucher au reste.