Introduction
L'abandon de panier représente en moyenne 70 % des sessions e-commerce. Un système de relance bien conçu peut récupérer jusqu'à 15 % de ces ventes perdues. Ce tutoriel vous guide pas à pas dans la construction d'une solution complète avec Next.js : détection des paniers inactifs, envoi d'emails automatisés et interface de relance. Nous utilisons Prisma pour la persistance et Resend pour les emails transactionnels.
Prérequis
- Node.js 20+
- Next.js 15
- Compétences de base en TypeScript
- Compte Resend et base de données PostgreSQL
- Connaissances Prisma
Initialisation du projet
npx create-next-app@latest cart-recovery --yes
cd cart-recovery
npm install prisma @prisma/client resend date-fns
npx prisma initNous initialisons un projet Next.js et installons les dépendances essentielles pour la base de données et l'envoi d'emails.
Schéma Prisma du panier
model Cart {
id String @id @default(cuid())
userId String?
sessionId String @unique
items Json
status String @default("active")
lastActive DateTime @default(now())
createdAt DateTime @default(now())
}Le modèle Cart stocke les articles, le statut et la dernière activité. Le champ lastActive est essentiel pour détecter les abandons.
Route API de mise à jour du panier
import { NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
export async function POST(req: Request) {
const { sessionId, items } = await req.json();
const cart = await prisma.cart.upsert({
where: { sessionId },
update: { items, lastActive: new Date() },
create: { sessionId, items }
});
return NextResponse.json(cart);
}Cette route met à jour le panier à chaque modification et rafraîchit lastActive pour éviter les faux positifs d'abandon.
Fonction de détection des abandons
import { prisma } from './prisma';
import { subHours } from 'date-fns';
export async function findAbandonedCarts() {
const threshold = subHours(new Date(), 2);
return prisma.cart.findMany({
where: {
status: 'active',
lastActive: { lt: threshold }
}
});
}Cette fonction identifie les paniers inactifs depuis plus de 2 heures, critère configurable selon votre cycle d'achat.
Envoi d'email de relance
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function sendAbandonmentEmail(cart: any) {
await resend.emails.send({
from: 'shop@learni.dev',
to: cart.userEmail || 'client@example.com',
subject: 'Votre panier vous attend',
html: `<p>Vous avez laissé ${cart.items.length} articles.</p>`
});
await prisma.cart.update({
where: { id: cart.id },
data: { status: 'reminded' }
});
}L'email est envoyé via Resend et le statut est mis à jour pour éviter les envois multiples.
Cron job de relance
import { findAbandonedCarts } from '@/lib/detectAbandonment';
import { sendAbandonmentEmail } from '@/lib/sendReminder';
export async function GET() {
const carts = await findAbandonedCarts();
for (const cart of carts) {
await sendAbandonmentEmail(cart);
}
return Response.json({ processed: carts.length });
}Ce endpoint cron peut être appelé par Vercel Cron ou un service externe toutes les heures.
Bonnes pratiques
- Toujours respecter le RGPD et proposer une désinscription claire
- Personnaliser les emails avec le contenu exact du panier
- Limiter les relances à 2 ou 3 maximum par panier
- Mesurer le taux de récupération avec des événements analytics
- Tester les emails sur plusieurs clients mail
Erreurs courantes à éviter
- Oublier de mettre à jour lastActive à chaque action utilisateur
- Envoyer des emails sans vérifier le statut du panier
- Utiliser des seuils de temps trop courts ou trop longs
- Ne pas gérer les erreurs d'envoi email (retry logic)
Pour aller plus loin
Découvrez nos formations avancées sur l'e-commerce moderne et l'automatisation marketing sur Learni Group.