💳 Billing UI Synchronization
This document explains the architecture and implementation of Adamondo's centralized billing UI system. By moving UI generation logic to the backend, we ensure consistency across Web, iOS, and Android clients while maintaining a single source of truth for all financial calculations.
🏛️ Architecture Overview
The Billing UI system follows a Server-Driven UI pattern. In this architecture, the backend BillingEngine serves as the authoritative source for all billing representations. It takes a Reservation and a specific ViewContext and generates a pre-structured set of "UI Sections" that clients (Mobile or Web) render directly.
This approach centralizes complex financial calculations (service fees, VAT, payout deductions) on the server, ensuring identical billing displays across all Adamondo platforms while simplifying client-side logic.
🧬 Data Model (GraphQL)
The billing UI is exposed via the billingUI field on the Reservation type.
GraphQL Schema Structure
type BillingUIResponse {
sections: [BillingUISection!]!
}
type BillingUISection {
header: String
items: [PriceBreakdownItem!]!
}
type PriceBreakdownItem {
label: String!
value: String!
isDeduction: Boolean
isTotal: Boolean
subText: String
code: String
}
Key Types:
BillingUIResponse: The root container for a specific billing view.BillingUISection: A logical grouping of items (e.g., "Reservation Details", "Payment Breakdown").PriceBreakdownItem: An individual line item with formatting flags (isDeduction,isTotal).
🚦 View Contexts
The billingUI field accepts a viewContext argument which determines the perspective and detail level of the generated UI.
| Context | Purpose | Key Features |
|---|---|---|
guest_booking | Initial quote during checkout. | Shows estimated totals and breakdown. |
guest_itinerary | Post-confirmation details for guest. | Shows paid amounts and remaining balances. |
guest_receipt | Formal invoice view. | Full breakdown including tax details. |
owner_itinerary | Host's view of a reservation. | Shows payout calculations and host-specific fees. |
owner_receipt | Host's financial record. | Detailed breakdown for accounting. |
guest_cancellation | Cancellation preview. | Highlights refund amounts and penalties. |
📥 Integration Guide
1. Fetching Data
Clients should request the billingUI field within their reservation queries.
query GetItinerary($reservationId: Int!) {
getItinerary(reservationId: $reservationId) {
id
currency
billingUI(viewContext: "guest_itinerary") {
sections {
header
items {
label
value
isTotal
}
}
}
}
}
2. Rendering
Because the backend provides the structure, the frontend component (BillingSummary) can remain lightweight and strictly presentational.
// Example implementation in Guest Frontend
const ReservationBilling = ({ data }) => (
<div className="billing-summary">
{data.billingUI.sections.map((section) => (
<div key={section.header}>
<h3>{section.header}</h3>
{section.items.map((item) => (
<div key={item.label} className={item.isTotal ? 'total' : ''}>
<span>{item.label}</span>
<span>{item.value}</span>
</div>
))}
</div>
))}
</div>
);
⚡ Performance & Caching
Billing calculations can be intensive (involving tax lookups, coupon validation, and multi-currency conversion). To maintain performance:
- Redis Caching: The generated
BillingUIResponseis cached in Redis using relevant prefixes likeitinerary:{id}:{context}. - Cache Invalidation: The cache is automatically cleared whenever the underlying
ReservationorListingchanges. - Pre-serialization: Date objects within the breakdown items are pre-formatted to strings on the server to avoid client-side timezone discrepancies.