Skip to main content

💳 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.

ContextPurposeKey Features
guest_bookingInitial quote during checkout.Shows estimated totals and breakdown.
guest_itineraryPost-confirmation details for guest.Shows paid amounts and remaining balances.
guest_receiptFormal invoice view.Full breakdown including tax details.
owner_itineraryHost's view of a reservation.Shows payout calculations and host-specific fees.
owner_receiptHost's financial record.Detailed breakdown for accounting.
guest_cancellationCancellation 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 BillingUIResponse is cached in Redis using relevant prefixes like itinerary:{id}:{context}.
  • Cache Invalidation: The cache is automatically cleared whenever the underlying Reservation or Listing changes.
  • Pre-serialization: Date objects within the breakdown items are pre-formatted to strings on the server to avoid client-side timezone discrepancies.