Car Booking is a multi-company (SaaS) car rental management system. One installation serves many rental companies. Each company (an owner) gets its own cars, customers, bookings, staff, website and mobile apps, and can never see another company's data. The platform itself is run by the super admin, who sells subscription plans.
| Role | What they do | Where |
|---|---|---|
| Super admin | Runs the platform: plans and prices, owners (companies), plan payments, system health, backups, platform settings and the store links of the mobile apps. | Admin panel |
| Owner | Runs one rental company: fleet, bookings, customers, drivers, money, website, apps. Can create staff with their own roles. | Admin panel, website, apps |
| Manager / staff | Works inside the owner's company with the permissions the owner gave them (for example only bookings and customers). | Admin panel |
| Driver | Sees only their own jobs, rental agreements, expenses and earnings. Has a trimmed menu (no WhatsApp inbox, branches, reports and so on). | Admin panel (My jobs), driver app |
| Customer | Books and pays on the company's website or the customer app, sees their bookings, documents and rewards. | Website, customer app |
Minimum PHP Version:
Required PHP Extensions:
| Item | Needed | Why |
|---|---|---|
| PHP | 8.2 or newer | Laravel 12 |
| PHP extensions | cURL, DOM, Fileinfo, Filter, Hash, Mbstring, OpenSSL, PCRE, PDO (MySQL), Session, Tokenizer, XML, GD, Zip, JSON, Ctype | Framework, image handling (photos, PDF), backups |
| Database | MySQL 5.7+ or MariaDB 10.3+ (utf8mb4) | All data |
| Web server | Apache with mod_rewrite and AllowOverride All (or Nginx with the Laravel rewrite rules) | Clean URLs, the security rules in .htaccess |
| Cron | One cron entry every minute | Reminders, expiry, backups, queue (see "Cron and queue") |
mysqldump | Available to PHP (exec allowed) | Nightly backups (optional but recommended) |
| HTTPS / SSL | Strongly recommended; required for store apps and payment gateways | Security, mobile apps, payments |
| Disk space | At least 2 GB free, more for photos and backups | Vehicle photos, documents, backups |
| Memory / time | PHP memory_limit 256 MB, max_execution_time 120 s | PDF reports, exports |
You do not need Node.js on the server: the compiled assets are included.
First, navigate to the Databases section of your hosting panel. Click on MySQL® Databases to begin the process. This tool allows you to create and manage your databases and database users.
On the next screen, you will be able to create a new database. You will see a field to enter the name of your new database. The hosting provider automatically adds a prefix to the database name for security. After entering the name, click Create Database.
After creating the database, you'll need to create a user for it. Scroll down on the same page to the Add New User section. Enter a Username and a strong Password in the provided fields. Use the Password Generator tool to create a secure password. Click Create User to save the new user.
The final step is to grant the new user permissions to manage the database. On the Manage User Privileges page, check the box for ALL PRIVILEGES. This gives the user full control over the database, allowing them to create, modify, and delete tables and data. Click Make Changes to finalize the process. Your database and user are now set up and ready to use.
Log in to your cPanel account. In the main menu, look for the Files section and click on File Manager. This will open the file manager interface in a new tab.
In the File Manager, navigate to the folder where you want to upload your files. For a primary domain, this is typically the public_html folder. This folder is the "web root" and contains all the files that are accessible to the public on your website.
Once you are in the correct directory, click the Upload button in the toolbar at the top of the page. This will open a new window where you can drag and drop your files or click Select File to browse for files on your computer.
After the upload is complete, you should see a 100% progress message. The uploaded file should now appear in the folder you selected in the File Manager.
Open a web browser and navigate to your website's installation URL, which is typically yourdomain.com/install. This action will initiate the installation process and lead you to the App Installation Process page.
The next step is to ensure your server meets all the necessary requirements for the application. Click the "Click to Check Your Server Requirements" button to proceed.
After confirming the server extensions, the installer will check the necessary directory permissions. Ensure all permissions are correct (often shown with a green checkmark next to them) and then click "Click to Configure Your App Environment".
You will be given options to configure the app's environment. For a straightforward installation, select "Using Form Wizard Setup".
This is a critical step where you link the application to your database. Fill in the required fields with the information you gathered when setting up your database. This includes the Database Host, Database Name, Database User Name, and Database Password. You will also need to enter an Application Name and Application Url. Once all the details are entered, click "Click to Install".
The installer will now run the installation process, which includes preparing the database and running migrations. Once you see the "Application has been successfully installed" message, you have successfully completed the process. Click "Click here to exit" to finish.
Email : superadmin@gmail.com
Password : 123456
Email : owner@gmail.com
Password : 123456
Email : manager@gmail.com
Password : 123456
Do these steps once after the installer has finished. Most problems reported by customers come from skipping one of them.
.env set APP_ENV=production, APP_DEBUG=false and the real APP_URL (with https://). Debug pages leak secrets.php artisan storage:link (or create the public/storage link in your host panel). Without it uploaded photos, logos and banners do not show.storage and bootstrap/cache (usually 775).https://your-site/.env and https://your-site/storage/logs/laravel.log. Both must answer 403. If a file downloads, your web server ignores .htaccess: fix it before going live..env (it holds APP_KEY; without it encrypted settings cannot be read).php artisan migrate --force then php artisan optimize:clear.One entry, every minute. Replace the path with the folder that contains artisan (the main_file folder):
* * * * * cd /path/to/main_file && php artisan schedule:run >> /dev/null 2>&1
What the scheduler runs:
| When | Command / job | What it does |
|---|---|---|
| Every minute | heartbeat | Lets "System health" know the scheduler is alive |
| Every minute (only if a real queue is set) | queue:work --stop-when-empty | Sends emails, SMS, WhatsApp, webhooks and push messages in the background |
| Every 5 minutes | bookings:expire-online | Releases unpaid online reservations whose hold time ran out |
| 00:10 | subscriptions:expire | Marks plans that ended as expired |
| 00:20 | maintenance:rollover | Moves repeating maintenance reminders to the next date |
| 02:30 | backup:run | Nightly database + uploads backup (needs mysqldump) |
| 03:10 | prune login attempts | Deletes login history older than 90 days |
| 07:00 | vehicles:expiry-alerts | Alerts for registration / insurance / document expiry |
| 08:00 | bookings:send-reminders | Pick-up and return reminders to customers (email, app push) |
| 09:00 | subscriptions:remind | Plan renewal reminders to owners |
| Monday 06:30 | ai:insights | Weekly AI business briefing for each company that uses AI |
With the default QUEUE_CONNECTION=sync emails and webhooks are sent during the request: simple and fine for small installs. To send them in the background set QUEUE_CONNECTION=database in .env; the cron entry above then drains the queue every minute (or run php artisan queue:work under Supervisor on a VPS). The old key QUEUE_DRIVER is ignored by Laravel.
The installer writes .env for you. These are the keys you may need to change later:
| Key | Example | Meaning |
|---|---|---|
APP_URL | https://rent.example.com | The public address of the platform. Used in emails, QR codes and payment return links. Must match the real address. |
APP_ENV / APP_DEBUG | production / false | Never leave debug on in production. |
APP_KEY | (generated) | Encrypts saved secrets and sessions. Back it up; do not change it on a live system. |
DB_* | host, database, user, password | Database connection. |
QUEUE_CONNECTION | sync | database | How background jobs run. |
MAIL_* | SMTP host, port, user, password | Fallback mail settings (each company can set its own under Settings). |
SITE_BASE_DOMAIN | example.com | Turns on free subdomains {company}.example.com. Needs a wildcard DNS record and wildcard SSL. Leave empty to switch off. |
CUSTOM_DOMAIN_IP | 203.0.113.10 | The server IP shown to owners who must create an A record for their own domain. |
BACKUP_ENABLED / BACKUP_KEEP | true / 14 | Nightly backups and how many zips to keep. |
FCM_CREDENTIALS | /home/user/firebase.json | Absolute path to the Firebase service-account file for mobile push notifications (keep it outside the web folder). |
SESSION_LIFETIME | 120 | Minutes until an inactive admin session ends. |
.env run php artisan config:clear (or optimize:clear).artisan is the application folder. In this product layout the root .htaccess sends requests to index.php and denies access to .env, storage/logs, storage/app/backups, vendor and source files. Keep those rules.public folder instead; then these files are never reachable.try_files $uri $uri/ /index.php?$query_string;) and deny /\. and /storage/logs.Host header. For certificates on customers' domains use a proxy that issues certificates on demand (Caddy on_demand_tls with ask pointing at /domain-check?domain=host, which answers 200 only for domains that belong to a company).APP_KEY.The super admin sees platform-wide numbers instead of one company's data.
Pricing > Packages. Each plan has a price, a duration (monthly, yearly, lifetime) and limits and feature switches. Owners choose a plan on sign-up or from their dashboard.
subscriptions:remind). Renewal is a reminder with a link: cards are not charged automatically. A plan that ends is marked expired by subscriptions:expire.Settings (top right menu) has one tab per area. The super admin's settings are the platform defaults; each owner has their own copy for their company.
| Tab | What to set |
|---|---|
| Account / Password / 2FA | Your name, photo, password and two-factor authentication. |
| General | Product name, logos (dark, light, landing), favicon, default language, footer text, date and number formats, theme colours. |
| Company | Company name, address, phone, email, currency and symbol, tax number, time zone, numbering of bookings and agreements. |
| Email (SMTP) | Mail server, port, encryption, sender name and address. Use the test button. Required for sign-in codes and booking emails. |
| Payment | Stripe, PayPal, Paystack, Flutterwave, Razorpay and bank transfer for plan payments (super admin) or for rental payments (owner). |
| Site SEO | Meta title, description, keywords, share image of the landing page. |
| Google reCAPTCHA | Site key and secret to protect login and forms from bots. |
| 2 Factor Authentication | Switch on two-factor sign-in for users. |
| Rental Agreement | Default terms and conditions and the text of the agreement. |
| Twilio | Account SID, token, SMS number and WhatsApp number. Used for SMS and WhatsApp messages and for phone sign-in in the customer app. |
| AI Settings | Main and backup AI provider (OpenAI, Claude, Gemini), key and model for each, daily limit, "read ID photos" switch. |
php artisan settings:encrypt-secrets --dry-run and then without --dry-run.backup:run writes a zip (database dump + uploads) to storage/app/backups and keeps the newest BACKUP_KEEP. Download or run one now from Backups. Copy the zips off the server too. Owners can export their own data as JSON (Backup & data export).php artisan lang:sync adds every text used in the code to each language file so translators have a complete list (untranslated texts keep the English words).A new company is ready to take bookings after these steps. Each one is a menu item on the left.
sunrentals). This creates your website and your app code.vehicles:expiry-alerts sends bell alerts / emails before they end; repeating maintenance moves to its next date on its own.tracker ability) and shows the last position on the car page.Rentals > Planning shows every car as a row. Drag a booking to another day or onto another car, or resize it to change the length. The server checks the car is free, recalculates the price and keeps the draft agreement in step; a refused move snaps back. Completed and cancelled bookings are fixed. Needs the update booking permission.
/sign/{token}). The signing page shows the full agreement; the customer draws a signature; name, address (IP) and time are stored. Signed agreements cannot be changed./driver-app/today can be added to a phone's home screen and the last loaded page stays readable offline. The Flutter driver app does the same and more..ics link for Google / Apple / Outlook calendars. Creating a new link disables the old one.Switch AI on under Settings > AI Settings (needs a plan that includes AI). Choose a main provider (OpenAI, Claude or Gemini) and an optional backup; if the main one fails the request is tried once on the backup. Keys are stored encrypted; leave a key empty to keep it, or tick "remove this key". A daily limit per company protects your costs. The permission use ai assistant controls who can use the features.
| Feature | Where | What it does |
|---|---|---|
| Owner assistant | Home > AI assistant | Ask questions about your business in plain words ("which cars earned least this month?"). It uses fixed read-only tools on your own data, so it cannot change anything. |
| Business insights | Dashboard card, weekly email / push | Finds facts with plain SQL (money trend, idle cars, unpaid finished rentals, late returns, expiring papers, waiting online requests…) and turns them into a headline and suggested actions. Works without AI as facts only. |
| Photo damage check | Booking page > Vehicle photos | Compares pick-up and return photos and gives a verdict (no new damage / possible new damage / cannot tell) with findings. A hint, never proof; no charge is made automatically. |
| Pricing suggestions | Fleet > Pricing suggestions | Which cars to raise or lower and a weekend rule, calculated from your own bookings; changes capped at 20%. Apply recalculates on the server and can be undone for 14 days. |
| Reply drafts | WhatsApp inbox, CMS inbox | Suggests a reply from the conversation and the customer's bookings. It flags complaints, refunds or disputes for a human. Nothing is sent by the AI. |
| ID and licence reading | Customer > Documents | Reads printed fields from a photo and lets staff copy chosen fields to the record. Off by default because the photo is sent to the AI provider. |
| Website chat helper | Online booking settings | A chat on the public site that knows your cars and rules; cannot book anything; capped per day. |
| Write with AI | Email templates and AI templates | Drafts template text. |
read, bookings:write, tracker. Base address /api/v1. Tokens can be revoked at any time.event and occurred_at. With a signing secret the call includes X-Signature: sha256=<HMAC-SHA256 of the raw body>.main_file/docs/mobile-api.md (login, driver, staff and customer endpoints).https://platform/book/{address}.checkout.session.completed event and paste its signing secret there. Payments are then recorded even if the renter closes the tab before returning.bookings:expire-online).Each tab saves on its own. Empty text falls back to good defaults.
| Tab | Controls |
|---|---|
| Page sections | Banner (headline, highlights, picture, layout classic / centered / split), switches for every home block, fleet, how it works, why us, call to action. |
| About & locations | About text with picture and bullet points, pick-up locations block. |
| Offers & stats | Numbers band and offers from active online promotion codes. |
| Pages | Extra pages (rental terms, privacy, cancellation…). Each can be in the footer and/or menu. Basic formatting and pictures are allowed; scripts are removed. |
| Blog | Posts with reading time, share buttons, author box and car suggestions. |
| FAQ | Questions and answers shown on the home page. |
| Theme & colors | Five colours, font, button shape, home layout, dark mode (off / switch / default dark). |
| Footer & social | Footer text, social links, Google Maps embed link for the contact page. |
| SEO | Meta title and description, share image, and the switch "Let search engines list my website". Search engines are blocked until you turn it on. Sitemap and robots.txt are created per company. |
| Inbox | Messages from the contact form (email copy too), with an AI reply draft. |
| Subscribers | Newsletter signups with CSV export. |
?lang=). Arabic switches the whole layout to right-to-left. Language files live in resources/lang; run php artisan lang:sync after adding texts.Every company's website can be opened on up to three addresses, all showing the same site:
https://platform/book/{address}. Always on.https://{address}.example.com. The super admin sets SITE_BASE_DOMAIN=example.com in .env and creates a wildcard DNS record *.example.com to the server (and a wildcard SSL certificate).rent.theircompany.com, creates the shown CNAME (or an A record to your CUSTOM_DOMAIN_IP) and the TXT record _carbook-verify.rent.theircompany.com with the shown value, then presses Verify. Only after verification does the domain serve the site./cars, /blog, /contact, /my-bookings, /app), robots.txt and sitemap.xml use the host, and login, admin and other companies are not served there (404). The mobile apps always talk to the platform address, never to a company's own domain.There are two Flutter apps (Android and iPhone) in the mobile folder: the driver app and the customer app. Both talk to the server's /api/mobile/v1 (documented in main_file/docs/mobile-api.md). A staff app is not part of the product.
Apple's guideline 4.2.6 rejects apps made from a template by a service provider on behalf of clients, and Google Play's repetitive-content policy discourages many near-identical apps; Google also does not allow tool-generated apps to be submitted by the tool's operator for others. The accepted ways are the shared "picker" app above, or an own-brand app that the company publishes from its own developer accounts. The build script in "Own-brand app" prepares that. Check the stores' current rules before launch.
sunrentals), shown on Website > Mobile apps with a QR code, a "copy message" button and the page customers see./book/{address}/app ("Get our app") with the QR code, the code in plain text and the store buttons. A "Get our app" link appears in the footer once store links are set.mobile/driver_app or mobile/customer_app:flutter pub get
# test on a device or emulator
flutter run --dart-define=API_BASE=https://your-domain.com/api/mobile/v1
# Android release files
flutter build apk --release --dart-define=API_BASE=https://your-domain.com/api/mobile/v1
flutter build appbundle --release --dart-define=API_BASE=https://your-domain.com/api/mobile/v1 # Google Play
# iPhone (on a Mac)
flutter build ipa --release --dart-define=API_BASE=https://your-domain.com/api/mobile/v1
API_BASE must be your platform address (not a company's own domain). Plain http:// works in debug builds only; store builds need https. The Android emulator reaches a server on your PC at http://10.0.2.2/... (the default in lib/config.dart).com.carbooking.driver_app, com.carbooking.customer_app in android/app/build.gradle* and in Xcode) and replace the launcher icons / names if you rebrand: the apps use the system logo (assets/logo.png, assets/mark.png).flutter test) and flutter analyze is clean, but run it on a real phone before release.The company must publish it from its own Apple and Google developer accounts. Prepare the project with the script (needs Python 3 and pip install pillow):
cd mobile/customer_app
python3 tools/build_branded.py --company sunrentals --name "Sun Rentals" --logo logo.png \
--api https://your-domain.com/api/mobile/v1 --package com.sunrentals.app --build appbundle
It copies the app to build_branded/sunrentals/ with the company's name, icons, package id, fixed company code (no code screen) and your API address. The company then enters its own store links under Website > Mobile apps, and those links win on its website.
Push is optional. Without these steps both apps work, only without push messages.
google-services.json into android/app/ and GoogleService-Info.plist into ios/Runner/ (add it to the Runner target in Xcode) of each app. Do this for both apps.FCM_CREDENTIALS=/path/to/file.json in .env.QUEUE_CONNECTION=sync). Rebuild the apps and test on a real phone (push does not work on most emulators).Who gets what: drivers get a push for a new or cancelled job; staff get the bell alerts; customers get booking confirmed / cancelled and reminders. The owner can switch each push on or off under Configuration > Email Notification (Phone app notifications).
QUEUE_CONNECTION=database emails go out in the background (cron drains the queue).| Provider | Key from | Default model (editable) |
|---|---|---|
| OpenAI | platform.openai.com | gpt-4o-mini |
| Anthropic (Claude) | console.anthropic.com | claude-sonnet-5-5 |
| Google (Gemini) | aistudio.google.com | gemini-2.5-flash |
Settings > AI Settings: pick the main provider and an optional backup, paste a key and model for each, set the daily limit, and decide on "Let AI read ID and licence photos". The Claude and Gemini connections were tested with simulated replies; try each key once after entering it.
php artisan migrate --force and
php artisan optimize:clear. Check that public/storage still
links to storage and that the cron entry is still in place.| Problem | Likely cause and fix |
|---|---|
| Photos, logos or banners do not show | The storage link is missing: run php artisan storage:link and check the public/storage link exists. |
| White page / error 500 | Check storage/logs/laravel.log; make storage and bootstrap/cache writable; run php artisan optimize:clear. After an update run php artisan migrate --force. |
| Emails or sign-in codes are not arriving | Check Settings > Email and press the test button; look at spam; if QUEUE_CONNECTION=database make sure the cron runs; check System health > failed jobs. |
| Reminders, expiry or backups never happen | The cron entry is missing or wrong. System health shows "scheduler never ran". Add the entry from "Cron and queue". |
| A menu item is missing for a user | Open System Settings > Roles and tick the permission for that role. |
/.env or logs can be downloaded | The server ignores .htaccess. Enable AllowOverride All or deny those paths in the server configuration, and change the database password and APP_KEY. |
| The website of a company shows "not found" | Online Booking must be switched on, the web address saved, the owner active and the plan not expired. |
| Own domain does not verify | The TXT record must be exactly the name and value shown; DNS can take up to a few hours. Then press Verify again. |
| Online payment not recorded | Add the Stripe webhook shown on the Online Booking page; check the gateway keys and that the site uses https. |
| The app says "company not found" | Check the code (the website address name), that Online Booking is on and the owner is active. A pasted link or the company's own domain also works. |
| The app cannot reach the server | The build uses the wrong API_BASE, the server has no https (store builds need it), or a firewall blocks the phone. Open API_BASE/company/your-code in a phone browser to test. |
| Push notifications do not arrive | Firebase files missing in the app, FCM_CREDENTIALS missing or wrong path, queue not running, or testing on an emulator. |
| Text message sign-in is not offered | The owner has not saved Twilio SMS details, or switched it off under Website > Mobile apps. |
| AI says it is not available | AI is off in Settings > AI Settings, the plan has no AI feature, the key is missing or the daily limit is reached. |
| Dates are off by hours | Set the time zone in Settings > Company and in config/app.php if needed. |
| Language texts are in English | Add the translation to the language file and run php artisan lang:sync; untranslated texts fall back to English. |
APP_DEBUG=false, strong passwords, and enable two-factor sign-in for admins..env (APP_KEY) and the storage folder before every update.If you have any queries, please create a support ticket, and our support team will respond as soon as possible.