One booking, many money movements
Travel payments are not a checkout problem. They are a lifecycle of collection, supplier settlement, partial changes, refunds and reconciliation that must share one source of truth.
A traveller sees one total and presses one button. Behind that moment, the booking may contain a flight, room and activity supplied by different companies, priced in different currencies and payable on different schedules.
Then the flight changes. The activity moves to another day. The room is partially refundable.
This is why travel payments cannot be designed as a thin checkout layer. Checkout is the front door to a money lifecycle. The architecture has to connect what the customer paid, what each supplier is owed, what the business earns and what changes later. That lineage should attach to the same item hierarchy described in the travel order lifecycle, rather than inventing a parallel finance-only booking model.
Begin with four distinct questions
Teams get into trouble when “payment status” tries to answer everything. Separate the questions:
- Collection: did we successfully collect or authorise customer funds?
- Order: which travel components are confirmed, pending or failed?
- Settlement: which supplier obligation is due, through which rail and currency?
- Accounting: how do all movements map to revenue, cost, liability and refund positions?
These states influence each other, but they are not interchangeable. A successful card charge does not prove a supplier confirmed. A confirmed booking does not prove the supplier has been paid. A supplier-approved refund does not mean the customer sees the money yet.
Give every movement a shared lineage
The most important design choice is identifier lineage. Every financial event should trace back through a consistent hierarchy:
trip → order → component → supplier obligation → payment movement
Customer collections, virtual-card authorisations, bank transfers, fees, FX adjustments, refunds and chargebacks should all carry enough reference data to rejoin that chain.
Do not rely on free-text descriptors or a supplier’s reference as the only key. Supplier references can arrive late, change format or represent several components. Your own immutable identifiers should anchor the system, while external references remain indexed attributes.
OnArrival’s Payments product is organised around a travel ledger that follows money from offer through booking, segment and ancillary. Whether you use that layer or build your own, the principle is the same: money needs to inherit the structure of the trip.
A practical state model
Use explicit state machines instead of a universal paid: true field.
| Object | Example states | Critical question |
|---|---|---|
| Customer payment | created, authorized, captured, failed, reversed, refunded | What did the customer’s payment method do? |
| Booking component | pending, confirmed, changed, cancelled, failed | What travel service currently exists? |
| Supplier payable | not due, scheduled, issued, accepted, failed, netted | What do we owe the supplier? |
| Refund | requested, supplier-approved, funded, submitted, completed, failed | Where is the return of funds now? |
| Ledger entry | pending, posted, reversed | Has finance recorded the economic event? |
Transitions should be event-driven, timestamped and idempotent. Store why a transition occurred and which command or external message caused it.
One charge can create a settlement tree
Consider a bundle with a flight, a hotel and an activity. The customer sees one charge, while the supplier side requires an airline settlement path, a hotel payment on a different rail and a third schedule for the activity operator. Margin may belong partly to each component. A cancellation can affect only one branch.
The correct collection shape on the customer side is one authorisation for the total, then a capture per component as each supplier confirms. That is the single-auth, staged-capture pattern the travel cart essay derives from the commit saga. It matters here because each capture is born knowing which component it pays for. Captures are the joint where customer money meets supplier money, and the joint only holds if it exists at component grain.
The settlement plan should therefore be generated from confirmed order components, not directly from the cart total. Each payable needs:
- Supplier and booking-component identity
- Amount and settlement currency
- Due date or release condition
- Selected rail
- FX rate and source where relevant
- Current position, retries and external reference
- Links to adjustments or refund netting
This model supports virtual cards, industry settlement systems and bank rails without pretending they behave identically. The OnArrival product surface describes several supplier payout methods under one reconciled booking model; the unifying layer is the ledger, not the rail.
Treat FX as order data
When display, collection and supplier settlement currencies differ, the exchange rate affects both customer experience and margin. Record the rate used at quote, any lock or expiry, the rate applied at capture or settlement, and resulting variance.
Do not recompute historical amounts using today’s rate. The order needs an immutable monetary snapshot: currency, precision, price components, rate context and timestamps.
If a booking changes, create adjustment entries rather than rewriting the past. Finance should be able to explain the difference between the original promise and the final outcome.
Refunds reverse economics, not history
A refund should reference the original payment and the specific cancelled or adjusted component. It should not delete the original sale or mutate a captured amount into a smaller fictional transaction.
A robust refund flow answers:
- What is the customer entitled to under the accepted rules?
- Has the supplier approved or returned the corresponding value?
- Is the business advancing funds before supplier recovery?
- Which fees or penalties remain?
- Which payment method receives the refund?
- What ledger entries reverse revenue, cost or liability?
- Can part of the supplier position be netted against a future payable?
In data terms, a refund is a first-class object that points backwards. For the cancelled hotel in the diagram above:
{
"refund_id": "ref_204",
"order_id": "ord_7ac1",
"component_id": "cmp_hotel_2",
"original_capture": "cap_c2",
"customer_amount": { "currency": "INR", "value": 17600 },
"supplier_recovery": {
"payable_id": "spl_02",
"expected": { "currency": "USD", "value": 168.75 },
"state": "supplier_approved",
"settlement": "net_against_next_payable"
},
"fx_context": "rate snapshot from cap_c2, never today's rate",
"advanced_before_recovery": true,
"ledger_entries": ["reverse_revenue", "reverse_supplier_cost", "recognize_recovery_receivable"]
}
advanced_before_recovery is the field finance argues about. If the business refunds the customer before the bedbank returns the USD, that float is a real position, and it should be visible as a receivable rather than hidden inside a net number.
Those decisions vary by product and commercial model. The architecture should represent them without losing the link to the booking.
Reconciliation should be continuous
Month-end should confirm the system, not discover it.
Reconcile at several levels:
- Gateway or bank movement to payment object
- Payment object to customer order
- Supplier payout to payable and booking component
- Refund movement to refund object and original collection
- Ledger balances to external statements and safeguarded or operating accounts as applicable
Build exception queues around typed causes: missing reference, amount mismatch, currency mismatch, unexpected duplicate, overdue supplier acceptance. A single “unmatched” bucket hides the product defect.
Concretely: the bundle from the settlement tree above crosses at least four external statements over its life, and each line has exactly one internal record it must match.
| External statement line | Amount | Should match | Outcome |
|---|---|---|---|
Gateway payout, batch PG-8841 | +INR 52,300 | Payment pay_9f2: captures C1, C2, C3 | Matched at capture level |
| Virtual card clearing, airline | -EUR 316.00 | Payable spl_01 for component C1 | Matched; INR 12.40 funding-rate variance posted as an adjustment |
| Bank debit, bedbank | -USD 171.00 | Payable spl_02, scheduled at USD 168.75 | Exception: amount mismatch, typed supplier-overcharge, owner assigned |
Card refund, ref RF-204 | -INR 17,600 | Refund ref_204 against capture C2 | Matched to the original collection; USD 168.75 recovery receivable stays open |
Rows one, two and four confirm the system. Row three is the process working as designed: the mismatch is caught the day the statement arrives, typed, owned and sized at USD 2.25, instead of surfacing months later as an unexplained write-off.
Product, support and finance need one vocabulary
The itinerary might say “cancelled” while finance says “refund submitted” and the bank says “processing.” All three can be true. They should be connected and translated into plain language.
Support needs read access to the relevant financial timeline without access to raw payment credentials. Finance needs component and supplier context without opening support tickets. Customers need the part that affects them: amount, destination, current stage and next expected event. The post-booking trust guide shows how those operational states become customer language.
This shared model also improves incident response. If a supplier payout rail fails, operators can identify affected obligations without treating every customer booking as failed. If capture succeeds but confirmation does not, the system can route recovery or reversal deliberately.
Architecture review checklist
Before launch, ask:
- Can every cent be traced to an order and component?
- Are collection, booking, settlement and refund states separate?
- Are commands idempotent across retries and webhooks?
- Are price and FX snapshots immutable?
- Can partial changes create adjustments without rewriting history?
- Does supplier settlement start from confirmed obligations?
- Can finance explain a customer refund from a single timeline?
- Are reconciliation exceptions typed and owned?
- Can support see financial status without seeing sensitive credentials?
- Do customer-facing labels reflect what the system actually knows?
Travel money becomes manageable when it follows the order instead of living beside it. One customer action may create many movements, but they should tell one consistent story.