Adios
BlogCommerce électronique avec Next.js

Commerce électronique avec Next.js

Créer un panier et intégrer Stripe Checkout avec Next.js

Mettez en place dans Next.js des paniers anonymes et clients, des prix contrôlés par le serveur, Stripe Checkout, des webhooks signés, des commandes idempotentes et la validation des stocks.

Équipe AdiosMise à jour 17 juillet 20268 min de lecture

Un panier enregistre l’intention d’achat. Il ne valide pas un prix, ne réserve pas automatiquement le stock et ne prouve pas la fin du paiement.

Choisir l'identité du panier et sa durée de vie

Créez un jeton de panier aléatoire pour les acheteurs anonymes et stockez-le dans un cookie sécurisé HTTP-only. La base gère le panier, les identifiants de variantes des lignes, les quantités, les horodatages et le statut. Après la connexion, associez-le ou fusionnez-le avec celui du client selon des règles explicites.

Fixez une expiration après inactivité et décidez si les paniers passés en caisse deviennent des instantanés immuables. Ne stockez ni le panier entier ni les prix faisant foi dans un cookie lisible par le navigateur, où clients et extensions peuvent les modifier.

Valider chaque mutation du panier

Une action d’ajout reçoit un identifiant de variante et une quantité, retrouve le panier à partir du jeton contrôlé par le serveur, vérifie que la variante est active, applique les limites de quantité et insère ou met à jour la ligne. Elle n’accepte jamais du navigateur le propriétaire du compte, le prix du SKU ou la devise.

Renvoyez une erreur attendue pour les variantes invalides ou indisponibles. Revalidez l’affichage du panier après l’écriture pour que l’utilisateur voie sa mise à jour. Les contrôles d’autorisation et de propriété doivent rester dans l’action même si l’interface n’expose que des commandes valides.

"use server";

export async function addToCart(variantId: string, quantity: number) {
  const cart = await requireCart();
  const variant = await getPurchasableVariant(variantId);
  if (!variant) return { error: "Variant unavailable" };

  await upsertCartLine(cart.id, variant.id, clampQuantity(quantity));
  updateTag("cart:" + cart.id);
}

Fusionner des paniers anonymes et clients

À la connexion, chargez les deux paniers dans une transaction serveur. Combinez les variantes identiques dans les limites d’achat, gardez les lignes distinctes valides et signalez les articles retirés ou modifiés. Marquez le panier anonyme comme fusionné pour que rejouer la requête ne double pas les quantités.

Ne choisissez pas discrètement le panier le plus cher ou le plus récent. Le produit doit décider si le panier client prévaut, si les paniers fusionnent ou si l’acheteur choisit. Testez plusieurs onglets et des callbacks de connexion répétés.

Recalculer les prix avant Checkout

Chargez toutes les variantes actives et leurs prix actuels sur le serveur, appliquez les remises validées, calculez les données fiscales et les options d’expédition, puis créez un instantané de commande en attente. Si un prix ou une disponibilité a changé, renvoyez le panier mis à jour pour confirmation explicite au lieu de facturer discrètement un autre montant.

Construisez les lignes Stripe à partir des enregistrements serveur. Ajoutez l’identifiant de commande en attente aux métadonnées et utilisez une clé d’idempotence stable pour les tentatives de paiement répétées lorsque le besoin métier exige une seule opération chez le prestataire.

Confirmer le paiement par webhooks

L’URL de succès montre le retour de Checkout, pas le règlement du paiement. Vérifiez la signature du webhook sur le corps brut, retrouvez la commande en attente à partir de métadonnées fiables et enregistrez l’événement Stripe avec une contrainte d’unicité.

Créez ou marquez la commande définitive une seule fois, puis mettez en file l’e-mail de confirmation et son traitement. Un événement en double doit renvoyer un succès sans répéter ces effets. Une session expirée peut libérer les réservations et rendre le panier à nouveau modifiable.

export async function POST(request: Request) {
  const rawBody = await request.text();
  const signature = request.headers.get("stripe-signature");
  const event = stripe.webhooks.constructEvent(
    rawBody,
    signature,
    process.env.STRIPE_WEBHOOK_SECRET,
  );

  await processCheckoutEventOnce(event);
  return new Response(null, { status: 200 });
}

Protéger les lectures du panier et des commandes

Accéder à un panier anonyme exige le jeton impossible à deviner émis par le serveur. Les paniers et commandes clients exigent un propriétaire authentifié. Les pages de confirmation doivent charger la commande avec une référence sans risque d’exposition publique et le contexte du propriétaire, plutôt que d’exposer des identifiants séquentiels de base de données.

Renvoyez des DTO limités aux Client Components. Les objets du prestataire de paiement, champs internes de fraude, données webhook et erreurs de base restent sur le serveur. Les journaux doivent utiliser les identifiants de commande et d’événement sans afficher inutilement les données de paiement ou d’adresse.

Tester la falsification, les nouvelles tentatives et l’expiration

Modifiez la variante, la quantité et le prix affiché envoyés ; rejouez les actions d’ajout et de fusion ; livrez deux fois un webhook et des événements hors ordre ; faites expirer Checkout ; retirez du stock pendant le paiement ; demandez la commande d’un autre client. Chaque résultat doit être sûr et explicable.

Déployez la version de production sur Adios avec des références aux secrets Stripe et de base de données. Vérifiez la route HTTPS générée en mode test, consultez les journaux d’exécution et n’enregistrez le webhook stable du domaine personnalisé qu’après un contrôle de santé réussi.

Tous les articles