Intégration Stripe Checkout avec React/Next.js
1. Introduction
Stripe est une plateforme de paiement en ligne qui permet aux entreprises et aux particuliers d'accepter et de gérer des paiements sur internet. On peut le voir un peu comme un intermédiaire sécurisé entre un client qui paye et l'entreprise qui reçoit l'argent. Dans ce tutoriel, nous allons apprendre à intégrer Stripe Checkout dans une application React/Next.js pour accepter des paiements en ligne de manière sécurisée.
Stripe propose deux méthodes principales pour intégrer les paiements :
1. Stripe Checkout
Il s'agit d'une page de paiement hébergée par Stripe et prête à l'emploi. Ses caractéristiques :
- Configuration rapide avec juste quelques lignes de code
- Sécurité gérée entièrement par Stripe
- Interface multilingue et responsive automatique
- Supporte cartes bancaires, Apple Pay, Google Pay, etc.
- Parfait pour démarrer rapidement
2. Stripe Elements / Payment Element
C'est un ensemble de composants UI personnalisables intégrés dans ton site. Ses caractéristiques :
- Personnalisation totale de l'interface
- Plus de contrôle sur l'expérience utilisateur
- Utilisateur reste sur ton site
- Plus complexe à implémenter
- Recommandé pour des besoins avancés
2. Configuration et installation
Créer un compte Stripe
Pour commencer, nous allons créer un compte sur Stripe Dashboard.
Récupérer les clés API
Stripe utilise deux types de clés pour sécuriser les transactions :
La clé publique (Publishable Key)
Elle commence par pk_test_ en mode test ou pk_live_ en production et est utilisée côté client. Elle va nous permettre d'initialiser Stripe.js dans notre application.
La clé secrète (Secret Key)
Elle commence par sk_test_ en mode test ou sk_live_ en production et est utilisée côté serveur. Eelle va nous permettre de créer des sessions de paiement et d'effectuer des opérations sensibles.
Pour récupérer les clés :
- Connecte-toi à votre Dashboard Stripe
- Active le "Mode Test" en haut à droite
- Vas dans Développeurs puis Clés API
- Copie la "Clé publique" et la "Clé secrète"
Installer les librairies Stripe
Nous allons installer deux packages npm essentiels :
Dans le terminal, à la racine de ton projet Next.js/React, exécute :
npm install @stripe/stripe-js stripeConfigurer les variables d'environnement
Nous allons maintenant stocker nos deux clés d'API dans un fichier.env. À la racine de ton projet, crée un fichier .env et copies-y le code suivant en y mettant les valeurs de tes clés :
# Clé publique
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxx
# Clé secrète
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxx3. Créer une API Route pour Stripe Checkout
Maintenant que notre environnement est configuré, nous allons créer une API route côté serveur qui génère une session de paiement Stripe.
Le flux de paiement ci-dessous va t(aider à comprendre les enchaînements depuis le clic de l'utilisateur sur le bouton "Payer" jusqu'à l'enregistrement du paiement.
Flux de paiement :
- L'utilisateur clique sur "Payer" dans l'interface React
- Le frontend appelle cette API route avec les détails du produit
- L'API crée une session Checkout sur Stripe
- Stripe retourne un
session.idunique - Le frontend redirige l'utilisateur vers la page Checkout Stripe
- L'utilisateur paie sur la page Stripe
- Stripe redirige l'utilisateur vers un
success_urlsi le paiement passe et uncancel_urlsi l'utilisateur annule le paiement.
Structure du fichier
Dans ton projet, crée la route suivante :
app/api/stripe/checkout/route.tsMets-y le code suivant :
import { NextResponse } from "next/server";
import Stripe from "stripe";
// Initialisation de Stripe avec la clé secrète
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
export async function POST() {
try {
// Création d'une session Checkout
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
// Ce mode de paiement peut être "subscription" dans le cas d'un abonnement
mode: "payment",
line_items: [
{
price_data: {
currency: "eur",
product_data: {
name: "Produit",
description: "Page de paiement",
},
unit_amount: 2000, // Prix en centimes
},
quantity: 1, // Quantité
},
],
// URL de redirection après paiement réussi
success_url: "http://localhost:3000/success",
// URL de redirection si l'utilisateur annule
cancel_url: "http://localhost:3000/cancel",
});
// Retourne l'ID de session au client
return NextResponse.json({ id: session.id });
} catch (err) {
// Gestion des erreurs
console.error("Erreur Stripe:", err);
return NextResponse.json({ error: err.message }, { status: 500 });
}
}4. Créer un composant React pour déclencher le paiement
Maintenant que notre API est prête, créons un composant React qui permet à l'utilisateur de lancer le processus de paiement. Ce composant va appeler notre API, récupérer l'ID de session, puis rediriger l'utilisateur vers la page Stripe Checkout.
Créer le fichier du composant
Crée un nouveau fichier pour le composant :
components/StripeButton.jsMets-y le code suivant :
"use client";
import { loadStripe } from "@stripe/stripe-js";
import { useState } from "react";
const stripePromise = loadStripe(
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
);
export default function StripeButton() {
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
try {
const res = await fetch("/api/checkout", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
const data = await res.json();
if (data.error) {
console.error("Erreur:", data.error);
alert("Erreur lors de la création du paiement");
setLoading(false);
return;
}
const stripe = await stripePromise;
const { error } = await stripe.redirectToCheckout({
sessionId: data.id
});
if (error) {
console.error("Erreur Stripe:", error);
alert(error.message);
}
} catch (err) {
console.error("Erreur:", err);
alert("Une erreur est survenue");
} finally {
setLoading(false);
}
};
return (
<button
onClick={handleClick}
disabled={loading}
className="bg-indigo-600 text-white px-6 py-3 rounded-lg hover:bg-indigo-700
disabled:bg-gray-500 disabled:cursor-not-allowed transition"
>
{loading ? "Chargement..." : "Payer avec Stripe"}
</button>
);
}5. Utiliser le composant dans l'application
Maintenant que notre composant est prêt, intégrons-le dans une page de notre application. Voici un exemple d'une page de checkout.
Crée un fichier app/checkout/page.js et mets-y le code suivant:
import StripeButton from "@/components/StripeButton";
export default function CheckoutPage() {
return (
<div className="flex flex-col items-center justify-center min-h-screen
bg-gradient-to-br from-gray-900 to-black text-white p-6">
<div className="max-w-md w-full bg-gray-800 rounded-2xl shadow-2xl p-8">
<h1 className="text-3xl font-bold mb-2 text-center">
Finaliser votre achat
</h1>
<p className="text-gray-400 text-center mb-8">
Paiement sécurisé via Stripe
</p>
<div className="bg-gray-700 rounded-lg p-6 mb-6">
<div className="flex justify-between items-center mb-4">
<span className="text-lg font-semibold">Produit</span>
<span className="text-2xl font-bold text-green-400">20,00 €</span>
</div>
<p className="text-gray-400 text-sm">
Formation complète React avec certificat
</p>
</div>
<div className="flex items-center gap-2 text-sm text-gray-400 mb-6">
<svg className="w-5 h-5 text-green-400" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0
01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clipRule="evenodd" />
</svg>
<span>Paiement 100% sécurisé par Stripe</span>
</div>
<StripeButton />
<p className="text-xs text-gray-500 text-center mt-6">
En poursuivant, vous acceptez nos conditions générales de vente.
</p>
</div>
</div>
);
}Exemple avancé : Page de panier e-commerce
Pour un cas plus réaliste avec plusieurs produits :
"use client";
import { useState } from "react";
import StripeButton from "@/components/StripeButton";
export default function CartPage() {
const [cartItems] = useState([
{ id: 1, name: "Formation React", price: 99, quantity: 1 },
{ id: 2, name: "Ebook JavaScript", price: 15, quantity: 2 },
]);
const total = cartItems.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
);
return (
<div className="min-h-screen bg-gray-900 text-white p-8">
<div className="max-w-4xl mx-auto">
<h1 className="text-4xl font-bold mb-8">Votre panier</h1>
<div className="bg-gray-800 rounded-lg p-6 mb-6">
{cartItems.map((item) => (
<div key={item.id} className="flex justify-between items-center py-4 border-b border-gray-700 last:border-0">
<div>
<h3 className="font-semibold text-lg">{item.name}</h3>
<p className="text-gray-400 text-sm">Quantité: {item.quantity}</p>
</div>
<div className="text-right">
<p className="text-xl font-bold">{item.price * item.quantity} €</p>
<p className="text-gray-400 text-sm">{item.price} € x {item.quantity}</p>
</div>
</div>
))}
<div className="flex justify-between items-center pt-6 border-t-2 border-indigo-600 mt-6">
<span className="text-2xl font-bold">Total</span>
<span className="text-3xl font-bold text-green-400">{total} €</span>
</div>
</div>
<StripeButton />
</div>
</div>
);
}Les pages de succès et d'annulation
N'oublie pas de créer les pages vers lesquelles Stripe redirige après le paiement. Pour la redirection après annulation du paiement, tu peux mettre le lien d'origine.
Voici un exemple de page de après succès du paiement :
Page de succès
import Link from "next/link";
export default function SuccessPage() {
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-900 text-white p-6">
<div className="max-w-md w-full bg-gray-800 rounded-2xl shadow-2xl p-8 text-center">
<div className="w-20 h-20 bg-green-500 rounded-full flex items-center justify-center mx-auto mb-6">
<svg className="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg>
</div>
<h1 className="text-3xl font-bold mb-4 text-green-400">
Paiement réussi !
</h1>
<p className="text-gray-300 mb-8">
Merci pour votre achat. Un email de confirmation vous a été envoyé.
</p>
<Link
href="/"
className="inline-block bg-indigo-600 hover:bg-indigo-700 text-white px-6 py-3 rounded-lg transition"
>
Retour à l'accueil
</Link>
</div>
</div>
);
}6. Tester notre intégration
Avant de passer en production, testons notre intégration avec les cartes de test factices de Stripe pour simuler différents scénarios :
| Numéro de carte | Scénario |
|---|---|
4242 4242 4242 4242 | Paiement réussi |
4000 0025 0000 3155 | Authentification 3D Secure requise |
4000 0000 0000 9995 | Carte insuffisamment provisionnée |
4000 0000 0000 0002 | Carte refusée |
Informations à utiliser avec les cartes de test :
- Date d'expiration : N'importe quelle date future
- CVC : N'importe quel code à 3 chiffres
- Code postal : N'importe quel code postal valide
En cas d'erreur
- Erreur "Invalid API Key" :
Vérifie que tes clés dans
.envsont correctes et que tu as redémarré le serveur. - Le bouton ne redirige pas :
Ouvre la console du navigateur pour voir les erreurs. Vérifie que
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYest bien défini. - Erreur 500 sur /api/checkout :
Vérifie les logs du serveur. Assure-toi que
STRIPE_SECRET_KEYest correcte et que le packagestripeest installé.
7. Passer en production
Une fois les tests validés, voici comment déployer l'intégration Stripe en production.
Étape 1 : Active ton compte Stripe
- Connecte-toi au Stripe Dashboard
- Clique sur "Activer votre compte" en haut
- Complète les informations requises :
- Informations sur ton entreprise
- Informations bancaires pour recevoir les paiements
- Documents légaux si demandés
- Attend la validation de Stripe. Elle prend généralement 24 à 48h
Étape 2 : Récupère les clés de production
Une fois ton compte activé, récupère les clés Live :
- Dans le Dashboard, désactive le mode Test
- Vas dans Développeurs puis Clés API
- Copie tes clés Live
⚠️ Les clés Live permettent de traiter de vrais paiements avec de vraies cartes.
Étape 3 : Configurer les variables d'environnement en production
Selon ta plateforme d'hébergement, voici configure tes variables d'environnement.
Étape 4 : Mettre à jour les URLs de redirection
Dans ton API route, remplace les URLs localhost par tes vraies URLs :
// Avant (développement)
success_url: "http://localhost:3000/success",
cancel_url: "http://localhost:3000/cancel",
// Après (production)
success_url: "https://tonsite.com/success",
cancel_url: "https://tonsite.com/cancel",Checklist avant le lancement
- ☐Compte Stripe activé et vérifié
- ☐Clés Live configurées dans les variables d'environnement
- ☐URLs de redirection mises à jour avec ton domaine
- ☐Test effectué avec une vraie carte en production
- ☐Webhooks configurés (optionnel, section suivante)
8. Webhooks (Optionnel mais efficace)
Les webhooks permettent à Stripe de notifier votre serveur en temps réel lorsqu'un événement se produit. Il peut s'agir d'un paiement réussi, un paiement en échec ou un remboursement. C'est essentiel pour synchroniser la base de données.
Pourquoi utiliser les webhooks ?
Les redirections avec success_url et cancel_url ne sont pas fiables à 100% car l'utilisateur peut :
- fermer son navigateur avant la redirection
- perdre sa connexion internet
- ou ne jamais revenir sur le site
Les webhooks garantissent que tu es toujours notifié du paiement, même si l'utilisateur disparaît.
Créer un endpoint webhook
Crée un fichier app/api/stripe/webhook/route.ts et, mets-y le code suivant :
import { NextResponse } from "next/server";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
export async function POST(req) {
const body = await req.text();
const signature = req.headers.get("stripe-signature");
let event;
try {
event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
} catch (err) {
console.error("Webhook signature verification failed:", err.message);
return NextResponse.json({ error: "Invalid signature" }, { status: 400 });
}
switch (event.type) {
case "checkout.session.completed":
const session = event.data.object;
console.log("Paiement réussi:", session.id);
// TODO: Enregistrer la commande dans ta base de données
// TODO: Envoyer un email de confirmation
// TODO: Débloquer l'accès au produit
break;
case "payment_intent.payment_failed":
const paymentIntent = event.data.object;
console.log("Paiement échoué:", paymentIntent.id);
// TODO: Notifier l'utilisateur de l'échec
break;
default:
console.log(`Événement non géré: ${event.type}`);
}
return NextResponse.json({ received: true });
}Configurer le webhook dans Stripe
- Va dans Développeurs puis Webhooks
- Clique sur "Ajouter un endpoint"
- URL de l'endpoint :
https://tonsite.com/api/webhook - Sélectionne les événements à écouter :
checkout.session.completedpayment_intent.succeededpayment_intent.payment_failed
- Copie le Signing secret généré
- Ajoute-le dans les variables d'environnement :
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxx
Développement local : Pour tester les webhooks en local, utilise leStripe CLIqui crée un tunnel sécurisé vers ton localhost.
9. Bonnes pratiques et astuces
Gestion des prix dynamiques
Au lieu de coder les prix en dur, tu peux les passer en paramètre :
export async function POST(req) {
const { items } = await req.json(); // Récupère les produits depuis le client
const line_items = items.map(item => ({
price_data: {
currency: "eur",
product_data: { name: item.name },
unit_amount: item.price * 100, // Conversion en centimes
},
quantity: item.quantity,
}));
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
mode: "payment",
line_items,
success_url: `${process.env.NEXT_PUBLIC_BASE_URL}/success`,
cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL}/cancel`,
});
return NextResponse.json({ id: session.id });
}Récupérer l'ID de session après paiement
Stripe ajoute automatiquement ?session_id=xxx à ton success_url. Tu peux le récupérer pour d'autres utilisations :
"use client";
import { useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";
export default function SuccessPage() {
const searchParams = useSearchParams();
const sessionId = searchParams.get("session_id");
const [orderDetails, setOrderDetails] = useState(null);
useEffect(() => {
if (sessionId) {
fetch(`/api/order-details?session_id=${sessionId}`)
.then(res => res.json())
.then(data => setOrderDetails(data));
}
}, [sessionId]);
return (
<div>
<h1>Paiement réussi !</h1>
{orderDetails && (
<p>Commande n°{orderDetails.orderNumber}</p>
)}
</div>
);
}Valider les montants côté serveur
Pour éviter de recevoir des données modifiées par un utilisateur malveillant depuis le frontend, il est nécéssaire de valider les données côté backend. Tu peux aussi éviter cela en mettant les prix en paramètres.
// Prix venant du client
export async function POST(req) {
const { price } = await req.json();
// L'utilisateur peut envoyer price: 1 au lieu de price: 9900
}
// Prix défini côté serveur
const PRODUCTS = {
"formation-react": { name: "Formation React", price: 9900 },
"ebook-js": { name: "Ebook JavaScript", price: 1500 },
};
export async function POST(req) {
const { productId } = await req.json();
const product = PRODUCTS[productId];
if (!product) {
return NextResponse.json({ error: "Produit invalide" }, { status: 400 });
}
const session = await stripe.checkout.sessions.create({
line_items: [{
price_data: {
currency: "eur",
product_data: { name: product.name },
unit_amount: product.price,
},
quantity: 1,
}],
// ...
});
}Personnaliser les emails de Stripe
Dans le Dashboard Stripe : Paramètres puis Emails, tu peux personnaliser le logo, les couleurs et les messages des emails automatiques.
Support multi-devises
Tu peux ajouter la détection automatique de devises selon la localisation :
const getUserCurrency = (countryCode) => {
const currencies = {
FR: "eur",
US: "usd",
GB: "gbp",
// ...
};
return currencies[countryCode] || "eur";
};
// Dans ton API
const currency = getUserCurrency(req.geo?.country || "FR");Dans ce code, la devise par défaut est l'Euro. Tu la redéfinir selon tes besoins.
Conclusion
Avec Stripe Checkout, tu disposes d'une solution simple, rapide et sécurisée pour intégrer le paiement en ligne dans tes applications sans avoir à gérer toute la complexité d’un système de paiement from scratch. En quelques étapes, tu peux proposer à tes utilisateurs une expérience fluide, professionnelle et conforme aux standards de sécurité.
Dans ce tutoriel, nous avons vu comment configurer Stripe, créer une session de paiement et rediriger l'utilisateur vers une page de checkout prête à l'emploi. Cette base te permet déjà d'accepter des paiements et de valider un parcours client fonctionnel. Pour aller plus loin, tu peux maintenant :
- gérer les webhooks pour automatiser les actions après le paiement comme la confirmation, la mise à jour en base de données ou l'envoi d'e-mails.
- personnaliser davantage l'expérience utilisateur
- mettre en place des abonnements, paiements récurrents et factures.
Stripe offre un écosystème très riche. A toi d'exploiter ces fonctionnalités pour construire des produits fiables et évolutifs.