ADR-014: Ticket Issuance Trigger Strategy — Operator-Configurable
Status: ✅ Approved — 2026-04-12 Impacts:
schema-commerce.md(tickets.status field),PRODUCT_domain-model.md(Ticket entity),event-catalog.md(BookingConfirmed side effects) Origin: Booking Lifecycle Protocol
Context
User Journey 1 Step 3 describes "instant" Apple Wallet pass delivery upon deposit payment — implying ticket issuance at DEPOSIT_PAID. However, many DACH bus operators prefer to issue tickets only after FULLY_PAID to prevent passengers from boarding without completing the final payment. The boarding gateway difference is significant:
- Early issuance (
DEPOSIT_PAID): Better UX (instant fulfillment), but risk of unpaid travelers holding valid tickets. Requires a voiding mechanism when the final payment is overdue. - Late issuance (
FULLY_PAID): Safe (no ticket without full payment), but worse UX — passengers receive only a booking confirmation at deposit, not the boarding-valid wallet pass.
The Ticket entity now carries a status field (ACTIVE, VOIDED) per the L2-2.1 review, which enables the voiding mechanism required for early issuance.
Decision
Operator-configurable. The ticket issuance trigger is set per Operator (default) or overridden per TourTemplate. Default: DEPOSIT_PAID (matching the user journey's "instant fulfillment" intent).
Configuration field: ticket_issuance_trigger: 'DEPOSIT_PAID' | 'FULLY_PAID' on backoffice.operators (global default) and optionally on backoffice.tour_templates (per-product override, nullable — falls back to operator default if null).
Consequences
Positive:
- Operators choose the strategy that fits their business model
DEPOSIT_PAIDdefault matches the product vision (instant digital fulfillment)Ticket.status = VOIDEDprovides the safety net for early issuance — if final payment is overdue, a Hasura Scheduled Trigger voids the ticket
Neutral:
- Checkout flow must conditionally check the issuance trigger before calling ticket generation
BookingConfirmedevent consumers must be aware that ticket issuance is conditional — some bookings atDEPOSIT_PAIDwill have tickets, others won't (depending on the operator's config)
Implementation notes:
- If
DEPOSIT_PAIDmode: the system issues the ticket duringBookingConfirmedevent handling. If final payment does not arrive bydeparture_date - 7 days, a Scheduled Trigger voids the ticket. - If
FULLY_PAIDmode: the system issues the ticket duringBookingFullyPaidevent handling. No voiding logic needed. - Schema addition:
backoffice.operators.ticket_issuance_trigger VARCHAR DEFAULT 'DEPOSIT_PAID'andbackoffice.tour_templates.ticket_issuance_trigger VARCHAR(nullable).