{"id":73422,"date":"2023-10-02T09:28:21","date_gmt":"2023-10-02T08:28:21","guid":{"rendered":"https:\/\/kinqsta.com\/fr\/?p=73422&#038;preview=true&#038;preview_id=73422"},"modified":"2023-10-12T17:03:13","modified_gmt":"2023-10-12T16:03:13","slug":"integration-stripe-spring-boot","status":"publish","type":"post","link":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/","title":{"rendered":"Un guide pour l&rsquo;int\u00e9gration de Stripe dans une application Spring Boot"},"content":{"rendered":"<p>Avec l&rsquo;augmentation des transactions num\u00e9riques, la capacit\u00e9 \u00e0 int\u00e9grer de fa\u00e7on transparente les passerelles de paiement est devenue une comp\u00e9tence essentielle pour les d\u00e9veloppeurs. Que ce soit pour les places de march\u00e9 ou les <a href=\"https:\/\/kinqsta.com\/fr\/blog\/produits-saas\/\">produits SaaS<\/a>, un processeur de paiement est crucial pour collecter et traiter les paiements des utilisateurs.<\/p>\n<p>Cet article explique comment int\u00e9grer <a href=\"https:\/\/kinqsta.com\/fr\/blog\/stripe-vs-adyen\/\">Stripe<\/a> dans un environnement <a href=\"https:\/\/spring.io\/projects\/spring-boot\" target=\"_blank\" rel=\"noopener noreferrer\">Spring Boot<\/a>, comment mettre en place des abonnements, proposer des essais gratuits et construire des pages en libre-service pour que tes clients puissent t\u00e9l\u00e9charger leurs factures de paiement.<br \/>\n<div><\/div><kinsta-auto-toc heading=\"Table of Contents\" exclude=\"last\" list-style=\"arrow\" selector=\"h2\" count-number=\"-1\"><\/kinsta-auto-toc><\/p>\n<h2>Qu&rsquo;est-ce que Stripe ?<\/h2>\n<p><a href=\"https:\/\/example.com\/en-in\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe<\/a> est une plateforme de traitement des paiements de renomm\u00e9e mondiale, disponible dans <a href=\"https:\/\/example.com\/en-in\/global\" target=\"_blank\" rel=\"noopener noreferrer\">46 pays<\/a>. C&rsquo;est un excellent choix si vous voulez int\u00e9grer un syst\u00e8me de paiement dans votre application web, en raison de sa grande port\u00e9e, de son nom r\u00e9put\u00e9 et de sa documentation d\u00e9taill\u00e9e.<\/p>\n<h3>Comprendre les concepts communs de Stripe<\/h3>\n<p>Il est utile de comprendre certains concepts communs que Stripe utilise pour coordonner et effectuer des op\u00e9rations de paiement entre plusieurs parties. Stripe propose deux approches pour mettre en \u0153uvre l&rsquo;int\u00e9gration des paiements dans votre application.<\/p>\n<p>Vous pouvez soit int\u00e9grer les formulaires de Stripe dans votre application pour une exp\u00e9rience client in-app (<a href=\"https:\/\/example.com\/docs\/payments\/payment-intents\" target=\"_blank\" rel=\"noopener noreferrer\">intention de paiement<\/a>), soit rediriger les clients vers une page de paiement h\u00e9berg\u00e9e par Stripe, o\u00f9 Stripe g\u00e8re le processus et informe votre application lorsqu&rsquo;un paiement a r\u00e9ussi ou \u00e9chou\u00e9 (<a href=\"https:\/\/example.com\/docs\/payment-links\" target=\"_blank\" rel=\"noopener noreferrer\">lien de paiement<\/a>).<\/p>\n<h4>Intention de paiement<\/h4>\n<p>Lorsque vous g\u00e9rez des paiements, il est important de recueillir les d\u00e9tails concernant le client et le produit avant de leur demander les informations relatives \u00e0 la carte et au paiement. Ces d\u00e9tails comprennent la description, le montant total, le moyen de paiement, etc.<\/p>\n<p>Stripe vous demande de collecter ces d\u00e9tails dans votre application et de g\u00e9n\u00e9rer un objet <code>PaymentIntent<\/code> dans son backend. Cette approche permet \u00e0 Stripe de formuler une demande de paiement pour cette intention. Une fois le paiement termin\u00e9, vous pouvez syst\u00e9matiquement r\u00e9cup\u00e9rer les d\u00e9tails du paiement, y compris son objet, par le biais de l&rsquo;objet <code>PaymentIntent<\/code>.<\/p>\n<h4>Liens de paiement<\/h4>\n<p>Pour \u00e9viter la complexit\u00e9 de l&rsquo;int\u00e9gration de Stripe directement dans votre base de code, envisagez d&rsquo;utiliser <a href=\"https:\/\/example.com\/docs\/payments\/checkout\/how-checkout-works\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe Checkouts<\/a> pour une solution de paiement h\u00e9berg\u00e9e. Comme pour la cr\u00e9ation d&rsquo;un <code>PaymentIntent<\/code>, vous cr\u00e9erez un <code>CheckoutSession<\/code> avec les d\u00e9tails du paiement et du client. Au lieu d&rsquo;initier un <code>PaymentIntent<\/code> in-app, le <code>CheckoutSession<\/code> g\u00e9n\u00e8re un lien de paiement vers lequel vous redirigez vos clients. Voici \u00e0 quoi ressemble une page de paiement h\u00e9berg\u00e9e :<\/p>\n<figure id=\"attachment_163048\" aria-describedby=\"caption-attachment-163048\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163048 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/stripe-hosted-checkout-page-1024x522.png\" alt=\"La page de paiement h\u00e9berg\u00e9e par Stripe.\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163048\" class=\"wp-caption-text\">La page de paiement h\u00e9berg\u00e9e par Stripe.<\/figcaption><\/figure>\n<p>Apr\u00e8s le paiement, Stripe redirige vers votre application, ce qui permet d&rsquo;effectuer des t\u00e2ches post-paiement telles que les confirmations et les demandes de livraison. Pour plus de fiabilit\u00e9, configurez un crochet web de backend pour mettre \u00e0 jour Stripe, assurant ainsi la conservation des donn\u00e9es de paiement m\u00eame si les clients ferment accidentellement la page apr\u00e8s le paiement.<\/p>\n<p>Bien qu&rsquo;efficace, cette m\u00e9thode manque de flexibilit\u00e9 en mati\u00e8re de personnalisation et de conception. Elle peut \u00e9galement \u00eatre d\u00e9licate \u00e0 configurer correctement pour les applications mobiles, o\u00f9 une int\u00e9gration native aurait l&rsquo;air bien plus transparente.<\/p>\n<h4>Cl\u00e9s API<\/h4>\n<p>Lorsque vous travaillez avec l&rsquo;API Stripe, vous devez avoir acc\u00e8s aux cl\u00e9s API pour que vos applications client et serveur puissent interagir avec le backend Stripe. Vous pouvez acc\u00e9der \u00e0 vos cl\u00e9s API Stripe sur <a href=\"https:\/\/dashboard.example.com\/test\/apikeys\" target=\"_blank\" rel=\"noopener noreferrer\">votre tableau de bord de d\u00e9veloppeur Stripe<\/a>. Voici \u00e0 quoi il ressemblerait :<\/p>\n<figure id=\"attachment_163050\" aria-describedby=\"caption-attachment-163050\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163050 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/stripe-dashboard-api-keys-1024x522.png\" alt=\"Le tableau de bord Stripe affichant les cl\u00e9s API\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163050\" class=\"wp-caption-text\">Le tableau de bord Stripe affichant les cl\u00e9s API<\/figcaption><\/figure>\n<h3>Comment fonctionnent les paiements dans Stripe ?<\/h3>\n<p>Pour comprendre comment fonctionnent les paiements dans Stripe, vous devez comprendre toutes les parties prenantes impliqu\u00e9es. Quatre parties prenantes sont impliqu\u00e9es dans chaque transaction de paiement :<\/p>\n<ol>\n<li><strong>Client <\/strong>: La personne qui a l&rsquo;intention de payer pour un service\/produit.<\/li>\n<li><strong>Marchand <\/strong>: Vous, le ou la propri\u00e9taire de l&rsquo;entreprise, vous \u00eates responsable de la r\u00e9ception des paiements et de la vente des services\/produits.<\/li>\n<li><strong>Acqu\u00e9reur <\/strong>: Une banque qui traite les paiements en votre nom (le commer\u00e7ant) et achemine votre demande de paiement aux banques de vos clients. Les acqu\u00e9reurs peuvent s&rsquo;associer \u00e0 un tiers pour aider \u00e0 traiter les paiements.<\/li>\n<li><strong>Banque \u00e9mettrice <\/strong>: La banque qui accorde des cr\u00e9dits et \u00e9met des cartes et d&rsquo;autres moyens de paiement aux consommateurs.<\/li>\n<\/ol>\n<p>Voici un flux de paiement typique entre ces parties prenantes \u00e0 un niveau tr\u00e8s \u00e9lev\u00e9.<\/p>\n<figure id=\"attachment_163049\" aria-describedby=\"caption-attachment-163049\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163049 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/how-online-payments-work-1024x201.png\" alt=\"Comment fonctionnent les paiements en ligne\" width=\"1024\" height=\"201\"><figcaption id=\"caption-attachment-163049\" class=\"wp-caption-text\">Comment fonctionnent les paiements en ligne<\/figcaption><\/figure>\n<p>Le client fait savoir au commer\u00e7ant qu&rsquo;il est pr\u00eat \u00e0 payer. Le commer\u00e7ant transmet ensuite les informations relatives au paiement \u00e0 sa banque acqu\u00e9reur, qui collecte le paiement aupr\u00e8s de la banque \u00e9mettrice du client et informe le commer\u00e7ant que le paiement a bien \u00e9t\u00e9 effectu\u00e9.<\/p>\n<p>Il s&rsquo;agit l\u00e0 d&rsquo;un aper\u00e7u de tr\u00e8s haut niveau du processus de paiement. En tant que commer\u00e7ant, vous n&rsquo;avez\u00a0 qu&rsquo;\u00e0 vous pr\u00e9occuper de la collecte de l&rsquo;intention de paiement, de sa transmission au processeur de paiement et du traitement du r\u00e9sultat du paiement. Cependant, comme nous l&rsquo;avons vu plus haut, il y a deux fa\u00e7ons de proc\u00e9der.<\/p>\n<p>Lors de la cr\u00e9ation d&rsquo;une session de paiement g\u00e9r\u00e9e par Stripe, o\u00f9 Stripe s&rsquo;occupe de la collecte des d\u00e9tails du paiement, voici \u00e0 quoi ressemble le flux typique :<\/p>\n<figure id=\"attachment_163051\" aria-describedby=\"caption-attachment-163051\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163051 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/stripe-hosted-payment-workflow-1024x749.png\" alt=\"Le flux de paiement de la session de paiement h\u00e9berg\u00e9e par Stripe.\" width=\"1024\" height=\"749\"><figcaption id=\"caption-attachment-163051\" class=\"wp-caption-text\">Le flux de paiement de la session de paiement h\u00e9berg\u00e9e par Stripe. (<strong>Source :<\/strong> <a href=\"https:\/\/example.com\/docs\/payments\/checkout\/how-checkout-works#:~:text=The%20Checkout%20Session%20provides%20a,checkout.session.completed%20event.\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe Docs<\/a>)<\/figcaption><\/figure>\n<p>Avec les flux de paiement personnalis\u00e9s, tout d\u00e9pend vraiment de vous. Vous pouvez concevoir l&rsquo;interaction entre votre client, le serveur, le client et l&rsquo;API Stripe en fonction des besoins de votre application. Vous pouvez ajouter \u00e0 ce flux de travail la collecte d&rsquo;adresses, la g\u00e9n\u00e9ration de factures, l&rsquo;annulation, les essais gratuits, etc. Selon vos besoins.<\/p>\n<p>Maintenant que vous comprennez le fonctionnement des paiements Stripe, vous \u00eates pr\u00eat \u00e0 commencer \u00e0 le construire dans votre application Java.<\/p>\n<h2>Int\u00e9gration de Stripe dans une application Spring Boot<\/h2>\n<p>Pour commencer l&rsquo;int\u00e9gration de Stripe, cr\u00e9e une application frontend pour interagir avec le backend Java et initier les paiements. Dans ce tutoriel, vous construirez une appli React pour d\u00e9clencher diff\u00e9rents types de paiements et d&rsquo;abonnements afin de bien comprendre leurs m\u00e9canismes.<\/p>\n<p><strong>Remarque <\/strong>: ce tutoriel ne couvrira pas la construction d&rsquo;un site de commerce \u00e9lectronique complet ; il vise principalement \u00e0 vous guider dans le processus simple d&rsquo;int\u00e9gration de Stripe dans Spring Boot.<\/p>\n<h3>Configuration des projets frontend et backend<\/h3>\n<p>Cr\u00e9ez un nouveau r\u00e9pertoire et \u00e9chafaudez un projet React \u00e0 l&rsquo;aide de Vite en ex\u00e9cutant la commande suivante :<\/p>\n<pre><code class=\"language-bash\">npm create vite@latest<\/code><\/pre>\n<p>D\u00e9finissez le nom du projet comme <strong>frontend<\/strong> (ou tout autre nom pr\u00e9f\u00e9r\u00e9), le framework comme <strong>React<\/strong> et la variante comme <strong>TypeScript<\/strong>. Naviguez vers le r\u00e9pertoire du projet et installez Chakra UI pour un \u00e9chafaudage rapide des \u00e9l\u00e9ments de l&rsquo;interface utilisateur en ex\u00e9cutant la commande suivante :<\/p>\n<pre><code class=\"language-bash\">npm i @chakra-ui\/react @emotion\/react @emotion\/styled framer-motion @chakra-ui\/icons<\/code><\/pre>\n<p>Vous installerez \u00e9galement <code>react-router-dom<\/code> dans votre projet pour le routage c\u00f4t\u00e9 client en ex\u00e9cutant la commande ci-dessous :<\/p>\n<pre><code class=\"language-bash\">npm i react-router-dom<\/code><\/pre>\n<p>Maintenant, vous \u00eates pr\u00eat \u00e0 construire votre application frontend. Voici la page d&rsquo;accueil que vous allez construire.<\/p>\n<figure id=\"attachment_163052\" aria-describedby=\"caption-attachment-163052\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163052 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/completed-home-page-1024x522.png\" alt=\"La page d'accueil termin\u00e9e de l'application frontend.\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163052\" class=\"wp-caption-text\">La page d&rsquo;accueil termin\u00e9e de l&rsquo;application frontend.<\/figcaption><\/figure>\n<p>En cliquant sur n&rsquo;importe quel bouton de cette page, vous acc\u00e8derez \u00e0 des pages de paiement s\u00e9par\u00e9es avec des formulaires de paiement. Pour commencer, cr\u00e9ez un nouveau dossier nomm\u00e9 <strong>routes<\/strong> dans votre r\u00e9pertoire <strong>frontend\/src<\/strong>. Dans ce dossier, cr\u00e9ez un fichier <strong>Home.tsx.<\/strong> Ce fichier contiendra le code de la route d&rsquo;accueil de votre application (<code>\/<\/code>). Collez le code suivant dans le fichier :<\/p>\n<pre><code class=\"language-typescript\">import {Button, Center, Heading, VStack} from \"@chakra-ui\/react\";\n\nimport { useNavigate } from \"react-router-dom\";\n\nfunction Home() {\n    const navigate = useNavigate()\n    const navigateToIntegratedCheckout = () =&gt; {\n        navigate(\"\/integrated-checkout\")\n    }\n\n    const navigateToHostedCheckout = () =&gt; {\n        navigate(\"\/hosted-checkout\")\n    }\n\n    const navigateToNewSubscription = () =&gt; {\n        navigate(\"\/new-subscription\")\n    }\n\n    const navigateToCancelSubscription = () =&gt; {\n        navigate(\"\/cancel-subscription\")\n    }\n\n    const navigateToSubscriptionWithTrial = () =&gt; {\n        navigate(\"\/subscription-with-trial\")\n    }\n\n    const navigateToViewInvoices = () =&gt; {\n        navigate(\"\/view-invoices\")\n    }\n\n    return (\n        &lt;&gt;\n            &lt;Center h={'100vh'} color='black'&gt;\n                &lt;VStack spacing='24px'&gt;\n                    &lt;Heading&gt;Stripe Payments With React & Java&lt;\/Heading&gt;\n                    &lt;Button\n                        colorScheme={'teal'}\n                        onClick={navigateToIntegratedCheckout}&gt;\n                        Integrated Checkout\n                    &lt;\/Button&gt;\n                    &lt;Button\n                        colorScheme={'blue'}\n                        onClick={navigateToHostedCheckout}&gt;\n                        Hosted Checkout\n                    &lt;\/Button&gt;\n                    &lt;Button\n                        colorScheme={'yellow'}\n                        onClick={navigateToNewSubscription}&gt;\n                        New Subscription\n                    &lt;\/Button&gt;\n                    &lt;Button\n                        colorScheme={'purple'}\n                        onClick={navigateToCancelSubscription}&gt;\n                        Cancel Subscription\n                    &lt;\/Button&gt;\n                    &lt;Button\n                        colorScheme={'facebook'}\n                        onClick={navigateToSubscriptionWithTrial}&gt;\n                        Subscription With Trial\n                    &lt;\/Button&gt;\n                    &lt;Button\n                        colorScheme={'pink'}\n                        onClick={navigateToViewInvoices}&gt;\n                        View Invoices\n                    &lt;\/Button&gt;\n                &lt;\/VStack&gt;\n            &lt;\/Center&gt;\n        &lt;\/&gt;\n    )\n}\n\nexport default Home<\/code><\/pre>\n<p>Pour permettre la navigation dans votre application, mettez \u00e0 jour votre fichier <strong>App.tsx<\/strong> pour configurer la classe <code>RouteProvider<\/code> \u00e0 partir de <code>react-router-dom<\/code>.<\/p>\n<pre><code class=\"language-js\">import Home from \".\/routes\/Home.tsx\";\nimport {\n    createBrowserRouter,\n    RouterProvider,\n} from \"react-router-dom\";\n\nfunction App() {\n\n    const router = createBrowserRouter([\n        {\n            path: \"\/\",\n            element: (\n                &lt;Home\/&gt;\n            ),\n        },\n    ]);\n\n  return (\n    &lt;RouterProvider router={router}\/&gt;\n  )\n}\n\nexport default App<\/code><\/pre>\n<p>Ex\u00e9cutez la commande <code>npm run dev<\/code> pour pr\u00e9visualiser votre application sur <a href=\"https:\/\/localhost:5173\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/localhost:5173<\/a>.<\/p>\n<p>Ceci termine la configuration initiale n\u00e9cessaire pour l&rsquo;appli frontend. Ensuite, cr\u00e9ez une application backend \u00e0 l&rsquo;aide de Spring Boot. Pour initialiser l&rsquo;application, vous pouvez utiliser le site web <a href=\"https:\/\/start.spring.io\/\" target=\"_blank\" rel=\"noopener noreferrer\">spring initializr<\/a> (si votre IDE prend en charge la cr\u00e9ation d&rsquo;applications Spring, vous n&rsquo;avez pas besoin d&rsquo;utiliser le site web).<\/p>\n<p>IntelliJ IDEA prend en charge la cr\u00e9ation d&rsquo;applications Spring Boot. Commencez par choisir l&rsquo;option <strong>New project<\/strong> sur IntelliJ IDEA. Ensuite, choisissez <strong>Spring Initializr<\/strong> dans le panneau de gauche. Saisissez les d\u00e9tails de votre projet backend : nom (<strong>backend<\/strong>), emplacement (<strong>stripe-payments-java<\/strong>), langage (<strong>Java<\/strong>) et type (<strong>Maven<\/strong>). Pour les noms de groupe et d&rsquo;artefact, utilisez respectivement <strong>com.kinsta.stripe-java<\/strong> et <strong>backend<\/strong>.<\/p>\n<figure id=\"attachment_163053\" aria-describedby=\"caption-attachment-163053\" style=\"width: 814px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163053 size-full\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/idea-new-project-dialog.png\" alt=\"La bo\u00eete de dialogue du nouveau projet IDEA.\" width=\"814\" height=\"727\"><figcaption id=\"caption-attachment-163053\" class=\"wp-caption-text\">La bo\u00eete de dialogue du nouveau projet IDEA.<\/figcaption><\/figure>\n<p>Cliquez sur le bouton <strong>Next<\/strong>. Ajoutez ensuite des d\u00e9pendances \u00e0 votre projet en choisissant <strong>Spring Web<\/strong> dans le menu d\u00e9roulant <strong>Web<\/strong> du volet des d\u00e9pendances et cliquez sur le bouton <strong>Create<\/strong>.<\/p>\n<figure id=\"attachment_163054\" aria-describedby=\"caption-attachment-163054\" style=\"width: 814px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163054 size-full\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/idea-new-project-dialog-dependencies.png\" alt=\"Choix des d\u00e9pendances.\" width=\"814\" height=\"727\"><figcaption id=\"caption-attachment-163054\" class=\"wp-caption-text\">Choix des d\u00e9pendances.<\/figcaption><\/figure>\n<p>Cela va cr\u00e9er le projet Java et l&rsquo;ouvrir dans votre IDE. Vous pouvez maintenant proc\u00e9der \u00e0 la cr\u00e9ation des diff\u00e9rents flux de paiement \u00e0 l&rsquo;aide de Stripe.<\/p>\n<h2>Accepter les paiements en ligne pour les achats de produits<\/h2>\n<p>La fonctionnalit\u00e9 la plus importante et la plus utilis\u00e9e de Stripe est l&rsquo;acceptation de paiements uniques de la part des clients. Dans cette section, vous apprendrez deux fa\u00e7ons d&rsquo;int\u00e9grer le traitement des paiements dans votre application avec Stripe.<\/p>\n<h3>Commande h\u00e9berg\u00e9e<\/h3>\n<p>Tout d&rsquo;abord, Vous construisez une page de commande qui d\u00e9clenche un flux de travail de paiement h\u00e9berg\u00e9 o\u00f9 vous ne d\u00e9clenchez un paiement qu&rsquo;\u00e0 partir de votre application frontend. Stripe se charge ensuite de recueillir les d\u00e9tails de la carte du client et de collecter le paiement, et ne partage le r\u00e9sultat de l&rsquo;op\u00e9ration de paiement qu&rsquo;\u00e0 la fin.<\/p>\n<p>Voici \u00e0 quoi ressemblerait la page de paiement :<\/p>\n<figure id=\"attachment_163055\" aria-describedby=\"caption-attachment-163055\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163055 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/completed-hosted-checkout-page-1024x506.png\" alt=\"La page de commande h\u00e9berg\u00e9e compl\u00e9t\u00e9e.\" width=\"1024\" height=\"506\"><figcaption id=\"caption-attachment-163055\" class=\"wp-caption-text\">La page de commande h\u00e9berg\u00e9e compl\u00e9t\u00e9e.<\/figcaption><\/figure>\n<p>Cette page comporte trois \u00e9l\u00e9ments principaux : <code>CartItem<\/code> &#8211; repr\u00e9sente chaque article du panier ; <code>TotalFooter<\/code> &#8211; affiche le montant total ; <code>CustomerDetails<\/code> &#8211; recueille les d\u00e9tails du client. Vous pouvez r\u00e9utiliser ces composants pour cr\u00e9er des formulaires de commande pour d&rsquo;autres sc\u00e9narios de cet article, comme le paiement int\u00e9gr\u00e9 et les abonnements.<\/p>\n<h4>Construire le frontend<\/h4>\n<p>Cr\u00e9ez un dossier <strong>components<\/strong> dans votre r\u00e9pertoire <strong>frontend\/src<\/strong>. Dans le dossier <strong>components<\/strong>, cr\u00e9ez un nouveau fichier <strong>CartItem.tsx<\/strong> et collez le code suivant :<\/p>\n<pre><code class=\"language-js\">import {Button, Card, CardBody, CardFooter, Heading, Image, Stack, Text, VStack} from \"@chakra-ui\/react\";\n\nfunction CartItem(props: CartItemProps) {\n    return &lt;Card direction={{base: 'column', sm: 'row'}}\n                 overflow='hidden'\n                 width={'xl'}\n                 variant='outline'&gt;\n        &lt;Image\n            objectFit='cover'\n            maxW={{base: '100%', sm: '200px'}}\n            src={props.data.image}\n        \/&gt;\n        &lt;Stack mt='6' spacing='3'&gt;\n            &lt;CardBody&gt;\n                &lt;VStack spacing={'3'} alignItems={\"flex-start\"}&gt;\n                    &lt;Heading size='md'&gt;{props.data.name}&lt;\/Heading&gt;\n                    &lt;VStack spacing={'1'} alignItems={\"flex-start\"}&gt;\n                        &lt;Text&gt;\n                            {props.data.description}\n                        &lt;\/Text&gt;\n                        {(props.mode === \"checkout\" ? &lt;Text&gt;\n                            {\"Quantity: \" + props.data.quantity}\n                        &lt;\/Text&gt; : &lt;&gt;&lt;\/&gt;)}\n                    &lt;\/VStack&gt;\n                &lt;\/VStack&gt;\n            &lt;\/CardBody&gt;\n\n            &lt;CardFooter&gt;\n                &lt;VStack alignItems={'flex-start'}&gt;\n                    &lt;Text color='blue.600' fontSize='2xl'&gt;\n                        {\"$\" + props.data.price}\n                    &lt;\/Text&gt;\n                &lt;\/VStack&gt;\n            &lt;\/CardFooter&gt;\n        &lt;\/Stack&gt;\n    &lt;\/Card&gt;\n}\n\nexport interface ItemData {\n    name: string\n    price: number\n    quantity: number\n    image: string\n    description: string\n    id: string\n}\n\ninterface CartItemProps {\n    data: ItemData\n    mode: \"subscription\" | \"checkout\"\n    onCancelled?: () =&gt; void\n}\n\nexport default CartItem<\/code><\/pre>\n<p>Le code ci-dessus d\u00e9finit deux interfaces \u00e0 utiliser comme types pour les propri\u00e9t\u00e9s transmises au composant. Le type <code>ItemData<\/code> est export\u00e9 pour \u00eatre r\u00e9utilis\u00e9 dans d&rsquo;autres composants.<\/p>\n<p>Le code renvoie la disposition d&rsquo;un composant d&rsquo;article de panier. Il utilise les propri\u00e9t\u00e9s fournies pour afficher l&rsquo;article \u00e0 l&rsquo;\u00e9cran.<\/p>\n<p>Ensuite, cr\u00e9ez un fichier <strong>TotalFooter.tsx<\/strong> dans le r\u00e9pertoire des <strong>compoents<\/strong> et collez le code suivant :<\/p>\n<pre><code class=\"language-js\">import {Divider, HStack, Text} from \"@chakra-ui\/react\";\n\nfunction TotalFooter(props: TotalFooterProps) {\n    return &lt;&gt;\n        &lt;Divider \/&gt;\n        &lt;HStack&gt;\n            &lt;Text&gt;Total&lt;\/Text&gt;\n            &lt;Text color='blue.600' fontSize='2xl'&gt;\n                {\"$\" + props.total}\n            &lt;\/Text&gt;\n        &lt;\/HStack&gt;\n        {props.mode === \"subscription\" &&\n            &lt;Text fontSize={\"xs\"}&gt;(Monthly, starting today)&lt;\/Text&gt;\n        }\n        {props.mode === \"trial\" &&\n            &lt;Text fontSize={\"xs\"}&gt;(Monthly, starting next month)&lt;\/Text&gt;\n        }\n    &lt;\/&gt;\n}\n\ninterface TotalFooterProps {\n    total: number\n    mode: \"checkout\" | \"subscription\" | \"trial\"\n}\n\nexport default TotalFooter\n<\/code><\/pre>\n<p>Le composant <code>TotalFooter<\/code> affiche la valeur totale du panier et utilise la valeur <code>mode<\/code> pour rendre un texte sp\u00e9cifique de <a href=\"https:\/\/kinqsta.com\/fr\/blog\/rendu-conditionnel-react\/\">mani\u00e8re conditionnelle<\/a>.<\/p>\n<p>Enfin, cr\u00e9ez le composant <code>CustomerDetails.tsx<\/code> et collez le code suivant :<\/p>\n<pre><code class=\"language-js\">import {ItemData} from \".\/CartItem.tsx\";\nimport {Button, Input, VStack} from \"@chakra-ui\/react\";\nimport {useState} from \"react\";\n\nfunction CustomerDetails(props: CustomerDetailsProp) {\n    const [name, setName] = useState(\"\")\n    const [email, setEmail] = useState(\"\")\n    const onCustomerNameChange = (ev: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {\n        setName(ev.target.value)\n    }\n\n\n\n    const onCustomerEmailChange = (ev: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {\n        setEmail(ev.target.value)\n    }\n\n    const initiatePayment = () =&gt; {\n        fetch(process.env.VITE_SERVER_BASE_URL + props.endpoint, {\n            method: \"POST\",\n            headers: {'Content-Type': 'application\/json'},\n            body: JSON.stringify({\n                items: props.data.map(elem =&gt; ({name: elem.name, id: elem.id})),\n                customerName: name,\n                customerEmail: email,\n            })\n        })\n            .then(r =&gt; r.text())\n            .then(r =&gt; {\n                window.location.href = r\n            })\n\n    }\n\n    return &lt;&gt;\n        &lt;VStack spacing={3} width={'xl'}&gt;\n            &lt;Input variant='filled' placeholder='Customer Name' onChange={onCustomerNameChange} value={name}\/&gt;\n            &lt;Input variant='filled' placeholder='Customer Email' onChange={onCustomerEmailChange} value={email}\/&gt;\n            &lt;Button onClick={initiatePayment} colorScheme={'green'}&gt;Checkout&lt;\/Button&gt;\n        &lt;\/VStack&gt;\n    &lt;\/&gt;\n}\n\ninterface CustomerDetailsProp {\n    data: ItemData[]\n    endpoint: string\n}\n\nexport default CustomerDetails\n<\/code><\/pre>\n<p>Le code ci-dessus affiche un formulaire avec deux champs de saisie &#8211; pour recueillir le nom et l&rsquo;e-mail de l&rsquo;utilisateur. Lorsque l&rsquo;utilisateur clique sur le bouton <strong>Commander<\/strong>, la m\u00e9thode <code>initiatePayment<\/code> est invoqu\u00e9e pour envoyer la demande de paiement au backend.<\/p>\n<p>Elle demande le point de terminaison que vous avez pass\u00e9 au composant et envoie les informations du client et les articles du panier dans le cadre de la demande, puis redirige l&rsquo;utilisateur vers l&rsquo;URL re\u00e7ue du serveur. Cette URL conduira l&rsquo;utilisateur \u00e0 une page de commande h\u00e9berg\u00e9e sur le serveur de Stripe. Vous apprendrez \u00e0 construire cette URL dans quelques instants.<\/p>\n<p><strong>Note :<\/strong> Ce composant utilise la variable d&rsquo;environnement <code>VITE_SERVER_BASE_URL<\/code> pour l&rsquo;URL du serveur backend. D\u00e9finissez-la en cr\u00e9ant un fichier <strong>.env<\/strong> \u00e0 la racine de votre projet :<\/p>\n<pre><code class=\"language-bash\">VITE_SERVER_BASE_URL=<a href=\"http:\/\/localhost:8080\">http:\/\/localhost:8080<\/a><\/code><\/pre>\n<p>Tous les composants ont \u00e9t\u00e9 cr\u00e9\u00e9s. Passons maintenant \u00e0 la construction de l&rsquo;itin\u00e9raire de commande h\u00e9berg\u00e9 \u00e0 l&rsquo;aide des composants. Pour cela, cr\u00e9ez un nouveau fichier <strong>HostedCheckout.tsx<\/strong> dans votre dossier <strong>routes<\/strong> avec le code suivant :<\/p>\n<pre><code class=\"language-js\">import {Center, Heading, VStack} from \"@chakra-ui\/react\";\nimport {useState} from \"react\";\nimport CartItem, {ItemData} from \"..\/components\/CartItem.tsx\";\nimport TotalFooter from \"..\/components\/TotalFooter.tsx\";\nimport CustomerDetails from \"..\/components\/CustomerDetails.tsx\";\nimport {Products} from '..\/data.ts'\n\nfunction HostedCheckout() {\n    const [items] = useState&lt;ItemData[]&gt;(Products)\n    return &lt;&gt;\n        &lt;Center h={'100vh'} color='black'&gt;\n            &lt;VStack spacing='24px'&gt;\n                &lt;Heading&gt;Hosted Checkout Example&lt;\/Heading&gt;\n                {items.map(elem =&gt; {\n                    return &lt;CartItem data={elem} mode={'checkout'}\/&gt;\n                })}\n                &lt;TotalFooter total={30} mode={\"checkout\"}\/&gt;\n                &lt;CustomerDetails data={items} endpoint={\"\/checkout\/hosted\"} mode={\"checkout\"}\/&gt;\n            &lt;\/VStack&gt;\n        &lt;\/Center&gt;\n    &lt;\/&gt;\n}\n\nexport default HostedCheckout\n<\/code><\/pre>\n<p>Cette route utilise les trois composants que vous venez de construire pour assembler un \u00e9cran de commande. Tous les modes des composants sont configur\u00e9s comme <strong>checkout<\/strong>, et le point de terminaison <code>\/checkout\/hosted<\/code> est fourni au composant de formulaire pour initier la demande de commande avec pr\u00e9cision.<\/p>\n<p>Le composant utilise un objet <code>Products<\/code> pour remplir le tableau des articles. Dans le monde r\u00e9el, ces donn\u00e9es proviennent de l&rsquo;API du panier et contiennent les articles s\u00e9lectionn\u00e9s par l&rsquo;utilisateur. Cependant, pour ce tutoriel, c&rsquo;est une liste statique provenant d&rsquo;un script qui remplit le tableau. D\u00e9finissez le tableau en cr\u00e9ant un fichier <strong>data.ts<\/strong> \u00e0 la racine de votre projet frontend et en y stockant le code suivant :<\/p>\n<pre><code class=\"language-typescript\">import {ItemData} from \".\/components\/CartItem.tsx\";\n\nexport const Products: ItemData[] = [\n    {\n        description: \"Premium Shoes\",\n        image: \"https:\/\/source.unsplash.com\/NUoPWImmjCU\",\n        name: \"Puma Shoes\",\n        price: 20,\n        quantity: 1,\n        id: \"shoe\"\n    },\n    {\n        description: \"Comfortable everyday slippers\",\n        image: \"https:\/\/source.unsplash.com\/K_gIPI791Jo\",\n        name: \"Nike Sliders\",\n        price: 10,\n        quantity: 1,\n        id: \"slippers\"\n    },\n]\n<\/code><\/pre>\n<p>Ce fichier d\u00e9finit deux \u00e9l\u00e9ments dans le tableau des produits qui sont affich\u00e9s dans le panier. N&rsquo;h\u00e9sitez pas \u00e0 modifier les valeurs des produits.<\/p>\n<p>La derni\u00e8re \u00e9tape de la construction du frontend consiste \u00e0 cr\u00e9er deux nouvelles routes pour g\u00e9rer les succ\u00e8s et les \u00e9checs. La page de commande h\u00e9berg\u00e9e par Stripe redirigera les utilisateurs dans votre application sur ces deux routes en fonction du r\u00e9sultat de la transaction. Stripe fournira \u00e9galement \u00e0 vos routes des donn\u00e9es utiles li\u00e9es \u00e0 la transaction, telles que l&rsquo;identifiant de la session de commande, que vous pourrez utiliser pour <a href=\"https:\/\/example.com\/docs\/api\/checkout\/sessions\/retrieve\" target=\"_blank\" rel=\"noopener noreferrer\">r\u00e9cup\u00e9rer l&rsquo;objet de session de paiement correspondant<\/a> et acc\u00e9der aux donn\u00e9es li\u00e9es au paiement, telles que le moyen de paiement, les d\u00e9tails de la facture, etc.<\/p>\n<p>Pour cela, cr\u00e9ez un fichier <strong>Success.tsx<\/strong> dans le r\u00e9pertoire <strong>src\/routes<\/strong> et enregistrez-y le code suivant :<\/p>\n<pre><code class=\"language-js\">import {Button, Center, Heading, Text, VStack} from \"@chakra-ui\/react\";\nimport {useNavigate} from \"react-router-dom\";\n\nfunction Success() {\n    const queryParams = new URLSearchParams(window.location.search)\n    const navigate = useNavigate()\n    const onButtonClick = () =&gt; {\n        navigate(\"\/\")\n    }\n    return &lt;Center h={'100vh'} color='green'&gt;\n        &lt;VStack spacing={3}&gt;\n            &lt;Heading fontSize={'4xl'}&gt;Success!&lt;\/Heading&gt;\n            &lt;Text color={'black'}&gt;{queryParams.toString().split(\"&\").join(\"n\")}&lt;\/Text&gt;\n            &lt;Button onClick={onButtonClick} colorScheme={'green'}&gt;Go Home&lt;\/Button&gt;\n        &lt;\/VStack&gt;\n    &lt;\/Center&gt;\n}\n\nexport default Success\n<\/code><\/pre>\n<p>Lors du rendu, ce composant affiche le message \u00ab Success ! \u00bb et affiche tous les param\u00e8tres de requ\u00eate de l&rsquo;URL \u00e0 l&rsquo;\u00e9cran. Il comprend \u00e9galement un bouton pour rediriger les utilisateurs vers la page d&rsquo;accueil de l&rsquo;application.<\/p>\n<p>Lors de la cr\u00e9ation d&rsquo;applications r\u00e9elles, c&rsquo;est sur cette page que se d\u00e9roulent les transactions non critiques qui d\u00e9pendent de la r\u00e9ussite de la transaction en cours. Par exemple, si vous construisez une page de commande pour une boutique en ligne, vous pouvez utiliser cette page pour montrer une confirmation \u00e0 l&rsquo;utilisateur et lui indiquer quand les produits qu&rsquo;il a achet\u00e9s seront livr\u00e9s.<\/p>\n<p>Ensuite, cr\u00e9ez un fichier <strong>Failure.tsx<\/strong> contenant le code suivant :<\/p>\n<pre><code class=\"language-js\">import {Button, Center, Heading, Text, VStack} from \"@chakra-ui\/react\";\nimport {useNavigate} from \"react-router-dom\";\n\nfunction Failure() {\n    const queryParams = new URLSearchParams(window.location.search)\n    const navigate = useNavigate()\n    const onButtonClick = () =&gt; {\n        navigate(\"\/\")\n    }\n\n    return &lt;Center h={'100vh'} color='red'&gt;\n        &lt;VStack spacing={3}&gt;\n            &lt;Heading fontSize={'4xl'}&gt;Failure!&lt;\/Heading&gt;\n            &lt;Text color={'black'}&gt;{queryParams.toString().split(\"&\").join(\"n\")}&lt;\/Text&gt;\n            &lt;Button onClick={onButtonClick} colorScheme={'red'}&gt;Try Again&lt;\/Button&gt;\n        &lt;\/VStack&gt;\n    &lt;\/Center&gt;\n}\n\nexport default Failure\n<\/code><\/pre>\n<p>Ce composant est similaire \u00e0 celui de <strong>Success.tsx<\/strong> et affiche le message \u00ab Failure ! \u00bb lorsqu&rsquo;il est rendu.<\/p>\n<p><aside role=\"note\" class=\"wp-block-kinsta-notice is-style-info\">\n            <h3>Info<\/h3>\n        <p>\u00c9vitez de transmettre des informations ou d&rsquo;effectuer des op\u00e9rations critiques sur la page de succ\u00e8s ou d&rsquo;\u00e9chec, car le client peut ne jamais arriver sur l&rsquo;une ou l&rsquo;autre page s&rsquo;il ferme l&rsquo;onglet du navigateur ou perd sa connexion.<\/p>\n<\/aside>\n.<\/p>\n<p>Pour les t\u00e2ches essentielles telles que la livraison des produits, l&rsquo;envoi d&rsquo;e-mails ou toute partie critique de votre flux d&rsquo;achat, utilisez des <a href=\"https:\/\/example.com\/docs\/webhooks\" target=\"_blank\" rel=\"noopener noreferrer\">crochets web<\/a>. Les crochets web sont des routes API sur votre serveur que Stripe peut invoquer lorsqu&rsquo;une transaction se produit.<\/p>\n<p>Le crochet web re\u00e7oit tous les d\u00e9tails de la transaction (via l&rsquo;objet <code>CheckoutSession<\/code> ), ce qui vous permet de l&rsquo;enregistrer dans la base de donn\u00e9es de votre application et de d\u00e9clencher les flux de travail de r\u00e9ussite ou d&rsquo;\u00e9chec correspondants. Comme votre serveur est toujours accessible \u00e0 Stripe, aucune transaction n&rsquo;est manqu\u00e9e, ce qui garantit la fonctionnalit\u00e9 constante de votre boutique en ligne.<\/p>\n<p>Enfin, mettez \u00e0 jour le fichier <strong>App.tsx<\/strong> pour qu&rsquo;il ressemble \u00e0 ceci :<\/p>\n<pre><code class=\"language-js\">import Home from \".\/routes\/Home.tsx\";\nimport {createBrowserRouter, RouterProvider,} from \"react-router-dom\";\nimport HostedCheckout from \".\/routes\/HostedCheckout.tsx\";\nimport Success from \".\/routes\/Success.tsx\";\nimport Failure from \".\/routes\/Failure.tsx\";\n\nfunction App() {\n\n    const router = createBrowserRouter([\n        {\n            path: \"\/\",\n            element: (\n                &lt;Home\/&gt;\n            ),\n        },\n        {\n            path: \"\/hosted-checkout\",\n            element: (\n                &lt;HostedCheckout\/&gt;\n            )\n        },\n        {\n            path: '\/success',\n            element: (\n                &lt;Success\/&gt;\n            )\n        },\n        {\n            path: '\/failure',\n            element: (\n                &lt;Failure\/&gt;\n            )\n        },\n    ]);\n\n    return (\n        &lt;RouterProvider router={router}\/&gt;\n    )\n}\n\nexport default App\n<\/code><\/pre>\n<p>Cela permettra de s&rsquo;assurer que les composants <code>Success<\/code> et <code>Failure<\/code> sont rendus sur les routes <code>\/success<\/code> et <code>\/failure<\/code>, respectivement.<\/p>\n<p>La configuration du frontend est termin\u00e9e. Ensuite, il faut configurer le backend pour cr\u00e9er le point de terminaison <code>\/checkout\/hosted<\/code>.<\/p>\n<h4>Construction du backend<\/h4>\n<p>Ouvrez le projet backend et installe le SDK Stripe en ajoutant les lignes suivantes dans le tableau des d\u00e9pendances de votre fichier <strong>pom.xml<\/strong>:<\/p>\n<pre><code class=\"language-bash\">        &lt;dependency&gt;\n            &lt;groupId&gt;com.stripe&lt;\/groupId&gt;\n            &lt;artifactId&gt;stripe-java&lt;\/artifactId&gt;\n            &lt;version&gt;22.29.0&lt;\/version&gt;\n        &lt;\/dependency&gt;\n<\/code><\/pre>\n<p>Ensuite, <a href=\"https:\/\/www.jetbrains.com\/help\/idea\/delegate-build-and-run-actions-to-maven.html\" target=\"_blank\" rel=\"noopener noreferrer\">chargez les modifications Maven<\/a> dans votre projet pour installer les d\u00e9pendances. Si votre IDE ne prend pas en charge cette op\u00e9ration via l&rsquo;interface utilisateur, ex\u00e9cutez la commande <code>maven dependency:resolve<\/code> ou <code>maven install<\/code>. Si vous n&rsquo;avez pas le CLI <code>maven<\/code>, utilisez le wrapper <code>mvnw<\/code> de Spring initializr lorsque vous cr\u00e9ez le projet.<\/p>\n<p>Une fois les d\u00e9pendances install\u00e9es, cr\u00e9ez un nouveau contr\u00f4leur REST pour g\u00e9rer les requ\u00eatesHTTP entrantes pour votre application backend. Pour cela, cr\u00e9ez un fichier <strong>PaymentController.java<\/strong> dans le r\u00e9pertoire <strong>src\/main\/java\/com\/kinsta\/stripe-java\/backend<\/strong> et ajoutez le code suivant :<\/p>\n<pre><code class=\"language-bash\">package com.kinsta.stripejava.backend;\n\nimport com.stripe.Stripe;\nimport com.stripe.exception.StripeException;\nimport com.stripe.model.Customer;\nimport com.stripe.model.Product;\nimport com.stripe.model.checkout.Session;\nimport com.stripe.param.checkout.SessionCreateParams;\nimport com.stripe.param.checkout.SessionCreateParams.LineItem.PriceData;\nimport org.springframework.web.bind.annotation.CrossOrigin;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@CrossOrigin\npublic class PaymentController {\n\n    String STRIPE_API_KEY = System.getenv().get(\"STRIPE_API_KEY\");\n\n    @PostMapping(\"\/checkout\/hosted\")\n    String hostedCheckout(@RequestBody RequestDTO requestDTO) throws StripeException {\n      return \"Hello World!\";\n    }\n\n}\n<\/code><\/pre>\n<p>Le code ci-dessus importe les d\u00e9pendances Stripe essentielles et \u00e9tablit la classe <code>PaymentController<\/code>. Cette classe porte deux annotations : <code>@RestController<\/code> et <code>@CrossOrigin<\/code>. L&rsquo;annotation <code>@RestController<\/code> indique \u00e0 Spring Boot de traiter cette classe comme un contr\u00f4leur, et ses m\u00e9thodes peuvent maintenant utiliser les annotations <code>@Mapping<\/code> pour traiter les requ\u00eates HTTP entrantes.<\/p>\n<p>L&rsquo;annotation <code>@CrossOrigin<\/code> marque tous les points de terminaison d\u00e9finis dans cette classe comme ouverts \u00e0 toutes les origines en vertu des r\u00e8gles CORS. Cependant, cette pratique est d\u00e9conseill\u00e9e en production en raison des failles de s\u00e9curit\u00e9 potentielles provenant de divers domaines Internet.<\/p>\n<p>Pour des r\u00e9sultats optimaux, il est conseill\u00e9 d&rsquo;h\u00e9berger les serveurs frontend et backend sur le m\u00eame domaine afin de contourner les probl\u00e8mes CORS. Sinon, si ce n&rsquo;est pas possible, vous pouvez sp\u00e9cifier le domaine de votre client frontend (qui envoie des requ\u00eates au serveur backend) en utilisant l&rsquo;annotation <code>@CrossOrigin<\/code>, comme ceci :<\/p>\n<pre><code class=\"language-java\">@CrossOrigin(origins = \"http:\/\/frontend.com\")<\/code><\/pre>\n<p>La classe <code>PaymentController<\/code> extrait la cl\u00e9 API Stripe des variables d&rsquo;environnement pour la fournir ult\u00e9rieurement au SDK Stripe. Lors de l&rsquo;ex\u00e9cution de l&rsquo;application, vous devez fournir votre cl\u00e9 API Stripe \u00e0 l&rsquo;application par le biais des variables d&rsquo;environnement.<\/p>\n<p>Localement, vous pouvez cr\u00e9er une nouvelle variable d&rsquo;environnement dans votre syst\u00e8me, soit de fa\u00e7on temporaire (en ajoutant une phrase <code>KEY=VALUE<\/code> avant la commande utilis\u00e9e pour d\u00e9marrer votre serveur de d\u00e9veloppement), soit de fa\u00e7on permanente (en mettant \u00e0 jour les fichiers de configuration de votre terminal ou en d\u00e9finissant une variable d&rsquo;environnement dans le panneau de configuration de Windows).<\/p>\n<p>Dans les environnements de production, votre fournisseur de d\u00e9ploiement (tel que Kinsta) vous fournira une option s\u00e9par\u00e9e pour remplir les variables d&rsquo;environnement utilis\u00e9es par votre application.<\/p>\n<p>Si vous utilisez IntelliJ IDEA (ou un IDE similaire), cliquez sur <strong>Ex\u00e9cuter les configurations<\/strong> en haut \u00e0 droite de l&rsquo;IDE et cliquez sur l&rsquo;option <strong>Modifier les configurations&#8230;<\/strong> dans la liste d\u00e9roulante qui s&rsquo;ouvre pour mettre \u00e0 jour votre commande d&rsquo;ex\u00e9cution et d\u00e9finir la variable d&rsquo;environnement.<\/p>\n<figure id=\"attachment_163056\" aria-describedby=\"caption-attachment-163056\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163056 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/run-debug-configurations-dialog-box-1024x420.png\" alt=\"Ouverture de la bo\u00eete de dialogue des configurations d'ex\u00e9cution\/d\u00e9bogage.\" width=\"1024\" height=\"420\"><figcaption id=\"caption-attachment-163056\" class=\"wp-caption-text\">Ouverture de la bo\u00eete de dialogue des configurations d&rsquo;ex\u00e9cution\/d\u00e9bogage.<\/figcaption><\/figure>\n<p>Une bo\u00eete de dialogue s&rsquo;ouvre alors, dans laquelle vous pouvez indiquer les variables d&rsquo;environnement de votre application \u00e0 l&rsquo;aide du champ <strong>Variables d&rsquo;environnement<\/strong>. Saisissez la variable d&rsquo;environnement <code>STRIPE_API_KEY<\/code> au format <code>VAR1=VALUE<\/code>. Vous pouvez trouver votre cl\u00e9 API sur le <a href=\"https:\/\/dashboard.example.com\/account\/apikeys\" target=\"_blank\" rel=\"noopener noreferrer\">site web des d\u00e9veloppeurs de Stripe<\/a>. Vous devez fournir la valeur de la <strong>cl\u00e9 secr\u00e8te<\/strong> \u00e0 partir de cette page.<\/p>\n<figure id=\"attachment_163057\" aria-describedby=\"caption-attachment-163057\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163057 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/stripe-dashboard-showing-api-keys-1024x504.png\" alt=\"Le tableau de bord Stripe affichant les cl\u00e9s API.\" width=\"1024\" height=\"504\"><figcaption id=\"caption-attachment-163057\" class=\"wp-caption-text\">Le tableau de bord Stripe affichant les cl\u00e9s API.<\/figcaption><\/figure>\n<p>Si vous ne l&rsquo;avez pas encore fait, <a href=\"https:\/\/dashboard.example.com\/register\" target=\"_blank\" rel=\"noopener noreferrer\">cr\u00e9ez un nouveau compte Stripe<\/a> pour avoir acc\u00e8s aux cl\u00e9s API.<\/p>\n<p>Une fois que vous avez configur\u00e9 la cl\u00e9 API, passez \u00e0 la construction du point de terminaison. Ce point de terminaison recueillera les donn\u00e9es du client (nom et e-mail), cr\u00e9era un profil client pour eux dans Stripe s&rsquo;il n&rsquo;en existe pas d\u00e9j\u00e0 un, et cr\u00e9era une <a href=\"https:\/\/example.com\/docs\/payments\/checkout\/how-checkout-works\" target=\"_blank\" rel=\"noopener noreferrer\">session de paiement<\/a> pour permettre aux utilisateurs de payer les articles du panier.<\/p>\n<p>Voici \u00e0 quoi ressemble le code de la m\u00e9thode <code>hostedCheckout<\/code>:<\/p>\n<pre><code class=\"language-java\">    @PostMapping(\"\/checkout\/hosted\")\n    String hostedCheckout(@RequestBody RequestDTO requestDTO) throws StripeException {\n\n        Stripe.apiKey = STRIPE_API_KEY;\n        String clientBaseURL = System.getenv().get(\"CLIENT_BASE_URL\");\n\n        \/\/ Start by finding an existing customer record from Stripe or creating a new one if needed\n        Customer customer = CustomerUtil.findOrCreateCustomer(requestDTO.getCustomerEmail(), requestDTO.getCustomerName());\n\n        \/\/ Next, create a checkout session by adding the details of the checkout\n        SessionCreateParams.Builder paramsBuilder =\n                SessionCreateParams.builder()\n                        .setMode(SessionCreateParams.Mode.PAYMENT)\n                        .setCustomer(customer.getId())\n                        .setSuccessUrl(clientBaseURL + \"\/success?session_id={CHECKOUT_SESSION_ID}\")\n                        .setCancelUrl(clientBaseURL + \"\/failure\");\n\n        for (Product product : requestDTO.getItems()) {\n            paramsBuilder.addLineItem(\n                    SessionCreateParams.LineItem.builder()\n                            .setQuantity(1L)\n                            .setPriceData(\n                                    PriceData.builder()\n                                            .setProductData(\n                                                    PriceData.ProductData.builder()\n                                                            .putMetadata(\"app_id\", product.getId())\n                                                            .setName(product.getName())\n                                                            .build()\n                                            )\n                                            .setCurrency(ProductDAO.getProduct(product.getId()).getDefaultPriceObject().getCurrency())\n                                            .setUnitAmountDecimal(ProductDAO.getProduct(product.getId()).getDefaultPriceObject().getUnitAmountDecimal())\n                                            .build())\n                            .build());\n        }\n\n        }\n\n        Session session = Session.create(paramsBuilder.build());\n\n        return session.getUrl();\n    }\n<\/code><\/pre>\n<p>Lors de la cr\u00e9ation de la session de paiement, le code utilise le nom du produit re\u00e7u du client mais n&rsquo;utilise pas les d\u00e9tails du prix provenant de la demande. Cette approche permet d&rsquo;\u00e9viter les manipulations de prix potentielles c\u00f4t\u00e9 client, o\u00f9 des acteurs malveillants pourraient envoyer des prix r\u00e9duits dans la demande de paiement afin de payer moins cher les produits et les services.<\/p>\n<p>Pour \u00e9viter cela, la m\u00e9thode <code>hostedCheckout<\/code> interroge votre base de donn\u00e9es de produits (via <code>ProductDAO<\/code>) pour r\u00e9cup\u00e9rer le prix correct de l&rsquo;article.<\/p>\n<p>En outre, Stripe propose diverses classes <code>Builder<\/code> qui suivent le mod\u00e8le de conception builder. Ces classes permettent de cr\u00e9er des objets param\u00e8tres pour les requ\u00eates Stripe. L&rsquo;extrait de code fourni fait \u00e9galement r\u00e9f\u00e9rence aux variables d&rsquo;environnement pour r\u00e9cup\u00e9rer l&rsquo;URL de l&rsquo;application cliente. Cette URL est n\u00e9cessaire pour que l&rsquo;objet de session de paiement puisse \u00eatre redirig\u00e9 de mani\u00e8re appropri\u00e9e apr\u00e8s un paiement r\u00e9ussi ou \u00e9chou\u00e9.<\/p>\n<p>Pour ex\u00e9cuter ce code, d\u00e9finissez l&rsquo;URL de l&rsquo;application cliente via les variables d&rsquo;environnement, de la m\u00eame mani\u00e8re que la cl\u00e9 API Stripe a \u00e9t\u00e9 fournie. Comme l&rsquo;application client est ex\u00e9cut\u00e9e par Vite, l&rsquo;URL de l&rsquo;application locale doit \u00eatre http:\/\/localhost:5173. Incluez ceci dans vos variables d&rsquo;environnement par l&rsquo;interm\u00e9diaire de votre IDE, de votre terminal ou du panneau de contr\u00f4le du syst\u00e8me.<\/p>\n<pre><code class=\"language-bash\">CLIENT_BASE_URL=http:\/\/localhost:5173<\/code><\/pre>\n<p>Fournissez \u00e9galement \u00e0 l&rsquo;application une adresse <code>ProductDAO<\/code> \u00e0 partir de laquelle elle pourra consulter les prix des produits. L&rsquo;objet d&rsquo;acc\u00e8s aux donn\u00e9es (Data Access Object ou DAO) interagit avec les sources de donn\u00e9es (telles que les bases de donn\u00e9es) pour acc\u00e9der aux donn\u00e9es li\u00e9es \u00e0 l&rsquo;application. Bien que la mise en place d&rsquo;une base de donn\u00e9es de produits sorte du cadre de ce tutoriel, une mise en \u0153uvre simple consiste \u00e0 ajouter un nouveau fichier <strong>ProductDAO.java<\/strong> dans le m\u00eame r\u00e9pertoire que <strong>PaymentController.java<\/strong> et \u00e0 y coller le code suivant :<\/p>\n<pre><code class=\"language-java\">package com.kinsta.stripejava.backend;\n\nimport com.stripe.model.Price;\nimport com.stripe.model.Product;\n\nimport java.math.BigDecimal;\n\npublic class ProductDAO {\n\n    static Product[] products;\n\n    static {\n        products = new Product[4];\n\n        Product sampleProduct = new Product();\n        Price samplePrice = new Price();\n\n        sampleProduct.setName(\"Puma Shoes\");\n        sampleProduct.setId(\"shoe\");\n        samplePrice.setCurrency(\"usd\");\n        samplePrice.setUnitAmountDecimal(BigDecimal.valueOf(2000));\n        sampleProduct.setDefaultPriceObject(samplePrice);\n        products[0] = sampleProduct;\n\n        sampleProduct = new Product();\n        samplePrice = new Price();\n\n        sampleProduct.setName(\"Nike Sliders\");\n        sampleProduct.setId(\"slippers\");\n        samplePrice.setCurrency(\"usd\");\n        samplePrice.setUnitAmountDecimal(BigDecimal.valueOf(1000));\n        sampleProduct.setDefaultPriceObject(samplePrice);\n        products[1] = sampleProduct;\n\n        sampleProduct = new Product();\n        samplePrice = new Price();\n\n        sampleProduct.setName(\"Apple Music+\");\n        sampleProduct.setId(\"music\");\n        samplePrice.setCurrency(\"usd\");\n        samplePrice.setUnitAmountDecimal(BigDecimal.valueOf(499));\n        sampleProduct.setDefaultPriceObject(samplePrice);\n        products[2] = sampleProduct;\n\n    }\n\n    public static Product getProduct(String id) {\n\n        if (\"shoe\".equals(id)) {\n            return products[0];\n        } else if (\"slippers\".equals(id)) {\n            return products[1];\n        } else if (\"music\".equals(id)) {\n            return products[2];\n        } else return new Product();\n\n    }\n}\n<\/code><\/pre>\n<p>Cela initialisera un tableau de produits et te permettra d&rsquo;interroger les donn\u00e9es du produit \u00e0 l&rsquo;aide de son identifiant (ID). Vous devrez \u00e9galement cr\u00e9er un <a href=\"https:\/\/en.wikipedia.org\/wiki\/Data_transfer_object\" target=\"_blank\" rel=\"noopener noreferrer\">DTO<\/a> (Data Transfer Object) pour permettre \u00e0 Spring Boot de s\u00e9rialiser automatiquement la charge utile entrante du client et de vous pr\u00e9senter un objet simple pour acc\u00e9der aux donn\u00e9es. Pour cela, cr\u00e9ez un nouveau fichier <strong>RequestDTO.java<\/strong> et collez le code suivant :<\/p>\n<pre><code class=\"language-java\">package com.kinsta.stripejava.backend;\n\nimport com.stripe.model.Product;\n\npublic class RequestDTO {\n    Product[] items;\n    String customerName;\n    String customerEmail;\n\n    public Product[] getItems() {\n        return items;\n    }\n\n    public String getCustomerName() {\n        return customerName;\n    }\n\n    public String getCustomerEmail() {\n        return customerEmail;\n    }\n\n}<\/code><\/pre>\n<p>Ce fichier d\u00e9finit un <a href=\"https:\/\/en.wikipedia.org\/wiki\/Plain_old_Java_object\" target=\"_blank\" rel=\"noopener noreferrer\">POJO<\/a> qui porte le nom du client, son e-mail et la liste des articles avec lesquels il passe la commande.<\/p>\n<p>Enfin, impl\u00e9mentez la m\u00e9thode <code>CustomerUtil.findOrCreateCustomer()<\/code> pour cr\u00e9er l&rsquo;objet Client dans Stripe s&rsquo;il n&rsquo;existe pas d\u00e9j\u00e0. Pour cela, cr\u00e9ez un fichier portant le nom <code>CustomerUtil<\/code> et ajoutez-y le code suivant :<\/p>\n<pre><code class=\"language-java\">package com.kinsta.stripejava.backend;\n\nimport com.stripe.exception.StripeException;\nimport com.stripe.model.Customer;\nimport com.stripe.model.CustomerSearchResult;\nimport com.stripe.param.CustomerCreateParams;\nimport com.stripe.param.CustomerSearchParams;\n\npublic class CustomerUtil {\n\n    public static Customer findCustomerByEmail(String email) throws StripeException {\n        CustomerSearchParams params =\n                CustomerSearchParams\n                        .builder()\n                        .setQuery(\"email:'\" + email + \"'\")\n                        .build();\n\n        CustomerSearchResult result = Customer.search(params);\n\n        return result.getData().size() &gt; 0 ? result.getData().get(0) : null;\n    }\n\n    public static Customer findOrCreateCustomer(String email, String name) throws StripeException {\n        CustomerSearchParams params =\n                CustomerSearchParams\n                        .builder()\n                        .setQuery(\"email:'\" + email + \"'\")\n                        .build();\n\n        CustomerSearchResult result = Customer.search(params);\n\n        Customer customer;\n\n        \/\/ If no existing customer was found, create a new record\n        if (result.getData().size() == 0) {\n\n            CustomerCreateParams customerCreateParams = CustomerCreateParams.builder()\n                    .setName(name)\n                    .setEmail(email)\n                    .build();\n\n            customer = Customer.create(customerCreateParams);\n        } else {\n            customer = result.getData().get(0);\n        }\n\n        return customer;\n    }\n}<\/code><\/pre>\n<p>Cette classe contient \u00e9galement une autre m\u00e9thode <code>findCustomerByEmail<\/code> qui vous permet de rechercher des clients dans Stripe \u00e0 l&rsquo;aide de leur adresse e-mail. L&rsquo;<a href=\"https:\/\/example.com\/docs\/api\/customers\/search\" target=\"_blank\" rel=\"noopener noreferrer\">API de recherche de clients<\/a> est utilis\u00e9e pour rechercher les enregistrements de clients dans la base de donn\u00e9es Stripe et l&rsquo;<a href=\"https:\/\/example.com\/docs\/api\/customers\/create\" target=\"_blank\" rel=\"noopener noreferrer\">API de cr\u00e9ation de clients<\/a> est utilis\u00e9e pour cr\u00e9er les enregistrements de clients selon les besoins.<\/p>\n<p>Ceci compl\u00e8te la configuration du backend n\u00e9cessaire pour le flux de paiement h\u00e9berg\u00e9. vous pouvez maintenant tester l&rsquo;application en ex\u00e9cutant l&rsquo;application frontend et l&rsquo;application backend dans leur IDE ou dans des terminaux s\u00e9par\u00e9s. Voici \u00e0 quoi ressemblerait le flux de r\u00e9ussite :<\/p>\n<figure id=\"attachment_163058\" aria-describedby=\"caption-attachment-163058\" style=\"width: 1912px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163058 size-full\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/successful-hosted-checkout-flow.gif\" alt=\"Un flux de commande h\u00e9berg\u00e9e r\u00e9ussi.\" width=\"1912\" height=\"1062\"><figcaption id=\"caption-attachment-163058\" class=\"wp-caption-text\">Un flux de commande h\u00e9berg\u00e9e r\u00e9ussi.<\/figcaption><\/figure>\n<p>Lorsque vous testez les int\u00e9grations Stripe, vous pouvez toujours utiliser les d\u00e9tails de carte suivants pour simuler les transactions par carte :<\/p>\n<p><strong>Num\u00e9ro de carte <\/strong>: 4111 1111 1111 1111<br \/>\n<strong>Mois et ann\u00e9e d&rsquo;expiration <\/strong>: 12 \/ 25<br \/>\n<strong>CVC <\/strong>: N&rsquo;importe quel nombre \u00e0 trois chiffres<br \/>\n<strong>Nom sur la carte <\/strong>: N&rsquo;importe quel nom<\/p>\n<p>Si vous choisissez d&rsquo;annuler la transaction au lieu de payer, voici \u00e0 quoi ressemblerait le flux d&rsquo;\u00e9chec :<\/p>\n<figure id=\"attachment_163059\" aria-describedby=\"caption-attachment-163059\" style=\"width: 1912px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163059 size-full\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/failed-hosted-checkout-flow.gif\" alt=\"Un flux de paiement h\u00e9berg\u00e9 \u00e9chou\u00e9.\" width=\"1912\" height=\"1062\"><figcaption id=\"caption-attachment-163059\" class=\"wp-caption-text\">Un flux de paiement h\u00e9berg\u00e9 \u00e9chou\u00e9.<\/figcaption><\/figure>\n<p>Ceci compl\u00e8te la configuration d&rsquo;une exp\u00e9rience de paiement h\u00e9berg\u00e9e par Stripe int\u00e9gr\u00e9e \u00e0 votre application. Vous ouvez consulter la <a href=\"https:\/\/example.com\/docs\/payments\/checkout\/how-checkout-works\" target=\"_blank\" rel=\"noopener noreferrer\">documentation de Stripe<\/a> pour en savoir plus sur la fa\u00e7on de personnaliser votre page de commande, de recueillir plus de d\u00e9tails aupr\u00e8s du client, et plus encore.<\/p>\n<h3>Commande int\u00e9gr\u00e9e<\/h3>\n<p>Une exp\u00e9rience de commande int\u00e9gr\u00e9e fait r\u00e9f\u00e9rence \u00e0 la construction d&rsquo;un flux de paiement qui ne redirige pas tes utilisateurs en dehors de votre application (comme c&rsquo;\u00e9tait le cas dans le flux de commande h\u00e9berg\u00e9) et qui rend le formulaire de paiement dans votre application elle-m\u00eame.<\/p>\n<p>Construire une exp\u00e9rience de paiement int\u00e9gr\u00e9e signifie g\u00e9rer les d\u00e9tails de paiement des clients, ce qui implique des informations sensibles telles que les num\u00e9ros de carte bancaire, l&rsquo;identifiant Google Pay, etc. Toutes les applications ne sont pas con\u00e7ues pour g\u00e9rer ces donn\u00e9es en toute s\u00e9curit\u00e9.<\/p>\n<p>Pour vous d\u00e9charger du fardeau de r\u00e9pondre \u00e0 des normes comme PCI-DSS, Stripe fournit des <a href=\"https:\/\/example.com\/payments\/elements\" target=\"_blank\" rel=\"noopener noreferrer\">\u00e9l\u00e9ments<\/a> que vous pouvez utiliser dans l&rsquo;application pour collecter les d\u00e9tails de paiement tout en laissant Stripe g\u00e9rer la s\u00e9curit\u00e9 et traiter les paiements en toute s\u00e9curit\u00e9 de leur c\u00f4t\u00e9.<\/p>\n<h4>Construire le frontend<\/h4>\n<p>Pour commencer, installez <a href=\"https:\/\/www.npmjs.com\/package\/@stripe\/react-stripe-js\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe React SDK<\/a> dans votre application frontent pour acc\u00e9der aux \u00e9l\u00e9ments Stripe en ex\u00e9cutant la commande suivante dans votre r\u00e9pertoire frontend :<\/p>\n<pre><code class=\"language-bash\">npm i @stripe\/react-stripe-js @stripe\/stripe-js<\/code><\/pre>\n<p>Ensuite, cr\u00e9ez un nouveau fichier appel\u00e9 <strong>IntegratedCheckout.tsx<\/strong> dans votre r\u00e9pertoire <strong>frontend\/src\/routes<\/strong> et enregistrez le code suivant dedans :<\/p>\n<pre><code class=\"language-js\">import {Button, Center, Heading, Input, VStack} from \"@chakra-ui\/react\";\nimport {useEffect, useState} from \"react\";\nimport CartItem, {ItemData} from \"..\/components\/CartItem.tsx\";\nimport TotalFooter from \"..\/components\/TotalFooter.tsx\";\nimport {Products} from '..\/data.ts'\nimport {Elements, PaymentElement, useElements, useStripe} from '@stripe\/react-stripe-js';\nimport {loadStripe, Stripe} from '@stripe\/stripe-js';\n\nfunction IntegratedCheckout() {\n\n    const [items] = useState&lt;ItemData[]&gt;(Products)\n    const [transactionClientSecret, setTransactionClientSecret] = useState(\"\")\n    const [stripePromise, setStripePromise] = useState&lt;Promise&lt;Stripe | null&gt; | null&gt;(null)\n    const [name, setName] = useState(\"\")\n    const [email, setEmail] = useState(\"\")\n    const onCustomerNameChange = (ev: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {\n        setName(ev.target.value)\n    }\n\n    const onCustomerEmailChange = (ev: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {\n        setEmail(ev.target.value)\n    }\n\n    useEffect(() =&gt; {\n        \/\/ Make sure to call `loadStripe` outside of a component\u2019s render to avoid\n        \/\/ recreating the `Stripe` object on every render.\n        setStripePromise(loadStripe(process.env.VITE_STRIPE_API_KEY || \"\"));\n\n    }, [])\n\n    const createTransactionSecret = () =&gt; {\n        fetch(process.env.VITE_SERVER_BASE_URL + \"\/checkout\/integrated\", {\n            method: \"POST\",\n            headers: {'Content-Type': 'application\/json'},\n            body: JSON.stringify({\n                items: items.map(elem =&gt; ({name: elem.name, id: elem.id})),\n                customerName: name,\n                customerEmail: email,\n            })\n        })\n            .then(r =&gt; r.text())\n            .then(r =&gt; {\n                setTransactionClientSecret(r)\n            })\n    }\n\n    return &lt;&gt;\n        &lt;Center h={'100vh'} color='black'&gt;\n            &lt;VStack spacing='24px'&gt;\n                &lt;Heading&gt;Integrated Checkout Example&lt;\/Heading&gt;\n                {items.map(elem =&gt; {\n                    return &lt;CartItem data={elem} mode={'checkout'}\/&gt;\n                })}\n                &lt;TotalFooter total={30} mode={\"checkout\"}\/&gt;\n\n                &lt;Input variant='filled' placeholder='Customer Name' onChange={onCustomerNameChange}\n                       value={name}\/&gt;\n                &lt;Input variant='filled' placeholder='Customer Email' onChange={onCustomerEmailChange}\n                       value={email}\/&gt;\n                &lt;Button onClick={createTransactionSecret} colorScheme={'green'}&gt;Initiate Payment&lt;\/Button&gt;\n\n                {(transactionClientSecret === \"\" ?\n                    &lt;&gt;&lt;\/&gt;\n                    : &lt;Elements stripe={stripePromise} options={{clientSecret: transactionClientSecret}}&gt;\n                        &lt;CheckoutForm\/&gt;\n                    &lt;\/Elements&gt;)}\n            &lt;\/VStack&gt;\n        &lt;\/Center&gt;\n    &lt;\/&gt;\n}\n\nconst CheckoutForm = () =&gt; {\n\n    const stripe = useStripe();\n    const elements = useElements();\n    const handleSubmit = async (event: React.MouseEvent&lt;HTMLButtonElement&gt;) =&gt; {\n        event.preventDefault();\n\n        if (!stripe || !elements) {\n            return;\n        }\n\n        const result = await stripe.confirmPayment({\n            elements,\n            confirmParams: {\n                return_url: process.env.VITE_CLIENT_BASE_URL + \"\/success\",\n            },\n        });\n\n        if (result.error) {\n            console.log(result.error.message);\n        }\n    };\n\n    return &lt;&gt;\n        &lt;VStack&gt;\n            &lt;PaymentElement\/&gt;\n            &lt;Button colorScheme={'green'} disabled={!stripe} onClick={handleSubmit}&gt;Pay&lt;\/Button&gt;\n        &lt;\/VStack&gt;\n    &lt;\/&gt;\n}\n\nexport default IntegratedCheckout\n<\/code><\/pre>\n<p>Ce fichier d\u00e9finit deux composants, <code>IntegratedCheckout<\/code> et <code>CheckoutForm<\/code>. <code>CheckoutForm<\/code> d\u00e9finit un formulaire simple avec un <code>PaymentElement<\/code> de Stripe qui recueille les d\u00e9tails de paiement des clients et un bouton <strong>Payer<\/strong> qui d\u00e9clenche une demande d&rsquo;encaissement du paiement.<\/p>\n<p>Ce composant appelle \u00e9galement le crochet <code>useStripe()<\/code> et <code>useElements()<\/code> pour cr\u00e9er une instance du SDK Stripe que vous pouvez utiliser pour cr\u00e9er des demandes de paiement. Une fois que vous avez cliqu\u00e9 sur le bouton <strong>Payer<\/strong>, la m\u00e9thode <code>stripe.confirmPayment()<\/code> du SDK Stripe est appel\u00e9e pour collecter les donn\u00e9es de paiement de l&rsquo;utilisateur \u00e0 partir de l&rsquo;instance d&rsquo;\u00e9l\u00e9ments et les envoyer au backend de Stripe avec une URL de r\u00e9ussite vers laquelle rediriger si la transaction est r\u00e9ussie.<\/p>\n<p>Le formulaire de paiement a \u00e9t\u00e9 s\u00e9par\u00e9 du reste de la page parce que les crochets <code>useStripe()<\/code> et <code>useElements()<\/code> doivent \u00eatre appel\u00e9s dans le contexte d&rsquo;un fournisseur <code>Elements<\/code>, ce qui a \u00e9t\u00e9 fait dans la d\u00e9claration de retour de <code>IntegratedCheckout<\/code>. Si vous d\u00e9placez les appels au crochet Stripe vers le composant <code>IntegratedCheckout<\/code> directement, ils seront en dehors de la port\u00e9e du fournisseur <code>Elements<\/code> et ne fonctionneront donc pas.<\/p>\n<p>Le composant <code>IntegratedCheckout<\/code> r\u00e9utilise les composants <code>CartItem<\/code> et <code>TotalFooter<\/code> pour afficher les articles du panier et le montant total. Il affiche \u00e9galement deux champs de saisie pour recueillir les informations du client et un bouton <strong>Initier le paiement<\/strong> qui envoie une demande au serveur Java pour cr\u00e9er la cl\u00e9 secr\u00e8te du client \u00e0 l&rsquo;aide des d\u00e9tails du client et du panier. Une fois la cl\u00e9 secr\u00e8te du client re\u00e7ue, le site <code>CheckoutForm<\/code> est rendu, ce qui permet de collecter les informations de paiement du client.<\/p>\n<p>En outre, <code>useEffect<\/code> est utilis\u00e9 pour appeler la m\u00e9thode <code>loadStripe<\/code>. Cet effet n&rsquo;est ex\u00e9cut\u00e9 qu&rsquo;une seule fois lors du rendu du composant afin que le SDK Stripe ne soit pas charg\u00e9 plusieurs fois lorsque les \u00e9tats internes du composant sont mis \u00e0 jour.<\/p>\n<p>Pour ex\u00e9cuter le code ci-dessus, vous devrez \u00e9galement ajouter deux nouvelles variables d&rsquo;environnement \u00e0 votre projet frontend : <code>VITE_STRIPE_API_KEY<\/code> et <code>VITE_CLIENT_BASE_URL<\/code>. La variable Stripe API key contiendra la cl\u00e9 API publiable du <a href=\"https:\/\/dashboard.example.com\/test\/apikeys\" target=\"_blank\" rel=\"noopener noreferrer\">tableau de bord Stripe<\/a>, et la variable client base URL contiendra le lien vers l&rsquo;application client (qui est l&rsquo;application frontend elle-m\u00eame) afin qu&rsquo;elle puisse \u00eatre transmise au Stripe SDK pour g\u00e9rer les redirections de succ\u00e8s et d&rsquo;\u00e9chec.<\/p>\n<p>Pour cela, ajoutez le code suivant \u00e0 votre fichier <strong>.env<\/strong> dans le r\u00e9pertoire frontend :<\/p>\n<pre><code class=\"language-bash\">VITE_STRIPE_API_KEY=pk_test_xxxxxxxxxx # Your key here\nVITE_CLIENT_BASE_URL=http:\/\/localhost:5173<\/code><\/pre>\n<p>Enfin, metez \u00e0 jour le fichier <strong>App.tsx<\/strong> pour inclure le composant <code>IntegratedCheckout<\/code> \u00e0 la route <code>\/integrated-checkout<\/code> de l&rsquo;application frontend. Ajoutez le code suivant dans le tableau pass\u00e9 \u00e0 l&rsquo;appel <code>createBrowserRouter<\/code> dans le composant <code>App<\/code>:<\/p>\n<pre><code class=\"language-bash\">       {\n            path: '\/integrated-checkout',\n            element: (\n                &lt;IntegratedCheckout\/&gt;\n            )\n        },<\/code><\/pre>\n<p>Ceci compl\u00e8te la configuration n\u00e9cessaire sur le frontend. Ensuite, cr\u00e9ez une nouvelle route sur votre serveur backend qui cr\u00e9e la cl\u00e9 secr\u00e8te du client n\u00e9cessaire pour g\u00e9rer les sessions de paiement int\u00e9gr\u00e9es sur votre application frontend.<\/p>\n<h4>Construction du backend<\/h4>\n<p>Pour s&rsquo;assurer que l&rsquo;int\u00e9gration frontend n&rsquo;est pas utilis\u00e9e de mani\u00e8re abusive par les attaquants (puisque le code du frontend est plus facile \u00e0 craquer que celui du backend), Stripe vous demande de g\u00e9n\u00e9rer un secret client unique sur votre serveur backend et de v\u00e9rifier chaque demande de paiement int\u00e9gr\u00e9 avec le secret client g\u00e9n\u00e9r\u00e9 sur le backend pour s&rsquo;assurer que c&rsquo;est bien votre application qui essaie de collecter les paiements. Pour cela, vous devez mettre en place une autre route dans le backend qui cr\u00e9e des secrets clients bas\u00e9s sur les informations relatives au client et au panier.<\/p>\n<p>Pour cr\u00e9er la cl\u00e9 du secret client sur votre serveur, cr\u00e9ez une nouvelle m\u00e9thode dans ta classe <code>PaymentController<\/code> avec le nom <code>integratedCheckout<\/code> et enregistrez le code suivant dans cette m\u00e9thode :<\/p>\n<pre><code class=\"language-java\">@PostMapping(\"\/checkout\/integrated\")\n    String integratedCheckout(@RequestBody RequestDTO requestDTO) throws StripeException {\n\n        Stripe.apiKey = STRIPE_API_KEY;\n\n        \/\/ Start by finding existing customer or creating a new one if needed\n        Customer customer = CustomerUtil.findOrCreateCustomer(requestDTO.getCustomerEmail(), requestDTO.getCustomerName());\n\n        \/\/ Create a PaymentIntent and send it's client secret to the client\n        PaymentIntentCreateParams params =\n                PaymentIntentCreateParams.builder()\n                       .setAmount(Long.parseLong(calculateOrderAmount(requestDTO.getItems())))\n                        .setCurrency(\"usd\")\n                        .setCustomer(customer.getId())\n                        .setAutomaticPaymentMethods(\n                                PaymentIntentCreateParams.AutomaticPaymentMethods\n                                        .builder()\n                                        .setEnabled(true)\n                                        .build()\n                        )\n                        .build();\n\n        PaymentIntent paymentIntent = PaymentIntent.create(params);\n\n        \/\/ Send the client secret from the payment intent to the client\n        return paymentIntent.getClientSecret();\n    }<\/code><\/pre>\n<p>De la m\u00eame mani\u00e8re que la session de commande a \u00e9t\u00e9 construite \u00e0 l&rsquo;aide d&rsquo;une classe de construction qui accepte la configuration de la demande de paiement, le flux de paiement int\u00e9gr\u00e9 exige que vous construisiez une session de paiement avec le montant, la devise et les moyens de paiement. Contrairement \u00e0 la session de paiement, vous ne pouvez pas associer d&rsquo;<a href=\"https:\/\/example.com\/docs\/api\/invoices\/line_item\" target=\"_blank\" rel=\"noopener noreferrer\">\u00e9l\u00e9ments de ligne<\/a> \u00e0 une session de paiement \u00e0 moins de cr\u00e9er une facture, ce que vous apprendrez dans une section ult\u00e9rieure du tutoriel.<\/p>\n<p>Comme vous ne transmettez pas les articles au constructeur de la session de paiement, vous devez calculer manuellement le montant total des articles du panier et l&rsquo;envoyer au backend de Stripe. Utilisez <code>ProductDAO<\/code> pour trouver et ajouter les prix de chaque produit du panier.<\/p>\n<p>Pour cela, d\u00e9finissez une nouvelle m\u00e9thode <code>calculateOrderAmount<\/code> et ajoutez-y le code suivant :<\/p>\n<pre><code class=\"language-java\">     static String calculateOrderAmount(Product[] items) {\n        long total = 0L;\n\n        for (Product item: items) {\n            \/\/ Look up the application database to find the prices for the products in the given list\n            total += ProductDAO.getProduct(item.getId()).getDefaultPriceObject().getUnitAmountDecimal().floatValue();\n        }\n        return String.valueOf(total);\n    }\n<\/code><\/pre>\n<p>Cela devrait suffire pour mettre en place le flux de paiement int\u00e9gr\u00e9 \u00e0 la fois sur le frontend et le backend. Vous pouvez red\u00e9marrer les serveurs de d\u00e9veloppement pour le serveur et le client et essayer le nouveau flux de paiement int\u00e9gr\u00e9 dans l&rsquo;application frontend. Voici \u00e0 quoi ressemblera le flux int\u00e9gr\u00e9 :<\/p>\n<figure id=\"attachment_163060\" aria-describedby=\"caption-attachment-163060\" style=\"width: 1912px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163060 size-full\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/integrated-checkout-flow.gif\" alt=\"Un flux de commande int\u00e9gr\u00e9.\" width=\"1912\" height=\"1062\"><figcaption id=\"caption-attachment-163060\" class=\"wp-caption-text\">Un flux de commande int\u00e9gr\u00e9.<\/figcaption><\/figure>\n<p>Ceci compl\u00e8te un flux de paiement int\u00e9gr\u00e9 de base dans votre application. Vous pouvez maintenant explorer davantage la documentation Stripe pour <a href=\"https:\/\/example.com\/docs\/payments\/customize-payment-element\" target=\"_blank\" rel=\"noopener noreferrer\">personnaliser les moyens de paiement<\/a> ou int\u00e9grer d&rsquo;autres composants pour vous aider dans d&rsquo;autres op\u00e9rations telles que la <a href=\"https:\/\/example.com\/docs\/elements\/address-element\" target=\"_blank\" rel=\"noopener noreferrer\">collecte d&rsquo;adresses<\/a>, les <a href=\"https:\/\/example.com\/docs\/payments\/elements\/link-authentication-element\" target=\"_blank\" rel=\"noopener noreferrer\">demandes de paiement<\/a>, l&rsquo;<a href=\"https:\/\/example.com\/docs\/stripe-js\/elements\/payment-request-button\" target=\"_blank\" rel=\"noopener noreferrer\">int\u00e9gration de liens<\/a>, et bien plus encore !<\/p>\n<h2>Configurer des abonnements pour des services r\u00e9currents<\/h2>\n<p>De nos jours, les boutiques en ligne proposent souvent des abonnements. Que vous construisiez une place de march\u00e9 pour des services ou que vous proposiez un produit num\u00e9rique de fa\u00e7on p\u00e9riodique, un abonnement est la solution parfaite pour donner \u00e0 vos clients un acc\u00e8s p\u00e9riodique \u00e0 votre service pour une somme modique par rapport \u00e0 un achat unique.<\/p>\n<p>Stripe peut vous aider \u00e0 mettre en place et \u00e0 annuler des abonnements facilement. Vous pouvez \u00e9galement proposer des essais gratuits dans le cadre de votre abonnement afin que les utilisateurs puissent essayer votre offre avant de s&rsquo;engager.<\/p>\n<h3>Configurer un nouvel abonnement<\/h3>\n<p>La mise en place d&rsquo;un nouvel abonnement est simple en utilisant le flux de paiement h\u00e9berg\u00e9. Il vous suffira de modifier quelques param\u00e8tres lors de la construction de la demande de paiement et de cr\u00e9er une nouvelle page (en r\u00e9utilisant les composants existants) pour afficher une page de paiement pour un nouvel abonnement. Pour commencer, cr\u00e9ez un fichier <strong>NewSubscription.tsx<\/strong> dans le dossier <strong>components<\/strong> du frontend. Collez le code suivant dans ce fichier :<\/p>\n<pre><code class=\"language-js\">import {Center, Heading, VStack} from \"@chakra-ui\/react\";\nimport {useState} from \"react\";\nimport CartItem, {ItemData} from \"..\/components\/CartItem.tsx\";\nimport TotalFooter from \"..\/components\/TotalFooter.tsx\";\nimport CustomerDetails from \"..\/components\/CustomerDetails.tsx\";\nimport {Subscriptions} from \"..\/data.ts\";\n\nfunction NewSubscription() {\n    const [items] = useState&lt;ItemData[]&gt;(Subscriptions)\n    return &lt;&gt;\n        &lt;Center h={'100vh'} color='black'&gt;\n            &lt;VStack spacing='24px'&gt;\n                &lt;Heading&gt;New Subscription Example&lt;\/Heading&gt;\n                {items.map(elem =&gt; {\n                    return &lt;CartItem data={elem} mode={'subscription'}\/&gt;\n                })}\n                &lt;TotalFooter total={4.99} mode={\"subscription\"}\/&gt;\n                &lt;CustomerDetails data={items} endpoint={\"\/subscriptions\/new\"} \/&gt;\n            &lt;\/VStack&gt;\n        &lt;\/Center&gt;\n    &lt;\/&gt;\n}\n\nexport default NewSubscription\n<\/code><\/pre>\n<p>Dans le code ci-dessus, les donn\u00e9es du panier proviennent du fichier <strong>data.ts<\/strong>, et il ne contient qu&rsquo;un seul article pour simplifier le processus. Dans les sc\u00e9narios r\u00e9els, vous pouvez avoir plusieurs articles dans le cadre d&rsquo;une commande d&rsquo;abonnement.<\/p>\n<p>Pour effectuer le rendu de ce composant sur la bonne route, ajoutez le code suivant dans le tableau pass\u00e9 \u00e0 l&rsquo;appel <code>createBrowserRouter<\/code> dans le composant <strong>App.tsx<\/strong>:<\/p>\n<pre><code class=\"language-bash\">       {\n            path: '\/new-subscription',\n            element: (\n                &lt;NewSubscription\/&gt;\n            )\n        },<\/code><\/pre>\n<p>Ceci compl\u00e8te la configuration n\u00e9cessaire sur le frontend. Sur le backend, cr\u00e9ez une nouvelle route <code>\/subscription\/new<\/code> pour cr\u00e9er une nouvelle session de paiement h\u00e9berg\u00e9e pour un produit d&rsquo;abonnement. Cr\u00e9ez une m\u00e9thode <code>newSubscription<\/code> dans le r\u00e9pertoire <strong>backend\/src\/main\/java\/com\/kinsta\/stripejava\/backend<\/strong> et enregistrez le code suivant dedans :<\/p>\n<pre><code class=\"language-java\">@PostMapping(\"\/subscriptions\/new\")\n    String newSubscription(@RequestBody RequestDTO requestDTO) throws StripeException {\n\n        Stripe.apiKey = STRIPE_API_KEY;\n\n        String clientBaseURL = System.getenv().get(\"CLIENT_BASE_URL\");\n\n        \/\/ Start by finding existing customer record from Stripe or creating a new one if needed\n        Customer customer = CustomerUtil.findOrCreateCustomer(requestDTO.getCustomerEmail(), requestDTO.getCustomerName());\n\n        \/\/ Next, create a checkout session by adding the details of the checkout\n        SessionCreateParams.Builder paramsBuilder =\n                SessionCreateParams.builder()\n                        \/\/ For subscriptions, you need to set the mode as subscription\n                        .setMode(SessionCreateParams.Mode.SUBSCRIPTION)\n                        .setCustomer(customer.getId())\n                        .setSuccessUrl(clientBaseURL + \"\/success?session_id={CHECKOUT_SESSION_ID}\")\n                        .setCancelUrl(clientBaseURL + \"\/failure\");\n\n        for (Product product : requestDTO.getItems()) {\n            paramsBuilder.addLineItem(\n                    SessionCreateParams.LineItem.builder()\n                            .setQuantity(1L)\n                            .setPriceData(\n                                    PriceData.builder()\n                                            .setProductData(\n                                                    PriceData.ProductData.builder()\n                                                            .putMetadata(\"app_id\", product.getId())\n                                                            .setName(product.getName())\n                                                            .build()\n                                            )\n                                            .setCurrency(ProductDAO.getProduct(product.getId()).getDefaultPriceObject().getCurrency())\n                                            .setUnitAmountDecimal(ProductDAO.getProduct(product.getId()).getDefaultPriceObject().getUnitAmountDecimal())\n                                            \/\/ For subscriptions, you need to provide the details on how often they would recur\n                                            .setRecurring(PriceData.Recurring.builder().setInterval(PriceData.Recurring.Interval.MONTH).build())\n                                            .build())\n                            .build());\n        }\n\n        Session session = Session.create(paramsBuilder.build());\n\n        return session.getUrl();\n    }<\/code><\/pre>\n<p>Le code de cette m\u00e9thode est assez similaire au code de la m\u00e9thode <code>hostedCheckout<\/code>, sauf que le mode d\u00e9fini pour la cr\u00e9ation de la session est l&rsquo;abonnement au lieu du produit et qu&rsquo;avant de cr\u00e9er la session, une valeur est d\u00e9finie pour l&rsquo;intervalle de r\u00e9currence de l&rsquo;abonnement.<\/p>\n<p>Cela indique \u00e0 Stripe de traiter cette commande comme une commande d&rsquo;abonnement au lieu d&rsquo;un paiement unique. Comme la m\u00e9thode <code>hostedCheckout<\/code>, cette m\u00e9thode renvoie \u00e9galement l&rsquo;URL de la page de paiement h\u00e9berg\u00e9e en tant que r\u00e9ponse HTTP au client. Le client est configur\u00e9 pour rediriger vers l&rsquo;URL re\u00e7ue, ce qui permet au client de terminer le paiement.<\/p>\n<p>Vous pouvez red\u00e9marrer les serveurs de d\u00e9veloppement pour le client et le serveur et voir la nouvelle page d&rsquo;abonnement en action. Voici \u00e0 quoi elle ressemble :<\/p>\n<figure id=\"attachment_163061\" aria-describedby=\"caption-attachment-163061\" style=\"width: 1914px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163061 size-full\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/hosted-subscription-checkout-flow.gif\" alt=\"Flux de commande d'un abonnement h\u00e9berg\u00e9.\" width=\"1914\" height=\"976\"><figcaption id=\"caption-attachment-163061\" class=\"wp-caption-text\">Flux de commande d&rsquo;un abonnement h\u00e9berg\u00e9.<\/figcaption><\/figure>\n<h3>Annulation d&rsquo;un abonnement existant<\/h3>\n<p>Maintenant que vous savez comment cr\u00e9er de nouveaux abonnements, apprenons \u00e0 permettre \u00e0 vos clients d&rsquo;annuler des abonnements existants. Puisque l&rsquo;application de d\u00e9monstration construite dans ce tutoriel ne contient aucune configuration d&rsquo;authentification, utilisez un formulaire pour permettre au client de saisir son e-mail afin de consulter ses abonnements, puis dotez chaque \u00e9l\u00e9ment d&rsquo;abonnement d&rsquo;un bouton d&rsquo;annulation pour permettre \u00e0 l&rsquo;utilisateur de l&rsquo;annuler.<\/p>\n<p>Pour cela, vous devrez proc\u00e9der comme ci-dessous :<\/p>\n<ol>\n<li>Mettre \u00e0 jour le composant <code>CartItem<\/code> pour afficher un bouton d&rsquo;annulation sur la page d&rsquo;annulation des abonnements.<\/li>\n<li>Cr\u00e9er un composant <code>CancelSubscription<\/code> qui affiche d&rsquo;abord un champ de saisie et un bouton permettant au client de rechercher des abonnements \u00e0 l&rsquo;aide de son adresse e-mail, puis qui affiche une liste d&rsquo;abonnements \u00e0 l&rsquo;aide du composant <code>CartItem<\/code> mis \u00e0 jour.<\/li>\n<li>Cr\u00e9er une nouvelle m\u00e9thode dans le serveur backend qui peut rechercher des abonnements \u00e0 partir du backend Stripe en utilisant l&rsquo;adresse e-mail du client.<\/li>\n<li>Cr\u00e9er une nouvelle m\u00e9thode dans le serveur backend qui peut annuler un abonnement en fonction de l&rsquo;identifiant de l&rsquo;abonnement qui lui a \u00e9t\u00e9 transmis.<\/li>\n<\/ol>\n<p>Commencez par mettre \u00e0 jour le composant <code>CartItem<\/code> pour qu&rsquo;il ressemble \u00e0 ceci :<\/p>\n<pre><code class=\"language-js\">\/\/ Existing imports here\n\nfunction CartItem(props: CartItemProps) {\n\n    \/\/ Add this hook call and the cancelSubscription method to cancel the selected subscription\n    const toast = useToast()\n    const cancelSubscription = () =&gt; {\n\n        fetch(process.env.VITE_SERVER_BASE_URL + \"\/subscriptions\/cancel\", {\n            method: \"POST\",\n            headers: {'Content-Type': 'application\/json'},\n            body: JSON.stringify({\n                subscriptionId: props.data.stripeSubscriptionData?.subscriptionId\n            })\n        })\n            .then(r =&gt; r.text())\n            .then(() =&gt; {\n                toast({\n                    title: 'Subscription cancelled.',\n                    description: \"We've cancelled your subscription for you.\",\n                    status: 'success',\n                    duration: 9000,\n                    isClosable: true,\n                })\n\n                if (props.onCancelled)\n                    props.onCancelled()\n            })\n    }\n\n    return &lt;Card direction={{base: 'column', sm: 'row'}}\n                 overflow='hidden'\n                 width={'xl'}\n                 variant='outline'&gt;\n        &lt;Image\n            objectFit='cover'\n            maxW={{base: '100%', sm: '200px'}}\n            src={props.data.image}\n        \/&gt;\n        &lt;Stack mt='6' spacing='3'&gt;\n            &lt;CardBody&gt;\n                &lt;VStack spacing={'3'} alignItems={\"flex-start\"}&gt;\n                    &lt;Heading size='md'&gt;{props.data.name}&lt;\/Heading&gt;\n                    &lt;VStack spacing={'1'} alignItems={\"flex-start\"}&gt;\n                        &lt;Text&gt;\n                            {props.data.description}\n                        &lt;\/Text&gt;\n                        {(props.mode === \"checkout\" ? &lt;Text&gt;\n                            {\"Quantity: \" + props.data.quantity}\n                        &lt;\/Text&gt; : &lt;&gt;&lt;\/&gt;)}\n                    &lt;\/VStack&gt;\n\n                    {\/* &lt;----------------------- Add this block ----------------------&gt; *\/}\n                    {(props.mode === \"subscription\" && props.data.stripeSubscriptionData ?\n                        &lt;VStack spacing={'1'} alignItems={\"flex-start\"}&gt;\n                            &lt;Text&gt;\n                                {\"Next Payment Date: \" + props.data.stripeSubscriptionData.nextPaymentDate}\n                            &lt;\/Text&gt;\n                            &lt;Text&gt;\n                                {\"Subscribed On: \" + props.data.stripeSubscriptionData.subscribedOn}\n                            &lt;\/Text&gt;\n                            {(props.data.stripeSubscriptionData.trialEndsOn ? &lt;Text&gt;\n                                {\"Free Trial Running Until: \" + props.data.stripeSubscriptionData.trialEndsOn}\n                            &lt;\/Text&gt; : &lt;&gt;&lt;\/&gt;)}\n                        &lt;\/VStack&gt; : &lt;&gt;&lt;\/&gt;)}\n                &lt;\/VStack&gt;\n\n            &lt;\/CardBody&gt;\n\n            &lt;CardFooter&gt;\n                &lt;VStack alignItems={'flex-start'}&gt;\n                    &lt;Text color='blue.600' fontSize='2xl'&gt;\n                        {\"$\" + props.data.price}\n                    &lt;\/Text&gt;\n                    {\/* &lt;----------------------- Add this block ----------------------&gt; *\/}\n                    {(props.data.stripeSubscriptionData ?\n                        &lt;Button colorScheme={'red'} onClick={cancelSubscription}&gt;Cancel Subscription&lt;\/Button&gt;\n                        : &lt;&gt;&lt;\/&gt;)}\n                &lt;\/VStack&gt;\n            &lt;\/CardFooter&gt;\n        &lt;\/Stack&gt;\n    &lt;\/Card&gt;\n}\n\n\/\/ Existing types here\n\nexport default CartItem\n<\/code><\/pre>\n<p>Ensuite, cr\u00e9ez un composant <strong>CancelSubscription.tsx<\/strong> dans le r\u00e9pertoire <strong>routes<\/strong> de votre page frontend et enregistrez-y le code suivant :<\/p>\n<pre><code class=\"language-js\">import {Button, Center, Heading, Input, VStack} from \"@chakra-ui\/react\";\nimport {useState} from \"react\";\nimport CartItem, {ItemData, ServerSubscriptionsResponseType} from \"..\/components\/CartItem.tsx\";\nimport {Subscriptions} from \"..\/data.ts\";\n\nfunction CancelSubscription() {\n    const [email, setEmail] = useState(\"\")\n    const [subscriptions, setSubscriptions] = useState&lt;ItemData[]&gt;([])\n\n    const onCustomerEmailChange = (ev: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {\n        setEmail(ev.target.value)\n    }\n\n    const listSubscriptions = () =&gt; {\n\n        fetch(process.env.VITE_SERVER_BASE_URL + \"\/subscriptions\/list\", {\n            method: \"POST\",\n            headers: {'Content-Type': 'application\/json'},\n            body: JSON.stringify({\n                customerEmail: email,\n            })\n        })\n            .then(r =&gt; r.json())\n            .then((r: ServerSubscriptionsResponseType[]) =&gt; {\n\n                const subscriptionsList: ItemData[] = []\n\n                r.forEach(subscriptionItem =&gt; {\n\n                    let subscriptionDetails = Subscriptions.find(elem =&gt; elem.id === subscriptionItem.appProductId) || undefined\n\n                    if (subscriptionDetails) {\n\n                        subscriptionDetails = {\n                            ...subscriptionDetails,\n                            price: Number.parseInt(subscriptionItem.price) \/ 100,\n                            stripeSubscriptionData: subscriptionItem,\n                        }\n\n                        subscriptionsList.push(subscriptionDetails)\n                    } else {\n                        console.log(\"Item not found!\")\n                    }\n                })\n\n                setSubscriptions(subscriptionsList)\n            })\n\n    }\n\n    const removeSubscription = (id: string | undefined) =&gt; {\n        const newSubscriptionsList = subscriptions.filter(elem =&gt; (elem.stripeSubscriptionData?.subscriptionId !== id))\n        setSubscriptions(newSubscriptionsList)\n    }\n\n    return &lt;&gt;\n        &lt;Center h={'100vh'} color='black'&gt;\n            &lt;VStack spacing={3} width={'xl'}&gt;\n                &lt;Heading&gt;Cancel Subscription Example&lt;\/Heading&gt;\n                {(subscriptions.length === 0 ? &lt;&gt;\n                    &lt;Input variant='filled' placeholder='Customer Email' onChange={onCustomerEmailChange}\n                           value={email}\/&gt;\n                    &lt;Button onClick={listSubscriptions} colorScheme={'green'}&gt;List Subscriptions&lt;\/Button&gt;\n                &lt;\/&gt; : &lt;&gt;&lt;\/&gt;)}\n                {subscriptions.map(elem =&gt; {\n                    return &lt;CartItem data={elem} mode={'subscription'} onCancelled={() =&gt; removeSubscription(elem.stripeSubscriptionData?.subscriptionId)}\/&gt;\n                })}\n            &lt;\/VStack&gt;\n        &lt;\/Center&gt;\n    &lt;\/&gt;\n}\n\nexport default CancelSubscription\n<\/code><\/pre>\n<p>Ce composant rend un champ de saisie et un bouton pour que les clients puissent saisir leur e-mail et commencer \u00e0 chercher des abonnements. Si des abonnements sont trouv\u00e9s, le champ de saisie et le bouton sont masqu\u00e9s et une liste d&rsquo;abonnements s&rsquo;affiche \u00e0 l&rsquo;\u00e9cran. Pour chaque \u00e9l\u00e9ment d&rsquo;abonnement, le composant transmet une m\u00e9thode <code>removeSubscription<\/code> qui demande au serveur backend Java d&rsquo;annuler l&rsquo;abonnement sur le backend Stripe.<\/p>\n<p>Pour l&rsquo;attacher \u00e0 la route <code>\/cancel-subscription<\/code> de votre application frontend, ajoutez le code suivant dans le tableau pass\u00e9 \u00e0 l&rsquo;appel <code>createBrowserRouter<\/code> dans le composant <code>App<\/code>:<\/p>\n<pre><code class=\"language-bash\">       {\n            path: '\/cancel-subscription',\n            element: (\n                &lt;CancelSubscription\/&gt;\n            )\n        },<\/code><\/pre>\n<p>Pour rechercher des abonnements sur le serveur backend, ajoutez une m\u00e9thode <code>viewSubscriptions<\/code> dans la classe <code>PaymentController<\/code> de votre projet backend avec le contenu suivant :<\/p>\n<pre><code class=\"language-java\">@PostMapping(\"\/subscriptions\/list\")\n    List&lt;Map&lt;String, String&gt;&gt; viewSubscriptions(@RequestBody RequestDTO requestDTO) throws StripeException {\n\n        Stripe.apiKey = STRIPE_API_KEY;\n\n        \/\/ Start by finding existing customer record from Stripe\n        Customer customer = CustomerUtil.findCustomerByEmail(requestDTO.getCustomerEmail());\n\n        \/\/ If no customer record was found, no subscriptions exist either, so return an empty list\n        if (customer == null) {\n            return new ArrayList&lt;&gt;();\n        }\n\n        \/\/ Search for subscriptions for the current customer\n        SubscriptionCollection subscriptions = Subscription.list(\n                SubscriptionListParams.builder()\n                        .setCustomer(customer.getId())\n                        .build());\n\n        List&lt;Map&lt;String, String&gt;&gt; response = new ArrayList&lt;&gt;();\n\n        \/\/ For each subscription record, query its item records and collect in a list of objects to send to the client\n        for (Subscription subscription : subscriptions.getData()) {\n            SubscriptionItemCollection currSubscriptionItems =\n                    SubscriptionItem.list(SubscriptionItemListParams.builder()\n                            .setSubscription(subscription.getId())\n                            .addExpand(\"data.price.product\")\n                            .build());\n\n            for (SubscriptionItem item : currSubscriptionItems.getData()) {\n                HashMap&lt;String, String&gt; subscriptionData = new HashMap&lt;&gt;();\n                subscriptionData.put(\"appProductId\", item.getPrice().getProductObject().getMetadata().get(\"app_id\"));\n                subscriptionData.put(\"subscriptionId\", subscription.getId());\n                subscriptionData.put(\"subscribedOn\", new SimpleDateFormat(\"dd\/MM\/yyyy\").format(new Date(subscription.getStartDate() * 1000)));\n                subscriptionData.put(\"nextPaymentDate\", new SimpleDateFormat(\"dd\/MM\/yyyy\").format(new Date(subscription.getCurrentPeriodEnd() * 1000)));\n                subscriptionData.put(\"price\", item.getPrice().getUnitAmountDecimal().toString());\n\n                if (subscription.getTrialEnd() != null && new Date(subscription.getTrialEnd() * 1000).after(new Date()))\n                    subscriptionData.put(\"trialEndsOn\", new SimpleDateFormat(\"dd\/MM\/yyyy\").format(new Date(subscription.getTrialEnd() * 1000)));\n                response.add(subscriptionData);\n            }\n\n        }\n\n        return response;\n    }<\/code><\/pre>\n<p>La m\u00e9thode ci-dessus trouve d&rsquo;abord l&rsquo;objet client pour l&rsquo;utilisateur donn\u00e9 dans Stripe. Ensuite, elle recherche les abonnements actifs du client. Une fois la liste des abonnements re\u00e7ue, elle en extrait les articles et trouve les produits correspondants dans la base de donn\u00e9es des produits de l&rsquo;application pour les envoyer au frontend. Ceci est important car l&rsquo;identifiant avec lequel le frontend identifie chaque produit dans la base de donn\u00e9es de l&rsquo;appli peut ou non \u00eatre le m\u00eame que l&rsquo;identifiant du produit stock\u00e9 dans Stripe.<\/p>\n<p>Enfin, cr\u00e9e un <code>cancelSubscription&lt;\/code method in the <code>PaymentController<\/code> class and paste the code below to delete a subscription based on the subscription ID passed.<\/code><\/p>\n<pre><code class=\"language-java\">@PostMapping(\"\/subscriptions\/cancel\")\n    String cancelSubscription(@RequestBody RequestDTO requestDTO) throws StripeException {\n        Stripe.apiKey = STRIPE_API_KEY;\n\n        Subscription subscription =\n                Subscription.retrieve(\n                        requestDTO.getSubscriptionId()\n                );\n\n        Subscription deletedSubscription =\n                subscription.cancel();\n\n        return deletedSubscription.getStatus();\n    }<\/code><\/pre>\n<p>Cette m\u00e9thode r\u00e9cup\u00e8re l&rsquo;objet d&rsquo;abonnement de Stripe, appelle la m\u00e9thode cancel dessus, puis renvoie l&rsquo;\u00e9tat de l&rsquo;abonnement au client. Cependant, pour pouvoir ex\u00e9cuter cette m\u00e9thode, vous devez mettre \u00e0 jour votre objet DTO pour ajouter le champ <code>subscriptionId<\/code>. Pour cela, ajoutez le champ et la m\u00e9thode suivants dans la classe <code>RequestDTO<\/code>:<\/p>\n<pre><code class=\"language-java\">package com.kinsta.stripejava.backend;\n\nimport com.stripe.model.Product;\n\npublic class RequestDTO {\n    \/\/ \u2026 other fields \u2026\n\n    \/\/ Add this\n    String subscriptionId;\n\n    \/\/ \u2026 other getters \u2026\n\n    \/\/ Add this\n    public String getSubscriptionId() {\n        return subscriptionId;\n    }\n\n}<\/code><\/pre>\n<p>Une fois que vous abvez ajout\u00e9 cela, vous pouvez maintenant r\u00e9ex\u00e9cuter le serveur de d\u00e9veloppement pour l&rsquo;application backend et frontend et voir le flux d&rsquo;annulation en action :<\/p>\n<figure id=\"attachment_163062\" aria-describedby=\"caption-attachment-163062\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163062 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/subscription-cancel-flow-1024x522.gif\" alt=\"A user flow showing how a successful subscription cancellation using the Stripe hosted page looks like.\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163062\" class=\"wp-caption-text\">Un flux d&rsquo;annulation d&rsquo;abonnement.<\/figcaption><\/figure>\n<h3>Mise en place d&rsquo;essais gratuits pour les abonnements avec des transactions \u00e0 valeur nulle<\/h3>\n<p>Une caract\u00e9ristique commune \u00e0 la plupart des abonnements modernes est d&rsquo;offrir une courte p\u00e9riode d&rsquo;essai gratuite avant de facturer l&rsquo;utilisateur. Cela permet aux utilisateurs d&rsquo;explorer le produit ou le service sans y investir. Cependant, il est pr\u00e9f\u00e9rable de stocker les donn\u00e9es de paiement du client lors de son inscription \u00e0 l&rsquo;essai gratuit afin de pouvoir facilement le facturer d\u00e8s la fin de l&rsquo;essai.<\/p>\n<p>Stripe simplifie grandement la cr\u00e9ation de tels abonnements. Pour commencer, g\u00e9n\u00e8rez un nouveau composant dans le r\u00e9pertoire <strong>frontend\/routes<\/strong> nomm\u00e9 <strong>SubscriptionWithTrial.tsx,<\/strong> et collez le code suivant :<\/p>\n<pre><code class=\"language-js\">import {Center, Heading, VStack} from \"@chakra-ui\/react\";\nimport {useState} from \"react\";\nimport CartItem, {ItemData} from \"..\/components\/CartItem.tsx\";\nimport TotalFooter from \"..\/components\/TotalFooter.tsx\";\nimport CustomerDetails from \"..\/components\/CustomerDetails.tsx\";\nimport {Subscriptions} from \"..\/data.ts\";\n\nfunction SubscriptionWithTrial() {\n    const [items] = useState&lt;ItemData[]&gt;(Subscriptions)\n    return &lt;&gt;\n        &lt;Center h={'100vh'} color='black'&gt;\n            &lt;VStack spacing='24px'&gt;\n                &lt;Heading&gt;New Subscription With Trial Example&lt;\/Heading&gt;\n                {items.map(elem =&gt; {\n                    return &lt;CartItem data={elem} mode={'subscription'}\/&gt;\n                })}\n                &lt;TotalFooter total={4.99} mode={\"trial\"}\/&gt;\n                &lt;CustomerDetails data={items} endpoint={\"\/subscriptions\/trial\"}\/&gt;\n            &lt;\/VStack&gt;\n        &lt;\/Center&gt;\n    &lt;\/&gt;\n}\n\nexport default SubscriptionWithTrial\n<\/code><\/pre>\n<p>Ce composant r\u00e9utilise les composants cr\u00e9\u00e9s pr\u00e9c\u00e9demment. La principale diff\u00e9rence entre ce composant et le composant <code>NewSubscription<\/code> est qu&rsquo;il transmet le mode pour <code>TotalFooter<\/code> en tant qu&rsquo;<strong>essai<\/strong> au lieu d&rsquo;<strong>abonnement<\/strong>. Ainsi, le composant <code>TotalFooter<\/code> affiche un texte indiquant que le client peut commencer l&rsquo;essai gratuit maintenant mais qu&rsquo;il sera factur\u00e9 apr\u00e8s un mois.<\/p>\n<p>Pour attacher ce composant \u00e0 la route <code>\/subscription-with-trial<\/code> de votre application frontend, ajoutez le code suivant dans le tableau pass\u00e9 \u00e0 l&rsquo;appel <code>createBrowserRouter<\/code> dans le composant <code>App<\/code>:<\/p>\n<pre><code class=\"language-bash\">       {\n            path: '\/subscription-with-trial',\n            element: (\n                &lt;SubscriptionWithTrial\/&gt;\n            )\n        },<\/code><\/pre>\n<p>Pour construire le flux de paiement pour les abonnements avec <strong>essai<\/strong> sur le backend, cr\u00e9ez une nouvelle m\u00e9thode nomm\u00e9e <code>newSubscriptionWithTrial<\/code> dans la classe <code>PaymentController<\/code> et ajoutez le code suivant :<\/p>\n<pre><code class=\"language-java\">    @PostMapping(\"\/subscriptions\/trial\")\n    String newSubscriptionWithTrial(@RequestBody RequestDTO requestDTO) throws StripeException {\n\n        Stripe.apiKey = STRIPE_API_KEY;\n\n        String clientBaseURL = System.getenv().get(\"CLIENT_BASE_URL\");\n\n        \/\/ Start by finding existing customer record from Stripe or creating a new one if needed\n        Customer customer = CustomerUtil.findOrCreateCustomer(requestDTO.getCustomerEmail(), requestDTO.getCustomerName());\n\n        \/\/ Next, create a checkout session by adding the details of the checkout\n        SessionCreateParams.Builder paramsBuilder =\n                SessionCreateParams.builder()\n                        .setMode(SessionCreateParams.Mode.SUBSCRIPTION)\n                        .setCustomer(customer.getId())\n                        .setSuccessUrl(clientBaseURL + \"\/success?session_id={CHECKOUT_SESSION_ID}\")\n                        .setCancelUrl(clientBaseURL + \"\/failure\")\n                        \/\/ For trials, you need to set the trial period in the session creation request\n                        .setSubscriptionData(SessionCreateParams.SubscriptionData.builder().setTrialPeriodDays(30L).build());\n\n        for (Product product : requestDTO.getItems()) {\n            paramsBuilder.addLineItem(\n                    SessionCreateParams.LineItem.builder()\n                            .setQuantity(1L)\n                            .setPriceData(\n                                    PriceData.builder()\n                                            .setProductData(\n                                                    PriceData.ProductData.builder()\n                                                            .putMetadata(\"app_id\", product.getId())\n                                                            .setName(product.getName())\n                                                            .build()\n                                            )\n                                            .setCurrency(ProductDAO.getProduct(product.getId()).getDefaultPriceObject().getCurrency())\n                                            .setUnitAmountDecimal(ProductDAO.getProduct(product.getId()).getDefaultPriceObject().getUnitAmountDecimal())\n                                            .setRecurring(PriceData.Recurring.builder().setInterval(PriceData.Recurring.Interval.MONTH).build())\n                                            .build())\n                            .build());\n        }\n\n        Session session = Session.create(paramsBuilder.build());\n\n        return session.getUrl();\n    }\n<\/code><\/pre>\n<p>Ce code est assez similaire \u00e0 celui de la m\u00e9thode <code>newSubscription<\/code>. La seule diff\u00e9rence (et la plus importante) est qu&rsquo;une p\u00e9riode d&rsquo;essai est transmise \u00e0 l&rsquo;objet des param\u00e8tres de cr\u00e9ation de session avec la valeur <code>30<\/code>, indiquant une p\u00e9riode d&rsquo;essai gratuite de 30 jours.<\/p>\n<p>Vous pouvez maintenant enregistrer les modifications et r\u00e9ex\u00e9cuter le serveur de d\u00e9veloppement pour le backend et le frontend afin de voir le flux de travail de l&rsquo;abonnement avec essai gratuit en action :<\/p>\n<figure id=\"attachment_163063\" aria-describedby=\"caption-attachment-163063\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163063 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/subscription-free-trial-flow-1024x522.gif\" alt=\"Un flux d'abonnement avec essai gratuit.\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163063\" class=\"wp-caption-text\">Un flux d&rsquo;abonnement avec essai gratuit.<\/figcaption><\/figure>\n<h2>G\u00e9n\u00e9rer des factures pour les paiements<\/h2>\n<p>Pour les abonnements, Stripe g\u00e9n\u00e8re automatiquement des factures pour chaque paiement, m\u00eame s&rsquo;il s&rsquo;agit d&rsquo;une transaction de valeur nulle pour l&rsquo;inscription \u00e0 un essai. Pour les paiements ponctuels, vous pouvez choisir de cr\u00e9er des factures si n\u00e9cessaire.<\/p>\n<p>Pour commencer \u00e0 associer tous les paiements \u00e0 des factures, mettez \u00e0 jour le corps du payload envoy\u00e9 dans la fonction <code>initiatePayment<\/code> du composant <code>CustomerDetails<\/code> dans l&rsquo;application frontend pour qu&rsquo;il contienne la propri\u00e9t\u00e9 suivante :<\/p>\n<pre><code class=\"language-js\">invoiceNeeded: true<\/code><\/pre>\n<p>Vous devrez \u00e9galement ajouter cette propri\u00e9t\u00e9 dans le corps de la charge utile envoy\u00e9e au serveur dans la fonction <code>createTransactionSecret<\/code> du composant <code>IntegratedCheckout<\/code>.<\/p>\n<p>Ensuite, mettez \u00e0 jour les routes du backend pour v\u00e9rifier la pr\u00e9sence de cette nouvelle propri\u00e9t\u00e9 et mets \u00e0 jour les appels Stripe SDK en cons\u00e9quence.<\/p>\n<p>Pour le moyen de paiement h\u00e9berg\u00e9, afin d&rsquo;ajouter la fonctionnalit\u00e9 de facturation, mettez \u00e0 jour la m\u00e9thode <code>hostedCheckout<\/code> en ajoutant les lignes de code suivantes :<\/p>\n<pre><code class=\"language-java\">@PostMapping(\"\/checkout\/hosted\")\n    String hostedCheckout(@RequestBody RequestDTO requestDTO) throws StripeException {\n\n        \/\/ \u2026 other operations being done after creating the SessionCreateParams builder instance       \n\n        \/\/ Add the following block of code just before the SessionCreateParams are built from the builder instance\n        if (requestDTO.isInvoiceNeeded()) {\n            paramsBuilder.setInvoiceCreation(SessionCreateParams.InvoiceCreation.builder().setEnabled(true).build());\n        }\n\n        Session session = Session.create(paramsBuilder.build());\n\n        return session.getUrl();\n    }<\/code><\/pre>\n<p>Ceci v\u00e9rifiera la pr\u00e9sence du champ <code>invoiceNeeded<\/code> et d\u00e9finira les param\u00e8tres de cr\u00e9ation en cons\u00e9quence.<\/p>\n<p>L&rsquo;ajout d&rsquo;une facture \u00e0 un paiement int\u00e9gr\u00e9 est l\u00e9g\u00e8rement d\u00e9licat. Vous ne pouvez pas simplement d\u00e9finir un param\u00e8tre pour demander \u00e0 Stripe de cr\u00e9er automatiquement une facture avec le paiement. Vous devez cr\u00e9er manuellement la facture, puis une intention de paiement li\u00e9e.<\/p>\n<p>Si l&rsquo;intention de paiement est pay\u00e9e avec succ\u00e8s et compl\u00e9t\u00e9e, la facture est marqu\u00e9e comme pay\u00e9e ; dans le cas contraire, la facture reste impay\u00e9e. Bien que cela soit logique, cela peut \u00eatre un peu complexe \u00e0 mettre en \u0153uvre (surtout lorsqu&rsquo;il n&rsquo;y a pas d&rsquo;exemples clairs ou de r\u00e9f\u00e9rences \u00e0 suivre).<\/p>\n<p>Pour mettre cela en \u0153uvre, mettez \u00e0 jour la m\u00e9thode <code>integratedCheckout<\/code> pour qu&rsquo;elle ressemble \u00e0 ceci :<\/p>\n<pre><code class=\"language-java\">String integratedCheckout(@RequestBody RequestDTO requestDTO) throws StripeException {\n\n        Stripe.apiKey = STRIPE_API_KEY;\n\n        \/\/ Start by finding an existing customer or creating a new one if needed\n        Customer customer = CustomerUtil.findOrCreateCustomer(requestDTO.getCustomerEmail(), requestDTO.getCustomerName());\n\n        PaymentIntent paymentIntent;\n\n        if (!requestDTO.isInvoiceNeeded()) {\n            \/\/ If the invoice is not needed, create a PaymentIntent directly and send it to the client\n            PaymentIntentCreateParams params =\n                    PaymentIntentCreateParams.builder()\n                            .setAmount(Long.parseLong(calculateOrderAmount(requestDTO.getItems())))\n                            .setCurrency(\"usd\")\n                            .setCustomer(customer.getId())\n                            .setAutomaticPaymentMethods(\n                                    PaymentIntentCreateParams.AutomaticPaymentMethods\n                                            .builder()\n                                            .setEnabled(true)\n                                            .build()\n                            )\n                            .build();\n\n            paymentIntent = PaymentIntent.create(params);\n        } else {\n            \/\/ If invoice is needed, create the invoice object, add line items to it, and finalize it to create the PaymentIntent automatically\n            InvoiceCreateParams invoiceCreateParams = new InvoiceCreateParams.Builder()\n                    .setCustomer(customer.getId())\n                    .build();\n\n            Invoice invoice = Invoice.create(invoiceCreateParams);\n\n            \/\/ Add each item to the invoice one by one\n            for (Product product : requestDTO.getItems()) {\n\n                \/\/ Look for existing Product in Stripe before creating a new one\n                Product stripeProduct;\n\n                ProductSearchResult results = Product.search(ProductSearchParams.builder()\n                        .setQuery(\"metadata['app_id']:'\" + product.getId() + \"'\")\n                        .build());\n\n                if (results.getData().size() != 0)\n                    stripeProduct = results.getData().get(0);\n                else {\n\n                    \/\/ If a product is not found in Stripe database, create it\n                    ProductCreateParams productCreateParams = new ProductCreateParams.Builder()\n                            .setName(product.getName())\n                            .putMetadata(\"app_id\", product.getId())\n                            .build();\n\n                    stripeProduct = Product.create(productCreateParams);\n                }\n\n                \/\/ Create an invoice line item using the product object for the line item\n                InvoiceItemCreateParams invoiceItemCreateParams = new InvoiceItemCreateParams.Builder()\n                        .setInvoice(invoice.getId())\n                        .setQuantity(1L)\n                        .setCustomer(customer.getId())\n                        .setPriceData(\n                                InvoiceItemCreateParams.PriceData.builder()\n                                        .setProduct(stripeProduct.getId())\n                                        .setCurrency(ProductDAO.getProduct(product.getId()).getDefaultPriceObject().getCurrency())\n                                        .setUnitAmountDecimal(ProductDAO.getProduct(product.getId()).getDefaultPriceObject().getUnitAmountDecimal())\n                                        .build())\n                        .build();\n\n                InvoiceItem.create(invoiceItemCreateParams);\n            }\n\n            \/\/ Mark the invoice as final so that a PaymentIntent is created for it\n            invoice = invoice.finalizeInvoice();\n\n            \/\/ Retrieve the payment intent object from the invoice\n            paymentIntent = PaymentIntent.retrieve(invoice.getPaymentIntent());\n        }\n\n        \/\/ Send the client secret from the payment intent to the client\n        return paymentIntent.getClientSecret();\n    }\n<\/code><\/pre>\n<p>L&rsquo;ancien code de cette m\u00e9thode est d\u00e9plac\u00e9 dans le bloc <code>if<\/code> qui v\u00e9rifie si le champ <code>invoiceNeeded<\/code> est <code>false<\/code>. S&rsquo;il est vrai, la m\u00e9thode cr\u00e9e maintenant une facture avec les \u00e9l\u00e9ments de la facture et la marque comme finalis\u00e9e pour qu&rsquo;elle puisse \u00eatre pay\u00e9e.<\/p>\n<p>Elle r\u00e9cup\u00e8re ensuite l&rsquo;intention de paiement cr\u00e9\u00e9e automatiquement lorsque la facture a \u00e9t\u00e9 finalis\u00e9e et envoie au client le secret de cette intention de paiement. Une fois que le client a termin\u00e9 le processus de paiement int\u00e9gr\u00e9, le paiement est collect\u00e9 et la facture est marqu\u00e9e comme pay\u00e9e.<\/p>\n<p>Ceci compl\u00e8te la configuration n\u00e9cessaire pour commencer \u00e0 g\u00e9n\u00e9rer des factures \u00e0 partir de ton application. Vous pouvez vous rendre dans la <a href=\"https:\/\/dashboard.example.com\/test\/invoices\" target=\"_blank\" rel=\"noopener noreferrer\">section des factures<\/a> de votre tableau de bord Stripe pour voir les factures que votre application g\u00e9n\u00e8re \u00e0 chaque paiement d&rsquo;achat et d&rsquo;abonnement.<\/p>\n<p>Cependant, Stripe vous permet \u00e9galement d&rsquo;acc\u00e9der aux factures via son API afin d&rsquo;offrir aux clients une exp\u00e9rience en libre-service leur permettant de t\u00e9l\u00e9charger les factures quand ils le souhaitent.<\/p>\n<p>Pour cela, cr\u00e9ez un nouveau composant dans le r\u00e9pertoire <strong>frontend\/routes<\/strong> nomm\u00e9 <strong>ViewInvoices.tsx<\/strong>. Collez le code suivant dans ce composant :<\/p>\n<pre><code class=\"language-js\">import {Button, Card, Center, Heading, HStack, IconButton, Input, Text, VStack} from \"@chakra-ui\/react\";\nimport {useState} from \"react\";\nimport {DownloadIcon} from \"@chakra-ui\/icons\";\n\nfunction ViewInvoices() {\n    const [email, setEmail] = useState(\"\")\n    const [invoices, setInvoices] = useState&lt;InvoiceData[]&gt;([])\n\n    const onCustomerEmailChange = (ev: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {\n        setEmail(ev.target.value)\n    }\n\n    const listInvoices = () =&gt; {\n\n        fetch(process.env.VITE_SERVER_BASE_URL + \"\/invoices\/list\", {\n            method: \"POST\",\n            headers: {'Content-Type': 'application\/json'},\n            body: JSON.stringify({\n                customerEmail: email,\n            })\n        })\n            .then(r =&gt; r.json())\n            .then((r: InvoiceData[]) =&gt; {\n                setInvoices(r)\n            })\n\n    }\n\n    return &lt;&gt;\n        &lt;Center h={'100vh'} color='black'&gt;\n            &lt;VStack spacing={3} width={'xl'}&gt;\n                &lt;Heading&gt;View Invoices for Customer&lt;\/Heading&gt;\n                {(invoices.length === 0 ? &lt;&gt;\n                    &lt;Input variant='filled' placeholder='Customer Email' onChange={onCustomerEmailChange}\n                           value={email}\/&gt;\n                    &lt;Button onClick={listInvoices} colorScheme={'green'}&gt;Look up Invoices&lt;\/Button&gt;\n                &lt;\/&gt; : &lt;&gt;&lt;\/&gt;)}\n                {invoices.map(elem =&gt; {\n                    return &lt;Card direction={{base: 'column', sm: 'row'}}\n                                 overflow='hidden'\n                                 alignItems={'center'}\n                                 justifyContent={'space-between'}\n                                 padding={'8px'}\n                                 width={500}\n                                 variant='outline'&gt;\n                        &lt;Text&gt;\n                            {elem.number}\n                        &lt;\/Text&gt;\n                        &lt;HStack spacing={\"3\"}&gt;\n                            &lt;Text color='blue.600' fontSize='2xl'&gt;\n                                {\"$\" + elem.amount}\n                            &lt;\/Text&gt;\n                            &lt;IconButton onClick={() =&gt; {\n                                window.location.href = elem.url\n                            }} icon={&lt;DownloadIcon\/&gt;} aria-label={'Download invoice'}\/&gt;\n                        &lt;\/HStack&gt;\n                    &lt;\/Card&gt;\n                })}\n            &lt;\/VStack&gt;\n        &lt;\/Center&gt;\n    &lt;\/&gt;\n}\n\ninterface InvoiceData {\n    number: string,\n    amount: string,\n    url: string\n}\n\nexport default ViewInvoices\n<\/code><\/pre>\n<p>Semblable au composant <code>CancelSubscription<\/code>, ce composant affiche un champ de saisie pour que le client saisisse son e-mail et un bouton pour rechercher des factures. Une fois les factures trouv\u00e9es, le champ de saisie et le bouton sont masqu\u00e9s, et une liste de factures avec le num\u00e9ro de la facture, le montant total et un bouton pour t\u00e9l\u00e9charger le PDF de la facture est affich\u00e9e au client.<\/p>\n<p>Pour impl\u00e9menter la m\u00e9thode du backend qui recherche les factures du client donn\u00e9 et renvoie les informations pertinentes (num\u00e9ro de facture, montant et URL PDF), ajoutez la m\u00e9thode suivante dans votre classe <code>PaymentController<\/code> sur le backend ;<\/p>\n<pre><code class=\"language-java\">@PostMapping(\"\/invoices\/list\")\n    List&lt;Map&lt;String, String&gt;&gt; listInvoices(@RequestBody RequestDTO requestDTO) throws StripeException {\n\n        Stripe.apiKey = STRIPE_API_KEY;\n\n        \/\/ Start by finding existing customer record from Stripe\n        Customer customer = CustomerUtil.findCustomerByEmail(requestDTO.getCustomerEmail());\n\n        \/\/ If no customer record was found, no subscriptions exist either, so return an empty list\n        if (customer == null) {\n            return new ArrayList&lt;&gt;();\n        }\n\n        \/\/ Search for invoices for the current customer\n        Map&lt;String, Object&gt; invoiceSearchParams = new HashMap&lt;&gt;();\n        invoiceSearchParams.put(\"customer\", customer.getId());\n        InvoiceCollection invoices =\n                Invoice.list(invoiceSearchParams);\n\n        List&lt;Map&lt;String, String&gt;&gt; response = new ArrayList&lt;&gt;();\n\n        \/\/ For each invoice, extract its number, amount, and PDF URL to send to the client\n        for (Invoice invoice : invoices.getData()) {\n            HashMap&lt;String, String&gt; map = new HashMap&lt;&gt;();\n\n            map.put(\"number\", invoice.getNumber());\n            map.put(\"amount\", String.valueOf((invoice.getTotal() \/ 100f)));\n            map.put(\"url\", invoice.getInvoicePdf());\n\n            response.add(map);\n        }\n\n        return response;\n    }\n<\/code><\/pre>\n<p>La m\u00e9thode recherche d&rsquo;abord le client par l&rsquo;adresse e-mail qui lui a \u00e9t\u00e9 fournie. Ensuite, elle recherche les factures de ce client qui sont marqu\u00e9es comme pay\u00e9es. Une fois la liste des factures trouv\u00e9e, elle extrait le num\u00e9ro de la facture, le montant et l&rsquo;URL du PDF et renvoie une liste de ces informations \u00e0 l&rsquo;application cliente.<\/p>\n<p>Voici \u00e0 quoi ressemble le flux des factures :<\/p>\n<figure id=\"attachment_163064\" aria-describedby=\"caption-attachment-163064\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163064 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/viewing-invoices-flow-1024x522.gif\" alt=\"Visualisation des factures\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163064\" class=\"wp-caption-text\">Visualisation des factures<\/figcaption><\/figure>\n<p>Ceci termine le d\u00e9veloppement de notre application Java de d\u00e9monstration (<a href=\"https:\/\/github.com\/krharsh17\/stripe-payments-java-react-frontend\" target=\"_blank\" rel=\"noopener noreferrer\">frontend<\/a> et <a href=\"https:\/\/github.com\/krharsh17\/stripe-payments-java-react-backend\" target=\"_blank\" rel=\"noopener noreferrer\">backend<\/a>). Dans la section suivante, vous apprendrez \u00e0 d\u00e9ployer cette application sur Kinsta afin que vous puissiez y acc\u00e9der en ligne.<\/p>\n<h2>D\u00e9ploiement de votre application sur Kinsta<\/h2>\n<p>Une fois que votre application est pr\u00eate, vous pouvez la d\u00e9ployer sur Kinsta. Kinsta prend en charge les d\u00e9ploiements \u00e0 partir de votre fournisseur Git pr\u00e9f\u00e9r\u00e9 (<a href=\"https:\/\/docs.sevalla.com\/applications\/git\/bitbucket#grant-access-to-the-kinsta-bitbucket-application\">Bitbucket<\/a>, <a href=\"https:\/\/docs.sevalla.com\/applications\/git\/github#authenticate-and-authorize\">GitHub<\/a> ou <a href=\"https:\/\/docs.sevalla.com\/applications\/git\/gitlab#authorize-the-kinsta-gitlab-application\">GitLab<\/a>). Connectez les d\u00e9p\u00f4ts de code source de votre application \u00e0 Kinsta, il d\u00e9ploie automatiquement votre application d\u00e8s qu&rsquo;il y a une modification dans le code.<\/p>\n<h3>Pr\u00e9parez vos projets<\/h3>\n<p>Pour d\u00e9ployer vos applications en production, identifiez les commandes de construction et de d\u00e9ploiement que Kinsta utilisera. Pour le frontend, assurez-vous que votre fichier <strong>package.json<\/strong> contient les scripts suivants :<\/p>\n<pre><code class=\"language-bash\">\"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"NODE_ENV=production tsc && vite build\",\n    \"start\": \"serve .\/dist\",\n    \"lint\": \"eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0\",\n    \"preview\": \"vite preview\"\n  },\n<\/code><\/pre>\n<p>Vous devrez \u00e9galement installer le paquet <a href=\"https:\/\/www.npmjs.com\/package\/serve\" target=\"_blank\" rel=\"noopener noreferrer\">serve<\/a> npm qui vous permet de servir des sites web statiques. Ce paquet sera utilis\u00e9 pour servir la version de production de votre application depuis l&rsquo;environnement de d\u00e9ploiement de Kinsta. Vous pouvez l&rsquo;installer en ex\u00e9cutant la commande suivante :<\/p>\n<pre><code class=\"language-bash\">npm i serve<\/code><\/pre>\n<p>Une fois que vous aurez construit votre application \u00e0 l&rsquo;aide de vite, l&rsquo;application enti\u00e8re sera empaquet\u00e9e dans un seul fichier, <strong>index.html<\/strong>, car la configuration de React que vous utilisez dans ce tutoriel est destin\u00e9e \u00e0 cr\u00e9er des applications \u00e0 page unique. Bien que cela ne fasse pas une \u00e9norme diff\u00e9rence pour vos utilisateurs, vous devez mettre en place une configuration suppl\u00e9mentaire pour g\u00e9rer le routage et la navigation du navigateur dans ce type d&rsquo;applications.<\/p>\n<p>Avec la configuration actuelle, vous ne pouvez acc\u00e9der \u00e0 votre application qu&rsquo;\u00e0 l&rsquo;URL de base de ton d\u00e9ploiement. Si l&rsquo;URL de base du d\u00e9ploiement est <strong>example.com<\/strong>, toutes les requ\u00eates adress\u00e9es \u00e0 <strong>example.com\/some-route<\/strong> conduiront \u00e0 des erreurs HTTP 404.<\/p>\n<p>En effet, votre serveur n&rsquo;a qu&rsquo;un seul fichier \u00e0 servir, le fichier <strong>index.html<\/strong>. Une requ\u00eate envoy\u00e9e \u00e0 <strong>example.com\/some-route<\/strong> commencera \u00e0 chercher le fichier <strong>some-route\/index.html<\/strong>, qui n&rsquo;existe pas ; elle recevra donc une r\u00e9ponse 404 Non Trouv\u00e9.<\/p>\n<p>Pour r\u00e9soudre ce probl\u00e8me, cr\u00e9ez un fichier nomm\u00e9 <strong>serve.json<\/strong> dans votre dossier <strong>frontend\/public<\/strong> et enregistrez le code suivant dans ce fichier :<\/p>\n<pre><code class=\"language-js\">{\n  \"rewrites\": [\n    { \"source\": \"*\", \"destination\": \"\/index.html\" }\n  ]\n}\n<\/code><\/pre>\n<p>Ce fichier demandera \u00e0 <code>serve<\/code> de r\u00e9\u00e9crire toutes les requ\u00eates entrantes pour les acheminer vers le fichier <strong>index.html<\/strong> tout en affichant le chemin vers lequel la requ\u00eate originale a \u00e9t\u00e9 envoy\u00e9e dans la r\u00e9ponse. Cela vous permettra de servir correctement les pages de succ\u00e8s et d&rsquo;\u00e9chec de votre application lorsque Stripe redirigera vos clients vers votre application.<\/p>\n<p>Pour le backend, cr\u00e9ez un <a href=\"https:\/\/kinqsta.com\/fr\/blog\/dockerfile-entrypoint\/\">Dockerfile<\/a> afin de mettre en place exactement le bon environnement pour votre application Java. L&rsquo;utilisation d&rsquo;un fichier Docker garantit que l&rsquo;environnement fourni \u00e0 votre application Java est le m\u00eame sur tous les h\u00f4tes (qu&rsquo;il s&rsquo;agisse de votre h\u00f4te de d\u00e9veloppement local ou de l&rsquo;h\u00f4te de d\u00e9ploiement Kinsta) et vous pouvez vous assurer que votre application s&rsquo;ex\u00e9cute comme pr\u00e9vu.<\/p>\n<p>Pour cela, cr\u00e9ez un fichier nomm\u00e9 <strong>Dockerfile<\/strong> dans le dossier <strong>backend<\/strong> et enregistrez-y le contenu suivant :<\/p>\n<pre><code class=\"language-bash\">FROM openjdk:22-oraclelinux8\n\nLABEL maintainer=\"krharsh17\"\n\nWORKDIR \/app\n\nCOPY . \/app\n\nRUN .\/mvnw clean package\n\nEXPOSE 8080\n\nENTRYPOINT [\"java\", \"-jar\", \"\/app\/target\/backend.jar\"]<\/code><\/pre>\n<p>Ce fichier indique au runtime d&rsquo;utiliser l&rsquo;image Java <a href=\"https:\/\/openjdk.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">OpenJDK<\/a> comme base du conteneur de d\u00e9ploiement, d&rsquo;ex\u00e9cuter la commande <code>.\/mvnw clean package<\/code> pour construire le fichier <strong>JAR<\/strong> de votre application et d&rsquo;utiliser la commande <code>java -jar &lt;jar-file&gt;<\/code> pour l&rsquo;ex\u00e9cuter. Ceci termine la pr\u00e9paration du code source pour le d\u00e9ploiement sur Kinsta.<\/p>\n<h3>Configurer les d\u00e9p\u00f4ts GitHub<\/h3>\n<p>Pour commencer \u00e0 d\u00e9ployer les applications, cr\u00e9ez deux d\u00e9p\u00f4ts GitHub pour h\u00e9berger le code source de vos applications. Si vous utilisez le CLI de GitHub, vous pouvez le faire via le terminal en ex\u00e9cutant les commandes suivantes :<\/p>\n<pre><code class=\"language-bash\"># Run these in the backend folder\ngh repo create stripe-payments-java-react-backend --public --source=. --remote=origin\ngit init\ngit add .\ngit commit -m \"Initial commit\"\ngit push origin main\n\n# Run these in the frontend folder\ngh repo create stripe-payments-java-react-frontend --public --source=. --remote=origin\ngit init\ngit add .\ngit commit -m \"Initial commit\"\ngit push origin main<\/code><\/pre>\n<p>Cela devrait cr\u00e9er de nouveaux d\u00e9p\u00f4ts GitHub dans votre compte et y pousser le code de vos applications. Vous devriez pouvoir acc\u00e9der aux d\u00e9p\u00f4ts frontend et backend. Ensuite, d\u00e9ployez cesd\u00e9p\u00f4ts sur Kinsta en suivant ces \u00e9tapes :<\/p>\n<ol>\n<li>Connectez-vous ou cr\u00e9ez votre compte Kinsta sur le tableau de bord <a href=\"https:\/\/my.kinqsta.com\/?lang=fr\">MyKinsta<\/a>.<\/li>\n<li>Dans la colonne lat\u00e9rale de gauche, cliquez sur <strong>Applications<\/strong>, puis sur <strong>Ajouter une application<\/strong>.<\/li>\n<li>Dans la modale qui s&rsquo;affiche, choisissez le d\u00e9p\u00f4t que vous voulez d\u00e9ployer. Si vous avez plusieurs branches, vous pouvez s\u00e9lectionner la branche souhait\u00e9e et donner un nom \u00e0 votre application.<\/li>\n<li>S\u00e9lectionnez l&rsquo;un des emplacements de centre de donn\u00e9es disponibles dans la liste des 24 options. Kinsta d\u00e9tecte automatiquement la commande de d\u00e9marrage de votre application.<\/li>\n<\/ol>\n<p>N&rsquo;oubliez pas que vous devez fournir \u00e0 vos applications frontend et backend certaines <a href=\"https:\/\/kinqsta.com\/fr\/blog\/variables-environnement\/\">variables d&rsquo;environnement<\/a> pour qu&rsquo;elles fonctionnent correctement. L&rsquo;application frontend a besoin des variables d&rsquo;environnement suivantes :<\/p>\n<ul>\n<li>VITE_STRIPE_API_KEY<\/li>\n<li>VITE_SERVER_BASE_URL<\/li>\n<li>VITE_CLIENT_BASE_URL<\/li>\n<\/ul>\n<p>Pour d\u00e9ployer l&rsquo;application backend, fa\u00eetes exactement ce que nous avons fait pour le frontend, mais pour l&rsquo;\u00e9tape <strong>Construire l&rsquo;environnement<\/strong>, s\u00e9lectionnez le bouton radio <strong>Utiliser Dockerfile pour configurer l&rsquo;image du conteneur<\/strong> et saisissez <code>Dockerfile<\/code> comme chemin d&rsquo;acc\u00e8s \u00e0 Dockerfile pour votre application backend.<\/p>\n<figure id=\"attachment_163065\" aria-describedby=\"caption-attachment-163065\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163065 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/application-form-build-environment-details-1024x523.png\" alt=\"D\u00e9finition des d\u00e9tails de l'environnement de construction\" width=\"1024\" height=\"523\"><figcaption id=\"caption-attachment-163065\" class=\"wp-caption-text\">D\u00e9finition des d\u00e9tails de l&rsquo;environnement de construction<\/figcaption><\/figure>\n<p>N&rsquo;oubliez pas d&rsquo;ajouter les variables d&rsquo;environnement de l&rsquo;application backend :<\/p>\n<ul>\n<li>CLIENT_BASE_URL<\/li>\n<li>STRIPE_API_KEY<\/li>\n<\/ul>\n<p>Une fois le d\u00e9ploiement termin\u00e9, rendez-vous sur la page de d\u00e9tails de vos applications et acc\u00e8dez \u00e0 l&rsquo;URL du d\u00e9ploiement \u00e0 partir de l\u00e0.<\/p>\n<figure id=\"attachment_163066\" aria-describedby=\"caption-attachment-163066\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-163066 size-large\" src=\"https:\/\/kinqsta.com\/wp-content\/uploads\/2023\/09\/hosted-url-for-kinsta-deployed-apps-1024x523.png\" alt=\"L'URL h\u00e9berg\u00e9e des applications d\u00e9ploy\u00e9es sur Kinsta.\" width=\"1024\" height=\"523\"><figcaption id=\"caption-attachment-163066\" class=\"wp-caption-text\">L&rsquo;URL h\u00e9berg\u00e9e des applications d\u00e9ploy\u00e9es sur Kinsta.<\/figcaption><\/figure>\n<p>Extrayez les URL des deux applications d\u00e9ploy\u00e9es. Dirigez-vous vers le <a href=\"https:\/\/dashboard.example.com\/test\/apikeys\" target=\"_blank\" rel=\"noopener noreferrer\">tableau de bord Stripe<\/a> pour obtenir vos cl\u00e9s d&rsquo;API secr\u00e8tes et publiables.<\/p>\n<p>Assurez-vous de fournir la cl\u00e9 publiable Stripe \u00e0 votre application frontend (et non la cl\u00e9 secr\u00e8te). Assurez-vous \u00e9galement que vos URL de base n&rsquo;ont pas de barre oblique avant (<code>\/<\/code>) \u00e0 la fin. Les itin\u00e9raires ont d\u00e9j\u00e0 des barres obliques avant, donc le fait d&rsquo;avoir une barre oblique avant \u00e0 la fin des URL de base entra\u00eenera l&rsquo;ajout de deux barres obliques aux URL finales.<\/p>\n<p>Pour votre application backend, ajoutez la cl\u00e9 secr\u00e8te du tableau de bord Stripe (pas la cl\u00e9 publiable). Assurez-vous \u00e9galement que l&rsquo;URL de votre client n&rsquo;a pas de barre oblique avant (<code>\/<\/code>) \u00e0 la fin.<\/p>\n<p>Une fois les variables ajout\u00e9es, allez dans l&rsquo;onglet <strong>D\u00e9ploiements <\/strong>de l&rsquo;application et cliquez sur le bouton <strong>Red\u00e9ployer<\/strong> pour votre application backend. Ceci compl\u00e8te la configuration unique dont vous avez besoin pour fournir \u00e0 vos d\u00e9ploiements Kinsta des informations d&rsquo;identification via les variables d&rsquo;environnement.<\/p>\n<p>Vous pouvez maintenant valider les modifications dans votre syst\u00e8me de contr\u00f4le de version. Kinsta red\u00e9ploiera automatiquement votre application si vous avez coch\u00e9 l&rsquo;option lors du d\u00e9ploiement ; sinon, vous devez d\u00e9clencher le red\u00e9ploiement manuellement.<\/p>\n<h2>R\u00e9sum\u00e9<\/h2>\n<p>Dans cet article, vous avez d\u00e9couvert le fonctionnement de Stripe et les flux de paiement qu&rsquo;il propose. Vous avez \u00e9galement appris \u00e0 travers un exemple d\u00e9taill\u00e9 comment int\u00e9grer Stripe dans votre application Java pour accepter des paiements ponctuels, mettre en place des abonnements, proposer des essais gratuits et g\u00e9n\u00e9rer des factures de paiement.<\/p>\n<p>En utilisant Stripe et Java ensemble, vous pouvez offrir \u00e0 vos clients une solution de paiement robuste qui peut \u00e9voluer et s&rsquo;int\u00e9grer de fa\u00e7on transparente \u00e0 votre \u00e9cosyst\u00e8me d&rsquo;applications et d&rsquo;outils existants.<\/p>\n<p><em>Vous utilisez Stripe dans votre application pour collecter des paiements ? Si oui, lequel des deux flux pr\u00e9f\u00e8rez-vous &#8211; h\u00e9berg\u00e9, personnalis\u00e9 ou dans l&rsquo;application ? Fa\u00eetes-le nous savoir dans les commentaires ci-dessous !<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Avec l&rsquo;augmentation des transactions num\u00e9riques, la capacit\u00e9 \u00e0 int\u00e9grer de fa\u00e7on transparente les passerelles de paiement est devenue une comp\u00e9tence essentielle pour les d\u00e9veloppeurs. Que ce &#8230;<\/p>\n","protected":false},"author":199,"featured_media":73423,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[],"topic":[953,1019,1004],"class_list":["post-73422","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","topic-api","topic-languages-developpement-web","topic-react"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v24.6 (Yoast SEO v24.6) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Un guide pour l&#039;int\u00e9gration de Stripe dans une application Spring Boot - Kinsta\u00ae<\/title>\n<meta name=\"description\" content=\"Apprenez \u00e0 int\u00e9grer Stripe de mani\u00e8re transparente dans votre application Spring Boot pour un processus de paiement efficace et adapt\u00e9.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Un guide pour l&#039;int\u00e9gration de Stripe dans une application Spring Boot\" \/>\n<meta property=\"og:description\" content=\"Apprenez \u00e0 int\u00e9grer Stripe de mani\u00e8re transparente dans votre application Spring Boot pour un processus de paiement efficace et adapt\u00e9.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/kinstafrance\/\" \/>\n<meta property=\"article:published_time\" content=\"2023-10-02T08:28:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-10-12T16:03:13+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1460\" \/>\n\t<meta property=\"og:image:height\" content=\"730\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Jeremy Holcombe\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:description\" content=\"Apprenez \u00e0 int\u00e9grer Stripe de mani\u00e8re transparente dans votre application Spring Boot pour un processus de paiement efficace et adapt\u00e9.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg\" \/>\n<meta name=\"twitter:creator\" content=\"@kinsta_fr\" \/>\n<meta name=\"twitter:site\" content=\"@kinsta_fr\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jeremy Holcombe\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"51 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/\"},\"author\":{\"name\":\"Jeremy Holcombe\",\"@id\":\"https:\/\/kinqsta.com\/fr\/#\/schema\/person\/4eee42881d7b5a73ebb4f58dd5223b21\"},\"headline\":\"Un guide pour l&rsquo;int\u00e9gration de Stripe dans une application Spring Boot\",\"datePublished\":\"2023-10-02T08:28:21+00:00\",\"dateModified\":\"2023-10-12T16:03:13+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/\"},\"wordCount\":8715,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinqsta.com\/fr\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg\",\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/\",\"url\":\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/\",\"name\":\"Un guide pour l'int\u00e9gration de Stripe dans une application Spring Boot - Kinsta\u00ae\",\"isPartOf\":{\"@id\":\"https:\/\/kinqsta.com\/fr\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg\",\"datePublished\":\"2023-10-02T08:28:21+00:00\",\"dateModified\":\"2023-10-12T16:03:13+00:00\",\"description\":\"Apprenez \u00e0 int\u00e9grer Stripe de mani\u00e8re transparente dans votre application Spring Boot pour un processus de paiement efficace et adapt\u00e9.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#primaryimage\",\"url\":\"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg\",\"contentUrl\":\"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg\",\"width\":1460,\"height\":730},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinqsta.com\/fr\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"API\",\"item\":\"https:\/\/kinqsta.com\/fr\/sujets\/api\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Un guide pour l&rsquo;int\u00e9gration de Stripe dans une application Spring Boot\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinqsta.com\/fr\/#website\",\"url\":\"https:\/\/kinqsta.com\/fr\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Solutions d&#039;h\u00e9bergement premium, rapides et s\u00e9curis\u00e9es\",\"publisher\":{\"@id\":\"https:\/\/kinqsta.com\/fr\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinqsta.com\/fr\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinqsta.com\/fr\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinqsta.com\/fr\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinqsta.com\/fr\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinqsta.com\/fr\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/kinstafrance\/\",\"https:\/\/x.com\/kinsta_fr\",\"https:\/\/www.instagram.com\/kinstahosting\/\",\"https:\/\/www.linkedin.com\/company\/kinsta\/\",\"https:\/\/www.pinterest.com\/kinstahosting\/\",\"https:\/\/www.youtube.com\/c\/Kinsta\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/kinqsta.com\/fr\/#\/schema\/person\/4eee42881d7b5a73ebb4f58dd5223b21\",\"name\":\"Jeremy Holcombe\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinqsta.com\/fr\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/0e17001f3bb37dbbe54fceef9bb547fa?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/0e17001f3bb37dbbe54fceef9bb547fa?s=96&d=mm&r=g\",\"caption\":\"Jeremy Holcombe\"},\"description\":\"Senior Editor at Kinsta, WordPress Web Developer, and Content Writer. Outside of all things WordPress, I enjoy the beach, golf, and movies. I also have tall people problems.\",\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/jeremyholcombe\/\"],\"url\":\"https:\/\/kinqsta.com\/fr\/blog\/author\/jeremyholcombe\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Un guide pour l'int\u00e9gration de Stripe dans une application Spring Boot - Kinsta\u00ae","description":"Apprenez \u00e0 int\u00e9grer Stripe de mani\u00e8re transparente dans votre application Spring Boot pour un processus de paiement efficace et adapt\u00e9.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/","og_locale":"fr_FR","og_type":"article","og_title":"Un guide pour l'int\u00e9gration de Stripe dans une application Spring Boot","og_description":"Apprenez \u00e0 int\u00e9grer Stripe de mani\u00e8re transparente dans votre application Spring Boot pour un processus de paiement efficace et adapt\u00e9.","og_url":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/kinstafrance\/","article_published_time":"2023-10-02T08:28:21+00:00","article_modified_time":"2023-10-12T16:03:13+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg","type":"image\/jpeg"}],"author":"Jeremy Holcombe","twitter_card":"summary_large_image","twitter_description":"Apprenez \u00e0 int\u00e9grer Stripe de mani\u00e8re transparente dans votre application Spring Boot pour un processus de paiement efficace et adapt\u00e9.","twitter_image":"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg","twitter_creator":"@kinsta_fr","twitter_site":"@kinsta_fr","twitter_misc":{"\u00c9crit par":"Jeremy Holcombe","Dur\u00e9e de lecture estim\u00e9e":"51 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#article","isPartOf":{"@id":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/"},"author":{"name":"Jeremy Holcombe","@id":"https:\/\/kinqsta.com\/fr\/#\/schema\/person\/4eee42881d7b5a73ebb4f58dd5223b21"},"headline":"Un guide pour l&rsquo;int\u00e9gration de Stripe dans une application Spring Boot","datePublished":"2023-10-02T08:28:21+00:00","dateModified":"2023-10-12T16:03:13+00:00","mainEntityOfPage":{"@id":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/"},"wordCount":8715,"commentCount":0,"publisher":{"@id":"https:\/\/kinqsta.com\/fr\/#organization"},"image":{"@id":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#primaryimage"},"thumbnailUrl":"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg","inLanguage":"fr-FR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/","url":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/","name":"Un guide pour l'int\u00e9gration de Stripe dans une application Spring Boot - Kinsta\u00ae","isPartOf":{"@id":"https:\/\/kinqsta.com\/fr\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#primaryimage"},"image":{"@id":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#primaryimage"},"thumbnailUrl":"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg","datePublished":"2023-10-02T08:28:21+00:00","dateModified":"2023-10-12T16:03:13+00:00","description":"Apprenez \u00e0 int\u00e9grer Stripe de mani\u00e8re transparente dans votre application Spring Boot pour un processus de paiement efficace et adapt\u00e9.","breadcrumb":{"@id":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/"]}]},{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#primaryimage","url":"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg","contentUrl":"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/09\/stripe-java-api.jpg","width":1460,"height":730},{"@type":"BreadcrumbList","@id":"https:\/\/kinqsta.com\/fr\/blog\/integration-stripe-spring-boot\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinqsta.com\/fr\/"},{"@type":"ListItem","position":2,"name":"API","item":"https:\/\/kinqsta.com\/fr\/sujets\/api\/"},{"@type":"ListItem","position":3,"name":"Un guide pour l&rsquo;int\u00e9gration de Stripe dans une application Spring Boot"}]},{"@type":"WebSite","@id":"https:\/\/kinqsta.com\/fr\/#website","url":"https:\/\/kinqsta.com\/fr\/","name":"Kinsta\u00ae","description":"Solutions d&#039;h\u00e9bergement premium, rapides et s\u00e9curis\u00e9es","publisher":{"@id":"https:\/\/kinqsta.com\/fr\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinqsta.com\/fr\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Organization","@id":"https:\/\/kinqsta.com\/fr\/#organization","name":"Kinsta","url":"https:\/\/kinqsta.com\/fr\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinqsta.com\/fr\/#\/schema\/logo\/image\/","url":"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinqsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinqsta.com\/fr\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/kinstafrance\/","https:\/\/x.com\/kinsta_fr","https:\/\/www.instagram.com\/kinstahosting\/","https:\/\/www.linkedin.com\/company\/kinsta\/","https:\/\/www.pinterest.com\/kinstahosting\/","https:\/\/www.youtube.com\/c\/Kinsta"]},{"@type":"Person","@id":"https:\/\/kinqsta.com\/fr\/#\/schema\/person\/4eee42881d7b5a73ebb4f58dd5223b21","name":"Jeremy Holcombe","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinqsta.com\/fr\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/0e17001f3bb37dbbe54fceef9bb547fa?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/0e17001f3bb37dbbe54fceef9bb547fa?s=96&d=mm&r=g","caption":"Jeremy Holcombe"},"description":"Senior Editor at Kinsta, WordPress Web Developer, and Content Writer. Outside of all things WordPress, I enjoy the beach, golf, and movies. I also have tall people problems.","sameAs":["https:\/\/www.linkedin.com\/in\/jeremyholcombe\/"],"url":"https:\/\/kinqsta.com\/fr\/blog\/author\/jeremyholcombe\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinqsta.com\/fr\/wp-json\/wp\/v2\/posts\/73422","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinqsta.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinqsta.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinqsta.com\/fr\/wp-json\/wp\/v2\/users\/199"}],"replies":[{"embeddable":true,"href":"https:\/\/kinqsta.com\/fr\/wp-json\/wp\/v2\/comments?post=73422"}],"version-history":[{"count":5,"href":"https:\/\/kinqsta.com\/fr\/wp-json\/wp\/v2\/posts\/73422\/revisions"}],"predecessor-version":[{"id":73673,"href":"https:\/\/kinqsta.com\/fr\/wp-json\/wp\/v2\/posts\/73422\/revisions\/73673"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinqsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/73422\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinqsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/73422\/translations\/it"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinqsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/73422\/translations\/pt"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinqsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/73422\/translations\/fr"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinqsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/73422\/translations\/de"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinqsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/73422\/translations\/es"},{"href":"https:\/\/kinqsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/73422\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinqsta.com\/fr\/wp-json\/wp\/v2\/media\/73423"}],"wp:attachment":[{"href":"https:\/\/kinqsta.com\/fr\/wp-json\/wp\/v2\/media?parent=73422"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinqsta.com\/fr\/wp-json\/wp\/v2\/tags?post=73422"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinqsta.com\/fr\/wp-json\/wp\/v2\/topic?post=73422"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}