Busflow Docs

Internal documentation portal

Skip to content

Cross-Context Event Contracts: Commerce (Booking Lifecycle) ​

Formal event contracts for all Commerce domain events. For the full Hasura Action and Scheduled Trigger specs, see booking-lifecycle-protocol.md and cancellation-protocol.md. For the broader domain events catalog, see event-catalog.md.


Event Catalog ​

EventEmitterConsumerPurpose
BookingConfirmedCommerce (Booking β†’ DEPOSIT_PAID)Backoffice (CostingSheet auto-lock β€” ADR-009), Communications (confirmation)Signals first payment received; triggers cost lock and passenger notification
BookingFullyPaidCommerce (Booking β†’ FULLY_PAID)Communications (full payment confirmation)Signals all payments complete
BookingCancelledCommerce (Booking β†’ CANCELLED)Communications (cancellation notification), Operations (manifest update)Signals booking cancellation with optional refund initiation
BookingRefundedCommerce (Booking β†’ REFUNDED)Communications (refund confirmation)Signals Mollie refund settlement; optionally triggers Invoice Storno
BookingCompletedCommerce (Booking β†’ COMPLETED)Communications (post-trip review request)Signals successful trip completion (Scheduled Trigger: end_date + 24h)
BookingNoShowCommerce (Booking β†’ NO_SHOW)Communications (operator-specific triggers)Signals no boarding event recorded (Scheduled Trigger: end_date + 48h)
PaymentReceivedCommerce (Payment β†’ COMPLETED)Commerce (FinancialLedger revenue tracking), BackofficeFires on every Mollie payment capture (deposit, final payment, ancillary). Distinct from BookingConfirmed β€” that event signals the state transition, this one signals the financial event.
FinancialLedgerClosedCommerce (FinancialLedger β†’ CLOSED)DATEV Export Service, CommunicationsSignals ledger finalization; triggers TaxLedgerEntry creation and DATEV export readiness
CheckoutAbandonedCommerce (CheckoutSession β†’ EXPIRED)Communications (re-engagement: email at +1h, WhatsApp at +24h)Signals checkout session expiry without conversion
FinalPaymentDueCommerce (Scheduled Trigger)Communications (payment reminder with Mollie payment link)Fires when departure_date - reminder_days_before_start for bookings in DEPOSIT_PAID
FinalPaymentOverdueCommerce (Scheduled Trigger)Communications (urgent notification), Backoffice (dispatcher flagging)Fires when departure_date - flag_days_before_start ≀ now() for DEPOSIT_PAID bookings with no FINAL_PAYMENT
SeatHoldExpiredCommerce (Scheduled Trigger)Commerce (self β€” availability count update)Fires when hold_expires_at < now() for HELD reservations
PassengerCancelledCommerce (Hasura Action)Commerce (seat release, ticket void, refund calc), Communications (notification)Fires when removing a single passenger from an active booking (partial cancellation)
AncillaryCancelledCommerce (Hasura Action)Communications (notification)Fires when removing an ancillary from an active booking
PassengerAddedCommerce (Hasura Action)Commerce (self β€” supplementary payment link), Communications (notification), Operations (manifest)Fires when adding a new passenger to an active booking
PassengerUpdatedCommerce (Hasura Action)Operations (manifest update)Fires when passenger details change on an active booking

Payload Schemas ​

BookingConfirmed ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDConfirmed booking
tour_offering_idUUIDTarget offering
price_matrix_idUUIDPriceMatrix version booked against
passenger_countINTTotal passengers on booking
deposit_amountDecimalDeposit payment amount
reference_numberStringHuman-readable booking reference
confirmed_atTIMESTAMPTZConfirmation timestamp

Note: Also consumed by Backoffice for CostingSheet auto-lock. See event-contracts-pricing.md Β§BookingConfirmed for the Backoffice consumption contract.

BookingFullyPaid ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDFully paid booking
total_amountDecimalTotal amount paid
payment_methodStringMollie payment method used for final payment
paid_atTIMESTAMPTZTimestamp of final payment completion

BookingCancelled ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDCancelled booking
reasonStringCancellation reason (user-provided or system-generated)
refund_initiatedBooleanWhether the system initiated a Mollie refund
cancelled_byStringActor: DISPATCHER, PASSENGER, SYSTEM (timeout)
cancelled_atTIMESTAMPTZCancellation timestamp

BookingRefunded ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDRefunded booking
refund_amountDecimalAmount refunded
refund_payment_idUUIDFK to payments.id (the refund Payment row)
refunded_atTIMESTAMPTZMollie refund settlement timestamp

BookingCompleted ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDCompleted booking
tour_offering_idUUIDTarget offering
passenger_countINTTotal passengers
completed_atTIMESTAMPTZCompletion timestamp

BookingNoShow ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDNo-show booking
passenger_idsUUID[]All passengers who did not board
detected_atTIMESTAMPTZDetection timestamp

NOTE

Trigger query (cross-schema): The NoShow evaluation window opens when ServiceLegCompleted fires for the final leg of a TourDeparture (payload: is_final_leg = true). The Commerce consumer queries:

sql
SELECT p.id AS passenger_id, b.id AS booking_id
FROM commerce.bookings b
JOIN commerce.passengers p ON p.booking_id = b.id
JOIN commerce.tickets t ON t.passenger_id = p.id
WHERE b.tour_offering_id = :offering_id
  AND b.status = 'CONFIRMED'
  AND t.status = 'ACTIVE'
  AND NOT EXISTS (
    SELECT 1 FROM operations.boarding_events be
    WHERE be.ticket_id = t.id
      AND be.check_in_status IN ('SUCCESS', 'MANUAL_OVERRIDE')
  );

For each booking_id with β‰₯1 unboarded passenger, emit one BookingNoShow event. Per-passenger granularity (not per-booking) ensures that group bookings with partial no-shows are handled correctly β€” e.g., a booking of 4 where 3 boarded and 1 did not emits a BookingNoShow with passenger_ids = [the_missing_one].

PaymentReceived ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDAssociated booking
payment_idUUIDThe payments.id row
payment_typeStringDEPOSIT, FINAL_PAYMENT, or PARTIAL_REFUND
amountDecimalPayment amount
payment_methodStringMollie payment method
provider_transaction_idStringMollie transaction ID
captured_atTIMESTAMPTZMollie capture timestamp

CheckoutAbandoned ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
session_idUUIDExpired checkout session
tour_offering_idUUIDOffering the session was for
contact_emailStringPassenger email for re-engagement
expired_atTIMESTAMPTZSession expiry timestamp

FinalPaymentDue ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDBooking awaiting final payment
passenger_emailStringPrimary contact email
amount_remainingDecimalOutstanding amount
due_dateDATEDerived from departure_date - operator.final_payment_config.reminder_days_before_start
payment_linkStringPre-generated Mollie payment URL
severityStringEscalation tier: REMINDER (first notice) or URGENT (second notice). See booking-lifecycle-protocol.md Β§final_payment_escalation_sweep.
channelStringPreferred delivery channel: EMAIL (REMINDER tier) or WHATSAPP (URGENT tier)

FinalPaymentOverdue ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDOverdue booking
severityStringAlways CRITICAL
flagged_atTIMESTAMPTZTimestamp when the system flagged the booking
tickets_voidedBooleanWhether the system voided tickets (true if the system issued ADR-014 tickets at DEPOSIT_PAID)

SeatHoldExpired ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
seat_reservation_idUUIDThe released reservation
service_leg_idUUIDAffected service leg
seat_identifierStringSeat position key
expired_atTIMESTAMPTZHold expiry timestamp

PassengerCancelled ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDParent booking
passenger_idUUIDCancelled passenger
refund_amountDecimalAmount refunded for this passenger (after cancellation fee)
cancelled_atTIMESTAMPTZCancellation timestamp

AncillaryCancelled ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDParent booking
ancillary_idUUIDCancelled ancillary
refund_amountDecimalAmount refunded (after cancellation fee)
cancelled_atTIMESTAMPTZCancellation timestamp

PassengerAdded ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDParent booking
passenger_idUUIDNewly added passenger
additional_amountDecimalPrice added to booking total
supplementary_payment_requiredBooleanWhether a the system generated a new Mollie payment link
added_atTIMESTAMPTZAddition timestamp

PassengerUpdated ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
booking_idUUIDParent booking
passenger_idUUIDUpdated passenger
changesString[]List of changed field names (e.g., ["first_name", "last_name"])
ticket_reissuedBooleanWhether the system voided and reissued the ticket due to a name change
updated_atTIMESTAMPTZUpdate timestamp

FinancialLedgerClosed ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
financial_ledger_idUUIDClosed ledger
tour_offering_idUUIDParent offering
realized_revenueDecimalFinal revenue at closure
realized_expenseDecimalFinal expense at closure
margin_deltaDecimalFinal margin deviation from plan
tax_entry_countINTNumber of TaxLedgerEntries created at closure
closed_atTIMESTAMPTZClosure timestamp

InvoiceIssued ​

FieldTypeDescription
event_idUUIDIdempotency key
tenant_idUUIDOwning operator
invoice_idUUIDThe issued invoice
booking_idUUIDParent booking
invoice_numberStringGap-free human-readable number
total_grossDecimalInvoice grand total
issued_atTIMESTAMPTZIssuance timestamp

Delivery Contracts ​

EventMechanismTriggerIdempotency
BookingConfirmedHasura Event Trigger (async, post-commit)commerce.bookings.status β†’ DEPOSIT_PAIDevent_id
BookingFullyPaidHasura Event Trigger (async, post-commit)commerce.bookings.status β†’ FULLY_PAIDevent_id
BookingCancelledNestJS @nestjs/event-emitter β†’ Hasura Event TriggercancelBooking Action handler commitsevent_id
BookingRefundedHasura Event Trigger (async, post-commit)commerce.bookings.status β†’ REFUNDEDevent_id
BookingCompletedNestJS Cron Trigger handlerbooking_completion_sweep (end_date + 24h)event_id
BookingNoShowNestJS Cron Trigger handlerno_show_detection_sweep (end_date + 48h)event_id
PaymentReceivedHasura Event Trigger (async, post-commit)commerce.payments.status β†’ COMPLETEDevent_id + provider_transaction_id uniqueness
CheckoutAbandonedNestJS Cron Trigger handlercheckout_abandoned_sweep (every 5 min)Session status = ACTIVE guard in WHERE clause
FinalPaymentDueNestJS Cron Trigger handlerfinal_payment_escalation_sweep (daily 08:00)NOT EXISTS check on pending/completed FINAL_PAYMENT + tier derivation from days_until_departure
FinalPaymentOverdueNestJS Cron Trigger handlerfinal_payment_escalation_sweep (daily 08:00)Same sweep as FinalPaymentDue, emitted at CRITICAL tier (flag_days_before_start). Sets booking.flagged = true, voids tickets if applicable.
SeatHoldExpiredNestJS Cron Trigger handlerseat_hold_cleanup (every 60s)status = HELD guard in WHERE clause
PassengerCancelledNestJS @nestjs/event-emitter β†’ Hasura Event TriggercancelPassenger Action handler commitsevent_id
AncillaryCancelledNestJS @nestjs/event-emitter β†’ Hasura Event TriggercancelAncillary Action handler commitsevent_id
PassengerAddedNestJS @nestjs/event-emitter β†’ Hasura Event TriggeraddPassengerToBooking Action handler commitsevent_id
PassengerUpdatedNestJS @nestjs/event-emitter β†’ Hasura Event TriggerupdatePassengerDetails Action handler commitsevent_id
FinancialLedgerClosedHasura Event Trigger (async, post-commit)commerce.financial_ledgers.status β†’ CLOSEDevent_id
InvoiceIssuedNestJS @nestjs/event-emitter β†’ Hasura Event TriggerissueInvoice Action handler commitsevent_id

All consumers implement at-least-once processing with event_id-based deduplication. See domain-driven-design.md Β§4 for the standard delivery pattern.

Internal documentation β€” Busflow