Retour au blog
20 avril 2026 Arthur Fabbri

Intégrer Stripe dans Next.js : du checkout aux webhooks

Session checkout, page succès, webhooks, Stripe CLI — guide complet pour accepter des paiements avec Stripe dans une app Next.js 15.

Stripe est LA référence pour les paiements en ligne. Voici comment l'intégrer proprement dans Next.js, de la session checkout à la gestion des webhooks.

Installation

npm install stripe @stripe/stripe-js

Variables d'environnement :

STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

1. Créer une session Checkout

// app/api/checkout/route.ts
import Stripe from "stripe";
import { NextRequest, NextResponse } from "next/server";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(req: NextRequest) {
  const { priceId, quantity = 1 } = await req.json();

  const session = await stripe.checkout.sessions.create({
    payment_method_types: ["card"],
    line_items: [{ price: priceId, quantity }],
    mode: "payment", // ou "subscription" pour abonnement
    success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/shop`,
    locale: "fr",
  });

  return NextResponse.json({ url: session.url });
}

2. Bouton d'achat côté client

// components/buy-button.tsx
"use client";

import { useState } from "react";

export function BuyButton({ priceId }: { priceId: string }) {
  const [loading, setLoading] = useState(false);

  async function handleClick() {
    setLoading(true);
    const res = await fetch("/api/checkout", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ priceId }),
    });
    const { url } = await res.json();
    window.location.href = url;
  }

  return (
    <button
      onClick={handleClick}
      disabled={loading}
      className="bg-primary text-white px-6 py-3 rounded-xl disabled:opacity-60"
    >
      {loading ? "Redirection…" : "Acheter"}
    </button>
  );
}

3. Page succès

// app/success/page.tsx
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

interface Props {
  searchParams: Promise<{ session_id?: string }>;
}

export default async function SuccessPage({ searchParams }: Props) {
  const { session_id } = await searchParams;

  if (!session_id) return <p>Session invalide.</p>;

  const session = await stripe.checkout.sessions.retrieve(session_id, {
    expand: ["line_items", "customer"],
  });

  return (
    <div className="text-center py-24">
      <div className="text-6xl mb-4">🎉</div>
      <h1 className="text-2xl font-bold mb-2">Paiement confirmé !</h1>
      <p>Merci pour votre achat. Un email de confirmation a été envoyé.</p>
      <p className="text-muted mt-2">
        Total : {(session.amount_total! / 100).toFixed(2)} €
      </p>
    </div>
  );
}

4. Webhooks Stripe

Les webhooks sont essentiels pour mettre à jour votre BDD après un paiement.

// app/api/webhooks/stripe/route.ts
import Stripe from "stripe";
import { NextRequest, NextResponse } from "next/server";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(req: NextRequest) {
  const body = await req.text();
  const sig = req.headers.get("stripe-signature")!;

  let event: Stripe.Event;

  try {
    event = stripe.webhooks.constructEvent(
      body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch {
    return NextResponse.json({ error: "Signature invalide" }, { status: 400 });
  }

  switch (event.type) {
    case "checkout.session.completed": {
      const session = event.data.object as Stripe.Checkout.Session;
      // Mettre à jour la BDD, envoyer un email, etc.
      console.log("Paiement reçu :", session.customer_email);
      break;
    }
    case "customer.subscription.deleted": {
      // Révoquer l'accès
      break;
    }
  }

  return NextResponse.json({ received: true });
}

5. Tester avec Stripe CLI

# Installer Stripe CLI
stripe login

# Forwarder les webhooks en local
stripe listen --forward-to localhost:3000/api/webhooks/stripe

# Déclencher un événement test
stripe trigger checkout.session.completed

Carte de test : 4242 4242 4242 4242 — n'importe quelle date future, n'importe quel CVC.

Abonnements (mode subscription)

const session = await stripe.checkout.sessions.create({
  mode: "subscription",
  line_items: [{ price: "price_monthly_id", quantity: 1 }],
  // ...
  subscription_data: {
    trial_period_days: 14, // essai gratuit
  },
});

Conclusion

Stripe + Next.js = une stack de paiement robuste en quelques heures. Les points clés : toujours vérifier la signature webhook, ne jamais faire confiance au client pour valider les montants, et utiliser Stripe CLI pour tester localement.