Skip to article

Checkout is the midpoint: designing the travel order lifecycle

A booking is not a receipt. It is a living order that must survive rechecks, confirmations, schedule changes, cancellations and money moving on different clocks.

In ordinary ecommerce, “order confirmed” can feel like an ending. The warehouse takes over and the purchased object stays mostly unchanged.

Travel behaves differently. The product is a promise about the future, assembled from systems that continue to change. A fare expires. A hotel changes a room. A flight moves by three hours. A traveller cancels one component but keeps the rest. A refund settles days after the decision.

So the central product object cannot be a receipt. It has to be a living order: one durable record of what was offered, what was accepted, what each supplier confirmed, what changed and what money followed.

The lifecycle in six moments

OnArrival’s platform page frames travel as discover, transact and service. For implementation, it helps to make the state changes more granular.

MomentCore questionEvidence to retain
SearchWhat could be bought?Query, traveller context, ranked offers
SelectionWhat did the traveller choose?Offer, conditions, expiry, ancillaries
RecheckIs that promise still valid?Current price and availability
ConfirmationWhat actually committed?Payment and supplier references
ServiceWhat changed after sale?Requests, quotes, approvals and events
SettlementWhere did the money finish?Capture, payout, adjustment and refund entries

Not every search should become an order. But once an offer is selected, create a stable transaction context early enough to join the later evidence.

Drawn as a state machine, those six moments collapse into five durable states, and the shape makes the argument by itself: checkout is a transition in the middle of the graph, not the exit. Two loops do most of the work. An expired offer falls back to intent and re-shops. And every supplier event or traveller request reopens the order into servicing, which commits a new order version and emits ledger entries, as many times as the trip demands.

schedule change · cancellation · change request select recheck · accept · pay event reopens commit emits entries INTENT OFFER ORDER SERVICING SETTLEMENT search + context priced · expiring versioned record quote then commit ledger entries offer expired · re-shop commit → order v(n+1) runs 0..n times per order refunds settle days later CHECKOUT COMMITS STATE 3 · SERVICING AND SETTLEMENT RUN FOR THE LIFE OF THE TRIP
The order state machine. Checkout commits state three of five; the servicing loop can run many times before settlement closes.

Offer state is not order state

An offer describes a purchasable possibility. It is time-bound, conditional and not yet fulfilled. An order describes a commitment and everything that happens to it.

Conflating them causes familiar bugs: a search response is treated as a booking quote long after it expires; a front end sends price fields back as if they were authoritative; support cannot tell which cancellation policy the traveller accepted.

A cleaner handoff looks like this:

  1. Search returns typed offers with an identifier and validity context.
  2. Selection attaches chosen ancillaries and traveller information.
  3. Recheck obtains the current supplier-backed price and terms.
  4. The customer accepts that rechecked total.
  5. Confirmation writes payment and supplier outcomes to the order.

The accepted commercial snapshot should remain inspectable even if the live product later changes.

Confirmation needs more than success and failure

Supplier calls can time out after a booking has been created. Payment can authorise before inventory fails. In a bundle, one component can confirm while another is unresolved.

A binary status collapses cases that require different action. Consider explicit states such as:

  • pending: work has not completed.
  • confirmed: the external commitment is known.
  • failed: the external system definitively rejected it.
  • unknown: the request may have committed; reconcile before retry.
  • compensating: a confirmed component is being reversed after a wider failure.

The exact names matter less than preserving uncertainty. “Unknown” is uncomfortable but honest. Turning it into “failed” and retrying can create a duplicate booking.

Every mutation needs an idempotency key and a persisted attempt record. If the client repeats the same confirmation request, it should receive the existing result or pending reconciliation, not create a second trip.

The commit itself, across several suppliers with no shared transaction coordinator, is a saga with ordered steps and compensating actions. The travel cart essay walks that sequence in detail, including why the flight books first and what happens when the hotel fails after the flight ticketed. The lifecycle inherits whatever the commit leaves behind, which is exactly why compensating belongs in the state vocabulary above.

One trip can contain several fulfilments

A bundle may contain a flight order, a hotel reservation, an experience voucher and an insurance policy. Each supplier has its own reference and lifecycle. The traveller still experiences one trip.

Model the hierarchy rather than flattening it:

  • Trip: the customer-facing journey and shared traveller context.
  • Order: the commercial agreement and overall totals.
  • Item: a flight, room, activity, transfer or cover product.
  • Fulfilment: the supplier-specific ticket, reservation, voucher or policy.
  • Ledger entry: the money movement attached to an order or item.

This structure lets one hotel room cancel without pretending the flight disappeared. It also allows item-level changes to roll up into a comprehensible trip status.

“Partially confirmed” is not an edge case to hide. It is a state to design, communicate and resolve.

Events are the order’s memory

The current snapshot answers “What is true now?” The event history answers “How did we get here?” Travel operations need both.

Useful events describe business facts rather than internal worker activity:

  • offer.rechecked
  • payment.authorized
  • booking.confirmed
  • schedule.changed
  • change.quoted
  • refund.requested
  • refund.settled

Each event should carry an order ID, event ID, version or sequence, occurrence time and enough context for a downstream consumer to act safely. Webhook delivery should assume retries. Consumers deduplicate by event ID and tolerate a later event arriving first.

Do not make webhooks the only record. They are delivery, not storage. The source system should expose the order and its event history so a consumer can repair missed state.

Servicing is a quote-and-commit product

Changes and refunds often repeat the shape of checkout. First, calculate the available action and its financial consequence. Then let a traveller, policy or agent accept it.

For a voluntary flight change, the quote may include fare difference, penalty and new conditions. For a hotel cancellation, it may show the exact refundable amount at that moment. Concretely, a service quote is a first-class object with its own identity, breakdown and expiry:

{
  "service_quote_id": "sq_4e19b2",
  "order_id": "ord_82f4c7",
  "item_id": "itm_flight_outbound",
  "action": "date_change",
  "expires_at": "2026-08-03T14:32:00Z",
  "breakdown": {
    "fare_difference": { "amount": 2100, "currency": "INR" },
    "change_penalty": { "amount": 1500, "currency": "INR" },
    "total_due_now": { "amount": 3600, "currency": "INR" }
  },
  "new_conditions": { "refundable": false, "changeable": true }
}

The commit step then references service_quote_id and nothing else, never totals reconstructed by a client. If the quote has expired, the server re-quotes instead of trusting arithmetic done against stale state.

That separation improves clarity and safety:

  • The customer sees the consequence before authorising it.
  • Policy can approve or block the action.
  • The server validates that the quote is still usable.
  • Retries reference one service intent.
  • The accepted terms remain attached to the event history.

OnArrival’s Flights page describes source-native servicing across 517+ carriers, including NDC order changes, GDS reissues and LCC flows, translated into one normalised result. The product-level principle is that the same request shape should not imply identical supplier mechanics; the order layer owns that translation. An NDC date change is an order amendment, a GDS one is a reissue with an exchange calculation, and a low-cost carrier may require cancel-and-rebook under the hood. The traveller should see one quote either way.

Keep the money lifecycle adjacent

A booking status without financial state is incomplete. “Cancelled” does not say whether payment was voided, a refund was requested, a supplier penalty applied or funds reached the customer.

Represent financial transitions explicitly:

Travel eventPossible financial event
Offer acceptedPayment authorised
Booking confirmedPayment captured
Item cancelledRefund calculated
Supplier reversesPayout adjusted
Refund completedRefund settled

The order total is a view over ledger entries, not a field repeatedly overwritten. That gives finance an audit trail and lets the customer experience say something more useful than “refund processing.” One booking, many money movements follows that financial lineage through collection, settlement and refund.

Product states deserve product language

Translate operational state into a precise customer promise:

  • “We are confirming your flight” instead of an endless spinner.
  • “Your room is confirmed; your flight is still resolving” for a partial bundle.
  • “The airline changed your departure” with available actions.
  • “Your refund was sent to the original payment method” when settlement occurs.

Do not display “confirmed” because payment succeeded before the supplier confirms, or “refunded” because a support request exists.

Lifecycle readiness checklist

Before shipping, verify that your order model can:

  • Reconstruct the accepted offer and its terms.
  • Distinguish definite failure from unknown outcome.
  • Retry every mutation without duplicate effects.
  • Hold multiple items and supplier fulfilments.
  • Quote a change or refund before committing it.
  • Publish deduplicable events and expose their history.
  • Connect each service action to ledger entries.
  • Explain partial states in traveller language.
  • Give a human operator a safe next action.

Search earns attention. The lifecycle earns trust. Build the order knowing that an itinerary will eventually change. Then test the customer-facing response against the post-booking trust checklist.