Balloons and Mario can be attached specifically to this delivery group.
Checkout stays simple for the customer, while the backend remains highly robust.
The customer sees a calm, understandable journey. Behind it sit Delivery Groups, slot capacity, Character capacity, temporary holds, wallet/coupon validation, a payment state machine and server-side recalculation before order creation.
The unified path from cart to authoritative result
Each step establishes context for the next. We do not ask for Character Delivery before the delivery time is known, and we never treat a browser return as proof of payment success.
A mixed cart is explained before it surprises the customer
When product readiness differs, the order becomes multiple Delivery Groups. Each group owns its own timing, fees and services, while all groups still belong to one order.
It has independent timing, fee and capacity while remaining part of the same order.
Slot and Character are independent systems — test the capacity yourself
A delivery slot may be available while Mario is full, or the reverse. Checkout combines both systems only when the customer requests the Character service.
effective capacity − (confirmed + valid holds). Expired holds do not count, even if the cleanup worker has not deleted the row yet.
Two customers see the last capacity unit at the same time
The UI may show “1 left” to both. Only one transaction should succeed; the other recalculates capacity and receives a clear recovery result.
Mario · 1 left
Ready to send a reservation request.
Mario · 1 left
Ready to send a reservation request.
Simulation has not started.
Simulation has not started.
Browser return ≠ Payment success
The backend owns the final state. Provider webhook and customer return can arrive in any order, so we need idempotency, deduplication and legal state transitions.
PENDING attempt
Before opening the provider session, create a payment attempt with a unique reference tied to the checkout/order context.
Webhook + callback
Both can report or request status from the backend, but no path may mark the same order paid twice.
Commit once
After verification: mark paid once, commit reservations, then emit notifications and analytics downstream.
Expected errors have explicit names and designed recovery
Instead of a generic “Something went wrong,” every critical error expresses a real business condition that the UI can handle deliberately.