Trackily Docs

Reviews

TL;DR : tes acheteurs peuvent noter un produit de 1 à 5 étoiles et laisser un commentaire depuis /p/<slug>. Les reviews tombent en status='pending', tu les modères dans l'admin, le badge verified_purchase est calculé à l'insertion en cherchant une commande avec le même email.

Modération des reviews

Concept

Les reviews sont la preuve sociale la plus efficace pour transformer un visiteur tiède en acheteur. Trackily intègre un système complet de collecte, modération et affichage des avis clients (Phase 17, table product_reviews).

Pas besoin de Trustpilot, pas de plugin Yotpo, pas de tracking pixel tiers. Le visiteur soumet sa review sur la même page que celle où il a acheté, tu la modères en deux clics, elle apparaît sous le produit.

Anatomie d'une review (product_reviews)

Champ Type Rôle
id SERIAL identifiant interne
native_product_id INTEGER FK produit noté
customer_email TEXT email du client (clé du dédoublonnage et du verified_purchase)
customer_name TEXT nom affiché publiquement
rating SMALLINT 1-5 note (CHECK strict)
title TEXT titre court de la review
body TEXT corps du commentaire (markdown léger autorisé)
status TEXT pending / approved / rejected
verified_purchase BOOLEAN calculé à l'INSERT — true si une commande existe avec le même email pour ce produit
ip TEXT conservé pour anti-abuse
user_agent TEXT idem
created_at TIMESTAMPTZ date de soumission
moderated_at TIMESTAMPTZ date de décision admin
moderator_user_id INTEGER FK utilisateur qui a modéré

Trois garde-fous anti-spam

  1. Rate limiting par IP via pixelRateLimit (le même middleware qui protège les pixels de tracking). Une IP qui soumet trop de reviews en peu de temps est ralentie côté serveur.
  2. Une review par couple (produit, email) grâce à l'index unique :
    CREATE UNIQUE INDEX idx_reviews_one_per_buyer
      ON product_reviews(native_product_id, LOWER(customer_email));
    
    Re-soumettre depuis le même email met à jour la review existante (le code applicatif fait un upsert), il n'en crée pas une nouvelle. Pratique pour un client qui veut revenir sur son avis après plus d'expérience produit.
  3. Gate allowed_domains : si le produit a un allowed_domains non vide, le POST /api/reviews est refusé quand la requête arrive depuis un host hors whitelist. Tu évites qu'un concurrent qui clone ta landing puisse seeder des reviews sous ton product_id.

Modération par défaut

status='pending' à l'INSERT. La review n'est pas publique tant qu'un admin n'a pas cliqué "Approve". Trois choix dans le moderation queue :

  • Approve → status passe à approved, la review apparaît sur /p/<slug> et sur /api/reviews/product/:id.
  • Reject → status passe à rejected. La review reste en base pour audit mais n'est plus visible.
  • Delete → suppression définitive de la ligne.

Le mode "auto-approval" n'est pas activé par défaut, parce qu'un système d'avis non-modéré est rapidement empoisonné par les concurrents et les bots. Si tu veux malgré tout l'activer pour un produit particulier (déjà connu, faible risque), tu peux flipper un flag dans les settings (commerce_reviews_auto_approve). Recommandé seulement quand tu reçois 50+ reviews / semaine et que la modération devient un goulet.

verified_purchase : comment c'est calculé

À l'INSERT de la review, le code applicatif exécute :

SELECT EXISTS (
  SELECT 1
    FROM orders o
    JOIN order_items oi ON oi.order_id = o.id
   WHERE LOWER(o.customer_email) = LOWER($1)
     AND oi.product_id = $2
     AND o.payment_status IN ('paid', 'refunded', 'partial_refund')
) AS verified;

Le flag est sauvé sur la review (donc immutable après coup — sauf si tu le re-calcules manuellement). Tous les statuts paiement qui prouvent un achat (paid, refunded, partial_refund) comptent. Les commandes encore pending ne déclenchent pas le verified — un acheteur qui poste une review avant que son paiement n'aboutisse aura un badge non-verified ; il devra repost s'il veut le badge (ou tu peux flipper le bit à la main en base).

Affichage frontal : un badge "✓ Verified purchase" en vert sous le nom du reviewer.

Comment faire (UI + MCP)

Côté client : soumettre une review

Le formulaire est rendu en bas de /p/<slug>. Le visiteur :

  1. Clique sur les 5 étoiles (1 à 5).
  2. Tape un titre (court — 60 chars max recommandés).
  3. Tape un commentaire (max 2000 chars).
  4. Renseigne son nom et email.
  5. Submit → POST /api/reviews.
  6. Voit "Merci, votre avis sera publié après modération."

L'endpoint :

POST /api/reviews
Content-Type: application/json

{
  "product_id": 12,
  "customer_email": "marie@example.com",
  "customer_name": "Marie L.",
  "rating": 5,
  "title": "Effet ressenti dès la deuxième semaine",
  "body": "Je dors mieux et je retiens mieux ce que je lis. Bravo."
}

Réponse :

{ "ok": true, "review_id": 87, "verified_purchase": true }

Côté admin : modérer

  1. Navigue vers Reviews dans la sidebar (ou tape /admin#native-reviews).
  2. Tu vois la queue triée par défaut sur les status='pending'. Filter par produit, par rating, par verified.
  3. Pour chaque ligne, trois boutons : Approve / Reject / Delete.
  4. Le bouton Approve all from this email te permet de white-list un client de confiance (toutes ses reviews pending passent à approved en un clic).

Côté MCP

Pas d'outil MCP dédié aux reviews dans l'inventaire actuel — la collection se fait via le formulaire public, la modération via l'admin UI. Si tu as besoin de scripter (par exemple importer 50 reviews depuis Trustpilot), passe par l'admin REST :

POST /admin/api/reviews/bulk-import
Body: { "product_id": 12, "reviews": [ {...}, {...} ] }

…à l'aide d'un script Node interne, le endpoint est protégé par authMiddleware.

Endpoints publics

Méthode URL Rôle
POST /api/reviews soumettre une review (rate-limité, allowed_domains gate)
GET /api/reviews/product/:id lister les reviews approuvées + aggregate (count, avg)

GET /api/reviews/product/:id réponse type :

{
  "ok": true,
  "reviews": [
    {
      "id": 87,
      "customer_name": "Marie L.",
      "rating": 5,
      "title": "Effet ressenti…",
      "body": "Je dors mieux…",
      "verified_purchase": true,
      "created_at": "2026-05-12T08:43:21Z"
    }
  ],
  "count": 24,
  "avg": 4.6,
  "distribution": { "1": 1, "2": 0, "3": 2, "4": 5, "5": 16 }
}

Utile si tu veux embed les reviews dans un autre site (widget JS qui fetch cet endpoint).

Exemples concrets

1. Workflow de lancement

Tu lances Memo Mind. Premiers 30 jours :

  1. Tu envoies un email post-purchase (séquence J+15, J+25) qui pointe sur /p/memo-mind#reviews-form avec un lien direct ancre.
  2. 18 % des clients laissent un avis (moyenne typique pour les suppléments). 80 % en 5 étoiles, 15 % en 4, 5 % en 3 ou moins.
  3. Tu modères tout au début pour ne pas laisser passer un troll ; tu approuves 95 % des soumissions, tu rejettes les 5 % manifestement spam.
  4. Tu actives commerce_reviews_auto_approve une fois passé le cap de 50 reviews validées — tu ne modères plus que les 1 ou 2 étoiles (alerté par une rule Automizer).

2. Reviews comme social proof sur la landing AI

Quand generate_landing_for_product génère une landing, le template inclut un slot "Reviews" qui pull les 3 dernières reviews approuvées avec rating ≥ 4. Le slot affiche aussi l'aggregate (par exemple "4.6 / 5 — 124 avis vérifiés"). Si tu n'as pas encore de reviews, le slot est masqué automatiquement.

3. Detect anomaly : pluie de mauvaises reviews

Crée une rule Automizer :

  • Trigger : product_reviews.rating <= 2
  • Condition : >= 3 reviews ≤ 2 étoiles soumises dans les dernières 24h sur le même native_product_id.
  • Action : webhook Slack vers ton channel #alerts + pause la campagne attachée au produit (pause_campaign).

Voir Automizer → Rules pour l'implémentation détaillée.

Erreurs courantes

  • "La review n'apparaît pas malgré l'approve" — vérifie que le produit n'est pas en status='draft' (les reviews approuvées d'un produit draft ne s'affichent pas non plus, parce que la page produit n'est pas accessible).
  • "Le verified_purchase est false alors que le client a acheté" — le verified est calculé à l'INSERT. Si le client a acheté après avoir laissé sa review, le flag reste false. Re-poster la review depuis le même email mettra à jour la ligne et re-calculera le flag.
  • "Index unique violation" — le client soumet une review pour un produit où il a déjà une review (sous le même email). Le code applicatif fait un upsert, donc l'erreur ne devrait pas remonter. Si elle remonte, c'est probablement parce que tu attaques l'endpoint à la main avec une casse différente sur l'email.
  • "Reviews rejetées par allowed_domains" — le produit a une whitelist non vide et le visiteur arrive depuis un host non listé. Ajoute le host à allowed_domains ou retire la whitelist.
  • "Spam massif soudain" — la rate limiting IP n'arrête qu'un IP à la fois. Si tu te fais arroser depuis un botnet, passe le produit en status='draft' quelques heures (les soumissions sont refusées 404), nettoie en masse via SQL DELETE FROM product_reviews WHERE created_at > X AND status='pending', ajoute un CAPTCHA front-end le temps d'investiguer.
  • "Comment supprimer définitivement une review insultante ?"Delete dans l'admin UI fait un DELETE physique. Pas de soft-delete sur cette table.

Voir aussi