Gérer les commandes et intégrer Stripe pour le paiement

Créez les commandes et connectez Stripe pour des paiements sécurisés et automatisés.

4 min read

Préparer la gestion des commandes et Stripe

Avant d’intégrer le paiement, assure-toi d’avoir :

  • Un panier fonctionnel avec React Context (voir [Le panier d’achat dynamique]).
  • Les routes Strapi pour commandes et ligne-de-commandes prêtes (voir module précédent).
  • Installé le SDK Stripe :
    Terminal
    1
    npm install stripe
  • Configuré tes variables d’environnement dans .env :
    1
    STRIPE_SECRET_KEY=sk_test_xxx
    2
    STRIPE_WEBHOOK_SECRET=whsec_xxx

1. Initialiser Stripe côté serveur

Crée un utilitaire pour réutiliser le client Stripe dans tes actions Loader/Action.

utils/stripe.server.ts
1
import Stripe from "stripe";
2
3
export const stripe = new Stripe(
4
process.env.STRIPE_SECRET_KEY!, // ^? clé secrète stockée en env
5
{ apiVersion: "2022-11-15" }
6
);

2. Créer la session de paiement

Dans Remix, on va exposer une route d’API pour générer la session Stripe.
Cette action :

  1. Lit le panier et l’email depuis le request.json().
  2. Crée d’abord la commande + lignes dans Strapi (createOrder).
  3. Prépare les line_items pour Stripe.
  4. Crée la session et redirige vers session.url.
app/routes/api/create-checkout-session.ts
1
import type { ActionFunction } from "@remix-run/node";
2
import { redirect } from "@remix-run/node";
3
import { stripe } from "~/utils/stripe.server";
4
import { createOrder } from "~/models/order.server";
5
6
export const action: ActionFunction = async ({ request }) => {
7
const { products, email } = await request.json();
8
9
// 1. Création de la commande dans Strapi
10
const { id: orderId } = await createOrder({ products, email });
11
12
// 2. Construction des line_items Stripe
13
const line_items = products.map((p) => ({
14
price_data: {
15
currency: "usd",
16
unit_amount: p.price * 100,
17
product_data: { name: p.name },
18
},
19
quantity: p.quantity,
20
}));
21
22
// 3. Création de la session Stripe
23
const session = await stripe.checkout.sessions.create({
24
payment_method_types: ["card"],
25
line_items,
26
mode: "payment",
27
success_url: `${request.headers.get("origin")}/order/${orderId}` +
28
"?session_id={CHECKOUT_SESSION_ID}",
29
cancel_url: `${request.headers.get("origin")}/cart`,
30
metadata: { orderId: String(orderId) }, // pour le webhook
31
});
32
33
return redirect(session.url!, 303);
34
};

3. Gérer le webhook Stripe

Stripe doit informer ton backend quand le paiement est complété.
Tu crées une route api/webhooks/stripe en loader (GET non utilisé).

app/routes/api/webhooks/stripe.ts
1
import { json } from "@remix-run/node";
2
import { stripe } from "~/utils/stripe.server";
3
import { updateOrderStatus } from "~/models/order.server";
4
5
export const loader = async ({ request }) => {
6
const payload = await request.text();
7
const sig = request.headers.get("stripe-signature")!;
8
9
let event: Stripe.Event;
10
try {
11
event = stripe.webhooks.constructEvent(
12
payload,
13
sig,
14
process.env.STRIPE_WEBHOOK_SECRET!
15
);
16
} catch {
17
return json({ error: "Invalid signature" }, { status: 400 });
18
}
19
20
if (event.type === "checkout.session.completed") {
21
const session = event.data.object as Stripe.Checkout.Session;
22
const orderId = session.metadata?.orderId!;
23
await updateOrderStatus(orderId, "paid");
24
}
25
26
return json({ received: true });
27
};

4. Afficher la confirmation de commande

Après paiement, l’utilisateur est redirigé sur /order/:orderId.
On récupère la commande Strapi (avec ses lignes et utilisateur).

app/routes/order/$orderId.tsx
1
import { LoaderFunction, useLoaderData } from "@remix-run/node";
2
import { getOrderById } from "~/models/order.server";
3
4
export const loader: LoaderFunction = async ({ params }) => {
5
const order = await getOrderById({ id: params.orderId! });
6
return { order };
7
};
8
9
export default function OrderDetail() {
10
const { order } = useLoaderData<typeof loader>();
11
return (
12
<main>
13
<h2>Merci pour ta commande ! 🎉</h2>
14
<p>Statut : {order.orderStatus}</p>
15
<ul>
16
{order.lines.map((line) => (
17
<li key={line.produit.id}>
18
{line.quantity}× {line.produit.name} – ${(line.price / 100).toFixed(2)}
19
</li>
20
))}
21
</ul>
22
<p>Total : ${(order.totalPrice / 100).toFixed(2)}</p>
23
</main>
24
);
25
}

5. Points clés

  • Crée d’abord ta commande et tes lignes dans Strapi avant de lancer Stripe Checkout.
  • Passe metadata.orderId à Stripe pour lier le paiement à la commande.
  • Utilise un webhook pour mettre à jour le statut de la commande (paid, cancelled, etc.).
  • Réserve les clés Stripe dans les variables d’environnement et ne les expose jamais au client.
  • La redirection success/cancel s’effectue côté Action Remix.

Exercices

  1. Configure un projet Remix pour ajouter la variable STRIPE_SECRET_KEY et teste que stripe se connecte sans erreur.
  2. Implémente la route /api/create-checkout-session et vérifie qu’elle renvoie bien une URL Stripe valide.
  3. Écris un test manuel du webhook : simule l’événement checkout.session.completed avec stripe-cli et vérifie que Strapi met à jour le statut de ta commande.