
Stripe no Brasil: integração técnica, webhooks, Pix e o que considerar antes de escolher
Integrar pagamentos é uma das partes mais sensíveis de qualquer produto: erros silenciosos viram cobrança duplicada, acesso liberado sem pagamento ou cliente pagante sem acesso. Este guia mostra a arquitetura recomendada de integração com Stripe, o tratamento correto de webhooks e os pontos específicos do mercado brasileiro que influenciam a escolha do provedor.
Regra número um: o servidor decide, o navegador não
Nunca libere acesso com base em um retorno de sucesso no navegador. O usuário pode fechar a página antes do redirecionamento, o pagamento pode ser aprovado depois de uma análise antifraude ou a URL de sucesso pode ser acessada diretamente. A confirmação válida chega por webhook, verificado no servidor.
| Etapa | Onde acontece | Responsabilidade |
|---|---|---|
| Escolha do plano | Navegador | Interface apenas |
| Criação da sessão de pagamento | Servidor | Definir preço e cliente; nunca aceitar valor do cliente |
| Coleta do cartão | Ambiente do provedor | Reduz escopo de conformidade PCI |
| Confirmação do pagamento | Webhook no servidor | Única fonte de verdade |
| Liberação de acesso | Servidor | Após evento verificado e registrado |
Criando a sessão de checkout
O caminho mais rápido e seguro é usar a página de checkout hospedada pelo provedor: ela cuida de validação de cartão, autenticação adicional e localização. O servidor define o que será cobrado e a quem.
import Stripe from "stripe";
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
export const criarCheckout = createServerFn({ method: "POST" })
.inputValidator((d) => z.object({ planoId: z.enum(["basico", "pro"]) }).parse(d))
.handler(async ({ data }) => {
const stripe = new Stripe(process.env["STRIPE_SECRET_KEY"]!);
// O preço vem do servidor, jamais do cliente
const precos = { basico: process.env["PRICE_BASICO"]!, pro: process.env["PRICE_PRO"]! };
const sessao = await stripe.checkout.sessions.create({
mode: "subscription",
line_items: [{ price: precos[data.planoId], quantity: 1 }],
success_url: "https://app.exemplo.com/assinatura/ok?session_id={CHECKOUT_SESSION_ID}",
cancel_url: "https://app.exemplo.com/planos",
client_reference_id: usuarioAtual.id, // vincula o pagamento ao usuário
metadata: { tenant_id: usuarioAtual.tenantId },
});
return { url: sessao.url };
});Webhooks: assinatura verificada e processamento idempotente
O endpoint de webhook é público e precisa provar que a requisição veio mesmo do provedor. Além disso, eventos podem chegar mais de uma vez ou fora de ordem, então o processamento precisa ser idempotente: processar o mesmo evento duas vezes não pode gerar efeito duplicado.
import { createFileRoute } from "@tanstack/react-router";
import Stripe from "stripe";
export const Route = createFileRoute("/api/public/stripe-webhook")({
server: {
handlers: {
POST: async ({ request }) => {
const stripe = new Stripe(process.env["STRIPE_SECRET_KEY"]!);
const assinatura = request.headers.get("stripe-signature");
const corpo = await request.text(); // corpo bruto: não usar JSON parseado
let evento: Stripe.Event;
try {
evento = stripe.webhooks.constructEvent(
corpo,
assinatura!,
process.env["STRIPE_WEBHOOK_SECRET"]!
);
} catch {
return new Response("Assinatura inválida", { status: 400 });
}
// Idempotência: grava o id do evento antes de agir
const novo = await registrarEventoSeInedito(evento.id);
if (!novo) return new Response("ok"); // já processado
switch (evento.type) {
case "checkout.session.completed":
await ativarAssinatura(evento.data.object);
break;
case "invoice.payment_failed":
await marcarPagamentoFalho(evento.data.object);
break;
case "customer.subscription.deleted":
await encerrarAcesso(evento.data.object);
break;
}
return new Response("ok"); // responda rápido; trabalho pesado vai para fila
},
},
},
});Responda ao webhook em poucos segundos. Processamento longo dentro do handler causa timeout e faz o provedor reenviar o evento, aumentando a chance de duplicidade. Enfileire o trabalho demorado e confirme o recebimento imediatamente.
Eventos que importam em assinaturas
| Evento | Significado | Ação típica |
|---|---|---|
| checkout.session.completed | Checkout concluído | Criar ou ativar assinatura |
| invoice.paid | Renovação cobrada com sucesso | Estender período de acesso |
| invoice.payment_failed | Falha na cobrança | Notificar e iniciar tolerância |
| customer.subscription.updated | Mudança de plano ou status | Ajustar limites do plano |
| customer.subscription.deleted | Cancelamento efetivado | Encerrar acesso ao fim do período |
| charge.dispute.created | Contestação aberta | Reunir evidências e avaliar suspensão |
Particularidades do mercado brasileiro
O comportamento de pagamento no Brasil difere do padrão internacional em três pontos que afetam diretamente a conversão: uso intenso de Pix, expectativa de parcelamento no cartão de crédito e boleto em vendas para empresas. Esses fatores pesam tanto quanto a qualidade técnica da API.
| Critério | Provedor internacional | Gateway nacional |
|---|---|---|
| Documentação e API | Geralmente muito madura | Variável entre provedores |
| Pix | Suporte crescente, conferir disponibilidade | Suporte nativo consolidado |
| Parcelamento no crédito | Limitado ou indireto | Nativo, com repasse de juros |
| Boleto | Depende do provedor | Padrão de mercado |
| Antifraude local | Genérico | Ajustado ao perfil brasileiro |
| Suporte e cobrança em real | Depende da configuração | Local, em português |
Confirme diretamente na documentação oficial quais métodos estão disponíveis para a sua conta e país de operação: cobertura de Pix, boleto e parcelamento muda com frequência e varia conforme o tipo de conta.
Ambiente de teste e casos que precisam ser exercitados
- Pagamento aprovado normalmente e liberação de acesso pelo webhook.
- Cartão recusado e nova tentativa bem-sucedida.
- Usuário fecha o navegador antes do redirecionamento — o acesso ainda deve ser liberado.
- Evento duplicado enviado duas vezes: verificar que o efeito ocorre uma única vez.
- Cancelamento no meio do ciclo e término do acesso na data correta.
- Falha de renovação seguida de suspensão após o período de tolerância.
# Encaminhar eventos reais de teste para o ambiente local
stripe login
stripe listen --forward-to localhost:8080/api/public/stripe-webhook
# Disparar um evento específico para exercitar o handler
stripe trigger checkout.session.completed
stripe trigger invoice.payment_failedConformidade e obrigações
- Nunca armazene número completo de cartão; use os elementos hospedados do provedor para manter o escopo PCI reduzido.
- Guarde apenas identificadores do provedor, últimos dígitos e bandeira, quando necessário para suporte.
- Emita nota fiscal conforme a legislação aplicável ao seu regime tributário; o gateway não faz isso por você.
- Informe claramente valor, recorrência e forma de cancelamento antes da cobrança, conforme o Código de Defesa do Consumidor.
- Trate dados de pagamento como dados pessoais sensíveis do ponto de vista de segurança e registre a base legal do tratamento.
Conclusão
Uma integração de pagamentos confiável tem sempre a mesma forma: preço definido no servidor, coleta de dados sensíveis no ambiente do provedor, confirmação exclusivamente por webhook verificado e processamento idempotente. Definida essa base, a escolha entre provedor internacional e gateway nacional passa a ser decidida pelos métodos de pagamento que seu público realmente usa e pelo custo por transação.
Perguntas frequentes
+Posso liberar o acesso na página de sucesso do checkout?
Não como fonte única de verdade. Use a página de sucesso apenas para dar retorno visual e confirme o pagamento pelo webhook, que é o único canal confiável e verificável no servidor.
+Como testar webhooks em ambiente local?
Com a CLI oficial do provedor, encaminhando eventos para a sua porta local, ou com um túnel HTTP público. Ambas as opções permitem exercitar eventos reais sem publicar a aplicação.
+O que fazer quando a renovação falha?
Configure novas tentativas automáticas, notifique o cliente com um link para atualizar o meio de pagamento e defina um período de tolerância antes de suspender. Suspensão imediata converte falha temporária em cancelamento definitivo.
+Vale usar dois provedores de pagamento?
Faz sentido quando cada um cobre um método relevante, por exemplo cartão internacional em um e Pix em outro. O custo é dobrar a lógica de conciliação e de webhooks, o que exige uma camada interna única de assinatura.
Fontes consultadas
Revisão editorial: publicado em . Última revisão em . Conteúdo educativo, sem patrocínio das ferramentas citadas.
Leia também

Freelancer de programação: contrato, escopo, precificação e operação sustentável
Como operar como desenvolvedor autônomo sem improviso: proposta com escopo fechado, cláusulas essenciais, controle de mudanças, cobrança e obrigações fiscais no Brasil.

LinkedIn para devs: perfil técnico, conteúdo e uso realista da rede
Como estruturar um perfil que aparece nas buscas de recrutadores técnicos, o que escrever em cada seção, como publicar sem virar influenciador e o que ignorar.

Burnout em tecnologia: sinais, causas organizacionais e o que efetivamente ajuda
O que caracteriza o esgotamento profissional segundo a OMS, quais fatores do trabalho em tecnologia o produzem e quais medidas individuais e de time reduzem o risco.