Índice:

Criando um fluxo de pagamentos com Stripe

Índice:

Criando um fluxo básico de pagamentos com cartão de credito.

_______________________________________________________________________________________________________

Introdução

O Stripe é uma empresa de pagamentos muito queridinha entre os desenvolvedores, devido à alta qualidade de sua documentação e da quantidade de recursos que a plataforma oferece ao público dev. Nesse artigo, vamos entender um pouco sobre o fluxo básico de pagamentos no Stripe e como implementar o pagamento por cartão de crédito com uma certa customização para cada caso de uso.

Entendendo o fluxo do Stripe

O importante é nos atentarmos, e dividir esse fluxo em duas etapas: a criação da “intenção de pagamento” e o pagamento em si.

Por mais complexo que possa parecer a primeira vista, o processo é relativamente simples e consiste em você realizar uma chamada na api do stripe, indicando uma “intenção de pagamento”, então, você recebe um client_secret e usa esse mesmo secret para realizar uma nova chamada e realizar o pagamento.

A criação de uma intenção de pagamento tem o intuito de criar métrica na sua página de pagamentos, e verificar quantos clientes estão chegando até ela, com quais produtos, e se estão desistindo ou não da compra.

Na prática

Vamos prosseguir da seguinte forma, faremos uma chamada no nosso backend e ele ficará responsável por fazer a primeira chamada no stripe e nos retornar o client_secret.

Depois, pelo próprio frontend, iremos realizar a chamada do pagamento no stripe e finalizar a compra.

Para esse exemplo, vamos usar o Node.js no backend e o React.js no frontend, usando um componente fornecido pelo próprio stripe para o input e validação do cartão de crédito.

Backend

Nesse exemplo, o nosso backend será responsável apenas por executar a chamada de intenção de pagamento do strapi e não fará nenhuma operação no banco de dados, ou seja, vamos apenas criar a estrutura de um controller e definir uma rota, que no nosso exemplo será “/orders/create-payment-intent”.

Você também pode fazer essa chamada no seu frontend, mas precisaria se atentar a alguns detalhes, por exemplo: o nosso backend possui os preços dos produtos de modo fidedigno, enquanto se eu realizar determinada chamada no meu frontend, usuários mal-intencionados podem de alguma forma alterar os preços do produto, gerando um compra com o valor alterado. Se mantermos a responsabilidade de criar a intenção de pagamento no backend, só enviamos os ids dos produtos e quantidade, se também for necessário, e o backend fica responsável por consultar os preços e outras informações do usuário, gerando o “orçamento” da nossa compra.

Para começar a criar o nosso controller, o primeiro passo será instalar a biblioteca do stripe:

yarn add stripe

Vamos fazer a nossa chamada na rota “/orders/create-payment-intent”, passando um array de ids dos nossos produtos no body da requisição.

Assim, iremos criar o controller da seguinte forma:

// importação da biblioteca do stripe
const stripe = require('stripe')(process.env.STRIPE_KEY);

// implementação de acordo com o seu backend

// método do seu controller
createPaymentIntent: async (ctx) => {
    const { cart } = ctx.request.body

		// cria um array que irá armazenar os produtos
    let products = []

    await Promise.all(
      cart?.map(async (product) => {
        const validatedProduct = // método para pegar as informações do produto

        if(validatedProduct) {
          products.push(validatedProduct);
        }
      })
    );

		// verifica se o carrinho não está vazio
    if(!products.length) {
      ctx.response.status = 404;
      return {
        error: "No valid products found!"
      }
    }

		// método para calcula o valor total do pedido
    const total = products.reduce((acc, product) => {
      return acc + product.price;
    }, 0)

		// faz a chamada no stripe para criar a intenção de pagamento
    try {
      const paymentIntent = await stripe.paymentIntents.create({
        amount: total * 100,
        currency: 'usd',
        automatic_payment_methods: {enabled: true},
      });
			
			// retorna à intenção de pagamento
      return paymentIntent
    } catch (err) {
      return {
        error: err.raw.message,
      }
    }
  }

Frontend

No front, vamos utilizar um componente que o próprio stripe nos fornece, o CardElement. Para isso, vamos instalar duas bibliotecas do stripe:

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

Agora vamos criar a nossa estrutura inicial, chamando o CardElement e criando uma função handleChange para verificar se o nosso input do cartão está vazio ou com algum erro:

import { useState } from 'react'
import { CardElement } from '@stripe/react-stripe-js'
import { StripeCardElementChangeEvent } from '@stripe/stripe-js'

const PaymentForm = () => {
	// estados para controlar os erros e habilitar o botão de compra
	const [error, setError] = useState<string | null>(null)
  const [disabled, setDisabled] = useState(true)

  const handleChange = async (event: StripeCardElementChangeEvent) => {
    setDisabled(event.empty)
    setError(event.error ? event.error.message : '')
  }

  return (
  <div>
      <CardElement
        options={{
					// tira a necessidade de CEP para processar o pagamento
          hidePostalCode: true,
        }}
        onChange={handleChange}
      />

			{// botão para submeter a compra}
      <button
        disabled={disabled || !!error}
      >
				{// mostra o erro no preenchimento ou na compra}
				{error && (
            <span>{error}</span>
        )}
        <span>Buy now</span>
      </button>
    </div>
  )
}

export default PaymentForm

Agora vamos criar a nossa intenção de pagamento. Para isso, utilizaremos um useEffect, para que, ao carregar a página, já seja gerado a nossa intenção:

import { useState, useEffect } from 'react'
import { CardElement } from '@stripe/react-stripe-js'
import { StripeCardElementChangeEvent } from '@stripe/stripe-js'
import { createPaymentIntent } from 'stripe/methods'

const PaymentForm = () => {
	// hook que pega os items do carrinho de compras
	const { items } = useCart()
	// estado que recebe o client_secret
	const [clientSecret, setClientSecret] = useState('')
	const [error, setError] = useState<string | null>(null)
  const [disabled, setDisabled] = useState(true)

  const handleChange = async (event: StripeCardElementChangeEvent) => {
    setDisabled(event.empty)
    setError(event.error ? event.error.message : '')
  }

	// criação da intenção de pagamento
	useEffect(() => {
    async function setPaymentMode() {
      if (items.length) {
        // chamada na API /orders/create-payment-intent
        const data = await createPaymentIntent({
          items
        })

        // se der algum erro, setar o erro
        if (data.error) {
          setError(data.error)
          return
        }

        // se o payment intent foi válido, set client secret
        setFreeGames(false)
        setClientSecret(data.client_secret)
      }
    }

    setPaymentMode()
  }, [items])

  return (
  <div>
      <CardElement
        options={{
          hidePostalCode: true,
        }}
        onChange={handleChange}
      />

      <button
        disabled={disabled || !!error}
      >
				{error && (
            <span>{error}</span>
        )}
        <span>Buy now</span>
      </button>
    </div>
  )
}

export default PaymentForm
// stripe/methods.tsx
import { CartItem } from 'hooks/use-cart'

type PaymentIntentParams = {
  items: CartItem[]
}

export const createPaymentIntent = async ({
  items
}: PaymentIntentParams) => {
  const response = await fetch(
    `${process.env.NEXT_PUBLIC_API_URL}/orders/create-payment-intent`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        cart: items
      })
    }
  )

  return await response.json()
}

Por fim, vamos criar a função de handleSubmit, para realizar a compra efetivamente:

import { useState, useEffect } from 'react'
import { CardElement, useElements, useStripe } from '@stripe/react-stripe-js'
import { StripeCardElementChangeEvent } from '@stripe/stripe-js'
import { createPaymentIntent } from 'stripe/methods'

const PaymentForm = () => {
	const { items } = useCart()
	// inicializar o hooks do stripe
	const stripe = useStripe()
  const elements = useElements()

	// estado de loading
	const [loading, setLoading] = useState(false)
	const [clientSecret, setClientSecret] = useState('')
	const [error, setError] = useState<string | null>(null)
  const [disabled, setDisabled] = useState(true)

  const handleChange = async (event: StripeCardElementChangeEvent) => {
    setDisabled(event.empty)
    setError(event.error ? event.error.message : '')
  }

	// criação da intenção de pagamento
	useEffect(() => {
    async function setPaymentMode() {
      if (items.length) {
        // chamada na API /orders/create-payment-intent
        const data = await createPaymentIntent({
          items
        })

        // se der algum erro, setar o erro
        if (data.error) {
          setError(data.error)
          return
        }

        // se o paymetintent foi válido, set client secret
        setFreeGames(false)
        setClientSecret(data.client_secret)
      }
    }

    setPaymentMode()
  }, [items])

	// função de submit
	const handleSubmit = async (event: React.FormEvent) => {
    event.preventDefault()
	
		// verifica se os hooks do stripe já foram carregados
    if (!stripe || !elements) {
      return
    }

    setLoading(true)

    const payload = await stripe.confirmCardPayment(clientSecret, {
      payment_method: {
        card: elements.getElement(CardElement)!
      }
    })

    if (payload.error) {
      setError(`Payment failed: ${payload.error.message}`)
      setLoading(false)
    } else {
      setError(null)
      setLoading(false)

      // Aqui você pode salvar a compra no seu banco de dados
      // E depois redirecionar para uma página de sucesso
    }
  }

  return (
  <div>
		<form onSubmit={handleSubmit}>
      <CardElement
        options={{
          hidePostalCode: true,
        }}
        onChange={handleChange}
      />

      <button
        disabled={disabled || !!error}
      >
				{error && (
            <span>{error}</span>
        )}
        {!loading && <span>Buy now</span>}
      </button>
		</form>
  </div>
  )
}

export default PaymentForm

Então criamos um handleChange para lidar com o preenchimento do nosso CardElement, um useEffect para criar a intenção de pagamento, e por fim um handleSubmit para gerar a nossa compra. Com apenas três lógicas você já consegue implementar um fluxo de pagamento no seu app!

Conclusão

Dessa forma concluímos o nosso projeto. Como você pode ver, criar um fluxo de pagamentos não é nenhum bicho de sete cabeças e o CardElement do stripe faz toda a parte de validação para nós, o que facilita bastante o nosso desenvolvimento, além de ser um componente muito bem feito. O restante nós podemos customizar de acordo com a nossa aplicação e nossas regras de negócio.

 

Publicado por:
Compartilhe:

Posts relacionados

What is Lorem Ipsum? Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an

métrica developer experience

Entender e otimizar a developer experience é crucial para o sucesso de qualquer projeto de software. Uma experiência positiva pode impulsionar a produtividade e a satisfação da equipe, elementos chave

developer experience

A developer experience, ou DX, é um conceito cada vez mais importante no cenário de desenvolvimento de software. Mas por que a experiência do desenvolvedor é tão importante? Como ela