Découverte de Stripe avec Next.js

par

Pierre

Pierre Crouillebois

7 minutes de lecture

Découverte de Stripe avec Next.js

Stripe est une suite d'API qui facilite la configuration des différents paiements en ligne et dans cet article, nous utiliserons Stripe pour créer un système de paiement simple à l'aide de Next.js.

Stripe offre une multitude de solutions de paiement comme par exemple la possibilité de créer des abonnements périodiques, une boutique en ligne ou bien une solution de financement participatif.

Dans cet exemple, nous allons utiliser la Payment Intents API qui comporte différents avantages :

  • Gestion automatique de l'authentification
  • Paiement unique, pas de doublons
  • Prise en charge de l'authentification du client (SCA) et des modifications réglementaires similaires

Installation

Pour commencer, nous pouvons initialiser notre projet Next avec Typescript :

yarn create next-app --example with-typescript stripe-demo && cd stripe-demo

Une fois notre projet créé, nous pouvons ajouter les dépendances nécessaires à l'utilisation de Stripe :

yarn add @stripe/react-stripe-js @stripe/stripe-js stripe

Il convient d'ajouter les types de Stripe en dépendance de développement :

yarn add -D @types/stripe

Ainsi que la librairie ChakraUI qui va nous aider à créer des interfaces personnalisées :

yarn add @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4

Maintenant que nos dépendances sont installées nous pouvons créer un fichier contenant nos variables d'environnement.

Nous ajoutons un fichier .env.local à la racine du projet Next contenant les clefs Stripe :

NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_12345
STRIPE_SECRET_KEY=sk_12345
Si vous n'avez pas de compte développeur Stripe, vous pouvez commencer gratuitement en créant un compte ici.

🚨 Attention : Les deux variables doivent être remplacées par vos clés Stripe personnelles présentes dans votre dashboard. Pensez à bien utiliser vos clés de test pendant le développement.

stripe-dashboard

Développement

En raison des exigences de conformité PCI, la bibliothèque Stripe doit être chargée à partir des serveurs de Stripe. Ce qui peut être complexe lorsque l'on travaille avec des applications rendues côté serveur, car l'objet window n'est pas disponible sur le serveur.

Pour nous aider à gérer cela, Stripe fournit un wrapper de chargement qui permet d'importer Stripe en tant que module ES. Nous pouvons créer un dossier utils à la racine de notre projet et y ajouter un fichier get-stripe.ts :

// ./utils/get-stripe.ts
import { loadStripe } from '@stripe/stripe-js';

const stripe = await loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);

Pour optimiser les performances de notre site, nous pouvons suspendre l'instanciation de Stripe jusqu'au premier rendu de votre page de paiement. Pour vous assurer de ne pas rétablir Stripe à chaque rendu, nous vous recommandons d'utiliser cet exemple afin de créer/récupérer l'instance Stripe facilement :

// ./utils/get-stripe.ts
import { Stripe, loadStripe } from '@stripe/stripe-js';

let stripePromise: Promise<Stripe | null>;
const getStripe = () => {
  if (!stripePromise) {
    stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
  }
  return stripePromise;
};

export default getStripe;

Sans oublier, le fichier _app.tsx dans le dossier pages comme le montre l'exemple ci-dessous afin d'initialiser correctement ChakraUI et Stripe Elements dans l'ensemble du projet Next tout en important notre fonction getStripe() créée précedemment :

// ./pages/_app.tsx
import { ChakraProvider } from "@chakra-ui/react";
import { Elements } from "@stripe/react-stripe-js";
import getStripe from "../utils/get-stripe";

function MyApp({ Component, pageProps }) {
	return (
		<ChakraProvider>
			<Elements stripe={getStripe()}>
				<Component {...pageProps} />
			</Elements>
		</ChakraProvider>
	);
}

export default MyApp;

Une fois les Providers ajoutés, nous pouvons développer notre interface en commençant par créer le composant CheckoutForm.tsx dans le dossier components :

// ./components/CheckoutForm.tsx
import React, { useEffect, useState } from "react";
import { CardElement, useElements } from "@stripe/react-stripe-js";
import { Button } from "@chakra-ui/button";
import getStripe from "../utils/get-stripe";
import { Spinner, Box, Text } from "@chakra-ui/react";

const CARD_OPTIONS = {
	iconStyle: "solid" as const,
	hidePostalCode: true,
	style: {
		base: {
			iconColor: "#5470e1",
			color: "#5470e1",
			fontWeight: 500,
			fontFamily: "Roboto, Open Sans, Segoe UI, sans-serif",
			fontSize: "16px",
			fontSmoothing: "antialiased",
			":-webkit-autofill": {
				color: "#5470e1",
			},
			"::placeholder": {
				color: "#5470e1",
			},
		},
		invalid: {
			iconColor: "#ffc7ee",
			color: "#ffc7ee",
		},
	},
};

const CheckoutForm = () => {
	const [loading, setLoading] = useState(false);
	const elements = useElements();

	const handleSubmit = async (event) => {
		event.preventDefault();
		// confirmer le paiement
	};

	return (
		<Box m="0 auto" py={5} textAlign="center">
			<Text fontWeight={700} fontSize={25} mb={10} color="#5470e1">
				Stripe + Next.JS Demo
			</Text>
			<form onSubmit={handleSubmit}>
					<>
						<CardElement options={CARD_OPTIONS} />
						{loading ? (
							<Spinner color="blue.500" size="lg" mt="md" />
						) : (
							<Button
								bgColor="#5470e1"
								color="white"
								fontSize={15}
								m="0 auto"
								type="submit"
								mt={10}
								_hover={{
									bgColor: "#425cc4",
								}}
							>
								Payer
							</Button>
						)}
					</>
			</form>
		</Box>
	);
};

export default CheckoutForm;

Ici nous avons utilisé le composant CardElement pour nous permettre de créer le formulaire de paiement tout en le reliant à Stripe. Ce qui devrait ressembler à ça :

form

Désormais, nous pouvons configurer notre code côté serveur pour récupérer le token qui va nous permettre de valider le paiement avec l'aide des API Routes de Next.js. Nous pouvons créer un dossier api contenant un fichier payment.ts dans le dossier pages.

// ./pages/api/payment.ts
import { NextApiRequest, NextApiResponse } from "next";
import Stripe from "stripe";

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

export default async function handler(
	req: NextApiRequest,
	res: NextApiResponse
) {
	if (req.method === "POST") {
		const paymentIntent = await stripe.paymentIntents.create({
			amount: 1000, // prix de votre produit / commande
			currency: "eur", // devise 
		});

		res.send({
			clientSecret: paymentIntent.client_secret, 
		});
	} else {
		res.setHeader("Allow", "POST");
		res.status(405).end("Method Not Allowed");
	}
}

Cette partie de code est exécutée côté serveur pour envoyer le token fourni par Stripe dans notre composant CheckoutForm.tsx. Nous avons utilisé la fonction paymentIntents.create() pour créer le paiement avec Stripe, c'est avec cette fonction que nous pourrons modifier le montant du paiement ou la devise utilisée.

Il faut que l'on ajoute un useEffect dans le composant CheckoutForm.tsx pour récupérer ainsi le token du client à chaque rendu de la page de paiement :

// ./components/CheckoutForm.tsx
const CheckoutForm = () => {
	const [clientSecret, setClientSecret] = useState(null);
	const [error, setError] = useState(false);

	useEffect(() => {
		window
			.fetch("/api/payment", {
				method: "POST",
				headers: {
					"Content-Type": "application/json",
				},
			})
			.then((res) => {
				if (res.status !== 200) {
					setError(true);
				} else {
					setError(false);
					return res.json().then((data) => {
						setClientSecret(data.clientSecret);
					});
				}
			});
	}, []);

	return (
		<Box m="0 auto" py={5} textAlign="center">
			<Text fontWeight={700} fontSize={25} mb={10} color="#5470e1">
				Stripe + Next.JS Demo
			</Text>
			<form onSubmit={handleSubmit}>
				{error ? (
					<Text fontSize={15} px={[0, 0, "md", "md"]}>
						SHOP OFFLINE
					</Text>
				) : (
					<>
						<CardElement options={CARD_OPTIONS} />
						{loading ? (
							<Spinner color="blue.500" size="lg" mt="md" />
						) : (
							<Button
								bgColor="#5470e1"
								color="white"
								fontSize={15}
								m="0 auto"
								type="submit"
								mt={10}
								_hover={{
									bgColor: "#425cc4",
								}}
							>
								Payer
							</Button>
						)}
					</>
				)}
			</form>
		</Box>
	);
};

export default CheckoutForm;

Et voila ! 🎉 On récupère le token de Stripe à chaque rendu du composant CheckoutForm.

Pour finir, il faut compléter l'envoi du formulaire créé précédemment en ajoutant ceci dans notre fonction handleSubmit() :

// ./components/CheckoutForm.tsx
const handleSubmit = async (event) => {
		event.preventDefault();
		setLoading(true);
		const stripe = await getStripe();

		if (!stripe || !elements) {
			setLoading(false);
			alert("Please enter a valid card number");
			return;
		}

		const payload = await stripe.confirmCardPayment(clientSecret, {
			payment_method: {
				card: elements.getElement(CardElement),
				billing_details: {
          email: "test@gmail.com",
          name: "Premier Octet",
          address: {
            city: "Paris",
            postal_code: "75000",
          },
        },
			},
		});

		if (payload.error) {
			alert(`Payment failed : ${payload.error.message}`);
			setLoading(false);
		} else {
			setLoading(false);
			alert("Payment succeeded ✅");
		}
	};

Nous passons le token dans la fonction confirmCardPayment() pour valider et finaliser la transaction. À noter que nous pouvons ajouter des informations supplémentaires concernant l'acheteur que l'on pourra retrouver ensuite dans notre dashboard Stripe. (nom, adresse, email...)

Notre paiement peut maintenant s'éxécuter correctement ✅

Demo

Stripe offre un moyen de tester les paiements avec des cartes bancaires fictives que vous pouvez retrouver ici.

Tout les paiements exécutés sont présents sur notre dashboard de test Stripe :

payment-success

Conclusion

Vous pouvez désormais payer en ligne sur votre site web avec l'aide de Stripe et Next.js ! ✨

Comme le montre cet exemple, les API Routes de Next peuvent s'avérer très utiles dans ce genre de projet, cela nous évite d'avoir à créer un serveur Express en supplément de notre application React.

De plus, l'API de Stripe est simple et modulaire avec une documentation bien complète ce qui nous permet d'encaisser des paiements facilement en toute sécurité.

Il est aussi possible d'utiliser la suite d'API de Stripe dans un projet React Native avec l'aide de la librairie @stripe/stripe-react-native 📲

Le code du projet est disponible en OpenSource ici

👋

Continuer la discussion sur Twitter

18 avenue Parmentier
75011 Paris
+33 1 43 57 39 11
hello@premieroctet.com

Suivez nos aventures

GitHub

Naviguez à vue