Diario
Tecnica3 min di lettura

Guidato dagli eventi per scelta: come un pagamento diventa fedeltà, ritorno e ricevuta

Una piattaforma di prenotazioni vive o muore di coerenza. Ecco come un singolo pagamento Stripe si dirama in punti fedeltà, riattivazione del cliente, ricevuta e riga contabile, perché ogni effetto è vincolato a un vero cambio di stato, e dove porteremo l'architettura d'ora in poi.

ITIl team tecnico di Bookatu

Quando un cliente paga una prenotazione, una caparra, un pacchetto o un abbonamento, quel singolo evento deve muovere tutto il sistema. Il suo saldo fedeltà deve crescere. Deve tornare a contare come cliente attivo, non come perso. Deve ricevere una ricevuta. E i soldi devono finire nei conti per la riconciliazione. Sbagliane uno e il prodotto perde fiducia in silenzio: un iscritto viene addebitato ma i suoi crediti non compaiono mai, oppure un nuovo tentativo di Stripe conta due volte la stessa vendita.

Per un po' ognuna di queste cose è stata un'isola a sé. Il webhook segnava le cose come pagate e si fermava lì. Così abbiamo reso il pagamento un evento, e lasciato che fosse il resto del sistema a reagire.

Lo schema: dirama, ma solo su un vero cambio di stato

Diagramma: un pagamento Stripe arriva a un webhook idempotente, che chiama applyPaidRipple e si dirama verso fedeltà, riattivazione, ricevuta e contabilità.
Un pagamento, quattro effetti. Il webhook è la giuntura; l'onda è a miglior tentativo e non blocca mai il percorso dei soldi.

Stripe invia checkout.session.completed a un solo webhook. Il gestore fa prima la scrittura critica per i soldi (segnare la caparra come pagata, portare la fattura a pagata, assegnare il credito del pacchetto), poi chiama un'unica funzione condivisa, applyPaidRipple, per la parte di ingaggio. L'onda è a miglior tentativo: ogni passaggio è avvolto in modo che un errore venga registrato e assorbito, perché un fornitore di email ballerino non deve mai impedirci di registrare un pagamento vero.

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.

L'idempotenza è tutto

Stripe consegnerà lo stesso evento più di una volta. È una caratteristica, non un difetto, e vuol dire che ogni gestore deve poter girare due volte senza danni. Ci proteggono due livelli. Primo, l'id dell'evento viene registrato in una tabella degli eventi già elaborati con un vincolo di unicità, così una riconsegna dello stesso identico evento si interrompe subito. Secondo, e più importante, ogni percorso vincola la sua onda a un proprio cambio di stato reale: un valore booleano che è vero solo la prima volta che la riga cambia davvero.

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.

La chiave di deduplicazione non è un optional. È la differenza tra un iscritto ringraziato una volta e un iscritto addebitato due.

Dove stiamo andando

Oggi l'onda è una diramazione dentro il processo, che è la quantità giusta di meccanica per il punto in cui siamo: è semplice, transazionale e facile da ragionare. Ma il webhook è una giuntura voluta, e la roadmap trasforma quella giuntura in un bus di eventi durevole. La forma resta la stessa; la consegna diventa più robusta.

  • Una coda davanti a ogni effetto (fedeltà, riattivazione, messaggistica), così un consumatore lento non blocca mai il webhook e i nuovi tentativi sono automatici con attesa progressiva.
  • Una tabella outbox scritta nella stessa transazione dei soldi, poi inoltrata al bus, così un effetto non si perde mai tra il "pagato" e il "pubblicato".
  • La separazione dei consumatori più pesanti (notifiche, punteggio di ritorno) in servizi propri da rilasciare a parte man mano che il carico cresce, mentre la scrittura centrale della prenotazione resta un'unica unità fortemente coerente.
  • Un livello gestito di eventi e code sul nostro cloud, così scalare diventa un cambio di configurazione invece che una migrazione.

La lezione che vale per tutto quanto: tieni il percorso dei soldi fortemente coerente e sincrono, e rendi tutto quello che ci si appende guidato dagli eventi, idempotente e a miglior tentativo. Un pagamento è un fatto. Tutto quello di buono che ne consegue è una reazione, e le reazioni devono poter essere rigiocate senza rischi.

ingegneriaguidato dagli eventiarchitetturastripeidempotenzaroadmap
Vuoi metterlo in pratica?

Bookatu ti dà una pagina di prenotazione con il tuo brand, acconti, abbonamenti, carte regalo e promemoria, con lo 0% di commissioni sulle tue prenotazioni.

Inizia gratis