Three ways to pay, one escrow
Every payment lands in the same escrow contract and is released only when the supplier confirms. The three modes differ in who signs and how often.
The boundary every flow respects
The part that sells never sees a wallet, and the part that pays never sees a trip. The only thing that crosses is an opaque reference, so there is no amount or address in the conversation to tamper with.
Approve in wallet
The backend pushes a payment request into the wallet session. The person approves it in the wallet; the web app shows waiting for your wallet and moves on by itself when the chain confirms. Used when there is no allowance or a payment is above the caps.
- Person→Web app“Yes, all four”
- Web app→Agent + travelconfirm order VT-7K3Q9D
- Agent + travel→Pay + railintent_ref pi_7Kq2… only
- Pay + rail→Walletpayment request, pushed into the session
- The person taps Approve in the wallet
- Wallet→Chainsigned transfer 1,817.00 USDT
- Chain→Pay + railescrowed · 19 confirmations
- Pay + rail→Web applive status: waiting → escrowed → booked
Delegated allowance
The person grants caps once, in the wallet. The SpendingPolicy contract enforces them, so a breach of our servers can take no more than the wallet allows, and only into the escrow.
- Once
- Person→Walletsets caps: 2,500 per payment · 4,000 per day · escrow only · until 2 Dec
- Wallet→SpendingPolicyapprove(SpendingPolicy), signed once
- Every purchase within the caps
- Person→Web app“Yes, all four” · one tap
- Web app→Pay + railintent_ref pi_7Kq2…
- Pay + rail→SpendingPolicypull 1,817.00 to escrow
- The contract checks per-payment, daily and destination
- SpendingPolicy→Chaintransfer into escrow
- Chain→Web appescrowed → booked, shown live
- Any time
- Wallet→SpendingPolicyrevoke · one transaction
Pay by transfer
For any wallet or an exchange withdrawal. The person sends the exact amount with the memo before the quote expires; the chain watcher matches the deposit. Nobody's word counts, only the chain's.
- Web app→Personaddress, amount, memo, QR
- Person→Any wallet or exchangesends from there
- Any wallet→Deposit address1,817.00 USDT · memo VNM-4Q7K-2D
- Deposit address→Chain watcherdeposit seen
- Chain watcher→Pay + railmatched on memo and amount → escrowed
- Pay + rail→Web appsettling → booked, shown live
- If nothing matches: no memo, wrong amount, too late
- Held in suspense and flagged to ops
- Chain watcher→Any walletrefund to the sender after review
How a change reaches the person
After payment the trip keeps moving. The status feed and the airline speak only to the travel program; it turns what they say into a timeline entry, and the platform turns that into a notification. Pay and rail hear nothing unless money moves.
- Status feed→TravelFZ 982 delayed 25 min · polled every 5 min
- Duffel→Travelairline change webhook
- Travel→Agentitem.updated with a timeline entry
- Agent→Web apppush and a notification
- Web app→Personchoices, each with its cost
- The person picks “keep the new time”
- Person→Web appanswer
- Web app→Travelchange capability, through the agent
- Travel→Duffelaccept airline change
Screens: airline change in chat, the same by hand, flight status states, notifications.
Money for one booking: 100 → 105
The person pays one amount and sees one amount. The fee is 5 % on top of the supplier price, split exactly in half. The split does not depend on the size of the booking: 200 means 210, with 5 to buyback and 5 to revenue.
The same rule on the Dubai booking
Order VT-7K3Q9D, paid in USDT on TRON. Amounts are integers of the asset's smallest unit (6 decimals for USDT); the person sees 1,817.00, the ledger keeps every digit.
- Supplier price (flights 780.95 + hotel 949.53, quoted)
- 1,730.476190USDT TRC20
- Service fee, 5 %
- 86.523810USDT
- buys VENOM
- 43.261905USDT
- revenue
- 43.261905USDT
- The person pays
- 1,817.000000USDT TRC20
The same function runs in the contracts, the rail and the UI, checked against shared test vectors. If one half of the fee has an odd last unit, it goes to revenue.