Commerce électronique avec Next.js
Créer une boutique Next.js pour la production : catalogue, paiement, stocks et SEO
Créez une boutique Next.js 16 prête pour la production avec produits, variantes, paniers, prix contrôlés par le serveur, Stripe Checkout, stocks, SEO et déploiement.
Une boutique en ligne devient un système de commerce lorsque le catalogue, les prix, les stocks, les paiements, les commandes et leur traitement doivent rester cohérents malgré des requêtes simultanées.
Définir les responsabilités du système de commerce
Trail Supply sert de fil conducteur : une boutique de plein air qui vend l’Alpine Shell en plusieurs couleurs et tailles. La vitrine présente le catalogue ; la couche commerciale fait autorité pour les prix, les stocks, les paniers, les paiements et les commandes ; les prestataires externes gèrent le paiement et les e-mails. Définissez ces responsabilités avant de choisir les composants.
Définissez la source de vérité de chaque valeur. Un système de contenu peut gérer les descriptions éditoriales, tandis qu’une base de données commerciale gère les références SKU, les prix et les stocks. La page produit peut combiner ces données, mais le paiement ne doit jamais se fier au contenu éditorial ni à l’état du navigateur pour les valeurs transactionnelles.
- —Catalogue : produits, variantes, catégories, médias et état de publication.
- —Commerce : prix, devise, données fiscales, stocks, paniers, commandes et remboursements.
- —Identité : jeton de panier anonyme, compte client, adresses et autorisations.
- —Exploitation : événements de paiement, tâches de traitement des commandes, e-mails, contrôles de santé, journaux et versions.
Modéliser les produits et les variantes disponibles à l’achat
Un produit décrit le modèle Alpine Shell dans son ensemble. Une variante correspond à une combinaison précise proposée à la vente, par exemple bleu, taille M, avec sa propre référence SKU, son prix, son stock et son statut. Associez les slugs lisibles à des identifiants stables pour pouvoir changer les URL sans perturber les commandes ni le rapprochement des événements.
Distinguez ce qui peut être publié de ce qui peut être acheté. Un produit peut rester visible quand une taille est indisponible ; un article retiré de la vente peut rester utile pour l’assistance et la recherche alors que le paiement est désactivé. Garantissez l’unicité des références SKU et la validité des relations de propriété avec des contraintes en base, sans vous appuyer uniquement sur la validation des formulaires.
type Product = {
id: string;
slug: string;
name: string;
published: boolean;
};
type Variant = {
id: string;
productId: string;
sku: string;
color: string;
size: string;
priceId: string;
active: boolean;
};Rendre les routes du catalogue sur le serveur
Utilisez les Server Components pour les catégories et les produits afin que les requêtes directes reçoivent les titres, descriptions, prix, liens produit et disponibilités. Réservez les Client Components à la galerie, au sélecteur de taille et à l’ajout au panier. Le client reçoit une vue limitée aux données utiles à l’achat, sans accès libre aux enregistrements de la base.
Choisissez la stratégie de cache en fonction du cycle de vie du contenu. Les descriptions et images des produits peuvent évoluer lentement ; les prix et les stocks demandent des règles plus strictes. Mettez en cache les lectures stables du catalogue avec des tags de produit et de catégorie, puis revalidez-les après une mise à jour approuvée. Le passage en caisse recharge toujours les valeurs transactionnelles.
Traiter le panier comme une proposition
Un client anonyme peut recevoir un jeton de panier aléatoire dans un cookie HTTP-only ; l’enregistrement associé en base stocke les identifiants des variantes et leurs quantités. Après la connexion, fusionnez ce panier avec celui du client selon des règles explicites pour les doublons, les variantes indisponibles et les limites de quantité. Ne stockez pas les prix faisant foi dans un cookie modifiable.
La page du panier peut afficher une estimation calculée, mais chaque mutation et demande de paiement doit recharger les variantes, les prix, les limites d’achat et les stocks sur le serveur. Si un produit a changé, renvoyez les corrections pour chaque ligne plutôt que de facturer discrètement un autre montant.
Finaliser le paiement de façon asynchrone
Créez la session Stripe Checkout sur le serveur à partir du panier validé et ajoutez un identifiant stable de panier ou de commande en attente dans les métadonnées. L’URL de retour peut indiquer que le paiement est en cours de confirmation, mais elle ne peut pas créer à elle seule la commande définitive.
Vérifiez la signature de Stripe sur le corps brut du webhook, enregistrez l’identifiant d’événement avec une contrainte d’unicité et créez ou faites avancer la commande une seule fois. Gérez les sessions terminées ou expirées, les paiements échoués, les remboursements et les événements répétés. Placez les e-mails et le traitement des commandes dans une file d’attente une fois l’état de la commande enregistré durablement.
const session = await stripe.checkout.sessions.create({
mode: "payment",
line_items: pricedCart.lines.map((line) => ({
price: line.stripePriceId,
quantity: line.quantity,
})),
success_url: SITE_URL + "/orders/confirm?session_id={CHECKOUT_SESSION_ID}",
cancel_url: SITE_URL + "/cart",
metadata: { pendingOrderId: pendingOrder.id },
});Garder une gestion transactionnelle des stocks
Décidez quand réserver le stock : à l’ajout au panier, à la création de la session de paiement ou à la confirmation du paiement. Les réservations au panier réduisent la disponibilité apparente et doivent expirer rapidement ; réserver au paiement risque d’accepter plus de sessions simultanées que le stock ne le permet. Le mode de traitement des commandes détermine ce compromis.
Quelle que soit votre règle, mettez les stocks à jour de façon atomique et empêchez les quantités négatives en base. Enregistrez l’expiration des réservations et rapprochez explicitement les paiements tardifs. Les caches peuvent afficher la disponibilité, mais la table de stock et la transaction décident si une commande peut réserver les articles.
Construire la découverte des produits et le SEO
Définissez un modèle canonique précis pour chaque produit. Générez les titres, descriptions, images Open Graph, données JSON-LD Product et Offer, fils d’Ariane et entrées du sitemap à partir des enregistrements publiés. Gardez les prix et la disponibilité cohérents avec la page visible et le parcours de paiement.
Les pages de catégorie doivent pointer vers les produits importants à l’aide de véritables liens HTML. Traitez les filtres comme un état de l’application, sauf si une combinaison choisie répond à une demande distincte et possède son propre contenu. Pour un produit retiré de la vente, choisissez délibérément entre une réponse 200, une redirection, un code 404 ou un code 410, selon l’utilité que la page conserve pour les clients.
Déployer et tester le parcours transactionnel
Adios exécute le serveur de production Next.js complet dans un processus persistant : les Server Components, les endpoints du panier, les webhooks signés et les pages de commande partagent une même version. Le manifeste décrit la compilation, le démarrage, la route de contrôle de santé, la dépendance à la base de données et les références aux secrets, aux côtés du code source.
Déployez un aperçu et testez le HTML des produits, un panier falsifié, deux acheteurs simultanés, Checkout en mode test, les événements en double, une session expirée, l’envoi des e-mails et un produit inconnu. Consultez les journaux de compilation et d’exécution, puis ne promouvez la version qu’après un contrôle de santé réussi. Le domaine personnalisé et le TLS géré restent associés à la version vérifiée.
adios.yaml
name: trail-supply
build_cmd: npm ci && npm run build
start_cmd: npm start
runtime:
name: node@24
port: 3000
health_path: /api/health
requires:
- db
env:
DATABASE_URL: secret://DATABASE_URL
STRIPE_SECRET_KEY: secret://STRIPE_SECRET_KEY
STRIPE_WEBHOOK_SECRET: secret://STRIPE_WEBHOOK_SECRET