devsnack
Documentation

StayAI documentation

Run it, point it at your own accounts, ship it under your own brand. Written against version 1.0.0 of the download you get on CodeCanyon.

$11 on CodeCanyon
  • Version v1.0.0
  • Updated
  • Platform Flutter
  • Stack Flutter · Firebase
On this page
01

Overview

AI-powered hotel booking & discovery for Flutter — a 3-in-1 bundle: customer app, admin panel, and Cloud Functions backend.

StayAI is a premium Flutter source-code product for buyers who want to launch a branded hotel booking and discovery platform on iOS, Android, and the web. Revenue is generated through affiliate commissions from Booking.com, Travelpayouts, and optionally Hotelbeds — meaning buyers can monetize on day one with zero inventory, zero payment processing, and zero airline/hotel contracts.

The 3-in-1 bundle

  • Customer app — Flutter for iPhone (App Store) and Android (Play Store) from a single codebase.
  • Admin panel — Flutter Web dashboard for content curation, user management, push composer, and AI configuration.
  • Cloud backend — Firebase project with Auth, Firestore, Cloud Functions (Node 22), Storage, Remote Config, FCM, Analytics, Crashlytics, and App Check.

Why StayAI wins

  • AI Travel Intelligence — conversational travel assistant, AI-powered hotel recommendations, AI review summaries (OpenAI + Gemini dual provider with Remote-Config-driven failover).
  • Accessible affiliate monetization — buyers register for Booking.com Affiliate and Travelpayouts (both free), drop in their tokens, and start earning.
  • Firebase-native architecture — zero hosting burden; free tier handles 10,000+ MAU.
Note
iPhone-only on iOS. The customer app ships with TARGETED_DEVICE_FAMILY = "1". iPad listings on App Store Connect are not supported. App Store reviewers expect the listing to match.

What’s included

  • Complete Flutter source for the customer app and admin panel.
  • Cloud Functions source (TypeScript, Node 22) — 18+ callables, scheduled jobs, and Firestore triggers.
  • Firebase configuration: Firestore rules + composite indexes, Storage rules.
  • Brand-asset generator: a small Dart tool that emits a launcher icon and splash logo from a single brand color + monogram letter.
  • Legal templates — Privacy Policy and Terms of Service markdown ready for buyer review and hosting.
  • Deep-links setup guide (AASA + assetlinks.json) under docs/deep-links-setup.md.
  • This documentation (HTML + markdown listing copy).
  • Six months of support + lifetime updates.
02

Features

Customer app

Onboarding
Branded splash, walkthrough, language & currency selection.
Authentication
Email + password, Google, Apple Sign-In (iPhone), anonymous guest, email verification.
Home
Curated featured-hotel rails, featured destinations, AI Recommended-for-you carousel.
Hotel detail
Photo gallery with Hero transition, AI review summary, Plan-Trip date picker, favorite, share, Book on Booking.com via affiliate deep link.
Favorites & folders
Saved tab, user-created folders, swipe-to-delete with undo, trip-date attachment.
AI Travel Assistant
Streaming chat, 20-message memory, context-aware (your search, favorites, home city).
Map view
Google Maps with brand-tinted dark style, hotel pins.
Settings
Theme override (Auto/Light/Dark) with persistence, notification preferences, account deletion.
StayAI Pro
In-app purchase upgrade — mocked by default; RevenueCat-ready.
Submission gates
Force-update, maintenance-mode, COPPA 13+ age gate, FTC affiliate disclosure on first launch.

Admin panel

Dashboard
KPI tiles (users, AI queries, affiliate clicks, commissions), recent affiliate clicks table.
Content CRUD
Featured hotels (with reference price for the price-drop watcher), featured destinations, promotions/banners.
Bulk hotel import
CSV import/export with a downloadable template, row-by-row preview, per-line validation, and batched writes. Blank id creates, filled id updates.
User management
Paginated table, email search, role + disable + GDPR delete.
AI console
Per-feature system prompt editor, model picker (gpt-4o · gpt-4o-mini · gemini-2.5-pro · flash), rate-limit knobs, in-page playground.
App settings
Currencies, languages, affiliate disclosure copy, force-update floors, maintenance-mode toggle, Booking affiliate ID.
Push composer
Title/body/image/segment/deep link/schedule. Live broadcast log streamed from Firestore.

Backend (Cloud Functions)

FunctionTypePurpose
onUserCreate
Auth trigger
Initialize user doc on signup; assign referral code.
onBlogPublish
Firestore trigger
Broadcast FCM topic blog on draft → published transition.
deleteOwnAccount
Callable (caller-side)
Self GDPR delete: fanout favorites/searches/AI chats + Auth user.
mergeAnonymousAccount
Callable
Recover guest data when an anon user upgrades into a credential that already has an account.
lookupDestination
Callable
Destination autocomplete proxy.
generateBookingDeepLink
Callable
Build the Booking.com affiliate URL with caller’s tracking ID.
generateAIAssistantReply
Callable
Streaming AI chat reply; rate-limited per aiPlan.
generateHotelRecommendations
Callable
Personalized 5-hotel suggestion list.
summarizeHotelReviews
Callable
Pros/cons/sentiment JSON; cached 7 days in aiReviewSummaries.
moderateText
Callable
OpenAI Moderation pre-check.
setUserRole
Admin callable
Promote/demote a user (writes Firestore + Auth claims).
setUserDisabled
Admin callable
Disable/enable an account (Auth + Firestore mirror).
deleteUser
Admin callable
Admin GDPR delete with full Firestore fanout.
getRemoteConfigParams
Admin callable
Read RC values (AI rate limits, provider knobs).
updateRemoteConfigParam
Admin callable
Server-side write to RC via the Admin SDK.
sendPushNotification
Admin callable
Multicast push with notificationPrefs filtering; supports immediate or scheduled send.
computeAnalyticsRollup
Cron · daily 00:05 UTC
Roll yesterday’s events into analyticsRollups/{yyyymmdd}.
checkPriceDrops
Cron · every 6h
Diff favorite priceSnapshot vs hotel priceFromUsd; FCM on ≥8% drop.
sendTripReminders
Cron · daily 09:00 (UTC+7)
T-3 / T-1 / T-0 push for favorites with tripDates.
dispatchScheduledPushes
Cron · every 5 min
Drains notificationBroadcasts queued by the admin composer.

Polish

  • Dark mode with persisted user preference + dark Google Maps style.
  • Hero transitions on hotel cards; fade-and-slide staggered list entrance.
  • WCAG AA pass — 48dp tap targets, semantic labels on icon-only buttons, no color-only state communication.
  • iOS APNS-token race fixed; Firebase Analytics bool-coercion guard.

Scaffolded but not wired in v1

The schema accommodates the items below but they don’t ship as feature code. Buyers wanting these should treat them as v1.1 work.

  • Phone OTP auth (PRD §7.2).
  • Referral program (schema fields exist; no UI).
  • In-app notification history feed (push delivers; no inbox screen).
  • Web push (Flutter Web is a no-op for FCM in v1).
  • Compare view (4 hotels side-by-side) — N/A given admin-curated inventory.
  • Localization beyond English (the intl scaffolding is in place; only English copy is filled).
  • Lottie empty states — Material icons used instead.
03

Architecture

Tech stack

LayerTechnologyPin
Mobile framework
Flutter
3.44.0+
Language
Dart
3.12+
State management
Riverpod
3.4.2
Routing
GoRouter
17.5
Models
Freezed + json_serializable
3.2 / 6.14
Networking
cloud_functions
6.3.6
Firebase
firebase_core
4.13.0
Maps
google_maps_flutter
2.18
Deep links
app_links
7.2.1
Cloud Functions
Node
22
firebase-admin / firebase-functions
14.2 / 7.3

Monorepo layout

stayai/
apps/
  customer/             Flutter iPhone + Android
  admin/                Flutter Web dashboard
packages/
  core/                 Pure-Dart models + validation
  data/                 Repositories + typed Function clients
  ui/                   Design tokens + shared widgets
  firebase_services/    FlutterFire abstractions
functions/              Cloud Functions (TypeScript, Node 22)
firebase/
  firestore.rules
  firestore.indexes.json
  storage.rules
  hosting/
    .well-known/        AASA + assetlinks templates
scripts/                Branding + license generators
docs/
  codecanyon/           This documentation
  legal/                Privacy + Terms templates
  deep-links-setup.md   Universal/App Links buyer guide

Per-app folder convention (feature-first MVVM)

apps/customer/lib/
apps/customer/lib/
  app.dart              Root widget + system gates
  router.dart           GoRouter configuration
  features/
    onboarding/
    auth/
    home/
    search/
    hotel_detail/
    favorites/
    ai_assistant/
    profile/
    settings/
    system/             Force-update + maintenance gates
  router/
    deep_links.dart     Unified Universal-/App-Link/FCM tap pipe
  l10n/                 ARB files (EN populated)
04

Firestore collections and indexes

users/{uid}
Profile, role, AI plan, notification prefs, FCM tokens.
users/{uid}/aiUsage/{yyyymmdd}
Per-day AI rate-limit counter.
favorites/{uid}/items/{hotelId}
Saved hotel snapshot, optional tripDates.
favoriteFolders/{uid}/folders/{id}
User-created folders.
searches/{uid}/history/{searchId}
Search history.
aiConversations/{uid}/chats/{chatId}
Travel-assistant chat history.
featuredDestinations/{id}, featuredHotels/{id}, promotions/{id}, blogs/{id}
Public read; admin write. featuredHotels.priceFromUsd drives the price-drop watcher.
aiReviewSummaries/{hotelId}
Cached AI review summary (7-day TTL).
affiliateClicks/{id}
Server-only writes; admin read.
notificationBroadcasts/{id}
Push composer audit log + scheduled queue.
analyticsRollups/{yyyymmdd}
Daily counter rollups.
appSettings/global · appSettings/aiConfig
Public-read, admin-write configuration.

Composite indexes

Six composite indexes ship in firebase/firestore.indexes.json: featured destinations / hotels / promotions ordered by active + display order; favorite items by folderId + addedAt; published blogs ordered by publishedAt; users by disabled + createdAt; notification broadcasts by status + sendAt for the dispatcher cron.

05

Firebase setup

  1. 01
    Create a Firebase project at console.firebase.google.com.
  2. 02
    Enable: Authentication, Firestore, Cloud Functions, Storage, Remote Config, Cloud Messaging, Analytics, Crashlytics, App Check.
  3. 03
    Enable Auth providers: Email/Password, Google, Apple, Anonymous.
  4. 04
    From apps/customer/ run flutterfire configure against your project; repeat in apps/admin/.
  5. 05

    Fill functions/.env using the .env.example template:

    functions/.env
    OPENAI_API_KEY=...
    GEMINI_API_KEY=...
    BOOKING_AFFILIATE_ID=...
    TRAVELPAYOUTS_TOKEN=...
    TRAVELPAYOUTS_MARKER=...
    AI_FREE_DAILY_LIMIT=10
    AI_PRO_DAILY_LIMIT=200
  6. 06

    Deploy backend:

    terminal
    firebase deploy --only firestore:rules,firestore:indexes,functions,storage:rules

    First Cloud Functions deploy may fail with an Eventarc 400; wait 5–10 minutes and re-run.

  7. 07
    Promote yourself to admin: edit users/{your-uid}.role = 'admin' in the Firebase console. Reload the admin app; you should see the full sidebar.
06

App Check

App Check posture differs by endpoint:

  • AI endpoint — functions/src/ai/generate_ai_assistant_reply.ts ships with enforceAppCheck: true. The streaming chat burns OpenAI / Gemini budget on every call, so it must reject unattested clients out of the box. You cannot deploy until App Check providers are registered or legitimate calls will be rejected.
  • Open callables — functions/src/common/callable.ts defaults to enforceAppCheck: false. These callables (hotel search, lookup, blog reads, logAffiliateClick) are designed to work for guest/anonymous users before sign-in and don’t spend rate-limited budget. Flip the default to true if you want defence-in-depth on every callable.
Warning
Before deploying: register DeviceCheck (iOS), Play Integrity (Android), and reCAPTCHA Enterprise (Web) providers in the Firebase Console, add a debug token for your simulator, then run firebase deploy --only functions.
07

Affiliate setup

ProviderAccessWhere the token goes
Booking.com Affiliate Partner Program
Free signup at booking.com/affiliate-program
BOOKING_AFFILIATE_ID in functions/.env + Firebase Remote Config booking_com_affiliate_id for the client to embed in deep links.
Travelpayouts (Hotellook)
Free signup at travelpayouts.com
TRAVELPAYOUTS_TOKEN + TRAVELPAYOUTS_MARKER in functions/.env.
Hotelbeds APITUDE
Contract required (slow)
Optional. HOTELBEDS_API_KEY + HOTELBEDS_SECRET. Gated behind an admin toggle.
Note
FTC + Apple/Google disclosure. The customer app surfaces the affiliate disclosure on first launch (modal) and inside Settings → About. The copy lives in packages/core/lib/src/legal/disclosures.dart — edit it in one place and both surfaces update.
08

AI provider setup

Two providers ship behind a common AIProvider abstraction. Active provider per feature is selected by Firebase Remote Config so admins can hot-swap without a deploy:

ai_provider_primary
openai
ai_provider_fallback
gemini
ai_model_assistant
gpt-4o
ai_model_recommendation
gpt-4o-mini
ai_model_review_summary
gpt-4o-mini

Per-feature rate limits

TierPer dayQuota source
Free
10 queries
AI_FREE_DAILY_LIMIT env var
Pro
200 queries
AI_PRO_DAILY_LIMIT env var

Enforcement runs in functions/src/ai/shared/rate_limit.ts. The function reads users/{uid}.aiPlan, picks the matching limit, and increments users/{uid}/aiUsage/{yyyymmdd}.count atomically. Over-limit calls throw resource-exhausted; the Dart client surfaces this as AiException.quotaExceeded.

Cost guidance

At gpt-4o-mini rates and average 600-token round-trips, 200 queries/day costs roughly $0.20 per Pro user / month. A Pro subscription priced at $4.99/month recovers that with margin.

09

In-app purchase (Pro)

The Pro upgrade ships as a working demo via MockPurchaseService: tapping Upgrade flips users/{uid}.aiPlan directly so you can show the flow end-to-end without a RevenueCat account, App Store Connect setup, or Play Console setup.

Warning
This is mock-only. Real billing requires the steps below. The mock leaves a documented swap point so the activation is mechanical.

Activation steps

  1. 01
    Create a RevenueCat account at app.revenuecat.com.
  2. 02
    Add an iOS + Android app under one project.
  3. 03
    Create a pro entitlement and an offering with a single monthly product, id stayai_pro_monthly.
  4. 04
    Configure App Store Connect + Google Play Console with the same product id.
  5. 05

    Add to apps/customer/pubspec.yaml:

    apps/customer/pubspec.yaml
    dependencies:
      purchases_flutter: ^8.0.0
  6. 06
    Implement packages/firebase_services/lib/src/purchases/revenuecat_purchase_service.dart — the file is stubbed with the exact Purchases SDK calls in comments.
  7. 07

    Flip the toggle in packages/firebase_services/lib/src/purchases/purchase_service.dart:

    purchase_service.dart
    const bool kEnableRevenueCat = true;
  8. 08
    Add a Cloud Function on the RevenueCat webhook (revenuecat.com/docs/webhooks) that flips users/{uid}.aiPlan on INITIAL_PURCHASE, RENEWAL, CANCELLATION, and EXPIRATION events.
10

Notifications

iOS prerequisites

  1. 01
    Generate an APNs Auth Key in App Store Connect → Keys.
  2. 02
    Upload the .p8 + Key ID + Team ID to Firebase Console → Project settings → Cloud Messaging.
  3. 03
    Run the customer app on a real iPhone — APNs tokens never arrive on simulators.

Topics and targets

TypeTopic / targetSender
Promotions / deals
topic deals
Admin push composer
Blog posts
topic blog
onBlogPublish trigger
Price drops
per-user fcmTokens
checkPriceDrops cron
Trip reminders
per-user fcmTokens
sendTripReminders cron
Manual broadcast
per-user multicast
Admin push composer

Per-user opt-in

The user doc carries notificationPrefs.<type> (deals, priceDrops, blog, tripReminders). The customer Settings → Notification preferences screen toggles each. sendPushNotification filters on notificationPrefs[type] !== false; missing or true means opted-in.

12

Branding and customization

Theme tokens (one-file rebrand)

Edit packages/ui/lib/src/theme/tokens.dart — every color, radius, spacing unit, and elevation flows from this file. Hot-restart the apps and every screen updates.

Launcher icon

  1. 01
    Drop a 1024×1024 RGBA PNG (transparent background, mark with ≥10% safe-area padding) at apps/<app>/assets/branding/launcher_icon.png.
  2. 02

    From apps/<app>/:

    terminal
    flutter pub get
    dart run flutter_launcher_icons
  3. 03
    Commit the regenerated platform files.

Splash screen

  1. 01
    Drop a 512×512 RGBA PNG at apps/customer/assets/branding/splash_logo.png.
  2. 02
    Optionally edit the flutter_native_splash: block in apps/customer/pubspec.yaml (background color etc.).
  3. 03
    Run dart run flutter_native_splash:create.

App name and bundle id

iOS bundle id
apps/customer/ios/Runner.xcodeproj/project.pbxproj (search PRODUCT_BUNDLE_IDENTIFIER)
Android applicationId
apps/customer/android/app/build.gradle.kts
Display name (iOS)
apps/customer/ios/Runner/Info.plist CFBundleDisplayName
Display name (Android)
apps/customer/android/app/src/main/AndroidManifest.xml android:label
Web manifest
apps/customer/web/manifest.json + index.html <title>

Adding a language

The customer app uses flutter_localizations + intl with ARB files in apps/customer/lib/l10n/. PRD §7.1 promises 10 languages; v1 ships English populated. To add a locale, copy intl_en.arb to intl_<code>.arb, translate the values, then add the locale to the onboarding language selector (features/onboarding/language_selector_screen.dart).

13

Production checklist

  1. 01
    Replace assets/branding/launcher_icon.png + splash_logo.png with your artwork.
  2. 02
    Edit packages/ui/lib/src/theme/tokens.dart brand colors.
  3. 03
    From apps/customer/: flutter pub get dart run flutter_launcher_icons dart run flutter_native_splash:create. Repeat in apps/admin/ (skip splash for admin).
  4. 04
    flutterfire configure against your Firebase project for both apps.
  5. 05
    Fill apps/customer/.env + functions/.env.
  6. 06
    terminal
    firebase deploy --only firestore:rules,firestore:indexes,functions,storage:rules
  7. 07

    Build & deploy admin Web:

    terminal
    cd apps/admin && flutter build web --release && cd ../..
    firebase deploy --only hosting:admin
  8. 08
    Promote yourself: users/{your-uid}.role = 'admin' in Firestore.
  9. 09
    Seed initial featured destinations + hotels via the admin panel. Set each hotel’s Reference price (USD/night) if you want price-drop alerts to fire.
  10. 10
    Register App Check providers (DeviceCheck / Play Integrity / reCAPTCHA Enterprise) — required, because the AI endpoint enforces App Check by default. Optionally flip enforceAppCheck: true in functions/src/common/callable.ts to tighten the open callables too. Register debug tokens for your simulators.
  11. 11
    Render docs/legal/privacy_policy.md + terms_of_service.md to HTML and host them. Update URLs in apps/customer/lib/features/profile/about_screen.dart.
  12. 12
    (Optional) Activate RevenueCat per In-app purchase (Pro) above.
  13. 13
    (Optional) Wire AASA + assetlinks per docs/deep-links-setup.md for Universal/App Links.
  14. 14
    Generate LICENSES.md: bash scripts/generate_licenses.sh.
  15. 15

    Build release artifacts:

    terminal
    # Android
    flutter build appbundle --release   # AAB for Play Console
    flutter build apk --release         # APK for ad-hoc distribution
    
    # iOS — open Xcode, archive, upload to App Store Connect
    flutter build ios --release
14

Code signing and store submission

Android — release signing

Flutter ships with a debug keystore that won’t be accepted by the Play Console. Generate a release keystore once, then wire it into the Gradle build.

  1. 01

    From apps/customer/android/:

    terminal
    keytool -genkey -v -keystore ~/keys/stayai-release.jks \
      -keyalg RSA -keysize 2048 -validity 10000 \
      -alias stayai

    Pick a strong password and store the keystore in a backup-safe location outside the repo — losing it means losing the ability to publish updates to the same Play listing.

  2. 02

    Create apps/customer/android/key.properties (gitignored — confirm it appears in .gitignore):

    apps/customer/android/key.properties
    storeFile=/Users/<you>/keys/stayai-release.jks
    storePassword=<keystore-password>
    keyAlias=stayai
    keyPassword=<key-password>
  3. 03

    Edit apps/customer/android/app/build.gradle.kts — load the properties at the top of the file and reference them in the signingConfigs block. Then point buildTypes.release at the new config:

    apps/customer/android/app/build.gradle.kts
    val keystoreProperties = Properties().apply {
        val f = rootProject.file("key.properties")
        if (f.exists()) f.inputStream().use { load(it) }
    }
    
    android {
      signingConfigs {
        create("release") {
          keyAlias       = keystoreProperties["keyAlias"] as String
          keyPassword    = keystoreProperties["keyPassword"] as String
          storeFile      = file(keystoreProperties["storeFile"] as String)
          storePassword  = keystoreProperties["storePassword"] as String
        }
      }
      buildTypes {
        release { signingConfig = signingConfigs.getByName("release") }
      }
    }
  4. 04

    Build a signed bundle for the Play Console:

    terminal
    flutter build appbundle --release

    The signed AAB lands at build/app/outputs/bundle/release/app-release.aab.

  5. 05

    Get your release SHA-256 fingerprint (used for App Links assetlinks.json and Google Sign-In):

    terminal
    keytool -list -v -keystore ~/keys/stayai-release.jks -alias stayai \
      | grep SHA256

    Add the fingerprint to Firebase Console → Project settings → Your Android app → Add fingerprint. Re-download google-services.json after.

  6. 06
    Play Console → Create app → upload the AAB. Fill the Data Safety form, content rating, and pricing. Internal testing first; promote to production once you have ≥ 12 testers and a clean review.
Note
Play Integrity API. Once App Check is flipped on (per App Check), Play Integrity replaces the SafetyNet attestation that Firebase used to require. It auto-attaches via the firebase_app_check SDK once you enable Play Integrity on the Firebase Console; no extra Android code.

iOS — Apple signing and Xcode archive

  1. 01
    Apple Developer Program enrollment ($99/yr). The customer bundle id com.devsnack.stayai must be registered as an App ID under your team. Also enable: Sign In with Apple, Push Notifications, Associated Domains.
  2. 02
    Create an App Store Connect app record matching the same bundle id. Submit a privacy policy URL (the rendered docs/legal/privacy_policy.md) under App Information.
  3. 03

    In Xcode, open apps/customer/ios/Runner.xcworkspace (always the workspace, not the project). Runner target → Signing & Capabilities:

    • Team — your Apple Developer team.
    • Bundle Identifier — com.devsnack.stayai.
    • Toggle Automatically manage signing on for development; switch to manual + an App Store distribution provisioning profile for the archive build.
    • Capabilities — confirm Push Notifications, Sign in with Apple, and Associated Domains (applinks:<your-domain>) are present.
  4. 04
    Generate an APNs Auth Key in App Store Connect → Keys → “+”. Download the .p8 file (one-time download). Upload it + the Key ID + your Team ID to Firebase Console → Project settings → Cloud Messaging → Apple app → APNs Authentication Key. This unlocks production push delivery.
  5. 05

    Build for upload:

    terminal
    flutter build ipa --release

    or in Xcode: Product → Archive. Pick the archive → Distribute App → App Store Connect → Upload.

  6. 06
    App Store Connect → TestFlight → wait for processing (≈ 30 min) → invite testers. Submit the same build for App Review once TestFlight passes your sanity checks.
15

Sign in with Apple and Google

Sign in with Apple — special configuration

Apple Sign-In on iOS requires two places to match:

  • Xcode → Signing & Capabilities → Sign In with Apple capability is added (the entitlement file already declares it).
  • Apple Developer portal → Identifiers → your App ID → Sign In with Apple is enabled.
  • Firebase Console → Authentication → Sign-in method → Apple is enabled. No client id / secret is required for native iOS — Firebase trusts the Apple identity token directly.
Note
Hidden-email relay. When users pick “Hide my email”, Apple gives you a relay address. The customer app stores it as-is; account deletion still works. If you later add Apple Sign-In on the web (out of scope for v1), you also need to register a Services ID and a domain-verified return URL.

Sign in with Google — Android signing fingerprint

Google Sign-In on Android cross-checks the calling app’s signing certificate. After you set up the release keystore (above), grab both SHA-1 and SHA-256 fingerprints:

terminal
keytool -list -v -keystore ~/keys/stayai-release.jks -alias stayai

Drop both into Firebase Console → Project settings → Your Android app → Add fingerprint. Re-download google-services.json and replace apps/customer/android/app/google-services.json. Without this step, the production build silently fails Google Sign-In with an internal error.

16

Changelog

Every version published so far. Updates are free for the life of the item.

v1.0.0
Initial release — customer app, admin panel, 18+ Cloud Functions, scheduled crons, Firestore rules and indexes, submission gates, RevenueCat-ready IAP.

Version 1.1.0 · August 2026Feature update

Added — bulk hotel import (CSV)

  • New Import CSV and Export CSV actions on the admin Hotels screen. Load a whole catalog from a spreadsheet instead of entering hotels one at a time.
  • Downloadable template with every supported column pre-filled.
  • Headers are matched by name, not position, and are case- and punctuation-insensitive — Booking Hotel ID, booking_hotel_id, and bookingHotelId all resolve to the same field. Unrecognised columns are reported back rather than silently dropped.
  • Row-by-row preview before anything is written, showing per-line New / Update / Skipped status and the exact validation error with its source line number. Invalid rows are skipped; the rest of the file still imports.
  • Blank id creates a hotel; a populated id updates one — so export → edit in Excel or Sheets → re-import round-trips without duplicating your catalog.
  • Writes are batched at 450 documents per commit, with live progress, so multi-thousand-row files import in one pass.
  • Exports carry a UTF-8 BOM so accented hotel names open correctly in Excel on Windows.

Added — one-command seeding

  • node scripts/seed_all.mjs now populates roughly 250 demo documents across all 13 collections in dependency order. --with-auth also creates sign-in-able demo accounts; --wipe removes only what the seeders wrote.

Changed — dependencies

  • Upgraded across the board to current majors: Riverpod 3.4, GoRouter 17.5, Freezed 3.2 + json_serializable 6.14, firebase_core 4.13, app_links 7.2, google_maps_flutter 2.18, and firebase-admin 14.2 / firebase-functions 7.3 on Node 22.

Version 1.0.0+1 · April 2026Initial release

Customer app

  • Onboarding (splash + walkthrough + language & currency).
  • Auth: email/password, Google, Apple Sign-In (iPhone), anonymous.
  • Home with curated rails, AI Recommended-for-you carousel.
  • Hotel detail with Hero-paired gallery, AI review summary, Plan-Trip date picker, favorite, share, Booking.com deep link.
  • Favorites with folders, swipe-to-delete + undo.
  • AI Travel Assistant with streaming responses + 20-message memory.
  • Map view with brand-tinted dark Google Maps style.
  • Settings: theme override (Auto/Light/Dark) with persistence, notification preferences, account deletion (Apple 5.1.1 compliant).
  • StayAI Pro paywall — mocked by default, RevenueCat-ready stub.

Admin panel

  • Dashboard with KPI tiles + recent affiliate clicks.
  • CRUD for hotels (with reference price), destinations, promotions.
  • User management with paginated table, role + disable + delete.
  • AI console with system prompt editor + model picker + playground.
  • Push composer (immediate or scheduled) + live broadcast log.
  • App settings editor (force update, maintenance, AI rate limits).

Backend

  • 18+ Cloud Functions: callables (search, AI, push, deleteOwnAccount, mergeAnonymousAccount), scheduled crons (price drops, trip reminders, push dispatcher, analytics rollup), Firestore triggers (onUserCreate, onBlogPublish).
  • Production-grade Firestore rules + 7 composite indexes.
  • Storage rules with admin-only writes on featured content.

Submission and compliance

  • Force-update gate, maintenance-mode gate, COPPA 13+ age gate, FTC affiliate disclosure modal, GDPR self-delete, anonymous-account merge.
  • iPhone-only target (TARGETED_DEVICE_FAMILY = "1").

Polish

  • Dark mode with persisted user preference, dark map style.
  • Hero transitions, staggered list entrance.
  • WCAG AA pass — 48dp tap targets, semantic labels, no color-only state.
  • Three production crash fixes folded in (APNS race, Analytics bool coercion, modal Navigator timing).

Lifetime free updates ship through CodeCanyon. Major releases (v1.x → v2) will keep upgrade notes in this section.

17

Support and licensing

Support covers setup help, bug reports, deployment troubleshooting, and clarifications about what the source does and how to extend it.

Response SLA: within 48 hours, business days (Mon–Fri, GMT+7).

Term: six months from the date of purchase, per CodeCanyon standard. Lifetime free updates regardless of support window.

Out of scope: custom feature work, third-party API account issues (Booking.com, Travelpayouts, OpenAI), Apple/Google review disputes, hosting administration on the buyer’s Firebase project.

What support covers

  • Six months of support from purchase, extendable at checkout
  • Support covers bugs in the template and questions about how it is put together
  • It does not cover custom feature work, third-party API changes or store review outcomes
  • The Regular licence covers one free end product. Charging users for the app itself needs the Extended licence