Adios
BlogNext.js SaaS

Next.js SaaS

Como implementar assinaturas do Stripe no Next.js: Checkout, webhooks e estado da cobrança

Implemente cobrança de assinaturas do Stripe no Next.js com Checkout Sessions criadas no servidor, webhooks verificados, direitos de acesso locais e processamento idempotente.

Equipe AdiosAtualizado 17 de julho de 20268 min de leitura

O redirecionamento de volta do Checkout faz parte da experiência do usuário. São os webhooks verificados que permitem à aplicação conciliar o estado assíncrono da cobrança de forma confiável.

Mantenha um modelo local de cobrança

Armazene os IDs de cliente e assinatura do provedor na conta responsável e normalize o plano, o status, o período atual, o estado de cancelamento e os direitos de acesso de que a aplicação precisa. O objeto do provedor não substitui um modelo de acesso específico do produto.

Defina como os estados trialing, active, past_due, canceled e incomplete afetam a aplicação. Mantenha o histórico de cobrança e as decisões de acesso compreensíveis. Um rótulo na página de preços deve corresponder a um ID de preço configurado no servidor, sem aceitar um preço arbitrário enviado pelo navegador.

Crie Checkout Sessions no servidor

Autentique a conta, valide o plano solicitado contra uma lista de opções permitidas, crie ou reutilize seu Stripe Customer e crie uma Checkout Session no modo de assinatura. Inclua o ID estável da conta nos metadados para conciliar os eventos posteriores sem depender de um endereço de e-mail.

Retorne a URL do provedor ou redirecione para ela. Nunca exponha a chave secreta ao navegador. Use uma chave de idempotência quando uma requisição repetida da aplicação não puder criar operações duplicadas no provedor.

"use server";

export async function startCheckout(plan: PlanId) {
  const account = await requireBillingAdmin();
  const price = PRICE_IDS[plan];
  if (!price) return { error: "Unknown plan" };

  const session = await stripe.checkout.sessions.create({
    mode: "subscription",
    customer: account.stripeCustomerId,
    line_items: [{ price, quantity: 1 }],
    success_url: SITE_URL + "/settings/billing?checkout=complete",
    cancel_url: SITE_URL + "/pricing",
    metadata: { accountId: account.id },
  });

  redirect(session.url);
}

Trate a página de retorno como uma confirmação pendente

O cliente pode chegar à URL de sucesso antes que a aplicação processe todos os eventos assíncronos, e uma URL copiada não comprova pagamento. Mostre um estado de confirmação pendente, leia o registro local da assinatura e atualize-o após o processamento do webhook.

Não conceda acesso permanente com base em um parâmetro de consulta ou em uma resposta do provedor recebida no cliente. Os direitos de acesso devem seguir o estado verificado no servidor. Quando a cobrança permanecer incompleta, deixe claro como o cliente pode tentar novamente ou buscar suporte.

Verifique o corpo original da requisição do webhook

Um Route Handler do Next.js pode ler o corpo sem alterações com request.text e a assinatura com request.headers. Passe o corpo original, o valor de Stripe-Signature e o segredo do endpoint para a biblioteca oficial. Interpretar o JSON antes altera o corpo usado na verificação e pode fazer a validação da assinatura falhar.

Rejeite assinaturas inválidas antes de realizar qualquer trabalho. Mantenha separados os segredos dos endpoints de teste e produção. Retorne rapidamente uma resposta de sucesso depois de registrar o trabalho aceito; o envio demorado de e-mails ou a sincronização devem ficar em uma tarefa em segundo plano.

export async function POST(request: Request) {
  const payload = await request.text();
  const signature = request.headers.get("stripe-signature");

  const event = stripe.webhooks.constructEvent(
    payload,
    signature,
    process.env.STRIPE_WEBHOOK_SECRET,
  );

  await recordBillingEvent(event);
  return new Response(null, { status: 200 });
}

Processe eventos de forma idempotente

A entrega de webhooks pode se repetir, e a ordem não é garantida. Insira o ID do evento do provedor com uma restrição de unicidade antes de aplicar seus efeitos. Se o evento já existir, confirme o recebimento sem enviar outro e-mail nem aplicar a alteração duas vezes.

Nas atualizações e exclusões de assinaturas, consulte ou determine o estado atual e oficial do provedor quando a ordem dos eventos puder deixar o registro local desatualizado. Sempre que possível, registre o evento e atualize o estado da cobrança na mesma transação.

  • —Registre o ID e o tipo do evento.
  • —Associe-o à conta local.
  • —Aplique o estado de assinatura normalizado uma vez.
  • —Coloque as tarefas complementares mais lentas na fila depois de persistir as mudanças de estado.

Trate todo o ciclo de vida da assinatura

Dê suporte à criação de clientes e assinaturas, mudanças de plano, renovações, falhas de pagamento, cancelamento agendado, cancelamento imediato e exclusão. Use o portal do cliente quando ele atender ao produto, em vez de recriar o gerenciamento de formas de pagamento e faturas sem necessidade.

A autorização continua sendo necessária: somente quem tiver a função de acesso adequada na conta pode iniciar o checkout ou abrir o gerenciamento de cobrança. Registre quem solicitou a mudança de plano e mostre a data em que ela entra em vigor para que o suporte consiga explicar o estado da conta.

Teste novas tentativas e falhas

Use a CLI do Stripe ou um destino de eventos de teste para encaminhar eventos assinados. Repita o mesmo evento, entregue uma atualização antiga depois de uma mais recente, use o segredo errado, interrompa o banco de dados e provoque uma falha de pagamento. Confirme que o acesso e o estado local continuam compreensíveis.

Teste a compilação de produção com credenciais do modo de teste antes de habilitar o endpoint real. Mantenha separados os IDs de produção e teste e impeça que uma implantação de prévia se registre por acidente como destino de produção.

Execute a cobrança em uma versão estável com HTTPS

O Stripe exige um endpoint de webhook HTTPS acessível publicamente em produção. O Adios fornece o ambiente de execução persistente do Next.js, a rota HTTPS gerada, domínios personalizados, TLS gerenciado, referências a segredos, verificações de saúde e logs de execução necessários para operar esse endpoint junto à interface do SaaS.

Implante e verifique o Checkout no modo de teste, investigue falhas de assinatura sem registrar segredos do payload nos logs e depois promova a versão saudável. Registre a URL estável do webhook de produção, em vez de uma prévia temporária. Se uma futura versão candidata falhar na compilação ou na verificação de saúde, ela não precisa substituir o endpoint de cobrança que está funcionando.

env:
  STRIPE_SECRET_KEY: secret://STRIPE_SECRET_KEY
  STRIPE_WEBHOOK_SECRET: secret://STRIPE_WEBHOOK_SECRET
  DATABASE_URL: secret://DATABASE_URL

runtime:
  name: node@24
  port: 3000
  health_path: /api/health
Todos os artigos