Como lidar com webhooks do Stripe com Edge Functions

Este guia demonstra como lidar com webhooks do Stripe de forma segura usando Azion Edge Functions. Você aprenderá a processar eventos de pagamento em tempo real, verificar assinaturas de webhook e construir um sistema robusto de processamento de pagamentos na edge.

Requisitos

Antes de começar, certifique-se de ter:

  • Uma conta na Azion
  • Uma conta no Stripe com acesso à API
  • Azion CLI instalada e configurada
  • Node.js versão 18 ou superior
  • Gerenciador de pacotes pnpm instalado
  • Conhecimento básico de JavaScript, Edge Functions e webhooks do Stripe
  • Chaves de API do Stripe (tanto chave secreta quanto secret de assinatura do webhook)

Primeiros passos

Passo 1: Configure seu ambiente de desenvolvimento

  1. Clone o repositório de exemplo de webhooks do Stripe:
Terminal window
git clone https://github.com/egermano/edge-functions-examples.git
cd edge-functions-examples/packages/stripe-webhooks
  1. Instale as dependências do projeto:
Terminal window
pnpm install
  1. Revise a estrutura do projeto para entender a implementação:
Terminal window
ls -la

Você deve ver os arquivos principais incluindo a implementação da Edge Function, manipuladores de webhook e arquivos de configuração.

Passo 2: Configure as variáveis de ambiente

  1. Crie um arquivo .env baseado no exemplo:
Terminal window
cp .env.example .env
  1. Edite o arquivo .env para incluir suas credenciais do Stripe:
Terminal window
# Configuração Stripe
STRIPE_SECRET_KEY=sk_test_sua_chave_secreta_stripe
STRIPE_WEBHOOK_SECRET=whsec_seu_secret_de_assinatura_webhook
  1. Obtenha suas credenciais do Dashboard do Stripe:
    • Chave Secreta: Disponível na seção API Keys
    • Secret do Webhook: Criado ao configurar endpoints de webhook

Passo 3: Configure endpoint de webhook do Stripe

  1. No seu Dashboard do Stripe, vá para Developers > Webhooks
  2. Clique em Add endpoint
  3. Configure a URL do endpoint para seu futuro domínio Azion (você atualizará isso após o deploy)
  4. Selecione os eventos que você quer escutar:
    • payment_intent.succeeded
    • payment_method.attached
    • charge.succeeded
    • invoice.payment_succeeded
    • Quaisquer outros eventos relevantes para sua aplicação

Passo 4: Compile o projeto

Compile sua Edge Function para deployment:

Terminal window
pnpm build

Este comando compila sua Edge Function com suporte TypeScript e a prepara para deployment.

Passo 5: Teste localmente

Antes de fazer o deploy, teste seu manipulador de webhook localmente:

Terminal window
pnpm dev

Isso inicia um servidor de desenvolvimento local onde você pode testar o processamento de webhooks.

Entendendo a verificação de webhook

Verificação de assinatura

Webhooks do Stripe incluem um cabeçalho de assinatura que você deve verificar para garantir que o webhook veio do Stripe:

import crypto from 'crypto';
function verifyStripeSignature(payload, signature, secret) {
const elements = signature.split(',');
const signatureHash = elements.find(element => element.startsWith('v1='));
if (!signatureHash) {
throw new Error('Formato de assinatura inválido');
}
const expectedHash = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const actualHash = signatureHash.split('=')[1];
if (expectedHash !== actualHash) {
throw new Error('Assinatura de webhook inválida');
}
return true;
}

Manipulação de eventos de webhook

A Edge Function processa diferentes eventos do Stripe:

async function handleWebhookEvent(event) {
switch (event.type) {
case 'payment_intent.succeeded':
await handlePaymentSuccess(event.data.object);
break;
case 'payment_intent.payment_failed':
await handlePaymentFailure(event.data.object);
break;
case 'charge.succeeded':
await handleChargeSuccess(event.data.object);
break;
case 'invoice.payment_succeeded':
await handleInvoicePayment(event.data.object);
break;
default:
console.log(`Tipo de evento não manipulado: ${event.type}`);
}
}

Deploy na Azion

Passo 1: Autentique-se na Azion

  1. Faça login na sua conta Azion via CLI:
Terminal window
azion login
  1. Siga os prompts de autenticação para conectar sua CLI com sua conta Azion.

Passo 2: Crie secrets para credenciais do Stripe

Para segurança, armazene suas credenciais do Stripe como secrets:

Terminal window
azion create secret STRIPE_SECRET_KEY
azion create secret STRIPE_WEBHOOK_SECRET

Quando solicitado, digite suas respectivas credenciais do Stripe. Isso garante que seus dados sensíveis sejam criptografados e seguros.

Passo 3: Faça o deploy da Edge Function

Faça o deploy de seu manipulador de webhook na rede edge da Azion:

Terminal window
azion deploy

O processo de deploy irá:

  • Fazer upload do código da sua Edge Function
  • Configurar a edge application
  • Configurar as regras de roteamento necessárias
  • Configurar variáveis de ambiente e secrets
  • Fornecer um domínio único

Passo 4: Atualize a configuração de webhook do Stripe

  1. Após o deploy, você receberá um domínio como https://xxxxxxx.map.azionedge.net
  2. Vá para seu Dashboard do Stripe > Developers > Webhooks
  3. Edite seu endpoint de webhook
  4. Atualize a URL para https://xxxxxxx.map.azionedge.net/webhook
  5. Salve as alterações

Passo 5: Teste entrega de webhook

  1. Dispare eventos de teste no Dashboard do Stripe
  2. Monitore entrega e respostas de webhook
  3. Verifique logs da Edge Function para confirmação de processamento

Eventos de webhook suportados

Eventos de pagamento

  • payment_intent.succeeded: Pagamento completado com sucesso
  • payment_intent.payment_failed: Tentativa de pagamento falhou
  • payment_intent.canceled: Pagamento foi cancelado

Eventos de cobrança

  • charge.succeeded: Cobrança completada com sucesso
  • charge.failed: Tentativa de cobrança falhou
  • charge.refunded: Cobrança foi reembolsada

Eventos de fatura

  • invoice.payment_succeeded: Pagamento de fatura completado
  • invoice.payment_failed: Pagamento de fatura falhou
  • invoice.created: Nova fatura criada

Eventos de cliente

  • customer.created: Novo cliente registrado
  • customer.updated: Informações do cliente atualizadas
  • customer.deleted: Conta do cliente excluída

Exemplos de implementação

Manipulador de sucesso de pagamento

async function handlePaymentSuccess(paymentIntent) {
const { id, amount, currency, customer } = paymentIntent;
// Atualizar status do pedido no seu banco de dados
await updateOrderStatus(paymentIntent.metadata.order_id, 'paid');
// Enviar email de confirmação
await sendPaymentConfirmation(customer, amount, currency);
// Disparar processo de fulfillment
await triggerFulfillment(paymentIntent.metadata.order_id);
console.log(`Pagamento bem-sucedido: ${id} por ${amount} ${currency}`);
}

Manipulação de assinatura

async function handleSubscriptionEvent(subscription) {
const { id, customer, status, current_period_end } = subscription;
switch (status) {
case 'active':
await activateSubscription(customer, id);
break;
case 'canceled':
await cancelSubscription(customer, id);
break;
case 'past_due':
await handlePastDueSubscription(customer, id);
break;
}
// Atualizar registro do cliente
await updateCustomerSubscription(customer, {
subscription_id: id,
status,
next_billing_date: current_period_end
});
}

Processamento de reembolso

async function handleRefund(charge) {
const { id, amount, currency, refunded } = charge;
if (refunded) {
// Processar reembolso no seu sistema
await processRefund({
charge_id: id,
amount,
currency,
order_id: charge.metadata.order_id
});
// Notificar cliente
await sendRefundNotification(charge.customer, amount, currency);
console.log(`Reembolso processado: ${id} por ${amount} ${currency}`);
}
}

Testando seu manipulador de webhook

Passo 1: Use Stripe CLI para teste local

  1. Instale Stripe CLI
  2. Encaminhe webhooks para seu servidor de desenvolvimento local:
Terminal window
stripe listen --forward-to localhost:3000/webhook
  1. Dispare eventos de teste:
Terminal window
stripe trigger payment_intent.succeeded
stripe trigger charge.succeeded
stripe trigger invoice.payment_succeeded

Passo 2: Teste em produção

  1. Use Dashboard do Stripe para enviar webhooks de teste
  2. Monitore entrega de webhook e tentativas de retry
  3. Verifique códigos de resposta e tempos de processamento
  4. Verifique precisão de manipulação de eventos

Passo 3: Teste de tratamento de erros

  1. Teste com assinaturas inválidas
  2. Teste com payloads malformados
  3. Teste cenários de timeout
  4. Verifique mecanismos de retry

Melhores práticas de segurança

Verificação de webhook

  • Sempre verifique assinaturas: Nunca processe webhooks não verificados
  • Use HTTPS: Certifique-se de que todos os endpoints de webhook usem HTTPS
  • Valide payload: Verifique estrutura do evento e campos obrigatórios
  • Implemente proteção contra replay: Rastreie IDs de eventos processados

Gerenciamento de secrets

  • Use secrets da Azion: Armazene credenciais de forma segura
  • Rotacione secrets regularmente: Atualize secrets de webhook periodicamente
  • Limite acesso: Restrinja acesso a secrets às funções necessárias
  • Monitore uso: Rastreie acesso e uso de secrets

Tratamento de erros

async function processWebhook(request) {
try {
const payload = await request.text();
const signature = request.headers.get('stripe-signature');
// Verificar assinatura do webhook
verifyStripeSignature(payload, signature, STRIPE_WEBHOOK_SECRET);
// Analisar evento
const event = JSON.parse(payload);
// Processar evento
await handleWebhookEvent(event);
return new Response('OK', { status: 200 });
} catch (error) {
console.error('Erro de webhook:', error);
return new Response('Error', { status: 400 });
}
}

Monitoramento e logging

Logging de eventos

function logWebhookEvent(event, status, processingTime) {
console.log({
event_id: event.id,
event_type: event.type,
status,
processing_time: processingTime,
timestamp: new Date().toISOString()
});
}

Monitoramento de performance

  • Tempos de resposta: Monitore velocidade de processamento de webhook
  • Taxas de sucesso: Rastreie processamento bem-sucedido vs falho
  • Padrões de erro: Identifique cenários comuns de falha
  • Frequência de retry: Monitore tentativas de retry de webhook

Solução de problemas

Problemas comuns e soluções

  • Falhas de verificação de assinatura: Verifique configuração de secret do webhook
  • Erros de timeout: Otimize lógica de processamento para velocidade
  • Processamento duplicado: Implemente verificações de idempotência
  • Eventos perdidos: Verifique configuração do endpoint de webhook

Dicas de debugging

  1. Habilite logging detalhado: Registre todos os eventos de webhook e etapas de processamento
  2. Use Dashboard do Stripe: Monitore tentativas de entrega de webhook
  3. Teste localmente: Use Stripe CLI para debugging local
  4. Verifique assinaturas: Verifique cálculo de assinatura de webhook

Recursos avançados

Manipulação de idempotência

const processedEvents = new Set();
async function processWebhookWithIdempotency(event) {
if (processedEvents.has(event.id)) {
console.log(`Evento ${event.id} já processado`);
return;
}
await handleWebhookEvent(event);
processedEvents.add(event.id);
}

Processamento em lote

async function processBatchEvents(events) {
const batchSize = 10;
for (let i = 0; i < events.length; i += batchSize) {
const batch = events.slice(i, i + batchSize);
await Promise.all(batch.map(event => handleWebhookEvent(event)));
}
}

Próximos passos

  • Implemente logging e monitoramento abrangente de eventos
  • Adicione funcionalidade de replay de webhook para eventos falhados
  • Integre com seu sistema de processamento de pagamentos existente
  • Implemente mecanismos avançados de detecção de fraude
  • Dimensione processamento de webhook para cenários de alto volume