APP DESCRIPTION
×

Best Price Flights: Real-Time Flight Price Tracker Mobile App with Multi-provider Aggregation and Google Auth

Best Price Flights is a live cloud product that monitors flight prices several times a day — querying Kiwi, Google Flights, and Skyscanner directly — and alerts users the moment fares drop. Rather than relying on occasional manual checks, it works on an operator-controlled poll schedule so people can catch a usable price while it is still bookable.

Creating a watch is straightforward: a structured form (one-way or round-trip, exact or flexible dates, adults/children/infants) or plain English in the Flight Assistant — “two adults from Bristol to Rome 23 May, flexible departure, roundtrip with return in 7 days, direct flights only”. The assistant extracts airports, dates, and passengers and starts tracking. Alerts arrive as Android push, browser Web Push, and optional email.

The stack is Microsoft .NET 10 on the backend and native clients, with an Angular 21 web portal at bestpriceflights.info. Google sign-in is shared. Secrets stay on Azure Functions — devices only hold a public client ID.

What Makes It Different

Plenty of products offer a price alert. Best Price Flights is built as a small SaaS around a different job: keep several independent providers honest, on your schedule, and get the user to a booking link in time.

• Multi-provider aggregation — Kiwi, Google Flights, and Skyscanner are queried in one watch, each through the current v2 backends.

• Native Android push plus browser Web Push plus email — the web portal is a first-class client, not a brochure.

• Optional Android notification agent (BPF Alerts) — install after sign-in if you manage searches on the web and only want alerts on the phone.

• Configurable polling — the operator owns the schedule and can run manual or per-provider polls from admin.

• Server-side Flight Assistant — Azure OpenAI behind POST /api/chat. Web and MAUI use the same API; no model key on the device.

• Flexible dates (±1 day) and family passengers.

• Full price history per search and provider, not a single “price changed” flag.

• Stripe Test subscriptions (Free / Basic / Pro) with live Cosmos tier documents — quotas and the flexible-dates flag can change without a redeploy.

Solution Structure

The GitHub solution is larger than the original four-project snapshot:

• BestPriceFlightsWeb — Angular 21 marketing site and signed-in portal on Azure Static Web Apps (bestpriceflights.info).

• BestPriceFlightsClient — .NET MAUI Blazor Hybrid full app (Android first; iOS still Release 2). Same Google account and API as the portal.

• BestPriceFlights.Agent — slim MAUI Android app (BPF Alerts) for push only. OAuth through Functions, same Desktop client as the full app.

• BestPriceFlights.Admin — Blazor Server operator console (users, devices, polls, tiers, Stripe purge). All /api/ops/* calls require a Functions key.

• BestPriceFlights.Functions — Azure Functions v4 isolated worker on .NET 10: HTTP API, timer polls, Stripe, chat, Web Push, APK SAS, Google token exchange.

• BestPriceFlights.Shared — models, catalog, chat prompt, search-draft rules. Covered by dedicated test projects (kept on purpose).

What the Product Does

A user signs in with Google, creates one or more watches, and leaves them running. The backend polls allowed providers, writes history, and raises alerts when a fare is cheaper than the stored best.

Capabilities now in production:

• One-way and round-trip tracking, exact or flexible dates

• Adults, children, and infants (provider quirks handled server-side)

• Quota check before a new search is created (web and MAUI)

• Alternative-flight exploration on JWT user routes (/api/searches/{id}/alt/…)

• Booking-link resolve for deferred Google/Sky tokens

• Alert fan-out: FCM (notifyByPush) → Web Push (notifyByWebPush) → email (notifyByEmail)

• Sideload APKs from private Azure Blob via short-lived SAS — public full-app download; agent only after portal sign-in

• Admin: schedule, manual poll, per-provider poll, device test/re-register, user delete + Stripe subscription cancel

  • Image: Main Screen (Android)
  • Image: Profile (Android)
  • Image: Main Portal Page (Web)
Azure Cosmos DB — configuration and persistence

Cosmos DB is the system of record. Containers include UserProfiles, SearchRequests, PriceRecords, PriceHistory, PollSettings, AlternativeFlights, TierDefinitions, poll-run analytics, and BillingEvents.

TierDefinitions stay live documents (Free / Basic / Pro): max searches, daily polls, allow-flexible-dates, price, and description. Admin edits take effect on the next API call. Startup seeding creates missing containers and default tiers without overwriting operator changes.

Profiles also store notification flags, registered FCM devices (full app and agent), and Web Push subscriptions.

Subscription tiers and Stripe

Paid upgrades go through Stripe Checkout (web return URLs on bestpriceflights.info; MAUI uses an info.bestpriceflights.app://billing deep link). The Functions webhook updates the Cosmos profile. A “purge billing” admin path cancels Stripe subscriptions before a user delete — and that route, like every other ops/* endpoint, requires a function key.

Quota still applies when saving, reactivating, listing, and during the scheduled poll (oldest excess watches pause). Admin diagnostic polls can bypass the user’s tier so operators can test any provider.

Scheduled price polling

A timer function (plus admin “poll all / poll one / poll one provider”) loads active searches, enforces quotas, archives expired date windows, queries each logical provider, writes history, and raises alerts.

Google and Sky booking URLs are often deferred: the client asks Functions to resolve a token when the user taps “Get booking link”, then the URL is persisted for the next click.

Notifications

Android delivery goes through Azure Notification Hubs and FCM. The agent and full app register separately. Browser users enable Web Push from the portal (VAPID keys on Functions; service worker on the Static Web App origin). Email still uses SMTP and the profile toggle.

Alert copy includes the provider name so a drop is attributable, not anonymous.

Identity

Google ID tokens are validated in Functions middleware (OIDC discovery). MAUI and the agent exchange the auth code on the server (POST /api/auth/google-token, client: "maui") so the Desktop client secret never ships in an APK. The Angular portal uses the Web OAuth client. Refresh stays on Functions as well.

  • Image: Search results page (Web)
  • Image: Flight Options (Web)
  • Image: Alternative flights (Web)

Clients

Web portal (Angular)

Marketing pages, Google sign-in, search list, new search (flexible dates and passengers), results and price history, alternatives, Flight Assistant, profile and Stripe, Web Push, and Android agent download after sign-in. The marketing “Download Android app” button hits Functions and redirects to a blob SAS URL.

Full Android app (MAUI Blazor Hybrid)

Portal-parity: same watches, chat, billing return, FCM. Package info.bestpriceflights.app. Sideload from the site; Play Store is not the current distribution path.

Android agent (BPF Alerts)

Sign in, register for push, group alerts per search. No second search UI. Download is gated on a signed-in portal request so the APK is not a public marketing file.

iPhone

Native app is still Release 2. Until then: web portal and Safari Home Screen / browser notifications.

Admin application

Blazor Server, Interactive Server Components, talking to Cosmos and to keyed Functions ops:

• Tier settings — quotas, flexible-dates flag, pricing text

• Poll schedule and manual / per-provider polls with diagnostics

• Users, searches, price history, booking-link resolve

• Devices — list, test push, re-register, delete Notification Hub installations

• Danger zone — cancel Stripe then delete user data

  • Image: Prices history (Web)
  • Image: Agent (Android)
  • Image: Admin web app - registered devices page

Key tools

Backend — Azure Functions (.NET 10 isolated)

• Microsoft.Azure.Functions.Worker + Http AspNetCore + Timer + Cosmos DB extensions

• Microsoft.Azure.NotificationHubs / FirebaseAdmin — Android push

• Lib.Net.Http.WebPush — browser push

• Stripe.net — Checkout, portal, webhooks, admin purge

• Azure.Storage.Blobs — private APK container and SAS

• Azure.AI.OpenAI / Microsoft.Extensions.AI — Flight Assistant

• Microsoft.IdentityModel.Protocols.OpenIdConnect + JWT — Google tokens

Web — Angular 21

Static Web Apps (West Europe), prerendered marketing routes, SPA portal. Public APKs are not in the SWA artifact.

MAUI

Microsoft.Maui.Controls, BlazorWebView, Xamarin.Firebase.Messaging, embedded appsettings with public Client ID only.

Admin

Microsoft.Azure.Cosmos, HttpClient to Functions with x-functions-key.

Technical summary

Platform — .NET 10 + Angular 21

End-user web — Azure Static Web Apps, bestpriceflights.info

Native — MAUI full app + MAUI agent (Android)

Admin — Blazor Server

API — Azure Functions v4 isolated

Data — Azure Cosmos DB

Auth — Google OAuth (Web + Desktop) with server token exchange

Payments — Stripe (Test)

Alerts — Notification Hubs / FCM, Web Push, email

APKs — private blob + SAS

Monitoring — Application Insights


Best Price Flights is a live, cloud-native fare-watch product: one Google identity across an Angular portal, a MAUI Android app, and a notification agent — backed by Azure Functions, Cosmos DB, and Stripe — built to catch a multi-provider drop in time to book.

Close
An unhandled error has occurred. Reload X