Gromart is a complete multi-store grocery ordering and delivery platform. Customers browse nearby stores, order items for delivery or takeaway, and track the driver live on a map. This document covers the complete setup of the Admin Panel, the Store (Vendor) Panel, the Website (Customer) Panel and the three mobile applications.
Gromart is a modern, scalable grocery-delivery marketplace. Stores publish their menus, customers order for delivery or takeaway, and drivers pick up and deliver each order while everyone follows the progress in real time. The platform is built on secure, modern technologies:
All clients (Admin, Store, Website and Mobile) share a single Firebase project, so data stays consistent in real time across the whole platform.
Important — third-party services & costs: Gromart integrates several external services operated by third parties that may incur their own charges, separate from this product:
You are responsible for creating and funding these third-party accounts. Please review each provider's current pricing before going live.
Gromart ships as six applications — three Flutter mobile apps (the User App, the Store App and the Delivery Man / Driver App) and three Laravel web panels (the Admin Panel, the Store Panel and the Website Panel) — all powered by Google Firebase. What you need depends on which part you are setting up.
Full steps are in App Documentation → Setting up Flutter.
mod_rewrite.Full server details are in Web Documentation → Server Requirement.
Gromart includes three Flutter (Dart) applications — the User App, the Store App and the Delivery Man / Driver App. Each is a separate project with its own source folder, package name and store listing, but they share one Firebase project and the setup below is identical for all three: install Flutter once, then repeat sections 4–10 for each app. The official reference is docs.flutter.dev, but you should not need it to complete this guide.
C:\src\flutter
on Windows, or ~/development/flutter on macOS/Linux.flutter/bin folder to your system PATH so the flutter
command works from any terminal.flutter doctor --android-licenses.sudo xcodebuild -runFirstLaunch, then install CocoaPods with
sudo gem install cocoapods.Run the doctor command and fix anything it reports:
flutter doctor -v
Every item that matters for your target platform should show a green check.
Unzip the application source, open the folder in your IDE, and fetch the dependencies:
flutter pub get
Start an emulator/simulator or connect a physical device, then run:
flutter devices flutter run
Hot reload (r in the terminal) applies code changes instantly while the app keeps running.
The package name (Android) and bundle identifier (iOS) uniquely identify an app on the stores. Use reverse-domain form, and give each of the three apps its own identifier — they are published as three separate listings and registered as three separate apps in Firebase:
com.yourcompany.gromart ← User App com.yourcompany.gromart.store ← Store App com.yourcompany.gromart.driver ← Delivery Man / Driver App
Change the identifier before registering the app in Firebase, because the Firebase config files are tied to it. The steps below apply to one app — repeat them in each of the three project folders.
On current Flutter versions the identifier lives in android/app/build.gradle, not in the
manifest. Open that file and change both values:
android {
namespace = "com.yourcompany.gromart"
...
defaultConfig {
applicationId = "com.yourcompany.gromart"
...
}
}
namespace — the package the generated R class and
MainActivity belong to.applicationId — the identifier Google Play and Firebase key off. This is the one
that must match your google-services.json.package attribute of the
<manifest> tag in
android/app/src/main/AndroidManifest.xml (and in the debug /
profile variants) — update it there too if your copy still has it.
Rename the folders under android/app/src/main/kotlin/ (or .../java/) to match the
new package, and update the package declaration at the top of MainActivity.kt /
MainActivity.java.
ios/Runner.xcworkspace in Xcode.
Tip: After renaming, run flutter clean followed by
flutter pub get, and download fresh google-services.json /
GoogleService-Info.plist files for the new identifiers (see
Firebase Setup (Mobile)).
Start from a single square PNG at 1024×1024, with no transparency for iOS.
ic_launcher.png inside each
android/app/src/main/res/mipmap-* folder (mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi), keeping
the same file names and sizes.AppIcon image set in
ios/Runner/Assets.xcassets (open the project in Xcode and drop the sizes in).Generating every size by hand is error-prone. Add the helper package to pubspec.yaml:
dev_dependencies: flutter_launcher_icons: ^0.13.1 flutter_launcher_icons: android: "launcher_icon" ios: true image_path: "assets/icon/icon.png"
Then generate all sizes at once:
flutter pub get dart run flutter_launcher_icons
This is the name shown under the launcher icon on the device.
Open android/app/src/main/AndroidManifest.xml, find the <application> tag
and change android:label:
<application android:label="Your App Name" ... >
Open ios/Runner/Info.plist and set both display keys:
<key>CFBundleDisplayName</key> <string>Your App Name</string> <key>CFBundleName</key> <string>Your App Name</string>
The apps use Google Maps for store locations, address selection and live driver tracking. Create the key first — see Create Google Map API Key — then add it to both platforms.
Open android/app/src/main/AndroidManifest.xml and set the key inside the
<application> element:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY_HERE"/>
Open ios/Runner/AppDelegate.swift and set the key in
didFinishLaunchingWithOptions:
GMSServices.provideAPIKey("YOUR_API_KEY_HERE")
Rebuild the app after changing the key on either platform.
All three apps — User, Store and Driver — talk to the same Firebase project as the web panels. Create the project first — see Create Firebase Project — then register every app inside it.
Six registrations, one project. Each app needs its own Android and iOS
entry, so a full Gromart setup registers six apps in the one Firebase project:
User (Android + iOS), Store (Android + iOS) and Driver (Android + iOS). Each produces its own
google-services.json / GoogleService-Info.plist — keep them
straight, as they are not interchangeable.
Repeat this for each of the three apps:
google-services.json.GoogleService-Info.plist. iOS does not need SHA fingerprints.
google-services.json in android/app/.ios/Runner.xcworkspace in Xcode and drag
GoogleService-Info.plist into the Runner group (tick "Copy items if
needed").
Instead of downloading the files by hand, you can let the FlutterFire CLI generate
lib/firebase_options.dart for both platforms:
npm install -g firebase-tools firebase login dart pub global activate flutterfire_cli flutterfire configure --project=YOUR_PROJECT_ID
The CLI writes a firebase_options.dart holding the per-platform configuration, which
main.dart passes to Firebase.initializeApp():
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(MyApp());
}
The Firebase plugins the app uses are already declared in pubspec.yaml — Core, Auth,
Firestore, Storage and Messaging. Just fetch them:
flutter pub get
dependencies:
flutter:
sdk: flutter
firebase_core: ^3.6.0
firebase_auth: ^5.3.1
cloud_firestore: ^5.4.4
firebase_storage: ^12.3.4
firebase_messaging: ^15.1.3
In the Firebase console open Authentication → Sign-in method and enable the providers Gromart uses:
Then open Authentication → Settings → Authorized domains and add the domains of your Website Panel and Store Panel.
SHA fingerprints are required for Google Sign-In and Phone (OTP) authentication on Android. Add the fingerprints for both your debug keystore (for testing) and your release keystore (for the Play Store build).
From the android folder of the project:
# macOS / Linux ./gradlew signingReport # Windows gradlew signingReport
The report lists the SHA-1 and SHA-256 values for each build variant.
# Debug keystore keytool -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore # Password: android # Release keystore keytool -list -v -keystore path/to/your-release-key.jks -alias your-key-alias
google-services.json and replace the one in
android/app/.The User App, Store App and Delivery Man / Driver App are published as three independent listings on each store, so this section runs three times — once per app. You can sign all three with the same keystore; only the package name, the store listing and the artwork differ.
Android release builds must be signed with your own keystore. Create it once and keep it safe — if you lose it you cannot update the app on Google Play.
keytool -genkey -v -keystore ~/my-release-key.jks -keyalg RSA \
-keysize 2048 -validity 10000 -alias my-key-alias
-keystore — where the .jks file is written.-keyalg RSA, -keysize 2048 — the signing algorithm and key length.-validity 10000 — validity in days (about 27 years).-alias — the name of the key inside the keystore.You are prompted for a keystore password, a key password and your organisation details.
Create android/key.properties (never commit this file):
storePassword=your-store-password keyPassword=your-key-password keyAlias=my-key-alias storeFile=/Users/yourusername/my-release-key.jks
Gradle reads this file at build time, so the keystore password never lives in the tracked
sources. Add android/key.properties and the .jks file itself to
.gitignore.
# Android App Bundle (required by Google Play) flutter build appbundle --release # Android APK (for direct distribution / testing) flutter build apk --release # iOS (macOS only) flutter build ipa --release
Outputs are written to build/app/outputs/ (Android) and build/ios/ipa/ (iOS).
.aab to a Production (or Internal Testing) release.version: in pubspec.yaml for every new upload — Play
rejects duplicate version codes..ipa with Transporter).Note: Apple requires Sign in with Apple on any iOS app that offers third-party sign-in such as Google. Enable the Apple provider in Firebase Authentication and add the capability in Xcode before submitting.
This Android build error means the google-services.json file is missing, in the wrong place,
or does not match the package name.
google-services.json must sit in
android/app/ — not in android/ and not in the project root.package_name inside the JSON must match the
applicationId in android/app/build.gradle. If you renamed the package,
register the new one in Firebase and download the file again.com.google.gms:google-services classpath in android/build.gradle is a
current version.flutter clean flutter pub get flutter run
All three web panels — Admin, Store and Website — are Laravel 12 applications and share the same server requirements. To run Gromart smoothly your hosting must provide:
mod_rewrite) or NGINX.BCMath,
Ctype, cURL, DOM, Fileinfo, JSON,
Mbstring, OpenSSL, PCRE, PDO,
Tokenizer, XML, GD (or Imagick) and ZIP.Each panel needs its own domain or subdomain, for example:
https://yourdomain.com — Website (Customer) Panelhttps://admin.yourdomain.com — Admin Panelhttps://store.yourdomain.com — Store (Vendor) PanelNode.js is needed for the Firebase CLI (indexes, rules and Cloud Functions), for the Firestore import/export utility, and for the scheduled scripts the Admin Panel executes.
Download the LTS installer from nodejs.org/en/download and run it. npm is installed alongside Node. Verify:
node -v npm -v
The Admin Panel needs the absolute path to the Node executable so its scheduled commands can run. On Linux/macOS:
which node
On Windows:
where node
The command prints something like /usr/local/bin/node. Copy that value into the
NODE_PATH variable of the Admin Panel's .env file.
[user@server ~]$ which node /usr/local/bin/node
Copy that path into the Admin Panel's .env:
NODE_PATH='/usr/local/bin/node'
npm install -g firebase-tools firebase login
firebase login opens a browser window; sign in with the Google account that owns your
Firebase project.
Firebase is the backend for the whole platform. Create one project and use it for the Admin Panel, the Store Panel, the Website Panel and every mobile app.
14.1. Go to firebase.google.com and click Go to console in the top-right corner.
14.2. Click Add project, enter a project name and click Continue. Choose whether to enable Google Analytics, select the default account for Firebase, then click Create project.
14.3. From the project overview, click the Web (</>)
icon to register a web app.
14.4. Enter an app nickname and click Register app. Scroll down to the
configuration snippet and copy the values — these are the FIREBASE_* credentials you
will paste into every panel's .env file.
The same values can be read again at any time from Project Settings → General → Your apps:
14.5. In the left sidebar open Firestore Database and click Create database. Pick a location close to your users and start in the mode you prefer — the rules are replaced in step 14.9.
14.6. Open Realtime Database and click Create Database. It is used for live driver location tracking.
14.7. Open Storage and click Get started. Firebase Storage holds all uploaded images — store and item photos, banners, avatars and verification documents.
14.8. Open Authentication → Sign-in method and enable Email/Password, Phone, Google and, for iOS, Apple. Under Authentication → Settings → Authorized domains, add your website and store-panel domains.
14.9. Open Firestore Database → Rules, replace the contents with the rules below, and click Publish.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
Apply the same rules under Storage → Rules so image uploads work. If a screen
later fails to load data with a PERMISSION_DENIED message, check that these rules were
published — see Troubleshooting.
14.10. Upgrade the project to the Blaze (pay-as-you-go) plan. Cloud Functions and any outbound call to a payment gateway require it.
14.11. Generate the service account key. Open
Project Settings → Service accounts, choose Node.js and click
Generate new private key. A JSON file downloads — you will use it for the Admin
Panel (storage/app/firebase/credentials.json), for push notifications, and for the
import/export utility.
Keep the service-account file private. It grants full administrative access to your
Firebase project. Never commit it to Git and never place it inside a web-accessible folder such as
public/.
The product ships with a demo dataset (settings, categories, currencies, email templates, CMS pages and sample stores). Import it so the panels start with working defaults instead of empty screens.
15.1. Install Node.js if you have not already — see Installing Node.js & npm.
15.2. Unzip the source file named "Firebase Import Export Collections.zip".
15.3. Create your Firebase project first if you have not — see Create Firebase Project.
15.4. Replace credentials.json in the extracted folder with the
service account key you generated in step 14.11.
15.5. Open a terminal in the extracted folder. On Windows, hold Ctrl+Shift, right-click inside the folder and choose "Open PowerShell window here".
15.6. Install the dependencies, then run the import or export command:
# Import every collection into your Firestore npx -p node-firestore-import-export firestore-import -a credentials.json -b collections.json # Export every collection out of your Firestore npx -p node-firestore-import-export firestore-export -a credentials.json -b collections.json
The export writes a collections.json file you can keep as a backup.
Please note: Before running either command, make sure credentials.json
holds the service-account key of the correct Firebase project. Importing into the wrong
project, or importing twice, can overwrite existing documents.
Firestore needs a composite index for every query that filters and sorts on more than one field. Gromart uses many of these (nearby stores, order lists, reports), so deploy the supplied index definitions before going live — otherwise those screens fail with "The query requires an index".
16.1. Install Node.js — see Installing Node.js & npm.
16.2. Unzip the source file named "Firebase Indexing.zip".
16.3. Open a terminal in the extracted folder (Ctrl+Shift + right-click → "Open PowerShell window here" on Windows).
16.4. Log in to Firebase, if you have not already:
firebase login
16.5. Initialise Firestore configuration in the folder:
firebase init
16.6. Confirm with Y and press Enter.
16.7. Choose Firestore: Configure security rules and index files for Firestore.
Use the arrow keys to move between options and the space bar to select one, then press Enter to confirm.
16.8. Choose Use an existing project.
16.9. Select your Gromart project.
16.10. Press Enter to accept the default rules file name
(firestore.rules).
16.11. Press Enter to accept the default index file name
(firestore.indexes.json).
16.12. Open the generated firestore.indexes.json, delete its contents, and
paste in everything from the supplied firestore_indexes.json file.
16.13. Deploy the indexes:
firebase deploy --only firestore:indexes
Building the indexes takes a few minutes. Watch the progress in Firebase Console → Firestore Database → Indexes and wait until every index shows Enabled.
The Cloud Functions Gromart needs are already written — you only have to deploy them to your own Firebase project. The package deploys two functions:
deliveryDispatch — a Firestore trigger on the restaurant_orders
collection. It runs whenever an order is written and drives the driver-assignment flow, following
the broadcast or sequential mode you chose under Settings → Feature Settings.deleteUser — a callable function that removes a user from Firebase
Authentication when an account is deleted from a panel or app.Without this package deployed, orders are created but never dispatched to a driver, so deploy it before going live.
Before you deploy — enable the required Google Cloud APIs. Cloud Functions
(2nd generation) build on Cloud Build, store images in Artifact
Registry and run on Cloud Run using your project's default compute
service account. On a new Firebase project the Compute Engine API is usually not enabled
yet, so the deploy fails for most functions with
"Compute Engine API has not been used…" or
"Unknown service account" (typically only one function deploys and the rest error). In
Google Cloud Console → APIs & Services → Enabled APIs & services,
make sure all of the following are enabled — enable any that are missing
(Compute Engine is the one most often turned off):
Or enable them all at once with the CLI, then wait 3–5 minutes for the change to propagate before deploying:
gcloud services enable compute.googleapis.com cloudbuild.googleapis.com \ artifactregistry.googleapis.com run.googleapis.com eventarc.googleapis.com \ cloudfunctions.googleapis.com --project=YOUR_PROJECT_ID
npm install -g firebase-tools
If you have already set up the Firebase tools, running npm install inside the functions
folder is enough.
firebase login
A browser window opens; sign in with the Google account that owns your Firebase project.
Extract the supplied Cloud Functions package. It contains:
Cloud Function/
├── firebase.json deploy configuration — leave as it is
├── .firebaserc which Firebase project to deploy to
└── functions/
├── .env which Firestore database to use
├── index.js the function code
├── products/delivery.js the order-dispatch trigger
├── package.json dependencies (Node 22 runtime)
└── serviceAccountKey.json shipped, but not used — see the note below
Step 1 — set the project. There are two ways to do this. They write the same value, so use one, not both:
Option A — let the CLI do it (recommended). From the folder containing
firebase.json:
firebase use YOUR_PROJECT_ID
This creates or updates .firebaserc for you, so there is nothing to edit by hand. Run
firebase projects:list if you are unsure of the exact project ID.
Option B — edit .firebaserc yourself. Open it and replace the project ID:
{
"projects": {
"default": "YOUR_PROJECT_ID"
}
}
Step 2 — set the database. Open functions/.env and set
FIRESTORE_DB to the same Firestore database your panels use:
FIRESTORE_DB=(default)
This value must match FIREBASE_PROJECT_DB in all three panels'
.env files. The dispatch trigger is a 2nd-generation function bound to one
named database, so if the panels write orders to (default) while the function listens
on a different one, the function deploys cleanly and then simply never fires — no error is
reported anywhere.
Step 3 — install the dependencies:
cd functions npm install
You do not need to touch serviceAccountKey.json. It ships inside
functions/, but the code never reads it — index.js calls
admin.initializeApp() with no arguments, so once deployed the function authenticates
automatically as your project's own service account. The credentials.json from step
14.11 is for the Laravel panels, not for this package. There is likewise no database URL to fill in
anywhere in index.js.
Run this from the folder containing firebase.json — the root of the extracted
package, not the functions/ sub-folder:
firebase deploy --only functions
When the deploy finishes, open Firebase Console → Functions to confirm every function is listed. Each function has its own logs, which are the fastest way to debug a failing order or payment.
Please note: Cloud Functions require the Firebase Blaze (pay-as-you-go) plan — 2nd-generation functions build on Cloud Build and run on Cloud Run, neither of which is available on the free Spark plan. Payment gateways are not part of this package; their credentials live in the Laravel panels, so rotating a gateway key never requires a redeploy here.
Some recurring Firebase tasks have been moved out of Cloud Functions and into the Laravel Admin Panel, so you can control their timing from the panel instead of redeploying. The scripts live in:
root > storage > app > firebase
autoCancelOrder.js — automatically cancels orders that were never accepted.scheduleNotification.js — sends reminders for scheduled orders.credentials.json.The Admin Panel is a Laravel 12 application. Make sure your server meets the requirements in Server Requirement, then upload the Admin Panel package to your domain or subdomain and extract it.
18.1.1. Connect over FTP/SFTP. Use a client such as FileZilla with your server IP, username and password — port 21 for FTP, port 22 for SFTP.
18.1.2. Transfer the ZIP file to the target path on the server, for example
/var/www/html.
18.1.3. Extract it from the server terminal, or with the cPanel File Manager:
unzip your-application.zip
18.1.4. Point the domain's document root at the project's public/
folder.
The admin panel uses MySQL only for panel authentication (users, roles, permissions), sessions and jobs
— every piece of platform data lives in Firestore. Create a database and user from your control
panel, then import the supplied .sql file into it (step 18.3.6).
Each panel needs its own database. The Admin, Store and Website panels ship
with a separate .sql file each, so create three databases — for example
gromart_admin, gromart_store and gromart_web — and
import the matching file into each one. They all share the same Firebase project;
only MySQL is per-panel.
18.2.1. Open MySQL Databases in cPanel.
18.2.2. Enter a name and create the database.
18.2.3. Create a database user with a strong password.
18.2.4. Add the user to the database, grant ALL PRIVILEGES, and click Make Changes.
18.3.1. Install the dependencies and generate the application key:
composer install cp .env.example .env php artisan key:generate
18.3.2. Edit .env and set the database connection and your
Firebase web app credentials (copied in step 14.4):
APP_NAME=Gromart APP_URL=https://admin.yourdomain.com DB_DATABASE=your_database DB_USERNAME=your_db_user DB_PASSWORD=your_db_password # Firestore named database — '(default)' unless you created a named one FIREBASE_PROJECT_DB=(default) FIREBASE_APIKEY= FIREBASE_AUTH_DOMAIN= FIREBASE_DATABASE_URL= FIREBASE_PROJECT_ID= FIREBASE_STORAGE_BUCKET= FIREBASE_MESSAAGING_SENDER_ID= FIREBASE_APP_ID= FIREBASE_MEASUREMENT_ID= # Absolute path to the Node binary — used by the scheduled scripts NODE_PATH='' # Timezone APP_TIMEZONE='Asia/Kolkata'
FIREBASE_PROJECT_DB must be identical in all three panels. Use
(default) unless you deliberately created a named Firestore database. A mismatch is the
most common cause of "data shows in one panel but not the other".
18.3.3. Place the Firebase service account file at
storage/app/firebase/credentials.json. It is required for server-side ID-token
verification, for sending push notifications, and by the scheduled scripts.
18.3.4. Install the Node modules the scheduled scripts need, from the panel root:
npm install
18.3.5. Set NODE_PATH in .env to the absolute path of your
Node binary — see Installing Node.js & npm.
18.3.6. Import the panel's database. Each panel ships with its own
.sql file inside the package — import it into the database you
created in step 18.2. Do not run php artisan migrate; the supplied
.sql file already contains the tables and the default data.
Using phpMyAdmin / cPanel: open phpMyAdmin, select your database from
the left-hand list, open the Import tab, choose the panel's .sql file and
click Go.
Using the command line:
mysql -u your_db_user -p your_database < admin_panel.sql
18.3.7. Set writable permissions on the runtime folders:
chmod -R 755 storage bootstrap/cache
The panel reads its configuration from Firestore, so before your first login import the supplied Firestore collections — see Firestore Database Collection Import / Export. They contain the settings documents, currencies, email templates, CMS pages and the sample catalog.
Two imports, two places. The .sql file (step 18.3.6) sets up MySQL
— the panel's login accounts, roles and permissions. The Firestore import sets up the platform
data the panel displays. Both are needed before the panel is usable.
Open the panel at your domain, for example https://admin.yourdomain.com, and sign in with
the default account that came with the imported .sql file:
Email: admin@gromart.com Password: 12345678
Choose your map provider under Settings → Map Settings and paste your Google Maps API key — see Create Google Map API Key.
Google Maps is recommended for accurate location, search and delivery-distance results.
Push notifications are delivered through Firebase Cloud Messaging (FCM). Connect the panel to FCM as follows:
1. Generate the Firebase credentials file
firebase-adminsdk-xxxxx.json).2. Upload it in the admin panel
3. Save and verify
Open Settings → AI Settings and paste your OpenAI API key.
With AI enabled, an item can be generated from its image plus a title and short description — the model returns an expanded description, specifications, variations and other attributes, which speeds up menu creation and keeps listings consistent.
The API secure key used by the mobile apps for AI features is set under Settings → Global Settings.
Gromart runs two recurring jobs from the Admin Panel, both triggered by Laravel's scheduler:
app:auto-cancel-order — cancels orders that were never accepted within the
window you configure.app:send-scheduled-order-notification — sends reminders for orders customers
scheduled for later.Both shell out to the Node scripts in storage/app/firebase/, so they will not run until the
Node path is set.
Set NODE_PATH in the Admin Panel's .env to the absolute path of your Node
executable — for example /usr/local/bin/node on Linux, or
C:/Program Files/nodejs/node.exe on Windows. Find it with which node (see
Installing Node.js & npm).
NODE_PATH='/usr/local/bin/node'
Add a single cron entry that runs the scheduler every minute. Laravel then decides which of its commands are due.
* * * * * cd /path-to-your-admin-panel && php artisan schedule:run >> /dev/null 2>&1
In cPanel, open Cron Jobs, choose Once Per Minute, and paste the command.
Please note: use the correct PHP binary path for your server
(/usr/local/bin/php is common but varies), point the command at your own admin-panel
directory, and make sure the panel's files are readable and storage/ is writable by
the cron user.
The cron entry only decides how often Laravel is asked. The actual timings come from the admin panel: the auto-cancellation window under Settings, and the reminder lead time under Settings → Schedule Order Notification.
Customer payments, vendor subscription payments and wallet top-ups all run through the gateways you enable under Payment Methods in the admin panel. Credentials are stored in Firestore and read server-side by the panels, so secret keys are never bundled into the mobile apps. Each gateway has a sandbox/live toggle — test in sandbox before switching to live.
Gromart integrates 18 online payment gateways, plus Cash on Delivery and the in-app Wallet:
Callback URLs. Gateways that redirect back to your site (PayFast, OrangePay, MTN MoMo and others) need their Return / Cancel / Notify URLs pointing at your live HTTPS domain, and the same URLs whitelisted in the provider's own dashboard. Payments will appear to "hang" on the gateway page if these do not match.
Gateways that use a secret key perform their sensitive operations — creating and verifying transactions — server-side inside the Laravel panels. The browser and the mobile apps send only non-sensitive data (amount, currency, reference); the panel reads the gateway's secret from the settings document the Admin Panel manages and talks to the provider itself, so no secret key is ever shipped in the apps or exposed to the browser.
This is also why the payment callback URLs must point at your live HTTPS domain: the provider redirects back into the panel, which then verifies the transaction before the order is marked paid.
The Store (Vendor) Panel is the Laravel 12 application store owners and their
employees log into. Upload the package to its own domain or subdomain (for example
store.yourdomain.com) and extract it.
22.1.1. Connect over FTP/SFTP with FileZilla or a similar client — port 21 for FTP, port 22 for SFTP.
22.1.2. Transfer the ZIP file to the server, for example /var/www/html.
22.1.3. Extract it from the terminal or the cPanel File Manager:
unzip your-application.zip
22.1.4. Point the subdomain's document root at the project's public/
folder.
22.2.1. Install the dependencies and generate the application key:
composer install cp .env.example .env php artisan key:generate
22.2.2. Create a MySQL database for this panel and import the
.sql file supplied with the Store Panel package — through
phpMyAdmin → Import, or from the command line:
mysql -u your_db_user -p your_database < store_panel.sql
There is no php artisan migrate step; the .sql file already contains the
tables and default data.
22.2.3. Add the same Firebase credentials you used for the Admin Panel, including the same database name:
APP_NAME=Store APP_URL=https://store.yourdomain.com DB_DATABASE=your_database DB_USERNAME=your_db_user DB_PASSWORD=your_db_password FIREBASE_PROJECT_DB=(default) FIREBASE_APIKEY= FIREBASE_AUTH_DOMAIN= FIREBASE_DATABASE_URL= FIREBASE_PROJECT_ID= FIREBASE_STORAGE_BUCKET= FIREBASE_MESSAAGING_SENDER_ID= FIREBASE_APP_ID= FIREBASE_MEASUREMENT_ID= # Required for "Sign in with Google" GOOGLE_CLIENT_ID=
Note: use the same Firebase project and the same
FIREBASE_PROJECT_DB value for the Admin Panel, the Store Panel, the Website Panel
and the mobile apps — that is what keeps all clients on one live dataset.
To enable "Sign in with Google" you need your Google Client ID.
Where to find it:
GOOGLE_CLIENT_ID in the panel's .env file.Authorise your domains for the OAuth 2.0 client:
https://yourdomain.comhttps://store.yourdomain.comhttps://yourdomain.com/__/auth/handlerhttps://store.yourdomain.com/__/auth/handlerAlso add both domains under Firebase Console → Authentication → Settings → Authorized domains.
The panel is now ready at your chosen address, for example https://store.yourdomain.com/. Store owners register from this panel and then wait for admin approval (unless auto-approve stores is enabled under Settings → Feature Settings in the Admin Panel). If document verification is switched on, the owner must upload the required documents before the rest of the menu unlocks.
The Website Panel is the customer-facing ordering site — a Laravel 12 application that reads and writes Firestore through the Firebase JS SDK. Upload it to your main domain (recommended) and extract it.
24.1.1. Connect over FTP/SFTP using your server IP, username and password — port 21 for FTP, port 22 for SFTP.
24.1.2. Transfer the ZIP file to the server, for example /var/www/html.
24.1.3. Extract it from the terminal or the cPanel File Manager:
unzip your-application.zip
24.1.4. Point the domain's document root at the project's public/
folder.
24.2.1. Install the dependencies and generate the application key:
composer install cp .env.example .env php artisan key:generate
24.2.2. Create a MySQL database for this panel and import the
.sql file supplied with the Website Panel package — through
phpMyAdmin → Import, or from the command line:
mysql -u your_db_user -p your_database < website_panel.sql
There is no php artisan migrate step; the .sql file already contains the
tables and default data.
24.2.3. Add the same Firebase credentials used by the other panels:
APP_NAME=Gromart APP_URL=https://yourdomain.com DB_DATABASE=your_database DB_USERNAME=your_db_user DB_PASSWORD=your_db_password FIREBASE_PROJECT_DB=(default) FIREBASE_APIKEY= FIREBASE_AUTH_DOMAIN= FIREBASE_DATABASE_URL= FIREBASE_PROJECT_ID= FIREBASE_STORAGE_BUCKET= FIREBASE_MESSAAGING_SENDER_ID= FIREBASE_APP_ID= FIREBASE_MEASUREMENT_ID= # Required for "Sign in with Google" GOOGLE_CLIENT_ID=
Add your Google Client ID exactly as described for the Store Panel:
GOOGLE_CLIENT_ID in this panel's .env.https://yourdomain.com/__/auth/handler to the Authorized redirect
URIs.Notes: use the same Firebase credentials here that you set for the
Admin and Store panels, including FIREBASE_PROJECT_DB. All three panels and the
mobile apps must point at one Firebase project and one Firestore database.
The site is now ready at your domain, for example https://yourdomain.com/. Its home banners, footer and content pages are all driven from the Admin Panel — see Customizing Branding.
Gromart uses Google Maps for store search, address selection, delivery-distance calculation and live driver tracking. One key can serve the panels and the apps, but restricting it per platform is safer.
25.1. Go to the Google Maps Platform and click Get started.
25.2. Open the Google Cloud Console, go to Billing, and confirm your billing details are up to date — Google Maps returns a grey map or a watermark without an active billing account.
25.3. Go to APIs & Services → Credentials and select or create a project.
25.4. Click Create credentials → API key. Copy the key that is displayed.
25.5. Enable the APIs Gromart uses under APIs & Services → Library:
25.6. Restrict the key to your domains (HTTP referrers) for web use, and to your package name + SHA-1 for Android, then save.
25.7. Add the key in the admin panel under Settings → Map Settings, and in each mobile app — see Google Maps API Key (Flutter).
All three panels follow the standard Laravel 12 layout. If you plan to customise the code, these are the folders that matter.
gromart-admin/ (also gromart-store/ and gromart-web/) ├── app/ Application code │ ├── Console/ Artisan commands + the scheduled tasks │ │ └── Commands/ AutoCancelOrder, SendScheduledOrderNotification │ ├── Helpers/ Shared helper functions │ ├── Http/ │ │ ├── Controllers/ Request handlers (thin — data is read from Firestore) │ │ └── Middleware/ Auth & the per-menu permission gate │ ├── Mail/ Mailables for the email templates │ ├── Models/ Eloquent models (panel auth: User, Role, Permission) │ └── Providers/ Service providers (incl. the Firebase service) ├── Modules/ │ └── AI/ The OpenAI item-listing module (admin & store panels) ├── bootstrap/ Framework bootstrap + cache ├── config/ Configuration files (app, database, firebase, …) ├── database/ │ ├── migrations/ MySQL schema reference (you import the shipped .sql) │ └── seeders/ Base data ├── public/ Web root — index.php, css/, js/, images/ ├── resources/ │ ├── views/ Blade templates (pages, layouts, partials) │ ├── css/ Panel stylesheets │ └── lang/ Translations (en/, ar/) ├── routes/ │ ├── web.php All web routes (route names double as permission keys) │ └── api.php API routes ├── storage/ │ └── app/firebase/ credentials.json + the scheduled Node scripts ├── vendor/ Composer dependencies (generated — not edited) ├── .env Environment config (DB + Firebase credentials) ├── artisan Laravel command-line entry point └── composer.json PHP dependencies
Note: vendor/ and node_modules/ are generated by
composer install / npm install and should not be edited by hand. Your
configuration lives in .env; your Firebase service account lives in
storage/app/firebase/credentials.json. Neither should ever be committed to Git or
placed under public/.
This section maps the external services Gromart integrates with and where each one is configured, so developers can extend or replace them. Every client — the mobile apps, the Admin Panel, the Store Panel and the Website Panel — talks to the same Google Firebase backend.
credentials.json) for privileged operations and
push notifications.deliveryDispatch Firestore trigger that
assigns orders to drivers, and the deleteUser callable that removes an account from
Firebase Authentication.There is no separate REST API layer to configure: Firestore is the API, and every client reads and writes the same collections in real time.
All platform data lives in Firestore. The main collections are:
users — customers, vendors, drivers and admins, each with a role.vendors, vendor_categories, vendor_products,
vendor_attributes, vendor_filters — stores and their menus.restaurant_orders, order_transactions —
orders and payments.coupons, gift_cards, cashback, cashback_redeem,
advertisements — promotions.subscription_plans, subscription_history, payouts,
driver_payouts, withdraw_method, wallet — money
movement.zone, tax, currencies, documents,
documents_verify — operational configuration.settings — every configurable option, one document per settings screen.chat, notifications,
dynamic_notification, email_templates — communication.cms_pages, menu_items, on_boarding, story
— content.Configured by the FIREBASE_* keys in each panel's .env. Access is governed by
the security rules you publish (see Create Firebase Project), and
multi-field queries need composite indexes (see
Deploy Firestore Indexes).
Handles email/password, phone (OTP), Google and Apple sign-in for every client. Providers are enabled in the Firebase console; the web panels additionally need the domain listed under Authorized domains and a matching OAuth 2.0 client for Google login.
Holds every uploaded file — store and item images, banners, avatars and verification
documents. Upload quality is controlled by IMAGE_COMPRESSOR_QUALITY in each panel's
.env.
Delivers order, chat and status notifications to customers, stores and drivers. Configured by uploading the service-account JSON and Sender ID under Settings → Notification Settings; the notification text itself comes from Dynamic Notifications.
Used for live driver location, which changes far more often than order state. It powers the customer's tracking map and the admin's God's Eye view.
18 gateways plus COD and the wallet. Public keys are read by the clients; secret-key operations run server-side in the Laravel panels, which read the credentials from the settings document the Admin Panel manages. See Payment Gateway Setup.
Store search, address selection, zone drawing, delivery distance and live tracking. One key, configured in Settings → Map Settings and in each mobile app.
The Modules/AI module in the Admin and Store panels. Given an item image, title and
short description it drafts the full listing — description, specifications, variations and
attributes. Configured under Settings → AI Settings; billed by OpenAI per
request.
Order receipts, password resets, vendor approvals and campaign mail. Set the SMTP host, port, username,
password and encryption in .env (or under Settings → Email
Settings); the message bodies come from Email Templates.
.env (each panel) — database, all FIREBASE_* keys,
FIREBASE_PROJECT_DB, GOOGLE_CLIENT_ID, NODE_PATH, SMTP.storage/app/firebase/credentials.json — the Firebase service
account.resources/views/settings/index.blade.php.routes/web.php wrapped in the
permission: middleware, add the entry to
resources/views/layouts/menu.blade.php, and grant the permission to the roles that need
it. Route names double as permission keys, so keep them stable.resources/lang/en to a new locale folder,
translate lang.php, and register the language under Languages in the
admin panel.All three panels are Laravel applications and can run on shared hosting (cPanel) or on a VPS/dedicated server. Choose the approach that matches your hosting.
Best for getting started quickly when you do not have SSH/root access.
public/ directory (or use the supplied .htaccess).composer install on your own machine
first and upload the project with its vendor/ folder already
installed..env (database + Firebase credentials) and place
credentials.json in storage/app/firebase/.755) on storage/ and
bootstrap/cache/..sql file into its database through
phpMyAdmin → Import in cPanel. No Artisan or shell access is needed for this
step.Note: make sure your host runs PHP 8.2+ with the required
extensions and that mod_rewrite is enabled. The Admin Panel's scheduled jobs need
Node.js, which basic shared plans often lack — choose a plan that offers it,
or use a VPS.
Recommended for production — you get SSH access, Composer, Node.js and full control.
composer install (and npm install
for the Admin Panel)..env.example to .env, set the database and Firebase credentials, then
run php artisan key:generate..sql file:
mysql -u user -p database < panel.sql.public/ directory.storage/ and
bootstrap/cache/.* * * * * php /path/artisan schedule:run) — see
Cron & Scheduled Tasks.php artisan config:cache and php artisan route:cache,
and serve everything over HTTPS.mod_rewrite is disabled or AllowOverride is not All, so
Laravel's public/.htaccess is ignored.RewriteBase to
public/.htaccess, e.g. RewriteBase /gromart/.public/. Point the
domain at public/, or add a root .htaccess that rewrites into it. Never
serve the project root — it makes .env and
storage/app/firebase/credentials.json reachable from the internet.php artisan storage:link (or
create the public/storage link manually if symlinks are blocked).755 on
storage/ and bootstrap/cache/, and confirm PHP is 8.2+.Options -MultiViews) to
.htaccess.APP_URL to your
https:// domain and force HTTPS in .htaccess.Always serve all three panels over HTTPS. Firebase Authentication, payment-gateway callbacks and secure cookies all require it, and browsers block mixed content on pages that are not fully secure.
Shared hosting (cPanel). Most hosts issue a free certificate automatically. Open cPanel → Security → SSL/TLS Status (or Let's Encrypt / AutoSSL), select your domains, and click Run AutoSSL.
VPS / dedicated server. Install a free Let's Encrypt certificate with Certbot:
# Nginx sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d yourdomain.com -d admin.yourdomain.com -d store.yourdomain.com # Apache sudo apt install certbot python3-certbot-apache sudo certbot --apache -d yourdomain.com
Certbot installs the certificate and adds an automatic renewal timer.
Force HTTPS. Add this at the top of each panel's public/.htaccess, just
inside <IfModule mod_rewrite.c> and before Laravel's front-controller rules:
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Then finish up:
APP_URL=https://… in every panel's .env.php artisan config:cache.This section walks through the platform from each side — customer, store, driver and admin — so you can see how the pieces you configured fit together.
Key screens (website). The customer journey — the home page with the location picker and search, the store listing, a store's menu page, the search results with filters, and the offers page:
Gromart is fully white-label — the application name, logos, favicon and brand colours can all be changed from the Admin Panel without editing any code. Branding is stored in Firebase, so a change made once is reflected across the Admin Panel, the Store Panel, the website and the mobile apps.
Log in to the Admin Panel and open Settings → Branding. From this screen you can update:
Click Save. The new logo and name appear as soon as the page reloads.
Still under Settings → Branding, set:
#00b761.The customer website is driven from the admin panel too:
Note: use the same Firebase project for all panels and the mobile apps so branding changes propagate everywhere. Clear your browser cache if an updated logo or colour does not appear right away.
Some mobile branding is baked into the build rather than read from Firebase, so it is set separately in each of the three app projects:
The App Colour set under Settings → Branding is read from Firebase, so it applies to all three apps at once.
Add or edit languages under Languages in the admin sidebar. English and Arabic (RTL) ship by
default; new languages are added by copying resources/lang/en and translating
lang.php.
This section lists the errors most often seen while setting up Gromart, with their causes and fixes. Also see the FAQ for the Flutter "Missing project_info object" error.
Firestore reads/writes are being blocked by your security rules.
FIREBASE_PROJECT_DB
below.A Firestore composite index is missing for a filter/sort query.
This almost always means the panels point at different Firestore databases.
FIREBASE_PROJECT_DB is identical in the Admin, Store and
panels' .env files — (default) in a normal installation.FIREBASE_* credential belongs to the same Firebase project.The demo Firestore collections were never imported.
settings collection in particular must exist — without it the
panels have no configuration to read.The sign-in provider you are using is not enabled. Enable Email/Password, Phone, Google (and Apple if used) under Authentication → Sign-in method.
storage/app/firebase/credentials.json.google-services.json / GoogleService-Info.plist are present
and match the package name.NODE_PATH in the Admin Panel's .env is empty or wrong. Run
which node and paste the absolute path.npm install was never run in the Admin Panel root, so the scripts have no dependencies.storage/logs/laravel.log in the panel that started the payment; the provider's
rejection reason is recorded there.upload_max_filesize and
post_max_size.The google-services.json file is missing or in the wrong place. See the detailed fix in the
FAQ.
A quick reference for the terms used throughout this documentation.
FIREBASE_PROJECT_DB — Firestore can host more than
one database per project. (default) is the standard one. Whatever you use, every panel and
the Cloud Functions package must target the same name.credentials.json — a private key file that lets the
server act on behalf of your Firebase project (verify tokens, send push, run scripts). Kept in
storage/app/firebase/ and never exposed publicly.google-services.json / GoogleService-Info.plist — the
Firebase configuration files added to the Android and iOS apps respectively.composer install)..env — the environment configuration file holding database and
Firebase credentials for each panel.php artisan key:generate).NODE_PATH — the absolute path to the Node binary, used by those
scheduled scripts..jks) — the file holding your Android signing key. Losing
it means you can no longer update the app on Google Play.To update the Admin, Store and Website panels, upload the latest source package to the root directory of each respective panel and extract it.
If your project includes custom modifications, we strongly recommend updating with Git so your customisations are preserved while the latest changes are merged in.
Check for a database update first. Every panel package includes its own
.sql file. When a release adds or changes tables, that .sql file is
updated too — and the new code will not work against the old tables. Read the
Changelog for the version you are moving to:
.sql file from the new package into that panel's
database (phpMyAdmin → Import, or
mysql -u user -p database < panel.sql).There is no php artisan migrate step in Gromart, on a fresh install or
an update — the .sql file is always the source of truth for MySQL.
.sql file if the release changed the database (see the note above)..env and storage/app/firebase/credentials.json — they
are not part of the package..env and credentials.json, then compare .env against
the new .env.example and add any new keys..sql file from the new package if the release changed the database.
Back up your existing database first — importing replaces the tables it contains.composer install php artisan config:clear php artisan cache:clear php artisan view:clear
Before you update: take a full backup of the panel folder, the MySQL database and your Firestore data (use the export command in Firestore Database Collection Import / Export). If a release adds new Firestore documents or indexes, re-run the import and index deployment afterwards.
To update the User App, Store App or Delivery Man / Driver App, download the latest source package and replace that app's project source. Each app updates independently, so you can ship one without rebuilding the others — but keep them on the same release when a version changes shared Firestore fields. As with the panels, Git is the safer route if you have customised the code.
google-services.json,
GoogleService-Info.plist, android/key.properties and release keystore
— none of them ship in the package.flutter clean flutter pub get flutter run
Then increment version: in pubspec.yaml and publish the new build — see
Signing, Building & Publishing the Apps. Update the version number
and store links under Settings → Version in the admin panel so existing users are
prompted to upgrade.
driverOrderAcceptReject setting, which finds another nearby driver after a waiting period.