Um Pagamento Bem-sucedido que Nunca se Tornou uma Reserva
Um cliente pagou. O Razorpay mostrou sucesso. O webhook enviou um HTTP 200. O pagamento foi capturado.
No entanto, a reserva ficou travada em "confirmando".
Nenhum erro apareceu. Nenhuma exceção quebrou o código. Nenhum alerta foi disparado. Todas as métricas mostravam um sistema saudável.
Mas o cliente não recebeu nada. O criador não tinha reserva.
Aceitar dinheiro é fácil. Garantir que cada pagamento resulte em uma reserva é o verdadeiro desafio.
A maioria dos tutoriais sugere este fluxo:
- Webhook recebe o evento
- Webhook atualiza a reserva
Isso é perigoso. Se a lógica de negócio reside dentro do webhook, você depende inteiramente do sucesso da entrega. Webhooks enfrentam tentativas de reenvio, duplicatas e falhas parciais.
Mudamos nossa arquitetura para separar essas tarefas. Agora, os webhooks apenas registram eventos. Eles não executam lógica de negócio.
Introduzimos um registro de eventos com três tabelas:
- payment_orders: A verdade do provedor
- payment_events: O registro imutável de eventos
- bookings: A verdade do negócio
O webhook agora tem apenas um trabalho:
- Verificar assinatura
- Armazenar evento
- Retornar 200
Isso protege o sistema. Se o webhook falhar, o evento ainda estará seguro.
Também aprendemos que o estado do pagamento e o estado da reserva são diferentes. Um pagamento capturado é uma entrada. Uma reserva confirmada é o resultado. Mantê-los separados permite a reconciliação.
Durante uma investigação, encontramos um bug. Os eventos existiam no banco de dados. O processador estava saudável. O webhook estava saudável.
Mas o processador nunca rodou. Ninguém estava acionando a função para processar eventos pendentes.
Desacoplar a ingestão do processamento é um bom design. Mas isso cria um novo requisito: algo deve acionar o processamento.
Implementamos um agendador para executar várias tarefas:
- Processar eventos de pagamento
- Recuperar webhooks perdidos
- Validar a consistência do sistema
Para evitar erros durante as tentativas de reenvio, usamos esta lógica:
- Selecionar eventos não processados
- Usar "SKIP LOCKED" para permitir múltiplos workers
- Garantir que entregas duplicadas não façam nada
Um sistema que só funciona quando cada webhook chega no prazo é um sistema frágil. Se a sua fila não tiver ninguém para esvaziá-la, o trabalho esperará para sempre.
Confiabilidade significa construir prevendo quando as coisas falham.