---
title: "Orientado a eventos por desenho: como um pagamento se transforma em fidelização, retenção e recibo"
description: "Uma plataforma de marcações vive ou morre da sua consistência. Veja como um único pagamento Stripe se ramifica em pontos de fidelização, retenção, um recibo e um lançamento contabilístico, porque cada efeito depende de uma mudança de estado real, e para onde vamos levar a arquitetura a seguir."
url: "https://bookatu.com/pt-pt/blog/event-driven-by-design-the-post-payment-ripple"
language: "pt"
section: "Blog"
category: "Engenharia"
author: "A equipa de engenharia do Bookatu"
published: "2026-06-24"
updated: "2026-06-24"
tags: ["engenharia", "orientado a eventos", "arquitetura", "stripe", "idempotência", "roadmap"]
---

# Orientado a eventos por desenho: como um pagamento se transforma em fidelização, retenção e recibo

Uma plataforma de marcações vive ou morre da sua consistência. Veja como um único pagamento Stripe se ramifica em pontos de fidelização, retenção, um recibo e um lançamento contabilístico, porque cada efeito depende de uma mudança de estado real, e para onde vamos levar a arquitetura a seguir.

> **A versão curta**
>
> - Um pagamento deve propagar-se: a fidelização, a retenção, o recibo e o razão decorrem todos dele.
> - Cada efeito a jusante depende de uma mudança de estado genuína, por isso uma repetição do Stripe nunca dispara duas vezes.
> - O webhook é a costura. Hoje é um fan-out arrumado dentro do processo; o roadmap transforma essa costura num barramento de eventos durável.

Quando um cliente paga uma marcação, um sinal, um pacote ou uma subscrição, esse único acontecimento deve mover o sistema inteiro. O saldo de fidelização dele deve crescer. Deve voltar a contar como ativo, e não como perdido. Deve receber um recibo. E o dinheiro deve entrar nas contas para conciliação. Se uma destas coisas falha, o produto perde confiança em silêncio: um membro é cobrado mas os créditos nunca aparecem, ou uma repetição do Stripe conta a mesma venda duas vezes.

Durante algum tempo, cada uma destas peças era uma ilha. O webhook marcava as coisas como pagas e ficava por aí. Por isso transformámos o pagamento num evento e deixámos o resto do sistema reagir a ele.

## O padrão: ramificar, mas só com uma mudança de estado real

![Diagrama: um pagamento Stripe chega a um webhook idempotente, que chama applyPaidRipple, ramificando para fidelização, retenção, recibo e razão.](https://bookatu.com/blog/eng-event-driven.jpg)

*Um pagamento, quatro efeitos. O webhook é a costura; a propagação é best-effort e nunca bloqueia o caminho do dinheiro.*

O Stripe envia checkout.session.completed para um webhook. O handler faz primeiro a escrita crítica do dinheiro (marcar o sinal como pago, passar a fatura a paga, atribuir o crédito do pacote) e depois chama uma única função partilhada, applyPaidRipple, para o lado do envolvimento. A propagação é best-effort: cada passo está envolvido de forma a que uma falha seja registada e engolida, porque um fornecedor de email instável nunca pode impedir-nos de registar um pagamento real.

```ts
// In the webhook, after the money-critical write succeeds:
if (fresh) {
  await applyPaidRipple({
    orgId,
    customerId,
    amountCents,
    loyaltyReason: "Package purchase",
    receiptTo: email,
    receiptDedupeKey: `payment_receipt:package:${session.id}`,
  });
}
// applyPaidRipple: loyalty -> lastVisitAt bump -> transactional receipt,
// each in its own try/catch. The ledger write runs unconditionally below.
```

## A idempotência é o jogo todo

O Stripe vai entregar o mesmo evento mais do que uma vez. Isso é uma funcionalidade e não um defeito, e significa que cada handler tem de ser seguro para correr duas vezes. Protegem-nos duas camadas. Primeiro, o id do evento é registado numa tabela de eventos processados com uma restrição de unicidade, por isso uma reentrega exatamente do mesmo evento é curto-circuitada. Segundo, e mais importante, cada caminho condiciona a sua propagação a uma mudança de estado genuína sua: um booleano que só é verdadeiro na primeira vez que a linha muda mesmo.

```ts
// markInvoicePaid flips 'open' -> 'paid' and returns true ONLY on that
// real transition. A redelivery sees 'paid' already and returns false.
const flipped = await markInvoicePaid(invoiceId, paymentIntentId, amountTotal);
if (flipped) {
  await applyPaidRipple({ /* ...loyalty, retention, receipt */ });
}
// So even if the event-id dedupe is bypassed, the ripple still fires once.
```

> A chave de deduplicação não é um extra simpático. É a diferença entre um membro ser agradecido uma vez e ser cobrado duas.

## Para onde isto vai

Hoje a propagação é um fan-out dentro do processo, o que é a quantidade certa de maquinaria para onde estamos: é simples, transacional e fácil de raciocinar. Mas o webhook é de propósito uma costura, e o roadmap transforma essa costura num barramento de eventos durável. A forma mantém-se; a entrega fica mais forte.

- Uma fila à frente de cada efeito (fidelização, retenção, mensagens), para que um consumidor lento nunca segure o webhook e as repetições sejam automáticas com backoff.
- Uma tabela outbox escrita na mesma transação que o dinheiro e depois retransmitida para o barramento, para que um efeito nunca se perca entre o 'pago' e o 'publicado'.
- Separar os consumidores mais pesados (notificações, cálculo de retenção) nos seus próprios serviços implantáveis à medida que a carga cresce, mantendo a escrita central da marcação como uma unidade única e fortemente consistente.
- Uma camada gerida de eventos e filas na nossa cloud, para que escalar seja uma alteração de configuração e não uma migração.

A lição que se aplica a tudo isto: mantenha o caminho do dinheiro fortemente consistente e síncrono, e torne tudo o que pende dele orientado a eventos, idempotente e best-effort. Um pagamento é um facto. Tudo de bom que dele decorre é uma reação, e as reações devem ser seguras de repetir.
