15 novembre 2021
Découverte de Stripe avec Next.js
6 minutes de lecture
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.
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 :
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 :
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
👋