Journal
Teknikk3 min lesetid

Hendelsesdrevet fra bunnen av: slik blir én betaling til lojalitetspoeng, gjenkjøp og en kvittering

En bestillingsplattform står og faller på konsistens. Her er hvordan én enkelt Stripe-betaling brer seg ut i lojalitetspoeng, kundeoppfølging, en kvittering og en post i regnskapet, hvorfor hver effekt er låst til en ekte tilstandsendring, og hvor vi tar arkitekturen videre.

UIUtviklerteamet i Bookatu

Når en kunde betaler for en bestilling, et depositum, en pakke eller et medlemskap, skal den ene hendelsen sette hele systemet i bevegelse. Lojalitetssaldoen deres skal vokse. De skal telles som aktive igjen, ikke som sovende. De skal få en kvittering. Og pengene skal havne i bøkene til avstemming. Bommer du på én av dem, mister produktet tillit i det stille: et medlem blir belastet, men klippene deres dukker aldri opp, eller et nytt forsøk fra Stripe teller et salg to ganger.

En stund var hver av dem sin egen øy. Webhooken merket ting som betalt og stoppet der. Så vi gjorde betalingen om til en hendelse, og lot resten av systemet reagere på den.

Mønsteret: fordel ut, men bare ved en ekte tilstandsendring

Diagram: en Stripe-betaling treffer en idempotent webhook, som kaller applyPaidRipple og fordeler ut til lojalitet, oppfølging, kvittering og regnskap.
Én betaling, fire effekter. Webhooken er skjøten, og ringvirkningen skjer etter beste evne og blokkerer aldri pengeveien.

Stripe sender checkout.session.completed til én webhook. Håndtereren gjør først skrivingen som er kritisk for pengene (merk depositumet betalt, sett fakturaen til betalt, gi klippene i pakken), og kaller så én felles funksjon, applyPaidRipple, for engasjementssiden. Ringvirkningen skjer etter beste evne: hvert steg er pakket inn slik at en feil logges og svelges, fordi en ustabil e-postleverandør aldri må hindre oss i å registrere en ekte betaling.

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.

Idempotens er hele greia

Stripe kommer til å levere den samme hendelsen mer enn én gang. Det er en funksjon og ikke en feil, og det betyr at hver håndterer må tåle å kjøre to ganger. To lag beskytter oss. For det første registreres hendelses-id-en i en tabell over behandlede hendelser med en unik nøkkel, så en ny levering av nøyaktig samme hendelse kortsluttes. For det andre, og viktigere, låser hver vei sin ringvirkning til sin egen ekte tilstandsendring: en boolsk verdi som bare er sann første gang raden faktisk snur.

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.

Nøkkelen som luker ut duplikater er ikke en bonus. Den er forskjellen på at et medlem blir takket én gang og at de blir belastet to ganger.

Hvor dette bærer hen

I dag er ringvirkningen en fan-out i samme prosess, som er akkurat passe maskineri for der vi er nå: den er enkel, transaksjonell og lett å resonnere om. Men webhooken er bevisst en skjøt, og veikartet gjør den skjøten om til en varig hendelsesbuss. Formen forblir den samme, leveringen blir sterkere.

  • En kø foran hver effekt (lojalitet, oppfølging, meldinger), så en treg konsument aldri holder igjen webhooken og nye forsøk skjer automatisk med økende ventetid.
  • En outbox-tabell som skrives i samme transaksjon som pengene, og som så videreformidles til bussen, så en effekt aldri går tapt mellom "betalt" og "publisert".
  • Å skille ut de tyngste konsumentene (varsler, scoring av oppfølging) i egne tjenester etter hvert som belastningen vokser, mens selve bestillingsskrivingen forblir én sterkt konsistent enhet.
  • Et administrert lag for hendelser og køer i skyen vår, så skalering blir en endring i konfigurasjonen i stedet for en migrering.

Lærdommen som holder gjennom alt sammen: hold pengeveien sterkt konsistent og synkron, og gjør alt som henger på den hendelsesdrevet, idempotent og etter beste evne. En betaling er et faktum. Alt godt som følger av den er en reaksjon, og reaksjoner skal være trygge å spille av på nytt.

utviklinghendelsesdrevetarkitekturstripeidempotensveikart
Klar til å ta dette i bruk?

Bookatu gir deg en bookingside med ditt eget preg, depositum, medlemskap, gavekort og påminnelser, med 0 % provisjon på bookingene dine.

Kom i gang gratis