Créez les commandes et connectez Stripe pour des paiements sécurisés et automatisés.
Avant d’intégrer le paiement, assure-toi d’avoir :
1npm install stripe
.env
:
1STRIPE_SECRET_KEY=sk_test_xxx2STRIPE_WEBHOOK_SECRET=whsec_xxx
Crée un utilitaire pour réutiliser le client Stripe dans tes actions Loader/Action.
1import Stripe from "stripe";23export const stripe = new Stripe(4process.env.STRIPE_SECRET_KEY!, // ^? clé secrète stockée en env5{ apiVersion: "2022-11-15" }6);
Dans Remix, on va exposer une route d’API pour générer la session Stripe.
Cette action :
request.json()
.createOrder
).line_items
pour Stripe.session.url
.1import type { ActionFunction } from "@remix-run/node";2import { redirect } from "@remix-run/node";3import { stripe } from "~/utils/stripe.server";4import { createOrder } from "~/models/order.server";56export const action: ActionFunction = async ({ request }) => {7const { products, email } = await request.json();89// 1. Création de la commande dans Strapi10const { id: orderId } = await createOrder({ products, email });1112// 2. Construction des line_items Stripe13const line_items = products.map((p) => ({14price_data: {15currency: "usd",16unit_amount: p.price * 100,17product_data: { name: p.name },18},19quantity: p.quantity,20}));2122// 3. Création de la session Stripe23const session = await stripe.checkout.sessions.create({24payment_method_types: ["card"],25line_items,26mode: "payment",27success_url: `${request.headers.get("origin")}/order/${orderId}` +28"?session_id={CHECKOUT_SESSION_ID}",29cancel_url: `${request.headers.get("origin")}/cart`,30metadata: { orderId: String(orderId) }, // pour le webhook31});3233return redirect(session.url!, 303);34};
On stocke orderId
dans metadata
pour relier la session Stripe
à la commande Strapi lors du webhook.
Stripe doit informer ton backend quand le paiement est complété.
Tu crées une route api/webhooks/stripe
en loader (GET non utilisé).
1import { json } from "@remix-run/node";2import { stripe } from "~/utils/stripe.server";3import { updateOrderStatus } from "~/models/order.server";45export const loader = async ({ request }) => {6const payload = await request.text();7const sig = request.headers.get("stripe-signature")!;89let event: Stripe.Event;10try {11event = stripe.webhooks.constructEvent(12payload,13sig,14process.env.STRIPE_WEBHOOK_SECRET!15);16} catch {17return json({ error: "Invalid signature" }, { status: 400 });18}1920if (event.type === "checkout.session.completed") {21const session = event.data.object as Stripe.Checkout.Session;22const orderId = session.metadata?.orderId!;23await updateOrderStatus(orderId, "paid");24}2526return json({ received: true });27};
N’expose jamais ta clé STRIPE_WEBHOOK_SECRET
. Remix
la lit depuis process.env
.
Après paiement, l’utilisateur est redirigé sur /order/:orderId
.
On récupère la commande Strapi (avec ses lignes et utilisateur).
1import { LoaderFunction, useLoaderData } from "@remix-run/node";2import { getOrderById } from "~/models/order.server";34export const loader: LoaderFunction = async ({ params }) => {5const order = await getOrderById({ id: params.orderId! });6return { order };7};89export default function OrderDetail() {10const { order } = useLoaderData<typeof loader>();11return (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}
Pense à formater les montants en cents pour Stripe (multiplicateur × 100).
metadata.orderId
à Stripe pour lier le paiement à la commande.paid
, cancelled
, etc.).STRIPE_SECRET_KEY
et teste que stripe
se connecte sans erreur./api/create-checkout-session
et vérifie
qu’elle renvoie bien une URL Stripe valide.checkout.session.completed
avec stripe-cli
et vérifie que
Strapi met à jour le statut de ta commande.