{"id":63663,"date":"2023-12-02T05:33:55","date_gmt":"2023-12-02T08:33:55","guid":{"rendered":"https:\/\/kinsta.com\/pt\/?p=63663&#038;preview=true&#038;preview_id=63663"},"modified":"2023-12-06T08:59:26","modified_gmt":"2023-12-06T11:59:26","slug":"stripe-java-api","status":"publish","type":"post","link":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/","title":{"rendered":"Guia para Integra\u00e7\u00e3o do Stripe no Spring Boot"},"content":{"rendered":"<p>Com o aumento das transa\u00e7\u00f5es digitais, a capacidade de integrar perfeitamente os gateways de pagamento se tornou uma habilidade essencial para os desenvolvedores. Seja para marketplaces ou <a href=\"https:\/\/kinsta.com\/pt\/blog\/produtos-saas\/\">produtos SaaS<\/a>, um processador de pagamentos \u00e9 crucial para coletar e processar os pagamentos dos usu\u00e1rios.<\/p>\n<p>Este artigo explica como integrar o <a href=\"https:\/\/kinsta.com\/pt\/blog\/stripe-vs-adyen\/\">Stripe<\/a> em um ambiente <a href=\"https:\/\/spring.io\/projects\/spring-boot\" target=\"_blank\" rel=\"noopener noreferrer\">Spring Boot<\/a>, como configurar assinaturas, oferecer testes gratuitos e criar p\u00e1ginas de autoatendimento para que seus clientes fa\u00e7am download das faturas de pagamento.<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>O que \u00e9 o Stripe?<\/h2>\n<p>O <a href=\"https:\/\/stripe.com\/en-in\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe<\/a> \u00e9 uma plataforma de processamento de pagamentos reconhecida mundialmente e dispon\u00edvel em <a href=\"https:\/\/stripe.com\/en-in\/global\" target=\"_blank\" rel=\"noopener noreferrer\">46 pa\u00edses<\/a>. \u00c9 uma \u00f3tima op\u00e7\u00e3o se voc\u00ea quiser criar uma integra\u00e7\u00e3o de pagamento em seu aplicativo web devido ao seu grande alcance, \u00f3tima reputa\u00e7\u00e3o e documenta\u00e7\u00e3o detalhada.<\/p>\n<h3>Entendendo os conceitos comuns do Stripe<\/h3>\n<p>\u00c9 \u00fatil entender alguns conceitos comuns que o Stripe usa para coordenar e realizar opera\u00e7\u00f5es de pagamento entre v\u00e1rias partes. O Stripe oferece duas abordagens para voc\u00ea implementar a integra\u00e7\u00e3o de pagamentos em seu aplicativo.<\/p>\n<p>Voc\u00ea pode incorporar os formul\u00e1rios do Stripe no seu aplicativo para uma experi\u00eancia de cliente dentro do aplicativo (<a href=\"https:\/\/stripe.com\/docs\/payments\/payment-intents\" target=\"_blank\" rel=\"noopener noreferrer\">Payment Intent<\/a>) ou redirecionar os clientes para uma p\u00e1gina de pagamento hospedada pelo Stripe, onde o Stripe gerencia o processo e informa ao seu aplicativo quando um pagamento \u00e9 bem-sucedido ou n\u00e3o (<a href=\"https:\/\/stripe.com\/docs\/payment-links\" target=\"_blank\" rel=\"noopener noreferrer\">Payment Link<\/a>).<\/p>\n<h4 id=\"payment-intent\" class=\"has-anchor-hash\">Payment Intent (inten\u00e7\u00e3o de pagamento)<\/h4>\n<p>Ao lidar com pagamentos, \u00e9 importante reunir os detalhes do cliente e do produto antecipadamente, antes de solicitar as informa\u00e7\u00f5es do cart\u00e3o e o pagamento. Esses detalhes incluem a descri\u00e7\u00e3o, o valor total, a forma de pagamento e muito mais.<\/p>\n<p>O Stripe requer que voc\u00ea colete esses detalhes no seu aplicativo e gere um objeto <code>PaymentIntent<\/code> no backend. Essa abordagem permite que o Stripe formule uma solicita\u00e7\u00e3o de pagamento para essa inten\u00e7\u00e3o. Ap\u00f3s a conclus\u00e3o do pagamento, voc\u00ea pode recuperar consistentemente os detalhes do pagamento, inclusive sua finalidade, por meio do objeto <code>PaymentIntent<\/code>.<\/p>\n<h4 id=\"payment-links\" class=\"has-anchor-hash\">Payment Links (links de pagamento)<\/h4>\n<p>Para evitar as complexidades de integrar o Stripe diretamente \u00e0 sua base de c\u00f3digo, considere utilizar o <a href=\"https:\/\/stripe.com\/docs\/payments\/checkout\/how-checkout-works\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe Checkouts<\/a> para uma solu\u00e7\u00e3o de pagamento hospedada. Assim como na cria\u00e7\u00e3o de um <code>PaymentIntent<\/code>, voc\u00ea criar\u00e1 um <code>CheckoutSession<\/code> com detalhes do pagamento e do cliente. Em vez de iniciar um <code>PaymentIntent<\/code> no aplicativo, o <code>CheckoutSession<\/code> gera um link de pagamento para o qual voc\u00ea redireciona seus clientes. Esta \u00e9 a apar\u00eancia de uma p\u00e1gina de pagamento hospedada:<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/stripe-hosted-checkout-page-1024x522.png\" alt=\"A p\u00e1gina de checkout hospedada pelo Stripe.\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163048\" class=\"wp-caption-text\">A p\u00e1gina de checkout hospedada pelo Stripe.<\/figcaption><\/figure>\n<p>Ap\u00f3s o pagamento, o Stripe redireciona de volta para o seu aplicativo, permitindo tarefas p\u00f3s-pagamento, como confirma\u00e7\u00f5es e solicita\u00e7\u00f5es de entrega. Para maior confiabilidade, configure um webhook de backend para atualizar o Stripe, garantindo a reten\u00e7\u00e3o dos dados de pagamento mesmo que os clientes fechem acidentalmente a p\u00e1gina ap\u00f3s o pagamento.<\/p>\n<p>Embora eficaz, esse m\u00e9todo carece de flexibilidade de personaliza\u00e7\u00e3o e design. Tamb\u00e9m pode ser dif\u00edcil configur\u00e1-lo corretamente para aplicativos para dispositivos m\u00f3veis, em que uma integra\u00e7\u00e3o nativa pareceria muito mais perfeita.<\/p>\n<h4>Chaves de API<\/h4>\n<p>Ao trabalhar com a API do Stripe, voc\u00ea precisar\u00e1 de acesso \u00e0s chaves de API para que seus aplicativos cliente e servidor interajam com o backend do Stripe. Voc\u00ea pode acessar suas chaves de API do Stripe no seu <a href=\"https:\/\/dashboard.stripe.com\/test\/apikeys\" target=\"_blank\" rel=\"noopener noreferrer\">painel de desenvolvedor do Stripe<\/a>. Veja como ficaria:<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/stripe-dashboard-api-keys-1024x522.png\" alt=\"O painel do Stripe mostrando as chaves de API.\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163050\" class=\"wp-caption-text\">O painel do Stripe mostrando as chaves de API.<\/figcaption><\/figure>\n<h3>Como funcionam os pagamentos no Stripe?<\/h3>\n<p>Para entender como funcionam os pagamentos no Stripe, voc\u00ea precisa entender todas as partes interessadas envolvidas. Quatro participantes est\u00e3o envolvidos em cada transa\u00e7\u00e3o de pagamento:<\/p>\n<ol>\n<li><strong>Cliente (comprador)<\/strong>: A pessoa que pretende pagar por um servi\u00e7o\/produto.<\/li>\n<li><strong>Comerciante<\/strong>: Voc\u00ea, o propriet\u00e1rio da empresa, \u00e9 respons\u00e1vel por receber pagamentos e vender servi\u00e7os\/produtos.<\/li>\n<li><strong>Adquirente<\/strong>: Um banco que processa pagamentos em seu nome (do comerciante) e encaminha sua solicita\u00e7\u00e3o de pagamento para o banco dos seus clientes. Os adquirentes podem fazer parcerias com terceiros para ajudar a processar os pagamentos.<\/li>\n<li><strong>Banco emissor<\/strong>: O banco que concede cr\u00e9dito e emite cart\u00f5es e outros m\u00e9todos de pagamento para os consumidores.<\/li>\n<\/ol>\n<p>Aqui est\u00e1 um fluxo de pagamento t\u00edpico entre essas partes interessadas em um n\u00edvel muito alto.<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/how-online-payments-work-1024x201.png\" alt=\"Como funcionam os pagamentos on-line.\" width=\"1024\" height=\"201\"><figcaption id=\"caption-attachment-163049\" class=\"wp-caption-text\">Como funcionam os pagamentos on-line.<\/figcaption><\/figure>\n<p>O cliente informa ao comerciante que est\u00e1 disposto a pagar. Ent\u00e3o o comerciante encaminha os detalhes relacionados ao pagamento para o banco adquirente, que coleta o pagamento do banco emissor do cliente e informa ao comerciante que o pagamento foi bem-sucedido.<\/p>\n<p>Essa \u00e9 uma vis\u00e3o geral de alt\u00edssimo n\u00edvel do processo de pagamento. Como comerciante, voc\u00ea s\u00f3 precisa se preocupar em coletar a inten\u00e7\u00e3o de pagamento, pass\u00e1-la para o processador de pagamento e lidar com o resultado do pagamento. Entretanto, conforme discutido anteriormente, h\u00e1 duas maneiras de fazer isso.<\/p>\n<p>Ao criar uma sess\u00e3o de checkout gerenciada pelo Stripe, na qual o Stripe cuida da coleta dos detalhes do pagamento, o fluxo t\u00edpico fica assim:<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/stripe-hosted-payment-workflow-1024x749.png\" alt=\"O fluxo de trabalho de pagamento no checkout hospedado pelo Stripe.\" width=\"1024\" height=\"749\"><figcaption id=\"caption-attachment-163051\" class=\"wp-caption-text\">O fluxo de trabalho de pagamento no checkout hospedado pelo Stripe. (Fonte: <a href=\"https:\/\/stripe.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>Com os fluxos de pagamento personalizados, a decis\u00e3o \u00e9 realmente sua. Voc\u00ea pode projetar a intera\u00e7\u00e3o entre cliente e servidor, o comprador e a API do Stripe com base nas necessidades do seu aplicativo. Voc\u00ea pode adicionar a esse fluxo de trabalho a coleta de endere\u00e7o, gera\u00e7\u00e3o de fatura, cancelamento, avalia\u00e7\u00f5es gratuitas, etc., conforme necess\u00e1rio.<\/p>\n<p>Agora que voc\u00ea entende como funcionam os pagamentos do Stripe, est\u00e1 pronto para come\u00e7ar a cri\u00e1-lo em seu aplicativo Java.<\/p>\n<h2>Integra\u00e7\u00e3o do Stripe no aplicativo Spring Boot<\/h2>\n<p>Para iniciar a integra\u00e7\u00e3o com o Stripe, voc\u00ea deve criar um aplicativo de frontend para interagir com o backend Java e iniciar os pagamentos. Neste tutorial, voc\u00ea criar\u00e1 um aplicativo React para acionar v\u00e1rios tipos de pagamentos e assinaturas, de modo a obter uma compreens\u00e3o clara dos seus mecanismos.<\/p>\n<p><strong>Observa\u00e7\u00e3o<\/strong>: este tutorial n\u00e3o abordar\u00e1 a cria\u00e7\u00e3o de um site de eCommerce completo; o objetivo principal \u00e9 orient\u00e1-lo no simples processo de integra\u00e7\u00e3o do Stripe ao Spring Boot.<\/p>\n<h3>Configurando os projetos de frontend e backend<\/h3>\n<p>Crie um novo diret\u00f3rio e estruture um projeto React usando o Vite, executando o seguinte comando:<\/p>\n<pre><code class=\"language-bash\">npm create vite@latest<\/code><\/pre>\n<p>Defina o nome do projeto como <strong>frontend<\/strong> (ou outro nome, se preferir), o framework como <strong>React<\/strong> e a variante como <strong>TypeScript<\/strong>. Navegue at\u00e9 o diret\u00f3rio do projeto e instale o Chakra UI para a r\u00e1pida estrutura\u00e7\u00e3o de elementos de interface de usu\u00e1rio executando o seguinte comando:<\/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>Voc\u00ea tamb\u00e9m instalar\u00e1 o <code>react-router-dom<\/code> em seu projeto para roteamento no lado do cliente executando o comando abaixo:<\/p>\n<pre><code class=\"language-bash\">npm i react-router-dom<\/code><\/pre>\n<p>Agora voc\u00ea est\u00e1 pronto para come\u00e7ar a criar seu aplicativo de frontend. Esta \u00e9 a p\u00e1gina inicial que voc\u00ea vai criar.<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/completed-home-page-1024x522.png\" alt=\"A p\u00e1gina inicial conclu\u00edda do aplicativo frontend.\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163052\" class=\"wp-caption-text\">A p\u00e1gina inicial conclu\u00edda do aplicativo frontend.<\/figcaption><\/figure>\n<p>Ao clicar em qualquer bot\u00e3o dessa p\u00e1gina, voc\u00ea ser\u00e1 levado a p\u00e1ginas de checkout separadas com formul\u00e1rios de pagamento. Para come\u00e7ar, crie uma nova pasta chamada <strong>routes<\/strong> em seu diret\u00f3rio <strong>frontend\/src<\/strong>. Dentro dessa pasta, crie um arquivo <strong>Home.tsx<\/strong>. Esse arquivo conter\u00e1 o c\u00f3digo do caminho inicial do seu aplicativo (<code>\/<\/code>). Cole o seguinte c\u00f3digo no arquivo:<\/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>Para ativar a navega\u00e7\u00e3o em seu aplicativo, atualize o arquivo <strong>App.tsx<\/strong> para configurar a classe <code>RouteProvider<\/code> 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>Execute o comando <code>npm run dev<\/code> para visualizar seu aplicativo em <a href=\"https:\/\/localhost:5173\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/localhost:5173<\/a>.<\/p>\n<p>Isso conclui a configura\u00e7\u00e3o inicial necess\u00e1ria para o aplicativo de frontend. Em seguida, crie um aplicativo de backend usando o Spring Boot. Para inicializar o aplicativo, voc\u00ea pode usar o site <a href=\"https:\/\/start.spring.io\/\" target=\"_blank\" rel=\"noopener noreferrer\">spring initializr<\/a> (se o seu IDE oferecer suporte \u00e0 cria\u00e7\u00e3o de aplicativos Spring, voc\u00ea n\u00e3o precisar\u00e1 usar o site).<\/p>\n<p>O IntelliJ IDEA oferece suporte \u00e0 cria\u00e7\u00e3o de aplicativos Spring Boot. Comece escolhendo a op\u00e7\u00e3o <strong>New Project <\/strong>no IntelliJ IDEA. Em seguida, escolha <strong>Spring Initializr<\/strong> no painel esquerdo. Insira os detalhes do seu projeto de backend: Name <strong>(backend<\/strong>), Location (o diret\u00f3rio <strong>stripe-payments-java<\/strong>), Language (<strong>Java<\/strong>), e Type (<strong>Maven<\/strong>). Para os nomes de Group e Artifact, use <strong>com.kinsta.stripe-java<\/strong> e <strong>backend,<\/strong> respectivamente.<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/idea-new-project-dialog.png\" alt=\"A caixa de di\u00e1logo do novo projeto IDEA.\" width=\"814\" height=\"727\"><figcaption id=\"caption-attachment-163053\" class=\"wp-caption-text\">A caixa de di\u00e1logo do novo projeto IDEA.<\/figcaption><\/figure>\n<p>.<\/p>\n<p>Clique no bot\u00e3o <strong>Next<\/strong>. Da\u00ed adicione depend\u00eancias ao seu projeto escolhendo <strong>Spring Web<\/strong> no menu suspenso <strong>Web<\/strong> no painel de depend\u00eancias, e clique no bot\u00e3o <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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/idea-new-project-dialog-dependencies.png\" alt=\"Escolhendo as depend\u00eancias.\" width=\"814\" height=\"727\"><figcaption id=\"caption-attachment-163054\" class=\"wp-caption-text\">Escolhendo as depend\u00eancias.<\/figcaption><\/figure>\n<p>Isso criar\u00e1 o projeto Java e o abrir\u00e1 em seu IDE. Agora voc\u00ea pode prosseguir com a cria\u00e7\u00e3o dos v\u00e1rios fluxos de checkout usando o Stripe.<\/p>\n<h2>Aceitando pagamentos on-line para compras de produtos<\/h2>\n<p>A funcionalidade mais importante e amplamente utilizada do Stripe \u00e9 aceitar pagamentos \u00fanicos de clientes. Nesta se\u00e7\u00e3o, voc\u00ea aprender\u00e1 duas maneiras de integrar o processamento de pagamentos no seu aplicativo com o Stripe.<\/p>\n<h3>Checkout hospedado<\/h3>\n<p>Primeiro, voc\u00ea cria uma p\u00e1gina de checkout que aciona um fluxo de trabalho de pagamento hospedado em que voc\u00ea s\u00f3 aciona um pagamento a partir do seu aplicativo frontend. O Stripe ent\u00e3o se encarrega de coletar os detalhes do cart\u00e3o do cliente e o pagamento, compartilhando apenas o resultado da opera\u00e7\u00e3o de pagamento no final.<\/p>\n<p>Esta \u00e9 a apar\u00eancia da p\u00e1gina de checkout:<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/completed-hosted-checkout-page-1024x506.png\" alt=\"A p\u00e1gina conclu\u00edda do checkout hospedado.\" width=\"1024\" height=\"506\"><figcaption id=\"caption-attachment-163055\" class=\"wp-caption-text\">A p\u00e1gina conclu\u00edda do checkout hospedado.<\/figcaption><\/figure>\n<p>Essa p\u00e1gina tem tr\u00eas componentes principais: <code>CartItem<\/code> \u2014 representa cada item no carrinho; <code>TotalFooter<\/code> \u2014 exibe o valor total; <code>CustomerDetails<\/code> \u2014 coleta os detalhes do cliente. Voc\u00ea pode reutilizar esses componentes para criar formul\u00e1rios de checkout para outros cen\u00e1rios neste artigo, tais como checkout integrado e assinaturas.<\/p>\n<h4>Cria\u00e7\u00e3o do frontend<\/h4>\n<p>Crie uma pasta <strong>components<\/strong> em seu diret\u00f3rio <strong>frontend\/src<\/strong>. Na pasta <strong>components<\/strong>, crie um novo arquivo <strong>CartItem.tsx<\/strong> e cole o c\u00f3digo a seguir:<\/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>O c\u00f3digo acima define duas interfaces a serem usadas como tipos para as propriedades passadas para o componente. O tipo <code>ItemData<\/code> \u00e9 exportado para ser reutilizado em outros componentes.<\/p>\n<p>O c\u00f3digo retorna o layout de um componente de item de carrinho. Ele utiliza as propriedades fornecidas para renderizar o item na tela.<\/p>\n<p>Em seguida, crie um arquivo <strong>TotalFooter.tsx<\/strong> no diret\u00f3rio <strong>components<\/strong> e cole o seguinte c\u00f3digo:<\/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>O componente <code>TotalFooter<\/code> exibe o valor total do carrinho e usa o valor <code>mode<\/code> para <a href=\"https:\/\/kinsta.com\/pt\/blog\/renderizacao-condicional-react\/\">renderizar condicionalmente<\/a> um texto espec\u00edfico.<\/p>\n<p>Por fim, crie o componente <code>CustomerDetails.tsx<\/code> e cole o seguinte c\u00f3digo:<\/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>O c\u00f3digo acima exibe um formul\u00e1rio com dois campos de entrada \u2014 para coletar o nome e o e-mail do usu\u00e1rio. Quando o bot\u00e3o <strong>Checkout<\/strong> \u00e9 clicado, o m\u00e9todo <code>initiatePayment<\/code> \u00e9 chamado para enviar a solicita\u00e7\u00e3o de checkout para o backend.<\/p>\n<p>Ele solicita o endpoint que voc\u00ea passou para o componente e envia as informa\u00e7\u00f5es do cliente e os itens do carrinho como parte da solicita\u00e7\u00e3o e, em seguida, redireciona o usu\u00e1rio para a URL recebida do servidor. Essa URL levar\u00e1 o usu\u00e1rio a uma p\u00e1gina de checkout hospedada no servidor do Stripe. Voc\u00ea ver\u00e1 como criar essa URL logo mais.<\/p>\n<p><strong>Observa\u00e7\u00e3o:<\/strong> esse componente usa a vari\u00e1vel de ambiente <code>VITE_SERVER_BASE_URL<\/code> para a URL do servidor backend. Defina-a criando um arquivo <strong>.env<\/strong> na raiz do seu projeto:<\/p>\n<pre><code class=\"language-bash\">VITE_SERVER_BASE_URL=<a href=\"http:\/\/localhost:8080\">http:\/\/localhost:8080<\/a><\/code><\/pre>\n<p>Todos os componentes foram criados. Agora vamos prosseguir para construir o caminho do checkout hospedado usando os componentes. Para fazer isso, crie um novo arquivo <strong>HostedCheckout.tsx<\/strong> na pasta <strong>routes<\/strong>\u00a0com o seguinte c\u00f3digo:<\/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>Esse caminho utiliza os tr\u00eas componentes que voc\u00ea acabou de criar para montar uma tela de checkout. Todos os modos do componente est\u00e3o configurados como <strong>checkout<\/strong>, e o endpoint <code>\/checkout\/hosted<\/code> \u00e9 fornecido ao componente de formul\u00e1rio para iniciar a solicita\u00e7\u00e3o de checkout com precis\u00e3o.<\/p>\n<p>O componente usa um objeto <code>Products<\/code> para preencher o array de itens. Em cen\u00e1rios reais, esses dados v\u00eam da API do carrinho, que cont\u00e9m os itens selecionados pelo usu\u00e1rio. No entanto, para este tutorial, uma lista est\u00e1tica de um script preenche o array. Defina o array criando um arquivo <strong>data.ts<\/strong> na raiz do seu projeto de frontend e armazenando o seguinte c\u00f3digo nele:<\/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>Esse arquivo define dois itens no array de produtos que s\u00e3o renderizados no carrinho. Voc\u00ea pode ajustar os valores dos produtos.<\/p>\n<p>Como \u00faltima etapa da cria\u00e7\u00e3o do frontend, crie duas novas rotas para lidar com o sucesso e o fracasso. A p\u00e1gina de checkout hospedada pelo Stripe redirecionar\u00e1 os usu\u00e1rios para o seu aplicativo nessas duas rotas com base no resultado da transa\u00e7\u00e3o. O Stripe tamb\u00e9m fornecer\u00e1 \u00e0s suas rotas um payload relacionado \u00e0 transa\u00e7\u00e3o, como o ID da sess\u00e3o de checkout, que voc\u00ea pode usar para <a href=\"https:\/\/stripe.com\/docs\/api\/checkout\/sessions\/retrieve\" target=\"_blank\" rel=\"noopener noreferrer\">recuperar o objeto da sess\u00e3o de checkout correspondente<\/a> e acessar dados relacionados ao checkout, como o m\u00e9todo de pagamento, detalhes da fatura, etc.<\/p>\n<p>Para fazer isso, crie um arquivo <strong>Success.tsx<\/strong> no diret\u00f3rio <strong>src\/routes<\/strong> e salve o seguinte c\u00f3digo nele:<\/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>Ap\u00f3s a renderiza\u00e7\u00e3o, esse componente mostra a mensagem &#8220;Success!&#8221; e imprime na tela todos os par\u00e2metros de consulta de URL. Ele tamb\u00e9m inclui um bot\u00e3o para redirecionar os usu\u00e1rios para a p\u00e1gina inicial do aplicativo.<\/p>\n<p>Ao criar aplicativos do mundo real, essa p\u00e1gina \u00e9 onde voc\u00ea lidaria com transa\u00e7\u00f5es n\u00e3o cr\u00edticas do lado do aplicativo que dependem do sucesso da transa\u00e7\u00e3o em quest\u00e3o. Por exemplo, se voc\u00ea estiver criando uma p\u00e1gina de checkout para uma loja on-line, poder\u00e1 usar essa p\u00e1gina para mostrar uma confirma\u00e7\u00e3o ao usu\u00e1rio e um prazo de entrega estimado dos produtos comprados.<\/p>\n<p>Em seguida, crie um arquivo <strong>Failure.tsx<\/strong> com o seguinte c\u00f3digo:<\/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>Esse componente \u00e9 semelhante ao do <strong>Success.tsx<\/strong> e exibe a mensagem &#8220;Failure!&#8221; (Falha!) quando renderizado.<\/p>\n<aside role=\"note\" class=\"wp-block-kinsta-notice is-style-info\">\n            <h3>Info<\/h3>\n        <p>Evite passar informa\u00e7\u00f5es cr\u00edticas ou realizar opera\u00e7\u00f5es cr\u00edticas na p\u00e1gina de sucesso ou de falha, pois o cliente pode jamais chegar a nenhuma das p\u00e1ginas se fechar a aba do navegador ou perder a conectividade.<\/p>\n<\/aside>\n\n<p>Para tarefas essenciais, como entrega de produtos, envio de e-mails ou qualquer parte cr\u00edtica do seu fluxo de compras, use <a href=\"https:\/\/stripe.com\/docs\/webhooks\" target=\"_blank\" rel=\"noopener noreferrer\">webhooks<\/a>. Webhooks s\u00e3o rotas de API em seu servidor que o Stripe pode invocar quando ocorre uma transa\u00e7\u00e3o.<\/p>\n<p>O webhook recebe detalhes completos da transa\u00e7\u00e3o (por meio do objeto <code>CheckoutSession<\/code>), permitindo que voc\u00ea a registre no banco de dados do seu aplicativo e acione os fluxos de trabalho de sucesso ou falha correspondentes. Como o seu servidor est\u00e1 sempre acess\u00edvel ao Stripe, nenhuma transa\u00e7\u00e3o \u00e9 perdida, garantindo a funcionalidade consistente da sua loja on-line.<\/p>\n<p>Por fim, atualize o arquivo <strong>App.tsx<\/strong> para que ele fique parecido com este:<\/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>Isso garantir\u00e1 que os componentes <code>Success<\/code> e <code>Failure<\/code> sejam renderizados nas rotas <code>\/success<\/code> e <code>\/failure<\/code>, respectivamente.<\/p>\n<p>Isso conclui a configura\u00e7\u00e3o do frontend. Em seguida, configure o backend para criar o endpoint <code>\/checkout\/hosted<\/code>.<\/p>\n<h4>Criando o backend<\/h4>\n<p>Abra o projeto de backend e instale o Stripe SDK adicionando as seguintes linhas ao array de depend\u00eancias no seu arquivo <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>Em seguida, <a href=\"https:\/\/www.jetbrains.com\/help\/idea\/delegate-build-and-run-actions-to-maven.html\" target=\"_blank\" rel=\"noopener noreferrer\">carregue as altera\u00e7\u00f5es do Maven<\/a> em seu projeto para instalar as depend\u00eancias. Se o seu IDE n\u00e3o for compat\u00edvel com isso por meio da interface do usu\u00e1rio, execute o comando <code>maven dependency:resolve<\/code> ou <code>maven install<\/code>. Se voc\u00ea n\u00e3o tiver a CLI <code>maven<\/code>, use o wrapper <code>mvnw<\/code> do Spring initializr ao criar o projeto.<\/p>\n<p>Depois que as depend\u00eancias estiverem instaladas, crie um novo controlador REST para tratar as solicita\u00e7\u00f5es HTTP de entrada para o seu aplicativo de backend. Para fazer isso, crie um arquivo <strong>PaymentController.java<\/strong> no diret\u00f3rio <strong>src\/main\/java\/com\/kinsta\/stripe-java\/backend<\/strong> e adicione o seguinte c\u00f3digo:<\/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>O c\u00f3digo acima importa as depend\u00eancias essenciais do Stripe e estabelece a classe <code>PaymentController<\/code>. Essa classe tem duas anota\u00e7\u00f5es: <code>@RestController<\/code> e <code>@CrossOrigin<\/code>. A anota\u00e7\u00e3o <code>@RestController<\/code> instrui o Spring Boot a tratar essa classe como um controlador, e seus m\u00e9todos agora podem usar anota\u00e7\u00f5es <code>@Mapping<\/code> para lidar com solicita\u00e7\u00f5es HTTP de entrada.<\/p>\n<p>A anota\u00e7\u00e3o <code>@CrossOrigin<\/code> marca todos os endpoints definidos nessa classe como abertos a todas as origens conforme as regras CORS. No entanto, essa pr\u00e1tica n\u00e3o \u00e9 recomendada na produ\u00e7\u00e3o devido \u00e0s poss\u00edveis vulnerabilidades de seguran\u00e7a de v\u00e1rios dom\u00ednios da internet.<\/p>\n<p>Para obter os melhores resultados, \u00e9 recomend\u00e1vel hospedar os servidores de backend e frontend no mesmo dom\u00ednio para contornar os problemas de CORS. Como alternativa, se isso n\u00e3o for vi\u00e1vel, voc\u00ea pode especificar o dom\u00ednio do seu cliente de frontend (que envia solicita\u00e7\u00f5es ao servidor de backend) usando a anota\u00e7\u00e3o <code>@CrossOrigin<\/code>, assim:<\/p>\n<pre><code class=\"language-java\">@CrossOrigin(origins = \"http:\/\/frontend.com\")<\/code><\/pre>\n<p>A classe <code>PaymentController<\/code> extrair\u00e1 a chave da API do Stripe das vari\u00e1veis de ambiente para que voc\u00ea possa fornec\u00ea-la ao Stripe SDK posteriormente. Ao executar o aplicativo, voc\u00ea deve fornecer a chave da API do Stripe ao aplicativo por meio de vari\u00e1veis de ambiente.<\/p>\n<p>Localmente, voc\u00ea pode criar uma nova vari\u00e1vel de ambiente no seu sistema temporariamente (adicionando uma frase <code>KEY=VALUE<\/code> antes do comando usado para iniciar o servidor de desenvolvimento) ou permanentemente (atualizando os arquivos de configura\u00e7\u00e3o do terminal ou definindo uma vari\u00e1vel de ambiente no painel de controle no Windows).<\/p>\n<p>Em ambientes de produ\u00e7\u00e3o, seu provedor de implanta\u00e7\u00e3o (como a Kinsta) fornecer\u00e1 a voc\u00ea uma op\u00e7\u00e3o separada para preencher as vari\u00e1veis de ambiente usadas pelo seu aplicativo.<\/p>\n<p>Se voc\u00ea estiver usando o IntelliJ IDEA (ou um IDE semelhante), clique em <strong>Run Configurations<\/strong> na parte superior direita do IDE e clique na op\u00e7\u00e3o <strong>Edit Configurations&#8230;<\/strong> na lista suspensa que se abre para atualizar o seu comando de execu\u00e7\u00e3o e definir a vari\u00e1vel de ambiente.<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/run-debug-configurations-dialog-box-1024x420.png\" alt=\"Abrindo a caixa de di\u00e1logo de configura\u00e7\u00f5es de execu\u00e7\u00e3o\/depura\u00e7\u00e3o.\" width=\"1024\" height=\"420\"><figcaption id=\"caption-attachment-163056\" class=\"wp-caption-text\">Abrindo a caixa de di\u00e1logo de configura\u00e7\u00f5es de execu\u00e7\u00e3o\/depura\u00e7\u00e3o.<\/figcaption><\/figure>\n<p>.<\/p>\n<p>Isso abrir\u00e1 uma caixa de di\u00e1logo na qual voc\u00ea poder\u00e1 fornecer as vari\u00e1veis de ambiente para o aplicativo usando o campo <strong>Environment variables<\/strong>. Digite a vari\u00e1vel de ambiente <code>STRIPE_API_KEY<\/code> no formato <code>VAR1=VALUE<\/code>. Voc\u00ea pode encontrar sua chave de API no site <a href=\"https:\/\/dashboard.stripe.com\/account\/apikeys\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe Developers<\/a>. Voc\u00ea deve fornecer o valor da <strong>Secret Key<\/strong> dessa p\u00e1gina.<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/stripe-dashboard-showing-api-keys-1024x504.png\" alt=\"O painel do Stripe que mostra as chaves de API.\" width=\"1024\" height=\"504\"><figcaption id=\"caption-attachment-163057\" class=\"wp-caption-text\">O painel do Stripe que mostra as chaves de API.<\/figcaption><\/figure>\n<p>Se ainda n\u00e3o o fez, <a href=\"https:\/\/dashboard.stripe.com\/register\" target=\"_blank\" rel=\"noopener noreferrer\">crie uma nova conta do Stripe<\/a> para obter acesso \u00e0s chaves da API.<\/p>\n<p>Depois que voc\u00ea tiver configurado a chave de API, prossiga para criar o endpoint. Esse endpoint coletar\u00e1 os dados do cliente (nome e e-mail), criar\u00e1 um perfil de cliente para ele no Stripe, caso ainda n\u00e3o exista, e criar\u00e1 uma <a href=\"https:\/\/stripe.com\/docs\/payments\/checkout\/how-checkout-works\" target=\"_blank\" rel=\"noopener noreferrer\">sess\u00e3o de checkout<\/a> para permitir que os usu\u00e1rios paguem pelos itens do carrinho.<\/p>\n<p>Aqui est\u00e1 o c\u00f3digo do m\u00e9todo <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>Ao criar a sess\u00e3o de checkout, o c\u00f3digo usa o nome do produto recebido do cliente, mas n\u00e3o usa os detalhes de pre\u00e7o da solicita\u00e7\u00e3o. Essa abordagem evita a poss\u00edvel manipula\u00e7\u00e3o de pre\u00e7os no lado do cliente, em que agentes mal-intencionados podem enviar pre\u00e7os reduzidos na solicita\u00e7\u00e3o de checkout para pagar menos por produtos e servi\u00e7os.<\/p>\n<p>Para evitar isso, o m\u00e9todo <code>hostedCheckout<\/code> consulta seu banco de dados de produtos (via <code>ProductDAO<\/code>) para recuperar o pre\u00e7o correto do item.<\/p>\n<p>Al\u00e9m disso, o Stripe oferece v\u00e1rias classes <code>Builder<\/code> seguindo o padr\u00e3o de design builder. Essas classes auxiliam na cria\u00e7\u00e3o de objetos de par\u00e2metro para solicita\u00e7\u00f5es ao Stripe. O snippet de c\u00f3digo fornecido tamb\u00e9m faz refer\u00eancia a vari\u00e1veis de ambiente para buscar a URL do aplicativo cliente. Essa URL \u00e9 necess\u00e1ria para que o objeto da sess\u00e3o de checkout seja redirecionado adequadamente ap\u00f3s pagamentos bem-sucedidos ou n\u00e3o.<\/p>\n<p>Para executar esse c\u00f3digo, defina a URL do aplicativo cliente por meio de vari\u00e1veis de ambiente, da mesma forma como a chave da API do Stripe foi fornecida. Como o aplicativo cliente \u00e9 executado por meio do Vite, a URL do aplicativo local deve ser http:\/\/localhost:5173. Inclua isso nas vari\u00e1veis de ambiente por meio do IDE, do terminal ou do painel de controle do sistema.<\/p>\n<pre><code class=\"language-bash\">CLIENT_BASE_URL=http:\/\/localhost:5173<\/code><\/pre>\n<p>Al\u00e9m disso, forne\u00e7a ao aplicativo um <code>ProductDAO<\/code> do qual voc\u00ea possa consultar os pre\u00e7os dos produtos. O Data Access Object (DAO) interage com fontes de dados (como bancos de dados) para acessar dados relacionados ao aplicativo. Embora a configura\u00e7\u00e3o de um banco de dados de produtos esteja fora do escopo deste tutorial, uma implementa\u00e7\u00e3o simples que voc\u00ea pode fazer seria adicionar um novo arquivo <strong>ProductDAO.java<\/strong> no mesmo diret\u00f3rio que o <strong>PaymentController.java<\/strong> e colar o seguinte c\u00f3digo:<\/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>Isso inicializar\u00e1 um array de produtos e permitir\u00e1 que voc\u00ea consulte os dados do produto usando seu identificador (ID). Voc\u00ea tamb\u00e9m precisar\u00e1 criar um <a href=\"https:\/\/en.wikipedia.org\/wiki\/Data_transfer_object\" target=\"_blank\" rel=\"noopener noreferrer\">DTO<\/a> (Data Transfer Object) para permitir que o Spring Boot serialize automaticamente o payload de entrada do cliente e apresente a voc\u00ea um objeto simples para acessar os dados. Para fazer isso, crie um novo arquivo <strong>RequestDTO.java<\/strong> e cole o c\u00f3digo a seguir:<\/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>Esse arquivo define um <a href=\"https:\/\/en.wikipedia.org\/wiki\/Plain_old_Java_object\" target=\"_blank\" rel=\"noopener noreferrer\">POJO<\/a> que cont\u00e9m o nome do comprador, o e-mail e a lista de itens com os quais ele est\u00e1 fazendo o checkout.<\/p>\n<p>Por fim, implemente o m\u00e9todo <code>CustomerUtil.findOrCreateCustomer()<\/code> para criar o objeto Customer no Stripe, caso ele ainda n\u00e3o exista. Para fazer isso, crie um arquivo com o nome <code>CustomerUtil<\/code> e adicione o seguinte c\u00f3digo a ele:<\/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>Essa classe tamb\u00e9m cont\u00e9m outro m\u00e9todo <code>findCustomerByEmail<\/code> que permite que voc\u00ea procure clientes no Stripe usando seus endere\u00e7os de e-mail. A <a href=\"https:\/\/stripe.com\/docs\/api\/customers\/search\" target=\"_blank\" rel=\"noopener noreferrer\">API Customer Search<\/a> \u00e9 usada para procurar os registros de clientes no banco de dados do Stripe e a <a href=\"https:\/\/stripe.com\/docs\/api\/customers\/create\" target=\"_blank\" rel=\"noopener noreferrer\">API Customer Create<\/a> \u00e9 usada para criar os registros de clientes conforme necess\u00e1rio.<\/p>\n<p>Com isso, voc\u00ea conclui a configura\u00e7\u00e3o de backend necess\u00e1ria para o fluxo de checkout hospedado. Agora voc\u00ea pode testar o aplicativo executando os aplicativos frontend e backend em seus IDEs ou terminais separados. Aqui est\u00e1 a apar\u00eancia do fluxo de sucesso:<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/successful-hosted-checkout-flow.gif\" alt=\"Um fluxo de checkout hospedado bem-sucedido.\" width=\"1912\" height=\"1062\"><figcaption id=\"caption-attachment-163058\" class=\"wp-caption-text\">Um fluxo de checkout hospedado bem-sucedido.<\/figcaption><\/figure>\n<p>Ao testar integra\u00e7\u00f5es com o Stripe, voc\u00ea sempre pode usar os seguintes detalhes de cart\u00e3o para simular transa\u00e7\u00f5es com cart\u00e3o:<\/p>\n<p><strong>N\u00famero do cart\u00e3o<\/strong>: 4111 1111 1111 1111 1111<br \/>\n<strong>M\u00eas e ano de validade<\/strong>: 12 \/ 25<br \/>\n<strong>CVV<\/strong>: [qualquer n\u00famero de tr\u00eas d\u00edgitos]<br \/>\n<strong>Nome no cart\u00e3o<\/strong>: [qualquer nome]<\/p>\n<p>Se voc\u00ea optar por cancelar a transa\u00e7\u00e3o em vez de pagar, veja como seria o fluxo de falha:<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/failed-hosted-checkout-flow.gif\" alt=\"Um fluxo de checkout hospedado com falha.\" width=\"1912\" height=\"1062\"><figcaption id=\"caption-attachment-163059\" class=\"wp-caption-text\">Um fluxo de checkout hospedado com falha.<\/figcaption><\/figure>\n<p>Isso conclui a configura\u00e7\u00e3o de uma experi\u00eancia de checkout hospedado no Stripe integrado ao seu aplicativo. Voc\u00ea pode consultar os <a href=\"https:\/\/stripe.com\/docs\/payments\/checkout\/how-checkout-works\" target=\"_blank\" rel=\"noopener noreferrer\">documentos do Stripe<\/a> para saber mais sobre como personalizar sua p\u00e1gina de checkout, coletar mais detalhes do cliente, e mais.<\/p>\n<h3>Checkout integrado<\/h3>\n<p>Uma experi\u00eancia de checkout integrado se refere \u00e0 cria\u00e7\u00e3o de um fluxo de pagamento que n\u00e3o redireciona o usu\u00e1rio para fora do seu aplicativo (como no fluxo de checkout hospedado) e renderiza o formul\u00e1rio de pagamento no pr\u00f3prio aplicativo.<\/p>\n<p>Criar uma experi\u00eancia de checkout integrado significa lidar com os detalhes de pagamento dos clientes, o que envolve informa\u00e7\u00f5es sens\u00edveis, como n\u00fameros de cart\u00e3o de cr\u00e9dito, ID do Google Pay, etc. Nem todos os aplicativos s\u00e3o projetados para lidar com esses dados de forma segura.<\/p>\n<p>Para eliminar o \u00f4nus de atender a padr\u00f5es como o PCI-DSS, o Stripe fornece <a href=\"https:\/\/stripe.com\/payments\/elements\" target=\"_blank\" rel=\"noopener noreferrer\">elementos<\/a> que voc\u00ea pode usar no aplicativo para coletar detalhes de pagamento, ao mesmo tempo deixando que o Stripe gerencie a seguran\u00e7a e processe os pagamentos de forma segura.<\/p>\n<h4>Criando o frontend<\/h4>\n<p>Para come\u00e7ar, instale o <a href=\"https:\/\/www.npmjs.com\/package\/@stripe\/react-stripe-js\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe React SDK<\/a> em seu aplicativo de frontend para acessar os Elementos Stripe executando o seguinte comando no diret\u00f3rio de frontend:<\/p>\n<pre><code class=\"language-bash\">npm i @stripe\/react-stripe-js @stripe\/stripe-js<\/code><\/pre>\n<p>Em seguida, crie um novo arquivo chamado <strong>IntegratedCheckout.tsx<\/strong> em seu diret\u00f3rio <strong>frontend\/src\/routes<\/strong> e salve o seguinte c\u00f3digo nele:<\/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>Esse arquivo define dois componentes, <code>IntegratedCheckout<\/code> e <code>CheckoutForm<\/code>. O <code>CheckoutForm<\/code> define um formul\u00e1rio simples com um <code>PaymentElement<\/code> do Stripe que coleta os detalhes de pagamento dos clientes e um bot\u00e3o <strong>Pay<\/strong> que aciona uma solicita\u00e7\u00e3o de coleta de pagamento.<\/p>\n<p>Esse componente tamb\u00e9m chama os hooks <code>useStripe()<\/code> e <code>useElements()<\/code> para criar uma inst\u00e2ncia do Stripe SDK que voc\u00ea pode usar para criar solicita\u00e7\u00f5es de pagamento. Ao clicar no bot\u00e3o <strong>Pay<\/strong>, o m\u00e9todo <code>stripe.confirmPayment()<\/code> do Stripe SDK \u00e9 chamado, coletando os dados de pagamento do usu\u00e1rio da inst\u00e2ncia dos elementos e <span style=\"font-size: 1rem\">enviando-os ao backend do Stripe com uma URL de sucesso para redirecionamento caso a transa\u00e7\u00e3o seja bem-sucedida.<\/span><\/p>\n<p>O formul\u00e1rio de checkout foi separado do restante da p\u00e1gina porque os hooks <code>useStripe()<\/code> e <code>useElements()<\/code> precisam ser chamados do contexto de um provedor <code>Elements<\/code>, o que foi feito na instru\u00e7\u00e3o de retorno <code>IntegratedCheckout<\/code>. Se voc\u00ea movesse as chamadas do hook do Stripe diretamente para o componente <code>IntegratedCheckout<\/code>, elas estariam fora do escopo do provedor <code>Elements<\/code> e, portanto, n\u00e3o funcionariam.<\/p>\n<p>O componente <code>IntegratedCheckout<\/code> reutiliza os componentes <code>CartItem<\/code> e <code>TotalFooter<\/code> para renderizar os itens do carrinho e o valor total. Ele tamb\u00e9m renderiza dois campos de entrada para coletar as informa\u00e7\u00f5es do cliente e um bot\u00e3o <strong>Initiate payment <\/strong>que envia uma solicita\u00e7\u00e3o ao servidor backend do Java para criar a chave secreta do cliente usando os detalhes do cliente e do carrinho. Assim que a chave secreta do cliente \u00e9 recebida, o <code>CheckoutForm<\/code> \u00e9 renderizado para lidar com a coleta dos detalhes de pagamento do cliente.<\/p>\n<p>Al\u00e9m disso, o <code>useEffect<\/code> \u00e9 usado para chamar o m\u00e9todo <code>loadStripe<\/code>. Esse efeito \u00e9 executado apenas uma vez quando o componente \u00e9 renderizado, para que o Stripe SDK n\u00e3o seja carregado v\u00e1rias vezes quando os estados internos do componente forem atualizados.<\/p>\n<p>Para executar o c\u00f3digo acima, voc\u00ea tamb\u00e9m precisar\u00e1 adicionar duas novas vari\u00e1veis de ambiente ao seu projeto de frontend: <code>VITE_STRIPE_API_KEY<\/code> e <code>VITE_CLIENT_BASE_URL<\/code>. A vari\u00e1vel de chave da API do Stripe manter\u00e1 a chave da API public\u00e1vel do <a href=\"https:\/\/dashboard.stripe.com\/test\/apikeys\" target=\"_blank\" rel=\"noopener noreferrer\">painel do Stripe<\/a>, e a vari\u00e1vel de URL da base do cliente conter\u00e1 o link para o aplicativo cliente (que \u00e9 o pr\u00f3prio aplicativo frontend), de modo que possa ser passado para o SDK do Stripe para lidar com redirecionamentos de sucesso e falha.<\/p>\n<p>Para fazer isso, adicione o seguinte c\u00f3digo ao seu arquivo <strong>.env<\/strong> no diret\u00f3rio 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>Por fim, atualize o arquivo <strong>App.tsx<\/strong> para incluir o componente <code>IntegratedCheckout<\/code> na rota <code>\/integrated-checkout<\/code> do aplicativo de frontend. Adicione o seguinte c\u00f3digo no array passado para a chamada <code>createBrowserRouter<\/code> no componente <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>Isso conclui a configura\u00e7\u00e3o necess\u00e1ria no frontend. Em seguida, crie uma nova rota no servidor de backend que cria a chave secreta do cliente necess\u00e1ria para lidar com sess\u00f5es de checkout integradas no aplicativo de frontend.<\/p>\n<h4>Cria\u00e7\u00e3o do backend<\/h4>\n<p>Para garantir que a integra\u00e7\u00e3o de frontend n\u00e3o seja abusada por invasores\u00a0(j\u00e1 que o c\u00f3digo do frontend \u00e9 mais f\u00e1cil de ser violado que o do backend), o Stripe exige que voc\u00ea gere um segredo de cliente exclusivo no seu servidor backend e verifica cada solicita\u00e7\u00e3o de pagamento integrado com o segredo de cliente gerado no backend para garantir que \u00e9 realmente o seu aplicativo que est\u00e1 tentando coletar pagamentos. Para fazer isso, voc\u00ea precisa configurar outro caminho no backend que crie segredos de cliente com base nas informa\u00e7\u00f5es do cliente e do carrinho.<\/p>\n<p>Para criar a chave secreta do cliente em seu servidor, crie um novo m\u00e9todo em sua classe <code>PaymentController<\/code> com o nome <code>integratedCheckout<\/code> e salve o seguinte c\u00f3digo nele:<\/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 maneira similar \u00e0quela como a sess\u00e3o de checkout foi criada usando uma classe builder que aceita a configura\u00e7\u00e3o para a solicita\u00e7\u00e3o de pagamento, o fluxo de checkout integrado exige que voc\u00ea crie uma sess\u00e3o de pagamento com o valor, a moeda e os m\u00e9todos de pagamento. Ao contr\u00e1rio da sess\u00e3o de checkout, voc\u00ea n\u00e3o pode associar <a href=\"https:\/\/stripe.com\/docs\/api\/invoices\/line_item\" target=\"_blank\" rel=\"noopener noreferrer\">itens de linha<\/a> a uma sess\u00e3o de pagamento, a menos que crie uma fatura, o que voc\u00ea aprender\u00e1 em uma se\u00e7\u00e3o posterior deste tutorial.<\/p>\n<p>Como voc\u00ea n\u00e3o est\u00e1 passando os itens de linha para o construtor da sess\u00e3o de checkout, precisa calcular manualmente o valor total dos itens do carrinho e enviar o valor para o backend do Stripe. Use o <code>ProductDAO<\/code> para localizar e adicionar os pre\u00e7os de cada produto no carrinho.<\/p>\n<p>Para fazer isso, defina um novo m\u00e9todo <code>calculateOrderAmount<\/code> e adicione o seguinte c\u00f3digo a ele:<\/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>Isso deve ser suficiente para configurar o fluxo de checkout integrado no frontend e no backend. Voc\u00ea pode reiniciar os servidores de desenvolvimento para o servidor e o cliente e experimentar o novo fluxo de checkout integrado no aplicativo frontend. \u00c9 assim que o fluxo integrado se parece:<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/integrated-checkout-flow.gif\" alt=\"Um fluxo de checkout integrado.\" width=\"1912\" height=\"1062\"><figcaption id=\"caption-attachment-163060\" class=\"wp-caption-text\">Um fluxo de checkout integrado.<\/figcaption><\/figure>\n<p>Isso completa um fluxo b\u00e1sico de checkout integrado em seu aplicativo. Agora voc\u00ea pode ir al\u00e9m e explorar a documenta\u00e7\u00e3o do Stripe para <a href=\"https:\/\/stripe.com\/docs\/payments\/customize-payment-element\" target=\"_blank\" rel=\"noopener noreferrer\">personalizar os m\u00e9todos de pagamento<\/a> ou integrar mais componentes para ajud\u00e1-lo com outras opera\u00e7\u00f5es, tais como <a href=\"https:\/\/stripe.com\/docs\/elements\/address-element\" target=\"_blank\" rel=\"noopener noreferrer\">coleta de endere\u00e7os<\/a>, <a href=\"https:\/\/stripe.com\/docs\/payments\/elements\/link-authentication-element\" target=\"_blank\" rel=\"noopener noreferrer\">solicita\u00e7\u00f5es de pagamento<\/a>, <a href=\"https:\/\/stripe.com\/docs\/stripe-js\/elements\/payment-request-button\" target=\"_blank\" rel=\"noopener noreferrer\">integra\u00e7\u00e3o de links<\/a> e muito mais!<\/p>\n<h2>Configura\u00e7\u00e3o de assinaturas para servi\u00e7os recorrentes<\/h2>\n<p>Atualmente, uma oferta comum das lojas on-line \u00e9 a assinatura. Se voc\u00ea estiver criando um mercado para servi\u00e7os ou oferecendo um produto digital periodicamente, a assinatura \u00e9 a solu\u00e7\u00e3o perfeita para dar aos seus clientes acesso peri\u00f3dico ao seu servi\u00e7o por uma taxa pequena em compara\u00e7\u00e3o com uma compra \u00fanica.<\/p>\n<p>O Stripe pode ajud\u00e1-lo a configurar e cancelar assinaturas facilmente. Voc\u00ea tamb\u00e9m pode oferecer avalia\u00e7\u00f5es gratuitas como parte da assinatura para que os usu\u00e1rios possam experimentar sua oferta antes de se comprometerem com ela.<\/p>\n<h3>Configura\u00e7\u00e3o de uma nova assinatura<\/h3>\n<p>A configura\u00e7\u00e3o de uma nova assinatura \u00e9 simples usando o fluxo de checkout hospedado. Voc\u00ea s\u00f3 precisar\u00e1 alterar alguns par\u00e2metros ao criar a solicita\u00e7\u00e3o de checkout e criar uma nova p\u00e1gina (reutilizando os componentes existentes) para mostrar uma p\u00e1gina de checkout para uma nova assinatura. Para come\u00e7ar, crie um arquivo <strong>NewSubscription.tsx<\/strong> na pasta <strong>components<\/strong> do frontend. Cole o seguinte c\u00f3digo nele:<\/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>No c\u00f3digo acima, os dados do carrinho s\u00e3o obtidos do arquivo <strong>data.ts<\/strong> e cont\u00eam apenas um item para simplificar o processo. Em cen\u00e1rios reais, voc\u00ea pode ter v\u00e1rios itens como parte de um pedido de assinatura.<\/p>\n<p>Para renderizar esse componente no caminho correto, adicione o seguinte c\u00f3digo no array passado para a chamada <code>createBrowserRouter<\/code> no componente <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>Isso conclui a configura\u00e7\u00e3o necess\u00e1ria no frontend. No backend, crie um novo caminho <code>\/subscription\/new<\/code> para criar uma nova sess\u00e3o de checkout hospedada para um produto de assinatura. Crie um m\u00e9todo <code>newSubscription<\/code> no diret\u00f3rio <strong>backend\/src\/main\/java\/com\/kinsta\/stripejava\/backend<\/strong> e salve o seguinte c\u00f3digo nele:<\/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>O c\u00f3digo nesse m\u00e9todo \u00e9 bastante semelhante ao c\u00f3digo no m\u00e9todo <code>hostedCheckout<\/code>, exceto pelo fato de que o modo definido para criar a sess\u00e3o \u00e9 assinatura em vez de produto, e, antes de criar a sess\u00e3o, um valor \u00e9 definido para o intervalo de recorr\u00eancia da assinatura.<\/p>\n<p>Isso instrui o Stripe a tratar esse checkout como um checkout de assinatura em vez de um pagamento \u00fanico. Semelhante ao m\u00e9todo <code>hostedCheckout<\/code>, esse m\u00e9todo tamb\u00e9m retorna a URL da p\u00e1gina do checkout hospedado como resposta HTTP para o cliente. O cliente \u00e9 configurado para redirecionar para a URL recebida, permitindo que o comprador conclua o pagamento.<\/p>\n<p>Voc\u00ea pode reiniciar os servidores de desenvolvimento para cliente e servidor e ver a nova p\u00e1gina de assinatura em a\u00e7\u00e3o. Aqui est\u00e1 o que voc\u00ea v\u00ea:<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/hosted-subscription-checkout-flow.gif\" alt=\"Um fluxo de checkout hospedado de assinatura.\" width=\"1914\" height=\"976\"><figcaption id=\"caption-attachment-163061\" class=\"wp-caption-text\">Um fluxo de checkout hospedado de assinatura.<\/figcaption><\/figure>\n<h3>Cancelamento de uma assinatura existente<\/h3>\n<p>Agora que voc\u00ea sabe como criar novas assinaturas, vamos aprender como permitir que seus clientes cancelem assinaturas existentes. Como o aplicativo de demonstra\u00e7\u00e3o criado neste tutorial n\u00e3o cont\u00e9m nenhuma configura\u00e7\u00e3o de autentica\u00e7\u00e3o, use um formul\u00e1rio para permitir que o cliente insira seu e-mail para procurar suas assinaturas e, em seguida, forne\u00e7a a cada item de assinatura um bot\u00e3o de cancelamento para que o usu\u00e1rio possa cancel\u00e1-lo.<\/p>\n<p>Para fazer isso, voc\u00ea precisar\u00e1 fazer o seguinte:<\/p>\n<ol>\n<li>Atualize o componente <code>CartItem<\/code> para mostrar um bot\u00e3o de cancelamento na p\u00e1gina de cancelamento de assinaturas.<\/li>\n<li>Crie um componente <code>CancelSubscription<\/code> que primeiro mostre um campo de entrada e um bot\u00e3o para o cliente pesquisar assinaturas usando seu endere\u00e7o de e-mail e, em seguida, renderize uma lista de assinaturas usando o componente <code>CartItem<\/code> atualizado.<\/li>\n<li>Crie um novo m\u00e9todo no servidor de backend que possa procurar assinaturas no backend do Stripe usando o endere\u00e7o de e-mail do cliente.<\/li>\n<li>Crie um novo m\u00e9todo no servidor de backend que possa cancelar uma assinatura com base no ID de assinatura passado a ele.<\/li>\n<\/ol>\n<p>Comece atualizando o componente <code>CartItem<\/code> para que ele fique parecido com este:<\/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>Em seguida, crie um componente <strong>CancelSubscription.tsx<\/strong> no diret\u00f3rio <strong>routes<\/strong> do frontend e salve o seguinte c\u00f3digo nele:<\/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>Esse componente renderiza um campo de entrada e um bot\u00e3o para que os clientes insiram seus e-mails e comecem a procurar assinaturas. Se assinaturas forem encontradas, o campo de entrada e o bot\u00e3o ser\u00e3o ocultados e uma lista de assinaturas ser\u00e1 exibida na tela. Para cada item de assinatura, o componente passa um m\u00e9todo <code>removeSubscription<\/code> que solicita ao servidor de backend Java que cancele a assinatura no backend do Stripe.<\/p>\n<p>Para anex\u00e1-lo ao caminho <code>\/cancel-subscription<\/code> em seu aplicativo de frontend, adicione o seguinte c\u00f3digo no array passado para a chamada <code>createBrowserRouter<\/code> no componente <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>Para pesquisar assinaturas no servidor de backend, adicione um m\u00e9todo <code>viewSubscriptions<\/code> na classe <code>PaymentController<\/code> do seu projeto de backend com o seguinte conte\u00fado:<\/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>O m\u00e9todo acima primeiro encontra o objeto do comprador para o usu\u00e1rio fornecido no Stripe. Em seguida, ele procura as assinaturas ativas do comprador. Quando a lista de assinaturas \u00e9 recebida, ele extrai os itens delas e encontra os produtos correspondentes no banco de dados de produtos do aplicativo para enviar ao frontend. Isso \u00e9 importante porque o ID com o qual o frontend identifica cada produto no banco de dados do aplicativo pode ou n\u00e3o ser o mesmo que o ID do produto armazenado no Stripe.<\/p>\n<p>Por fim, crie um m\u00e9todo <code>cancelSubscription&lt;\/code<\/code> na classe <code>PaymentController<\/code> e cole o c\u00f3digo abaixo para excluir uma assinatura com base no ID de assinatura que foi passado:<\/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>Esse m\u00e9todo recupera o objeto de assinatura do Stripe, chama o m\u00e9todo cancel nele, e ent\u00e3o retorna o status da assinatura para o cliente. Entretanto, para poder executar esse m\u00e9todo, voc\u00ea precisa atualizar seu objeto DTO para adicionar o campo <code>subscriptionId<\/code>. Para isso, voc\u00ea deve adicionar o seguinte campo e m\u00e9todo na 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>Depois de adicionar isso, execute novamente o servidor de desenvolvimento para os aplicativos backend e frontend e veja o fluxo de cancelamento em a\u00e7\u00e3o:<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/subscription-cancel-flow-1024x522.gif\" alt=\"Um fluxo de cancelamento de assinatura.\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163062\" class=\"wp-caption-text\">Um fluxo de cancelamento de assinatura.<\/figcaption><\/figure>\n<h3>Configura\u00e7\u00e3o de avalia\u00e7\u00f5es gratuitas para assinaturas com transa\u00e7\u00f5es de valor zero<\/h3>\n<p>Um recurso comum \u00e0 maioria das assinaturas modernas \u00e9 oferecer um curto per\u00edodo de teste gratuito antes de cobrar do usu\u00e1rio. Isso permite que os usu\u00e1rios explorem o produto ou servi\u00e7o sem investir nele. No entanto, \u00e9 melhor armazenar os detalhes de pagamento do cliente ao inscrev\u00ea-lo para a avalia\u00e7\u00e3o gratuita, para que voc\u00ea possa cobr\u00e1-lo facilmente assim que a avalia\u00e7\u00e3o terminar.<\/p>\n<p>O Stripe simplifica muito a cria\u00e7\u00e3o de assinaturas assim. Para come\u00e7ar, gere um novo componente no diret\u00f3rio <strong>frontend\/routes<\/strong> chamado <strong>SubscriptionWithTrial.tsx<\/strong> e cole o seguinte c\u00f3digo:<\/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>Esse componente reutiliza os componentes criados anteriormente. A principal diferen\u00e7a entre este e o componente <code>NewSubscription<\/code> \u00e9 que ele passa o modo para <code>TotalFooter<\/code> como <strong>trial<\/strong> em vez de <strong>subscription<\/strong>. Isso faz com que o componente <code>TotalFooter<\/code> renderize um texto dizendo que o cliente pode iniciar a avalia\u00e7\u00e3o gratuita agora, mas ser\u00e1 cobrado depois de um m\u00eas.<\/p>\n<p>Para anexar esse componente ao caminho <code>\/subscription-with-trial<\/code> em seu aplicativo frontend, adicione o seguinte c\u00f3digo no array passado para a chamada <code>createBrowserRouter<\/code> no componente <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>Para criar o fluxo de checkout para assinaturas com <strong>trial<\/strong>\u00a0no backend, crie um novo m\u00e9todo chamado <code>newSubscriptionWithTrial<\/code> na classe <code>PaymentController<\/code> e adicione o seguinte c\u00f3digo:<\/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>Esse c\u00f3digo \u00e9 bastante semelhante ao do m\u00e9todo <code>newSubscription<\/code>. A \u00fanica (e fundamental) diferen\u00e7a \u00e9 que um per\u00edodo de avalia\u00e7\u00e3o \u00e9 passado para o objeto de par\u00e2metros de cria\u00e7\u00e3o de sess\u00e3o com o valor de <code>30<\/code>, indicando um per\u00edodo de avalia\u00e7\u00e3o gratuita de 30 dias.<\/p>\n<p>Agora voc\u00ea pode salvar as altera\u00e7\u00f5es e executar novamente o servidor de desenvolvimento para o backend e o frontend para ver a assinatura com fluxo de trabalho de avalia\u00e7\u00e3o gratuita em a\u00e7\u00e3o:<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/subscription-free-trial-flow-1024x522.gif\" alt=\"Uma assinatura com fluxo de avalia\u00e7\u00e3o gratuita.\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163063\" class=\"wp-caption-text\">Uma assinatura com fluxo de avalia\u00e7\u00e3o gratuita.<\/figcaption><\/figure>\n<h2>Gera\u00e7\u00e3o de faturas para pagamentos<\/h2>\n<p>No caso de assinaturas, o Stripe gera faturas automaticamente para cada pagamento, mesmo que seja uma transa\u00e7\u00e3o de valor zero para a inscri\u00e7\u00e3o na vers\u00e3o de avalia\u00e7\u00e3o. Para pagamentos \u00fanicos, voc\u00ea pode optar por criar faturas se necess\u00e1rio.<\/p>\n<p>Para come\u00e7ar a associar todos os pagamentos a faturas, atualize o corpo do payload que est\u00e1 sendo enviado na fun\u00e7\u00e3o <code>initiatePayment<\/code> do componente <code>CustomerDetails<\/code> no aplicativo de frontend para que contenha a seguinte propriedade:<\/p>\n<pre><code class=\"language-js\">invoiceNeeded: true<\/code><\/pre>\n<p>Voc\u00ea tamb\u00e9m precisar\u00e1 adicionar essa propriedade no corpo do payload que est\u00e1 sendo enviado ao servidor na fun\u00e7\u00e3o <code>createTransactionSecret<\/code> do componente <code>IntegratedCheckout<\/code>.<\/p>\n<p>Em seguida, atualize os caminhos de backend para verificar essa nova propriedade e atualizar as chamadas do Stripe SDK concordemente.<\/p>\n<p>No m\u00e9todo de checkout hospedado, para adicionar a funcionalidade de faturamento, atualize o m\u00e9todo <code>hostedCheckout<\/code> adicionando as seguintes linhas de c\u00f3digo:<\/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>Isso verificar\u00e1 o campo <code>invoiceNeeded<\/code> e definir\u00e1 os par\u00e2metros de cria\u00e7\u00e3o de acordo com isso.<\/p>\n<p>Adicionar uma fatura a um pagamento integrado \u00e9 um pouco complicado. Voc\u00ea n\u00e3o pode simplesmente definir um par\u00e2metro para instruir o Stripe a criar automaticamente uma fatura com o pagamento. Voc\u00ea deve criar manualmente a fatura e, em seguida, uma inten\u00e7\u00e3o de pagamento vinculada.<\/p>\n<p>Se a inten\u00e7\u00e3o de pagamento for paga e conclu\u00edda com \u00eaxito, a fatura ser\u00e1 marcada como paga; caso contr\u00e1rio, a fatura permanecer\u00e1 n\u00e3o paga. Embora isso fa\u00e7a sentido l\u00f3gico, pode ser um pouco complexo de implementar (especialmente quando n\u00e3o h\u00e1 exemplos claros ou refer\u00eancias a seguir).<\/p>\n<p>Para implementar isso, atualize o m\u00e9todo <code>integratedCheckout<\/code> para que ele fique assim:<\/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>O c\u00f3digo antigo desse m\u00e9todo \u00e9 movido para o bloco <code>if<\/code> que verifica se o campo <code>invoiceNeeded<\/code> \u00e9 <code>false<\/code>. Se for achado verdadeiro, o m\u00e9todo agora cria uma fatura com itens de fatura e a marca como finalizada para que possa ser paga.<\/p>\n<p>Em seguida, ele recupera a inten\u00e7\u00e3o de pagamento criada automaticamente quando a fatura foi finalizada e envia o segredo do cliente dessa inten\u00e7\u00e3o de pagamento para o cliente. Quando o cliente conclui o fluxo de checkout integrado, o pagamento \u00e9 coletado e a fatura \u00e9 marcada como paga.<\/p>\n<p>Com isso voc\u00ea conclui a configura\u00e7\u00e3o necess\u00e1ria para come\u00e7ar a gerar faturas a partir do seu aplicativo. Voc\u00ea pode ir at\u00e9 a <a href=\"https:\/\/dashboard.stripe.com\/test\/invoices\" target=\"_blank\" rel=\"noopener noreferrer\">se\u00e7\u00e3o de faturas<\/a> no painel do Stripe para ver as faturas que seu aplicativo gera com cada compra e pagamento de assinatura.<\/p>\n<p>No entanto, o Stripe tamb\u00e9m permite que voc\u00ea acesse as faturas por meio de sua API para criar uma experi\u00eancia de autoatendimento para os clientes baixarem as faturas sempre que quiserem.<\/p>\n<p>Para fazer isso, crie um novo componente no diret\u00f3rio <strong>frontend\/routes<\/strong> chamado <strong>ViewInvoices.tsx<\/strong>. Cole o seguinte c\u00f3digo nele:<\/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>Semelhante ao componente <code>CancelSubscription<\/code>, esse componente exibe um campo de entrada para que o cliente digite seu e-mail e um bot\u00e3o para pesquisar faturas. Quando as faturas s\u00e3o encontradas, o campo de entrada e o bot\u00e3o s\u00e3o ocultos, e uma lista de faturas com o n\u00famero da fatura, o valor total e um bot\u00e3o para fazer o download do PDF da fatura \u00e9 exibida para o cliente.<\/p>\n<p>Para implementar o m\u00e9todo de backend que pesquisa as faturas de um determinado cliente e envia de volta as informa\u00e7\u00f5es relevantes (n\u00famero da fatura, valor e URL do PDF), adicione o seguinte m\u00e9todo em sua classe <code>PaymentController<\/code> no 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>O m\u00e9todo primeiro procura o cliente pelo endere\u00e7o de e-mail fornecido a ele. Em seguida, procura as faturas desse cliente marcadas como pagas. Quando a lista de faturas \u00e9 encontrada, ele extrai o n\u00famero da fatura, o valor e a URL do PDF e envia de volta uma lista dessas informa\u00e7\u00f5es para o aplicativo cliente.<\/p>\n<p>\u00c9 assim que o fluxo de faturas se parece:<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/viewing-invoices-flow-1024x522.gif\" alt=\"A user flow showing how to retrieve and access invoices for a user.\" width=\"1024\" height=\"522\"><figcaption id=\"caption-attachment-163064\" class=\"wp-caption-text\">Visualizando faturas.<\/figcaption><\/figure>\n<p>Com isso, voc\u00ea conclui o desenvolvimento do nosso aplicativo Java de demonstra\u00e7\u00e3o (<a href=\"https:\/\/github.com\/krharsh17\/stripe-payments-java-react-frontend\" target=\"_blank\" rel=\"noopener noreferrer\">frontend<\/a> e <a href=\"https:\/\/github.com\/krharsh17\/stripe-payments-java-react-backend\" target=\"_blank\" rel=\"noopener noreferrer\">backend<\/a>). Na pr\u00f3xima se\u00e7\u00e3o, voc\u00ea aprender\u00e1 como implantar esse aplicativo na Kinsta para poder acess\u00e1-lo on-line.<\/p>\n<h2>Implementando seu aplicativo na Kinsta<\/h2>\n<p>Quando seu aplicativo estiver pronto, voc\u00ea poder\u00e1 implant\u00e1-lo na Kinsta. A Kinsta suporta implanta\u00e7\u00f5es a partir do seu provedor Git de prefer\u00eancia (<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>). Ao conectar o reposit\u00f3rio do c\u00f3digo-fonte do seu aplicativo \u00e0 Kinsta, ela implantar\u00e1 automaticamente seu aplicativo sempre que houver uma altera\u00e7\u00e3o no c\u00f3digo.<\/p>\n<h3>Prepare seus projetos<\/h3>\n<p>Para implantar seus aplicativos na produ\u00e7\u00e3o, identifique os comandos de build e implanta\u00e7\u00e3o que a Kinsta usar\u00e1. Para o frontend, certifique-se de que seu arquivo <strong>package.json<\/strong> tenha os seguintes scripts definidos:<\/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>Voc\u00ea tamb\u00e9m precisar\u00e1 instalar o pacote npm <a href=\"https:\/\/www.npmjs.com\/package\/serve\" target=\"_blank\" rel=\"noopener noreferrer\">serve<\/a>, que permite que voc\u00ea sirva sites est\u00e1ticos. Esse pacote ser\u00e1 usado para servir a build de produ\u00e7\u00e3o do seu aplicativo a partir do ambiente de implanta\u00e7\u00e3o da Kinsta. Voc\u00ea pode instal\u00e1-lo executando o seguinte comando:<\/p>\n<pre><code class=\"language-bash\">npm i serve<\/code><\/pre>\n<p>Depois que voc\u00ea compilar seu aplicativo usando o vite, todo o aplicativo ser\u00e1 empacotado em um \u00fanico arquivo, <strong>index.html<\/strong>, visto que a configura\u00e7\u00e3o do React que voc\u00ea est\u00e1 usando neste tutorial destina-se a criar aplicativos de p\u00e1gina \u00fanica. Embora isso n\u00e3o fa\u00e7a uma grande diferen\u00e7a para os usu\u00e1rios, voc\u00ea precisa definir algumas configura\u00e7\u00f5es extras para lidar com o roteamento e a navega\u00e7\u00e3o nesses aplicativos por meio do navegador.<\/p>\n<p>Com a configura\u00e7\u00e3o atual, voc\u00ea s\u00f3 pode acessar o aplicativo na URL de base da sua implanta\u00e7\u00e3o. Se a URL base da implanta\u00e7\u00e3o for <strong>example.com<\/strong>, qualquer solicita\u00e7\u00e3o para <strong>example.com\/some-route<\/strong> levar\u00e1 a erros HTTP 404.<\/p>\n<p>Isso ocorre porque seu servidor tem apenas um arquivo para servir, o arquivo <strong>index.html<\/strong>. Uma solicita\u00e7\u00e3o enviada para <strong>example.com\/some-route<\/strong> come\u00e7ar\u00e1 a procurar o arquivo <strong>some-route\/index.html<\/strong>, que n\u00e3o existe; portanto, receber\u00e1 uma resposta 404 Not Found.<\/p>\n<p>Para corrigir isso, crie um arquivo chamado <strong>serve.json<\/strong> na pasta <strong>frontend\/public<\/strong> e salve o seguinte c\u00f3digo nele:<\/p>\n<pre><code class=\"language-js\">{\n  \"rewrites\": [\n    { \"source\": \"*\", \"destination\": \"\/index.html\" }\n  ]\n}\n<\/code><\/pre>\n<p>Esse arquivo instruir\u00e1 o <code>serve<\/code> a reescrever todas as solicita\u00e7\u00f5es recebidas para serem roteadas para o arquivo <strong>index.html<\/strong>, enquanto ainda mostra na resposta o caminho para o qual a solicita\u00e7\u00e3o original foi enviada. Isso ajudar\u00e1 voc\u00ea a servir corretamente as p\u00e1ginas de sucesso e fracasso do seu aplicativo quando o Stripe redirecionar os clientes de volta para o seu aplicativo.<\/p>\n<p>Para o backend, crie um <a href=\"https:\/\/kinsta.com\/pt\/blog\/dockerfile-entrypoint\/\">Dockerfile<\/a> para configurar o ambiente certo para o seu aplicativo Java. O uso de um Dockerfile garante que o ambiente fornecido ao seu aplicativo Java seja o mesmo em todos os hosts (seja o host de desenvolvimento local ou o host de implanta\u00e7\u00e3o da Kinsta) e voc\u00ea pode garantir que seu aplicativo seja executado conforme esperado.<\/p>\n<p>Para fazer isso, crie um arquivo chamado <strong>Dockerfile<\/strong> na pasta <strong>backend<\/strong> e salve o seguinte conte\u00fado nele:<\/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>Esse arquivo instrui o tempo de execu\u00e7\u00e3o a usar a imagem Java do <a href=\"https:\/\/openjdk.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">OpenJDK<\/a> como base para o cont\u00eainer de implanta\u00e7\u00e3o, a executar o comando <code>.\/mvnw clean package<\/code> para criar o arquivo <strong>JAR<\/strong> do seu aplicativo e a usar o comando <code>java -jar &lt;jar-file&gt;<\/code> para execut\u00e1-lo. Isso conclui a prepara\u00e7\u00e3o do c\u00f3digo-fonte para a implanta\u00e7\u00e3o na Kinsta.<\/p>\n<h3>Configure reposit\u00f3rios do GitHub<\/h3>\n<p>Para come\u00e7ar a implantar os aplicativos, crie dois reposit\u00f3rios do GitHub para hospedar o c\u00f3digo-fonte dos aplicativos. Se voc\u00ea usa a CLI do GitHub, fa\u00e7a isso por meio do terminal executando os seguintes comandos:<\/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>Isso deve criar novos reposit\u00f3rios do GitHub em sua conta e enviar o c\u00f3digo dos aplicativos para eles. Voc\u00ea deve poder acessar os reposit\u00f3rios de frontend e backend. Em seguida, implemente esses reposit\u00f3rios na Kinsta seguindo estas etapas:<\/p>\n<ol>\n<li>Fa\u00e7a login ou crie sua conta Kinsta no painel <a href=\"https:\/\/my.kinsta.com\/?lang=pt\">MyKinsta<\/a>.<\/li>\n<li>Na barra lateral esquerda, clique em <strong>Aplicativos<\/strong> e, em seguida, em <strong>Adicionar aplicativo<\/strong>.<\/li>\n<li>No modal que aparece, escolha o reposit\u00f3rio que voc\u00ea deseja implantar. Se tiver v\u00e1rios branches, voc\u00ea pode selecionar o branch desejado e dar um nome ao seu aplicativo.<\/li>\n<li>Selecione um dos locais de centros de dados dispon\u00edveis na lista de 24 op\u00e7\u00f5es. A Kinsta detecta automaticamente o comando start para o seu aplicativo.<\/li>\n<\/ol>\n<p>Lembre-se de que voc\u00ea precisa fornecer aos aplicativos frontend e backend algumas <a href=\"https:\/\/kinsta.com\/pt\/blog\/o-que-sao-variaveis-de-ambiente\/\">vari\u00e1veis de ambiente<\/a> para que eles funcionem corretamente. O aplicativo de frontend precisa das seguintes vari\u00e1veis de ambiente:<\/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>Para implantar o aplicativo de backend, fa\u00e7a exatamente o que fizemos para o frontend, mas na etapa <strong>Ambiente de build<\/strong>, selecione o bot\u00e3o de op\u00e7\u00e3o <strong>Usar Dockerfile para configurar a imagem do cont\u00eainer<\/strong> e insira <code>Dockerfile<\/code> como o caminho do Dockerfile para o aplicativo de 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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/application-form-build-environment-details-1024x523.png\" alt=\"The add application form asking to provide build environment details..\" width=\"1024\" height=\"523\"><figcaption id=\"caption-attachment-163065\" class=\"wp-caption-text\">Defini\u00e7\u00e3o dos detalhes do ambiente de build.<\/figcaption><\/figure>\n<p>Lembre-se de adicionar as vari\u00e1veis de ambiente de backend:<\/p>\n<ul>\n<li>CLIENT_BASE_URL<\/li>\n<li>STRIPE_API_KEY<\/li>\n<\/ul>\n<p>Quando a implanta\u00e7\u00e3o estiver conclu\u00edda, v\u00e1 para a p\u00e1gina de detalhes dos aplicativos e acesse a URL da implanta\u00e7\u00e3o a partir dela.<\/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:\/\/kinsta.com\/wp-content\/uploads\/2023\/09\/hosted-url-for-kinsta-deployed-apps-1024x523.png\" alt=\"A URL hospedada para os aplicativos implantados na Kinsta.\" width=\"1024\" height=\"523\"><figcaption id=\"caption-attachment-163066\" class=\"wp-caption-text\">A URL hospedada para os aplicativos implantados na Kinsta.<\/figcaption><\/figure>\n<p>Extraia as URLs dos dois aplicativos implantados. V\u00e1 at\u00e9 o <a href=\"https:\/\/dashboard.stripe.com\/test\/apikeys\" target=\"_blank\" rel=\"noopener noreferrer\">painel do Stripe<\/a> para obter suas chaves de API secretas e public\u00e1veis.<\/p>\n<p>Certifique-se de fornecer a chave public\u00e1vel do Stripe para o seu aplicativo frontend (n\u00e3o a chave secreta). Al\u00e9m disso, certifique-se de que suas URLs de base n\u00e3o tenham uma barra (<code>\/<\/code>) no final. As rotas j\u00e1 possuem barras, e assim, se voc\u00ea tamb\u00e9m tiver uma barra no final das URLs de base, duas barras ser\u00e3o adicionadas \u00e0s URLs finais.<\/p>\n<p>Para o seu aplicativo backend, adicione a chave secreta do painel do Stripe (n\u00e3o a chave public\u00e1vel). Al\u00e9m disso, certifique-se de que a URL do cliente n\u00e3o tenha uma barra (<code>\/<\/code>) no final.<\/p>\n<p>Depois que as vari\u00e1veis forem adicionadas, v\u00e1 para a aba <strong>Implanta\u00e7\u00f5es <\/strong>do aplicativo e clique no bot\u00e3o de <strong>reimplanta\u00e7\u00e3o<\/strong> do seu aplicativo de backend. Isso conclui a configura\u00e7\u00e3o \u00fanica que voc\u00ea precisa para fornecer credenciais \u00e0s suas implanta\u00e7\u00f5es Kinsta por meio de vari\u00e1veis de ambiente.<\/p>\n<p>Em seguida, voc\u00ea pode fazer o commit das altera\u00e7\u00f5es no seu controle de vers\u00e3o. A Kinsta reimplantar\u00e1 automaticamente seu aplicativo se voc\u00ea marcar a op\u00e7\u00e3o durante a implanta\u00e7\u00e3o; caso contr\u00e1rio, voc\u00ea precisar\u00e1 acionar a reimplanta\u00e7\u00e3o manualmente.<\/p>\n<h2>Resumo<\/h2>\n<p>Neste artigo, voc\u00ea aprendeu como o Stripe funciona e os fluxos de pagamento que ele oferece. Voc\u00ea tamb\u00e9m aprendeu, por meio de um exemplo detalhado, a integrar o Stripe ao seu aplicativo Java para aceitar pagamentos \u00fanicos, configurar assinaturas, oferecer avalia\u00e7\u00f5es gratuitas e gerar faturas de pagamento.<\/p>\n<p>Usando o Stripe e o Java juntos, voc\u00ea pode oferecer uma solu\u00e7\u00e3o de pagamentos robusta aos seus clientes, que pode ser bem dimensionada e se integrar perfeitamente ao seu ecossistema existente de aplicativos e ferramentas.<\/p>\n<p><em>Voc\u00ea usa o Stripe em seu aplicativo para coletar pagamentos? Se sim, qual dos fluxos voc\u00ea prefere: hospedado, personalizado ou no aplicativo? Diga-nos nos coment\u00e1rios abaixo!<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Com o aumento das transa\u00e7\u00f5es digitais, a capacidade de integrar perfeitamente os gateways de pagamento se tornou uma habilidade essencial para os desenvolvedores. Seja para marketplaces &#8230;<\/p>\n","protected":false},"author":199,"featured_media":63664,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[],"topic":[951,1017,1002],"class_list":["post-63663","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","topic-api","topic-linguagens-desenvolvimento-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>Guia para Integra\u00e7\u00e3o do Stripe no Spring Boot - Kinsta\u00ae<\/title>\n<meta name=\"description\" content=\"Aprenda como incorporar de maneira eficiente o Stripe em seu aplicativo Spring Boot para um processamento de pagamentos \u00e1gil e amig\u00e1vel ao usu\u00e1rio.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/\" \/>\n<meta property=\"og:locale\" content=\"pt_PT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Guia para Integra\u00e7\u00e3o do Stripe no Spring Boot\" \/>\n<meta property=\"og:description\" content=\"Aprenda como incorporar de maneira eficiente o Stripe em seu aplicativo Spring Boot para um processamento de pagamentos \u00e1gil e amig\u00e1vel ao usu\u00e1rio.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/kinstapt\/\" \/>\n<meta property=\"article:published_time\" content=\"2023-12-02T08:33:55+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-12-06T11:59:26+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/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=\"Aprenda como incorporar de maneira eficiente o Stripe em seu aplicativo Spring Boot para um processamento de pagamentos \u00e1gil e amig\u00e1vel ao usu\u00e1rio.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/09\/stripe-java-api.jpg\" \/>\n<meta name=\"twitter:creator\" content=\"@kinsta_pt\" \/>\n<meta name=\"twitter:site\" content=\"@kinsta_pt\" \/>\n<meta name=\"twitter:label1\" content=\"Escrito por\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jeremy Holcombe\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tempo estimado de leitura\" \/>\n\t<meta name=\"twitter:data2\" content=\"50 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/\"},\"author\":{\"name\":\"Jeremy Holcombe\",\"@id\":\"https:\/\/kinsta.com\/pt\/#\/schema\/person\/4eee42881d7b5a73ebb4f58dd5223b21\"},\"headline\":\"Guia para Integra\u00e7\u00e3o do Stripe no Spring Boot\",\"datePublished\":\"2023-12-02T08:33:55+00:00\",\"dateModified\":\"2023-12-06T11:59:26+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/\"},\"wordCount\":8113,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/09\/stripe-java-api.jpg\",\"inLanguage\":\"pt-PT\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/\",\"url\":\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/\",\"name\":\"Guia para Integra\u00e7\u00e3o do Stripe no Spring Boot - Kinsta\u00ae\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/09\/stripe-java-api.jpg\",\"datePublished\":\"2023-12-02T08:33:55+00:00\",\"dateModified\":\"2023-12-06T11:59:26+00:00\",\"description\":\"Aprenda como incorporar de maneira eficiente o Stripe em seu aplicativo Spring Boot para um processamento de pagamentos \u00e1gil e amig\u00e1vel ao usu\u00e1rio.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#breadcrumb\"},\"inLanguage\":\"pt-PT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/09\/stripe-java-api.jpg\",\"contentUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/09\/stripe-java-api.jpg\",\"width\":1460,\"height\":730},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/pt\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"API\",\"item\":\"https:\/\/kinsta.com\/pt\/topicos\/api\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Guia para Integra\u00e7\u00e3o do Stripe no Spring Boot\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/pt\/#website\",\"url\":\"https:\/\/kinsta.com\/pt\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Solu\u00e7\u00f5es de hospedagem Premium, r\u00e1pida e segura\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/pt\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"pt-PT\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/pt\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/pt\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/kinstapt\/\",\"https:\/\/x.com\/kinsta_pt\",\"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:\/\/kinsta.com\/pt\/#\/schema\/person\/4eee42881d7b5a73ebb4f58dd5223b21\",\"name\":\"Jeremy Holcombe\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinsta.com\/pt\/#\/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:\/\/kinsta.com\/pt\/blog\/author\/jeremyholcombe\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Guia para Integra\u00e7\u00e3o do Stripe no Spring Boot - Kinsta\u00ae","description":"Aprenda como incorporar de maneira eficiente o Stripe em seu aplicativo Spring Boot para um processamento de pagamentos \u00e1gil e amig\u00e1vel ao usu\u00e1rio.","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:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/","og_locale":"pt_PT","og_type":"article","og_title":"Guia para Integra\u00e7\u00e3o do Stripe no Spring Boot","og_description":"Aprenda como incorporar de maneira eficiente o Stripe em seu aplicativo Spring Boot para um processamento de pagamentos \u00e1gil e amig\u00e1vel ao usu\u00e1rio.","og_url":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/kinstapt\/","article_published_time":"2023-12-02T08:33:55+00:00","article_modified_time":"2023-12-06T11:59:26+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/09\/stripe-java-api.jpg","type":"image\/jpeg"}],"author":"Jeremy Holcombe","twitter_card":"summary_large_image","twitter_description":"Aprenda como incorporar de maneira eficiente o Stripe em seu aplicativo Spring Boot para um processamento de pagamentos \u00e1gil e amig\u00e1vel ao usu\u00e1rio.","twitter_image":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/09\/stripe-java-api.jpg","twitter_creator":"@kinsta_pt","twitter_site":"@kinsta_pt","twitter_misc":{"Escrito por":"Jeremy Holcombe","Tempo estimado de leitura":"50 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/"},"author":{"name":"Jeremy Holcombe","@id":"https:\/\/kinsta.com\/pt\/#\/schema\/person\/4eee42881d7b5a73ebb4f58dd5223b21"},"headline":"Guia para Integra\u00e7\u00e3o do Stripe no Spring Boot","datePublished":"2023-12-02T08:33:55+00:00","dateModified":"2023-12-06T11:59:26+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/"},"wordCount":8113,"commentCount":0,"publisher":{"@id":"https:\/\/kinsta.com\/pt\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/09\/stripe-java-api.jpg","inLanguage":"pt-PT","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/","url":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/","name":"Guia para Integra\u00e7\u00e3o do Stripe no Spring Boot - Kinsta\u00ae","isPartOf":{"@id":"https:\/\/kinsta.com\/pt\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/09\/stripe-java-api.jpg","datePublished":"2023-12-02T08:33:55+00:00","dateModified":"2023-12-06T11:59:26+00:00","description":"Aprenda como incorporar de maneira eficiente o Stripe em seu aplicativo Spring Boot para um processamento de pagamentos \u00e1gil e amig\u00e1vel ao usu\u00e1rio.","breadcrumb":{"@id":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#breadcrumb"},"inLanguage":"pt-PT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/"]}]},{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#primaryimage","url":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/09\/stripe-java-api.jpg","contentUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/09\/stripe-java-api.jpg","width":1460,"height":730},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/pt\/blog\/stripe-java-api\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/pt\/"},{"@type":"ListItem","position":2,"name":"API","item":"https:\/\/kinsta.com\/pt\/topicos\/api\/"},{"@type":"ListItem","position":3,"name":"Guia para Integra\u00e7\u00e3o do Stripe no Spring Boot"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/pt\/#website","url":"https:\/\/kinsta.com\/pt\/","name":"Kinsta\u00ae","description":"Solu\u00e7\u00f5es de hospedagem Premium, r\u00e1pida e segura","publisher":{"@id":"https:\/\/kinsta.com\/pt\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/pt\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"pt-PT"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/pt\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/pt\/","logo":{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/kinstapt\/","https:\/\/x.com\/kinsta_pt","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:\/\/kinsta.com\/pt\/#\/schema\/person\/4eee42881d7b5a73ebb4f58dd5223b21","name":"Jeremy Holcombe","image":{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinsta.com\/pt\/#\/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:\/\/kinsta.com\/pt\/blog\/author\/jeremyholcombe\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts\/63663","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/users\/199"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/comments?post=63663"}],"version-history":[{"count":13,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts\/63663\/revisions"}],"predecessor-version":[{"id":65652,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts\/63663\/revisions\/65652"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/63663\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/63663\/translations\/it"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/63663\/translations\/pt"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/63663\/translations\/fr"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/63663\/translations\/de"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/63663\/translations\/es"},{"href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/63663\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/media\/63664"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/media?parent=63663"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/tags?post=63663"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/topic?post=63663"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}