Web Suite Active
WEB / 01 Full Stack
Laravel 11 + Blade

Modern monolithic marketplace engine with Tailwind CSS and Alpine.js.

WEB / 02 Multi-Vendor
3 Unified Front Doors

Storefront, Vendor Portal, and Superadmin Dashboard in one repository.

WEB / 03 Payment Ready
14+ Gateways Built-In

Stripe, PayPal, Razorpay, Flutterwave, Mollie, Cash On Delivery & Wallet.

WEB / 04 Globalized
Multi-Language & RTL

Visual translation manager, dynamic currency switch, and geo-taxes.

MONO MarketplaceStorefront · Vendor · Admin

Web product

Mono. Marketplace.

Multi-vendor commerce, one Laravel application.

Complete documentation for the MONO Marketplace web product: the themeable storefront, the vendor dashboard, the admin panel, and the Laravel services behind all three. Install it, run the marketplace, and extend it.

A / 01

Engine

Laravel 13 / PHP 8.3Blade + Tailwind 4
A / 02

Surface

3 panels / 9 themes14 payment methods
A / 03

Scale

94 tables / 78 models32 permissions

01 / Overview

One codebase, three front doors

MONO Marketplace is a multi-vendor commerce platform. A single Laravel application serves three completely separate front doors, and they all share one database, one permission system, and one theme engine.

STOREFRONT

Customer website

  • Themeable catalog with nine shipped storefront themes.
  • Cart, coupons, vouchers, wishlist, loyalty, and wallet.
  • Fourteen payment methods including cash on delivery and bank transfer.
  • Digital goods, bundles, and vendor chat with realtime delivery.
VENDOR PANEL

Seller dashboard

  • Applications, product, bundle, and taxonomy management.
  • Order fulfilment with per-item digital delivery.
  • Settlements, withdrawal requests, subscription packages.
  • AI suite with metered text, translation, and image quotas.
ADMIN PANEL

Platform control

  • Orders, invoices, refunds, and a full finance ledger.
  • Vendor approvals, subscriptions, payouts, and delivery operations.
  • Roles, permissions, languages, media, and site settings.
  • Push broadcasting, newsletters, and an AI administration toolset.

What ships in the package

Storefront themes

Nine themes on disk: mono (built in), plus beauty, electronics, fashion, fullwidth, grocery, home, petshop, and sports. Each is a self-contained package with its own views, assets, palette, and optional database tables.

Mobile applications

Two Flutter clients ship alongside: the customer app and the vendor app. Both talk to the same versioned API described in this document.

Web installer

public/install.php is a self-contained wizard that creates the database, writes the environment file, creates the first administrator, and activates a theme. No Composer, Node, or shell access is required on the host.

The three front doors, at a glance

Each interface is purpose-built for desktop web workflows — responsive Blade templates styled with Tailwind CSS, with zero client-side compilation steps.

Storefront — Customer web marketplace
Vendor Panel — Seller dashboard & catalog
Admin Panel — Platform operations control

Who this documentation is for

This guide covers the web product: the storefront, the vendor panel, the admin panel, and the backend services that support them. The two Flutter applications have their own companion documents. Everything here is written against the shipped source, so route names, table names, and permission keys match what you will find in the package.

API PREFIXhttps://your-domain.com/api/v1

02 / Overview

Stack, panels, and how a request flows

Technology stack

LayerChoiceNotes
LanguagePHP ^8.3Platform is pinned to 8.3.30.
FrameworkLaravel ^13.8Blade templates, no SPA build step.
API authLaravel Sanctum ^4.3Bearer personal access tokens.
Realtime chatAbly ^1.1Server-minted short-lived client tokens.
PDF invoicesdompdf ^3.1Renders and emails invoice documents.
Machine translationgoogle-translate-php ^5.3Backs the admin AI translation tools.
CSSTailwind CSS ^4.3CSS-first config via an @theme block. No Vite, no bundler.
DatabasesMySQL or SQLiteBoth drivers are supported; MySQL is the production target.
There is no Vite pipeline.

Assets are plain Blade, plain CSS compiled by the Tailwind CLI, and plain JavaScript served from public/js. If you add a JavaScript entry point, add the file to public/js and reference it directly; do not look for vite.config.js or an npm run dev asset server.

The three panels

Storefront

  • Public routes in routes/web.php.
  • Views resolve through ThemeServiceProvider, which prepends the active theme's view path so theme templates win over the default ones.
  • A theme that does not supply a view falls back to resources/views automatically.
  • Uploaded media resolves through PublicMedia::url().

Vendor panel

  • Routes under prefix('dashboard') with auth:vendor, vendor.verified, and vendor.approved.
  • Layout is layouts/dashboard.blade.php.
  • Signed app-bridge routes let the Flutter vendor app open specific panel pages.

Admin panel

  • Routes under prefix('admin') with auth:admin.
  • Layout is layouts/admin.blade.php and owns the entire sidebar navigation map.
  • Every page is additionally guarded by a permission: middleware key.

Request lifecycle

The API stack prepends ForceJsonResponse and SetApiLocale; the web stack appends SetLocale. Successful API responses use a { success, data, meta } envelope. Errors are rendered by App\Support\ApiException into { success: false, error: { message, code, fields } }, so the mobile clients only ever parse one shape.

Guest redirects are area aware: an unauthenticated admin/* request lands on admin.login, a vendor or dashboard request lands on vendor.login, and everything else lands on the customer login route.

Custom middleware

AliasClassWhat it enforces
permissionHasPermissionAdmin permission check. Pipe-separated values mean "any of".
auth.optionalOptionalSanctumAuthResolves the user when a token is present but never rejects, so public catalog routes can resolve wishlist flags.
vendor.verifiedEnsureVendorEmailVerifiedVendor email must be verified.
vendor.approvedEnsureVendorApprovedVendor registration must be approved.
guest.checkoutEnsureGuestCheckoutAllowedHonours the guest-checkout setting.

03 / Overview

Installation on shared hosting and on a dev machine

MONO installs two ways. The web installer is the supported path for shared hosting, where you have a control panel but no shell. The Artisan path is for developers and CI. The walkthrough below was captured on a cPanel account; other control panels follow the same shape.

Official Video Tutorial cPanel • 34:39 Full Guide Step-by-Step

Complete Guide to Installing and Setting Up the Mono Market Website and Backend on cPanel

Follow the complete real-time video walkthrough by ClotCode demonstrating every step from blank cPanel hosting to a fully running multivendor marketplace: MySQL database configuration, File Manager upload & archive extraction, running the 5-step interactive web wizard, PHP 8.3 & extension checks, automated background cron scheduling, and live storefront verification.

Before you start

You need the release archive mono-marketplace-installer.zip (built on your machine with python tools/build_release.py — it already contains vendor/, the compiled CSS, and the public tree, so the host needs no Composer, Node, or shell access), a cPanel or similar hosting account with MySQL, and PHP 8.3 or newer. Every screenshot below is clickable for a full-size view.

Part 1 — Create the database in cPanel

  1. Open the database manager.

    In cPanel, scroll to the Databases section and open Manage My Databases.

    cPanel → Databases → Manage My Databases
  2. Create the database.

    Under Create New Database, type a name after your account prefix and click Create Database. cPanel prepends the account prefix — mono becomes something like clotcode_mono. Note the full name; the installer will ask for it exactly.

    Create New Database — note the prefixed name
  3. Create the database user.

    Scroll to Database Users → Add New User, pick a username, and generate a strong password. Note the username and password too, then click Create User.

    Database Users → Add New User → Create User
  4. Attach the user to the database.

    In Add User To Database, select the user and the database you just created, and click Add.

    Add User To Database → Add
  5. Grant all privileges.

    cPanel opens the privilege screen for that user/database pair. Tick ALL PRIVILEGES, then click Make Changes to save. The installer migrates and seeds through this user, so it needs full rights on this database.

    ALL PRIVILEGES → Make Changes

Part 2 — Upload and extract the installer

  1. Open File Manager.

    In cPanel, open Files → File Manager and enter the document root of the domain you are installing on — public_html for the primary domain, or the subfolder the domain points at.

    cPanel → Files → File Manager → public_html
  2. Upload the archive.

    Click Upload and select mono-marketplace-installer.zip. Stay on the page until the progress bar reaches 100%.

    Upload mono-marketplace-installer.zip
  3. Extract it in place.

    Back in File Manager, select the zip, click Extract, keep the destination path /public_html, and click Extract Files. The archive expands into the application tree; delete the zip afterwards to reclaim the space.

    Extract into /public_html

Part 3 — Run the installer wizard

Visit https://your-domain.com/install.php. If .env is still missing, public/index.php also redirects the storefront's first request there automatically. The wizard is five steps and takes about a minute.

  1. Welcome — server requirements.

    The installer checks PHP 8.3+, every required extension, the presence of vendor/, and that storage/, bootstrap/cache/, and .env are writable. When each row reads Ready, continue to the database step.

    install.php — step 1 / 5 — server requirements
  2. Database connection.

    Keep the type on MySQL / MariaDB, leave the host on localhost and the port on 3306 unless your host says otherwise, and enter the database name, username, and password from Part 1. Test connection & create tables verifies the login, writes .env, and runs the migrations. SQLite is also accepted for small or staging installs.

    Step 2 / 5 — database connection & tables
  3. Site & admin account.

    The site URL is auto-detected — change it only if the store will be served from another address or a subfolder. Set the store name and tagline, then create the first administrator; the account is attached to the super_admin role automatically. Leave Install the current catalog demo ticked if you want starter products, categories, brands, vendors, and bundles to explore.

    Step 3 / 5 — site & admin account
  4. Pick a theme.

    Choose one of the nine storefront themes. The installer runs that theme's migrations and copies its assets. You can switch or add themes later in the admin panel under Appearance → Themes.

    Step 4 / 5 — pick a theme
  5. Finish.

    The installer writes storage/app/installed.lock and shows your admin login and URLs. Log in to the Admin Panel → signs you in with a one-time token that deletes itself on first use.

    Step 5 / 5 — installation complete
Two essential post-install actions

1. Delete public/install.php immediately from the server via cPanel File Manager. Until you do, anyone navigating to the URL could re-trigger configuration.
2. Register the Laravel scheduler in cPanel Cron Jobs so order status workflows, coupon expirations, payout calculations, and SMS queues execute on time.

cPanel Server Environment & PHP Configuration

To ensure peak performance and seamless file uploads on cPanel, configure your PHP environment according to the specifications below:

DirectiveRecommended ValuePurpose
PHP VersionPHP 8.3 or PHP 8.4Select in cPanel → MultiPHP Manager.
memory_limit256M or 512MRequired for processing catalog imports, image resizing, and PDF invoices.
upload_max_filesize64MSupports product images, downloadable digital archives, and vendor attachments.
post_max_size64MMust match or exceed upload_max_filesize.
max_execution_time300Allows ample execution time for multi-vendor batch migrations and catalog seeds.
max_input_vars3000Required for large multi-attribute product matrices and dynamic nested permissions.

cPanel Cron Jobs Configuration

In cPanel, open Advanced → Cron Jobs. Set the schedule to Once Per Minute (* * * * *), and enter the command line matching your hosting account user path:

cPanel Laravel Task Scheduler Cron (Every Minute)
* * * * * /usr/local/bin/php /home/username/public_html/artisan schedule:run >> /dev/null 2>&1

Note: Replace username with your actual cPanel username. If your host uses a versioned PHP CLI path, specify /usr/local/bin/ea-php83 instead of /usr/local/bin/php.

Background Queue Processing on Shared Hosting

If your shared hosting plan does not permit daemonized Supervisor processes, process your background job queues (order confirmation emails, push notifications, and gateway status checks) by configuring a secondary cron job running every 2 to 3 minutes:

cPanel Background Queue Worker (Every 2 Minutes)
*/2 * * * * /usr/local/bin/php /home/username/public_html/artisan queue:work --stop-when-empty --tries=3 >> /dev/null 2>&1

Email (SMTP) Configuration

Configure outgoing email delivery directly via the Admin panel under Settings → Mail / SMTP, or by adjusting your server's .env file:

Production .env Mail Setup
MAIL_MAILER=smtp
MAIL_HOST=mail.yourdomain.com
MAIL_PORT=465
MAIL_USERNAME=noreply@yourdomain.com
MAIL_PASSWORD=your_secure_email_password
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS=noreply@yourdomain.com
MAIL_FROM_NAME="Mono Marketplace"

Host requirements

Required extensions

pdo_mysql or pdo_sqlite, mbstring, openssl, tokenizer, xml, ctype, json, fileinfo, gd, zip, intl.

Not required on host

Composer, Node, npm, and shell access are all unnecessary on the host. Everything the runtime needs is already in the release archive.

Writable paths

storage/, bootstrap/cache/, and public/storage must be writable. public/storage is a symlink to storage/app/public, which is where every upload lands.

Developer install

01 / Installcomposer install
02 / Keyphp artisan key:generate
03 / Schemaphp artisan migrate --seed
04 / Storage linkphp artisan storage:link
05 / Servephp artisan serve
06 / CSSnpm run build:css

After migrate --seed the demo data creates an administrator at admin@mono.test with the password password, a customer at jordan@example.com, and eight vendors whose passwords are also password. Change or remove all of these before the site is reachable by anyone but you.

04 / Overview

Updates, scheduled jobs, and the queue worker

Applying an update

  1. Back up first.

    Copy the database and the storage/ directory. Uploads and the environment file both live outside version control.

  2. Replace the code.

    Upload the new release archive and extract it over the existing files. Do not delete storage/, .env, or public/storage during extraction.

  3. Migrate.

    Run php artisan migrate. Theme migrations are registered automatically by ThemeServiceProvider, so migrate picks up tables declared by the active theme.

  4. Rebuild CSS if you customised it.

    Run npm run build:css only if you edited resources/css/app.css or a theme stylesheet.

  5. Clear caches.

    php artisan config:clear, php artisan cache:clear, and php artisan view:clear are enough after a migration run.

There is no in-app updater.

MONO is updated by replacing files and migrating. It deliberately does not phone home or self-modify, which is what makes it safe to run on a locked-down shared host.

Scheduled jobs

Background work is data-driven rather than hard-coded in routes/console.php. The scheduled_jobs table holds each job's key, Artisan command, frequency, and enabled flag, and ScheduledJobService registers them with Laravel's scheduler at boot. Admins edit the table at /admin/settings/scheduled-jobs and can run any job on demand.

FrequencySeeded jobs
Every minuteorders:cancel-expired, ai-credits:cancel-expired
Every five minutesfeatured:expire
Dailyvendor-packages:notify-expiry, loyalty:expire-points, badges:expire-new

For these to run, the host's cron must hit /<path>/artisan schedule:run every minute. On shared hosting that is usually a cron entry in the control panel rather than a real crontab.

EVERY MINUTE* * * * * cd /path/to/mono && php artisan schedule:run >> /dev/null 2>&1

Queue worker

Three jobs send mail or push notifications: newsletter campaigns, customer push, and vendor push. A long newsletter campaign sets timeout = 1800 precisely because it chunks through subscribers in a single worker run. If you leave notifications and newsletters enabled, run a worker.

QUEUEphp artisan queue:work --tries=2
WINDOWSqueue-worker.bat

Without a worker the jobs simply sit in the jobs table. Orders, payments, and the admin panel all continue to work; only asynchronous email and push are delayed.

05 / Storefront

Themes and the theme engine

A theme is a folder under themes/ that contains a theme.json manifest. The filesystem is the source of truth: a theme exists because its folder exists, and removing the folder removes it from the admin panel.

Every theme below was activated live and captured from its home page at a desktop viewport, so what you see is what each theme actually ships — hero, palette, typography, and storefront sections.

MONO — built-in default
Beauty & Wellness
Electronics
Fashion
Mono Edge (fullwidth)
Grocery
Home & Living
Pet Shop
Sports

Switching themes is instant and reversible — the admin panel activates a theme by writing one setting; no rebuild, no downtime. Captures above were taken by activating each theme in turn on the same demo catalog.

SlugNameOwn tables
monoMono (built in)None. The manifest lives at resources/theme/mono.json, outside the removable tree.
beautyBeauty & WellnessNo extra tables.
electronicsElectronicsYes, electronics_features.
fashionFashionYes, fashion_looks.
fullwidthMono EdgeNo extra tables.
groceryGroceryYes, grocery_promos.
homeHome & LivingNo extra tables.
petshopPet ShopYes, petshop_banners.
sportsSportsYes, sports_banners.
config/themes.php is stale.

That file still lists the original eight and omits fullwidth. ThemeManager is the real source of truth; trust the themes/ directory, not the config file.

How view resolution works

ThemeServiceProvider registers resources/views/core under the core:: namespace and calls prependLocation() with the active theme's views path. That single call is what makes theming work: a theme view shadows the default view of the same name, and anything the theme does not provide falls through to the core views. The provider also registers the active theme's migrations/ path with the migrator.

Theme manifests

Each theme.json declares the slug, name, description, version, owned tables[], a full palette, and an assets block (CSS path, Google Fonts URL, body class). The palette carries per-colour labels, defaults, and CSS variable names, shape radii, an isDark flag, status colours for the seven order statuses, and logo and invoice accents.

Uninstalling deletes data.

Removing a theme drops every table declared in its manifest. Take a database backup first. This is why mono is deliberately kept outside themes/: it cannot be uninstalled out from under the site.

App themes are a separate axis

AppThemeRegistry controls the mobile app's visual system, not the website. It offers standard, marketplace, aurora, and nova, with fresh-market as the default. The registry keeps an integer version stamp in active_app_theme_version that devices diff against so the apps know when to re-fetch. Colours always come from the active storefront palette, so the app and the site stay visually consistent.

06 / Storefront

Products, variations, and taxonomy

Product model

ConcernFields
Identityvendor_id, category_id, brand_id, name, slug
Pricingprice, old_price
Merchandisingbadge (new / sale / bestseller), rating, reviews_count, is_featured, featured_until
Inventorystock, released on fulfilment by OrderInventoryService
Digital goodsis_digital, digital_delivery_window, plus assets, licence keys, and entitlements
Contentdescription, long_description, return_days, delivery_method
SEO and i18nseo_title, seo_meta, translations, seo_translations

Variations

Variable products carry product_variations rows. Buyers pick attributes on the storefront and the variation modal resolves the matching row: it opens from any product card, lists one group per attribute, disables unavailable combinations, and enables Add to bag only when the chosen combination is in stock.

Variation picker
Variation selected

Shared taxonomy

Categories, brands, and attributes are shared platform-wide but can also be vendor-owned. Each carries vendor_id and an is_global flag: vendors create their own, and an administrator can promote an entry to global with the "make global" action. Categories are a tree via parent_id and can require approval before they appear.

Attributes carry values in attribute_values. The seeded demo set includes Size, Color, Storage, and Capacity.

Bundles

ProductBundleService validates and replaces a bundle's ordered item list. Variable children can be pinned to a specific variation rather than left open, and cancellation restocks every bundle line through OrderInventoryService.

Digital products are first class.

A digital product owns digital_assets, optional digital_license_keys, and per-purchase digital_entitlements. DigitalDeliveryService owns the lifecycle: an entitlement starts pending on order, becomes active on delivery, and is revoked on cancellation or refund. Download limits and expiry windows are enforced server-side.

07 / Storefront

Cart, coupons, tax, shipping, and order placement

Cart

Carts live in carts and cart_items and are shared between the website and the mobile apps. ShippingMeter produces the product-aware free-shipping progress data the cart drawer renders, and OrderInventoryService is the only place stock is decremented or restored.

Discounts

TableRole
couponsDiscount rules: type, value, minimum spend, maximum discount, usage limit and count, expiry, free-shipping flag, and scope.
coupon_customerPer-customer redemption limits.
customer_vouchersVouchers issued to customers, including loyalty tier redemptions.
loyalty_redemption_tiersTier definitions: name, points cost, voucher value, minimum spend.

VoucherService decides which coupons are publishable as vouchers and serialises the voucher cards the apps display.

Tax and shipping

TaxService is a country-and-state aware tax engine with tax classes, rates, and priorities, so it behaves the way a WooCommerce store does. Shipping is configured per method with a flat rate, a free-shipping threshold, and a free-shipping zone. Both are managed from /admin/settings/shipping and /admin/settings/taxes.

Order placement

OrderPlacementService is the single canonical path for placing an order, used by both the website and the API. It revalidates, server-side, everything the client submitted: order ownership, gateway availability, stock, coupon validity, shipping cost, and tax. Totals are always recomputed on the server; the client total is never trusted.

Never trust a client total.

That is the entire point of this service. If you add a checkout entry point, route it through OrderPlacementService rather than writing totals by hand.

Orders carry a human-readable code such as MN-98412, a denormalised customer name and email for display, and snapshot shipping details so historical invoices stay accurate after a customer edits their address book.

Storefront Shop — faceted filtering, category navigation, and product grid
Product Detail — multi-image gallery, dynamic price, size selection, and voucher claiming

08 / Storefront

Payment gateways

Fourteen payment methods ship in the box: twelve online gateways plus cash on delivery and bank transfer. They are defined as constants on PaymentGatewayService:

ONLINE 12OFFLINE 2TOTAL 14REFUNDABLE 3
GatewayKeyConfiguration fields
Stripecardmode, publishable key, secret key
SSLCommerzsslcommerzmode, store id, store password, currency
PayPalpaypalmode, client id, client secret, currency
Molliemolliemode, API key, currency
Paystackpaystackmode, secret key, currency
Flutterwaveflutterwavemode, secret key, webhook hash, currency
Razorpayrazorpaymode, key id, key secret, currency
Mercado Pagomercadopagomode, access token
MyFatoorahmyfatoorahmode, API token
Xenditxenditmode, secret key
PhonePephonepemode, merchant id, salt key, salt index
Midtransmidtransmode, server key
Bank transferbank_transferlabel, description, instructions, and a list of accounts (bank name, account name, account number)
Cash on deliverycodlabel, description, instructions, extra fee
card means Stripe.

The internal key is card for historical reasons. normalize() maps stripe to card, and configKey() maps card back to stripe for settings storage. Use card in code and stripe in the settings table.

Configuration and availability

Credentials are stored in the settings table under gw_<gateway>_… keys and read through Setting::gateway(). They are deliberately not in .env, so an operator can rotate a key from the admin panel without touching the server. A gateway is only offered at checkout when it is enabled and every required credential is filled, as decided by PaymentGatewayService::isConfigured().

Admins configure all of this at /admin/settings/payment-gateways.

Where the code lives

Order payments

CheckoutController holds a pay* and an initiate* pair per gateway, plus paymentSuccess, paymentCancel, paymentFail, and the paymentIpn webhook receiver.

Everything else

OnlineGatewayPaymentService handles non-order payables: vendor package subscriptions, featured product promotions, and wallet top-ups. It exposes one launch() and one verify() for all twelve gateways.

Raw cURL, no SDKs.

Gateway calls are hand-rolled cURL requests in OnlineGatewayPaymentService. There is no vendor SDK in composer.json, which keeps the install light but means signature verification is your responsibility when you add a gateway.

Scope limits worth knowing

  • Vendor-facing payments are restricted to VENDOR_ONLINE_METHODS: Stripe, SSLCommerz, and PayPal only.
  • Wallet top-ups accept all twelve online gateways, but never wallet or bank_transfer.
  • Only three gateways support refunds: Stripe, PayPal, and SSLCommerz. refundStripe, refundPaypal, and refundSslCommerz live in the admin controller.

09 / Storefront

Customer accounts, loyalty, wallet, and digital downloads

Accounts

Customers register with email and password or sign in with Google. The Google client id is served from the API so the apps and the site share one configuration. Password recovery is an OTP flow: request, verify, then reset. Addresses live in a per-customer address book.

Customer sign-in

Loyalty

LoyaltyService earns points on delivered and paid orders, lets customers redeem tiers as vouchers, and expires stale balances. Every movement is a row in loyalty_transactions with its type, points, linked order or coupon, description, and expiry, so a balance is always auditable.

Administrators configure earning rules at /admin/loyalty and can adjust any customer's balance by hand with a recorded reason.

Wallet

ConcernImplementation
Balancewallets, one per customer.
Ledgerwallet_transactions with type, amount, status, reference, method, description, the acting admin, and free-form metadata.
Top-upsPending by default; an administrator approves or rejects before the balance moves.
Manual adjustmentAdministrators credit or debit any wallet from /admin/wallets.

The use it or lose it rule is deliberate: a pending top-up that is never approved is reversed rather than left dangling.

Digital library

Purchased digital goods appear in the customer's library as digital_entitlements with a download count, a download limit, and an expiry. File delivery uses a signed URL with a thirty-minute expiry, and each download is written to digital_download_logs.

Signed URLs are not access control.

A signed URL proves the link was minted by this server; it does not prove who is asking. Entitlement access is checked when the link is minted, and the file route must verify the caller before streaming. See the launch checklist before going live.

10 / Storefront

Pages, blog, menus, media, and the page builder

Pages

Built-in pages (home, about, contact, FAQ, privacy, terms) are edited at /admin/pages/<slug>/edit. Additional pages are created with the visual custom page builder at /admin/pages/custom, so non-technical staff can compose landing pages without touching Blade.

The homepage hero is a separate editor at /admin/hero. HomePageContent and HomePageSchema make it theme-aware: each theme declares which content keys exist and what fields they accept, so switching themes does not strand a half-configured homepage.

Menus

MenuService reads and writes the header and footer navigation trees at /admin/pages/menus. Menus are nestable, and the reset action restores the theme's shipped default rather than leaving an empty menu.

Blog

Posts are managed at /admin/blog with draft and publish state, cover imagery, categories, and bulk actions. Post content goes through the same translation pipeline as products and pages.

Media library

MediaService backs a shared library at /admin/media, with upload, delete, and bulk delete. Uploads are written under storage/app/public and served through PublicMedia::url().

Upload roots are enumerated by BackfillMedia and cover products, categories, brands, blog, branding, customers, vendors, admins, and AI output. The library is isolated per owner where it matters: a vendor sees their own media, an administrator sees everything.

Validate uploads server-side.

Do not trust the client extension or MIME type. Store uploads outside the document root or with a fixed safe extension, and never let an upload land on a path the web server will execute.

Cookie consent

The consent banner is configured at /admin/settings/cookie and rendered by the shared layout partial, so it appears on every theme without per-theme work.

11 / Storefront

Languages, RTL, and the translation editor

MONO ships English, Arabic, and Bengali. Arabic is a full right-to-left experience, not a mirrored stylesheet: layouts, the admin and vendor panels, and the storefront all flip, and PanelRtlLocalizationTest guards that behaviour.

Two independent defaults

This is the detail that trips people up. Each language has a web default and a separate mobile default, stored as default and default_mobile. Setting English as the web default does not make English the app default. Admins toggle each independently at /admin/languages.

CapabilityWhere
Enable or disable a language/admin/languages
Set the web default/admin/languages
Set the mobile default/admin/languages, the mobile toggle
Edit translations/admin/languages/<language>/translations
Switch locale, signed in/locale/<code>, /admin/locale/<code>, /vendor/locale/<code>

Where strings live

Interface strings

PHP translation files under lang/en, lang/ar, and lang/bn, with JSON files for the long-tail strings.

Mobile strings

Dedicated lang/mobile-en and lang/mobile-ar namespaces, served to the apps from the /translations/{code} API endpoint.

Content translation

Products, categories, pages, and posts all carry a translations column, with a separate seo_translations on products so metadata can be translated independently of body copy. Bulk translation runs through AiTranslationService, which chunks long fields and keeps every field that succeeded, returning the list of anything it had to skip rather than failing the whole record.

12 / Vendor panel

Vendor dashboard and store operations

The vendor portal is served from /dashboard behind the auth:vendor guard, vendor.verified, and vendor.approved middleware. Each vendor operates inside an isolated tenancy boundary: every query is scoped to vendor_id, so merchants only ever see and manage their own store catalog, orders, media, and finances.

Vendor Dashboard — seller KPIs, real-time revenue trend, AI quotas, and quick actions

Dashboard metrics

The landing page provides immediate operational visibility for merchants:

  • Net Earnings (30d): Net income from paid, fulfilled items minus marketplace commission and refunds.
  • Order Volume: Total order count with monthly delta comparisons.
  • Average Order Value (AOV): Average transaction spend across store orders.
  • Live Products: Published catalog count with inventory growth metrics.
  • AI Usage & Quotas: Real-time consumption meter for text generation tokens, translation tokens, and generated product imagery. Displays clear status indicators when allowances are reached.
  • 12-Week Revenue Trajectory: Rolling financial bar chart plotting sales momentum.
  • Top Products Leaderboard: Top 5 revenue-generating items in the vendor's catalog.

Vendor portal navigation

ModuleRouteCapabilities
Overview/dashboardHigh-level store performance, revenue charts, and quota indicators.
Manage Products/dashboard/productsFull catalog CRUD, attribute variations, digital asset uploads, and bundle manager.
Media Library/dashboard/mediaDedicated vendor image and document repository scoped to the merchant.
Manage Orders/dashboard/ordersOrder status updates, customer delivery info, and 1-click digital delivery dispatch.
Delivery & Pickup/dashboard/deliveryOperational delivery tracking and shipping partner handoffs.
Direct Messaging/dashboard/messagesReal-time peer-to-peer customer chat powered by Ably channels.
Settlements & Ledger/dashboard/settlementsItemized commissions, sales ledger, and CSV financial exports.
Withdrawals/dashboard/withdrawalsPayout requests to bank accounts or payment channels against system minimum thresholds.
Store Followers/dashboard/followersCustomer audience engagement and broadcast conversation initiator.
Packages/dashboard/packagesTiered vendor membership upgrades with listing quotas and commission discounts.
AI Credits/dashboard/ai-creditsCredit pack purchases for AI description generation and automated translations.
Store Settings/dashboard/settingsStore name, banner, logo, business address, and payment preferences.

13 / Vendor panel

Products, digital fulfilment, settlements, and AI

Vendors manage their complete ecommerce lifecycle from the web dashboard, supporting physical goods with complex variations as well as instantly deliverable digital goods.

Catalog and variations

Product creation in DashboardController handles both simple and variable products. Variable products synchronize attribute combinations (sizes, colors, fabrics) using VariationSyncService:

  • Each variation has its own SKU, inventory stock, regular price, and sale price.
  • Dynamic matrix creation generates all valid permutations without manual database configuration.
  • Vendors can request new categories (/dashboard/categories/request) or register custom brand names (/dashboard/brands) subject to admin review.

Product bundles

The bundle editor at /dashboard/product-bundles lets vendors group complementary products into discounted bundle packages. ProductBundleController validates inventory across all bundle items and ensures accurate pricing on checkout.

Digital goods & instant delivery

Products marked is_digital support downloadable files, license keys, and digital entitlements. When an order is placed and paid:

  • The vendor accesses /dashboard/orders/{order} and triggers orders.digital-deliver.
  • The system mints a DigitalEntitlement record with access limits and secure download tokens.
  • The customer immediately receives download access in their account library and via email notification.

Settlements and withdrawals

Every completed sale generates a settlement row calculating the exact revenue breakdown:

Settlement Equation: Net Vendor Payout = Item Subtotal - Platform Commission + Tax (if vendor-collected)

The settlements table at /dashboard/settlements tracks pending, available, and paid earnings. Vendors request payouts via /dashboard/withdrawals once their balance satisfies the minimum withdrawal threshold configured in platform settings.

Featured sponsorships

Vendors can promote individual products to prime homepage and category slots via /dashboard/products/{product}/feature. The vendor chooses a featured duration package and completes payment directly in the panel using Stripe, SSLCommerz, or PayPal. Upon payment verification, the product is flagged is_featured = 1 until the expiration timestamp.

Vendor AI suite

The dashboard incorporates integrated AI tooling powered by OpenAI / Google Gemini providers:

  • Product Description Writer: Generates compelling ecommerce copy and bullet points from minimal attribute inputs.
  • Multilingual Translation: 1-click translation of titles and descriptions across English, Arabic, and Bengali.
  • Credit Packs: When monthly package quotas are exhausted, merchants purchase non-expiring AI credit packs directly through /dashboard/ai-credits.

14 / Admin panel

Dashboard and admin navigation

The admin panel is served from /admin behind auth:admin. The sidebar is defined as PHP arrays in resources/views/layouts/admin.blade.php and filtered by permission, so an administrator only ever sees the modules they can actually use. The same permission keys guard the routes, which means the menu is not the security boundary — the middleware is.

Admin Dashboard — real-time sales overview, revenue trends, and operational status

Navigation groups

GroupModules
OverviewDashboard
CommerceOrders, Invoices, Coupons, Loyalty, Reviews
NotificationPush notifications, Newsletter, Promos
CustomersCustomers
MarketplaceVendors, Delivery pickup, Applications
ContentPages, Blog, Media
CommunicationMessages, Inbox
AdministrationAdministrators, Languages
Themes & appearanceThemes
SettingsSettings

Dashboard

The landing page shows revenue, order, vendor, and customer totals each with a thirty-day delta, a revenue chart, order status counts, the six most recent orders, the five most recent pending vendor applications, and the five top vendors. Every figure is computed from current rows rather than a cached summary, so it cannot drift.

Global admin search

/admin/search is a JSON endpoint that searches a fixed index of roughly forty admin pages plus records, so an operator can jump straight to a setting or an order from anywhere in the panel.

Your own profile

/admin/profile edits your name, email, avatar, and password. It is deliberately outside every permission gate: changing your own password must never depend on a role assignment that someone else controls.

15 / Admin panel

Administrators, roles, and the permission system

There is no admin self-registration. The first account is created by the installer; every account after that is created by another administrator at /admin/administrators. Administrators authenticate against the admin guard and the App\Models\Admin model, which is entirely separate from customers and vendors.

How permissions work

Roles and permissions are hand-rolled, not Spatie. Five tables carry it: roles, permissions, the role_permission pivot, the admin_role pivot, and admins. A role carries an is_super flag, and Admin::hasPermission() returns true unconditionally for a super administrator.

Routes declare a permission through the permission: middleware. Pipe-separated values mean "any of", so permission:products.manage|blogs.manage|pages.manage grants access to any one of the three.

Seeded roles

RoleSuperAccess
super_adminYesEverything, plus the bypass in hasPermission().
administratorNoAll 32 permissions. Effectively identical day to day; it simply cannot grant itself new ones by virtue of existing.
editorNodashboard.view, orders.view, coupons.manage, reviews.manage, products.manage, customers.manage.

Administrators and roles are managed at /admin/administrators and /admin/roles, guarded by admins.manage and roles.manage.

The thirty-two permissions

GroupPermissions
Overviewdashboard.view
Commerceorders.view, coupons.manage, loyalty.manage, notifications.manage, promos.manage, reviews.manage, products.manage, customers.manage, wallets.manage
Marketplacevendors.manage, vendors.impersonate, registrations.manage, packages.manage, delivery.settings.manage, applications.manage, payouts.manage, delivery.manage
Administrationmessages.manage, languages.manage, themes.manage, shipping.manage, taxes.manage, locations.manage, payments.manage, settings.manage, blogs.manage, pages.manage, media.manage, roles.manage, admins.manage
Impersonation is a separate grant.

vendors.impersonate is deliberately not bundled into vendors.manage. "Login as vendor" signs you in as a seller with their full session, so it should be held by as few people as possible and revoked when someone leaves.

16 / Admin panel

Orders, invoices, and refunds

Everything in the commerce group sits behind orders.view. The order list filters by status, including the returned state, and the detail page is the hub for everything about a purchase.

Order actions

ActionRouteEffect
CreatePOST /admin/ordersManual order, with product and customer search endpoints behind it.
Change statusPATCH /admin/orders/{order}/statusDrives customer notifications and loyalty accrual.
Change paymentPATCH /admin/orders/{order}/paymentStatus, method, and reference.
Generate invoicePOST /admin/orders/{order}/invoiceRenders a PDF through dompdf.
Email invoicePOST /admin/orders/{order}/invoice/sendSends the rendered document.
DeleteDELETE /admin/orders/{order}Single, or bulk via bulk-destroy.

Invoices

InvoiceService issues numbered invoices and is idempotent per (kind, trigger, reference), so a repeated trigger cannot produce a duplicate document. Each invoice stores its number, sequence, fiscal year, kind, trigger, bill-to details, a line breakdown, the company block as a JSON snapshot, the generated PDF path, and its email delivery status and error.

Triggers are configurable at /admin/settings/invoices: order placed, paid, delivered, status change, refund, wallet adjustment, package purchase, and featured promotion. The number prefix, tax ID, and footer are set in the same place.

One active invoice per order.

Regenerating supersedes rather than stacks, and the invoice list at /admin/invoices shows the current document for each order plus the full history of downloads and re-sends.

Refunds

Refunds are supported for three gateways only: Stripe, PayPal, and SSLCommerz. The admin controller exposes refundStripe, refundPaypal, and refundSslCommerz. There is no generic refund path, because a generic path would be a generic way to lose money.

17 / Admin panel

Catalog administration

The whole catalog block shares one permission, products.manage. That is a deliberate simplification: a catalog that a role can add to but not moderate is worse than one it cannot touch at all.

PRODUCTS

Listing and editing

  • Create and edit products with digital assets, variations, SEO, and per-field translations.
  • Bulk and single delete.
  • Digital assets, removed files, and bulk licence keys are handled by DigitalProductService, shared with the vendor form.
BUNDLES

Bundled sets

  • Create and edit bundles at /admin/product-bundles.
  • Candidate lookup for choosing line items.
  • Variable children can be pinned to a specific variation.
  • Cancellation restocks every line.
FEATURED

Paid promotion

  • Feature or unfeature any product by hand.
  • Approve or reject vendor featured-placement requests.
  • Define and price the featured packages themselves.
  • featured:expire clears lapsed placements automatically.

Taxonomy approval

Vendors create their own categories, brands, and attributes. Those entries are pending until an administrator acts on them:

  • Categories are a tree via parent_id and are approved or rejected at /admin/categories.
  • Brands and attributes can be promoted to global with "make global", which makes them available to every vendor.
  • Every list view has a JSON search endpoint, which is what the pickers in the product forms call.

Review moderation

Reviews are managed at /admin/reviews with approve, reject, status change, and bulk delete. ProductReviewService resolves who is allowed to review what from the review settings, so the eligibility rule is one decision rather than one per entry point.

18 / Admin panel

Customers, wallets, coupons, and loyalty

customers.manage

Customer accounts

  • List, create, edit, and delete customer accounts.
  • Bulk delete for cleanup.
  • Search powers the manual-order customer picker.
wallets.manage

Wallets

  • Wallet list, single wallet detail, and the full transaction ledger.
  • Pending top-ups queue for approval or rejection.
  • Manual credit and debit with an attributed administrator.
coupons.manage · loyalty.manage

Discounts and points

  • Coupon CRUD, bulk delete, and scoped target search.
  • Loyalty settings and redemption tier CRUD.
  • Manual point adjustment with a customer search.

Wallet approval flow

  1. Customer requests a top-up.

    The request is written to wallet_transactions as pending. Money has not moved and the balance has not changed.

  2. It appears in the admin queue.

    /admin/wallets/pending lists every outstanding request with its method, reference, and amount.

  3. An administrator approves or rejects.

    Approval credits the wallet and records the administrator. Rejection leaves the balance untouched. Neither is silent: both are ledger rows.

Unapproved requests that expire are reversed rather than left open, which is why a pending top-up never becomes a surprise balance later.

Wallet writes are auditable by design.

Every transaction row carries the acting admin_id when a person caused it, plus a reference, method, and free-form metadata. If you add a balance path, populate all four.

19 / Admin panel

Vendors, packages, settlements, and withdrawals

This is the largest group in the admin panel, and it is split across three permissions so that approving a seller, selling them a plan, and paying them are three separate decisions.

Getting vendors on the platform

SurfaceRoutePermissionWhat it does
Applications/admin/applicationsapplications.manageSubmissions from the public "become a vendor" form, with per-item and bulk status actions.
Registrations/admin/vendors/registrationsregistrations.manageApproval workflow with a configurable approval mode and its own settings page.
Vendors/admin/vendorsvendors.manageCRUD, verify toggle, suspend toggle, bulk delete.
ImpersonationPOST /admin/vendors/{vendor}/impersonatevendors.impersonateOpens a full vendor session as that seller.
Store FAQs/admin/faqsvendors.managePer-store FAQs, with translations.

Verification, suspension, and approval are independent states. A verified vendor can still be suspended, and suspending one does not delete their catalog.

Subscription packages

Vendors buy plans. A vendor_packages row carries price, duration, a benefits array, and either a global commission or a per-package commission percentage and fixed amount. VendorBillingService owns the maths, and subscriptions snapshot price, duration, and commission at the moment of purchase so a later plan edit cannot rewrite history.

Lifecycle

  • Requested, then approved, rejected, or cancelled by an administrator.
  • Payment status tracked separately from subscription status.
  • vendor-packages:notify-expiry warns vendors before a plan lapses.

Settlement lines

  • One line per vendor and subscription.
  • Gross, discount, commission, vendor earning, admin earning.
  • Refund columns so a reversed commission stays visible instead of vanishing.

Money out

  • Settlements at /admin/vendors/settlements show the full ledger: gross, discount, commission, vendor earning, and admin earning per line.
  • Withdrawal requests at /admin/vendors/withdrawals are approved or rejected; paid and rejected history is kept separately at /admin/vendors/withdrawals/history.
  • Global presets at /admin/vendors/withdrawal-presets define the supported methods (bank transfer, PayPal, bKash, and so on).
  • Per-vendor accounts at /admin/vendors/{vendor}/withdrawal-methods hold the payout destination for that seller.
Route ordering matters here.

The payouts group must stay above the vendors group in routes/web.php. The static withdrawal-presets path has to match before the /vendors/{vendor} wildcard swallows it. If you reorder those groups, that page will 404.

AI credit packs

Sellors can buy token packs for the AI suite. Packs are defined at /admin/vendors/ai-packs, purchases are approved or rejected, and a pack can also be granted directly to a vendor. AiCreditPurchaseObserver fires the "credits added" notification when a purchase becomes active from any flow, and de-duplicates it against the explicit notification call so sellers are not told twice.

20 / Admin panel

Delivery operations

Delivery covers the physical path from a vendor's shelf to a customer's door. It is split into operations and configuration, because the people who run shipments are rarely the people who set courier contracts.

Operations, behind delivery.manage

SurfaceRouteWhat it does
Pickup requests/admin/delivery/pickup-requestsVendor to warehouse pickups.
Drop-off requests/admin/delivery/dropoff-requestsWarehouse to customer drop-offs.
Request lifecyclePOST /admin/delivery/requests/{id}/…Approve, reject, schedule, assign personnel, change status, add a note, set the warehouse.
Shipments/admin/delivery/shipmentsOutbound shipments with courier, tracking number, and tracking URL.
Tracking/admin/delivery/trackingOne merged timeline per order.

Every state change on a request is written to delivery_request_events, so the timeline is a record rather than a reconstruction. TrackingService merges request events and shipment events into the single per-order timeline that customers and vendors both see.

Order status is synced conservatively.

ShipmentService moves the parent order forward only when the evidence is unambiguous. It will not downgrade an order to match a lagging shipment, because a late courier scan should never tell a customer their delivered order is processing again.

Configuration, behind delivery.settings.manage

Delivery personnel

CRUD for delivery staff at /admin/delivery/personnel, assignable to any request.

Warehouses and pickup points

/admin/delivery/locations, created, updated, and deleted.

Couriers

/admin/delivery/couriers, the courier partners available when creating a shipment.

Feature flags

/admin/delivery/settings via DeliverySettings, including the delivery_notify_* switches that gate vendor, admin, and customer notifications.

DeliveryService is the single source of validation shared by the vendor panel, the admin panel, and the API, so a request created from any of the three is checked the same way.

21 / Admin panel

Chat, messages, push, and newsletters

Realtime chat inbox

The admin inbox is the platform operator's view of every customer-vendor conversation. It uses Ably for delivery with a REST polling fallback, so the inbox still works if the realtime connection drops.

RouteRate limitPurpose
inboxnoneConversation list
inbox/{conversation}noneMessage thread
inbox/reply20 / 1 minSend as the platform
inbox/{id}/pollnonePolling fallback
inbox/{id}/delivered60 / 1 minRead and delivered receipts
inbox/ably/token30 / 1 minShort-lived client token
inbox/messages/removenoneModeration removal

The Ably secret never leaves the server. AblyTokenService mints short-lived client tokens per request, and routes/channels.php authorises each private channel against the correct guard. Admin access to a conversation requires both messages.manage and that the conversation belongs to the platform vendor.

Message content is untrusted.

It arrives from customers and vendors. Escape it on render, and do not allow it to introduce markup. Chat attachments are served through signed URLs with a one-hour expiry, and the download route must verify the caller.

Contact messages

The public contact form feeds /admin/messages. ContactMessageService persists the message first and then treats the admin notification email as best-effort, so a mail failure never loses a customer's enquiry.

Push notifications

/admin/notifications/push broadcasts to customer devices over Firebase Cloud Messaging. FcmClient implements the FCM HTTP v1 API directly, including JWT-bearer OAuth2 token caching, so no Firebase Admin SDK is required. SendPushNotification fans out across a customer's registered devices and prunes tokens that have gone dead.

Service-account credentials are uploaded at /admin/settings/firebase and stored in the database, not .env.

Newsletters

/admin/newsletter manages subscribers, a campaign editor, CSV export, test sends, and dispatch. SendNewsletterCampaign chunks through active subscribers in one worker run with a thirty-minute timeout, which is why a queue worker matters on a real site.

Promo banners

/admin/promos controls the promotional banner shown on the mobile apps' home screen. It is a separate surface from the storefront hero, so a campaign can target app users without changing the website.

22 / Admin panel

AI tools, providers, and vendor quotas

The AI toolset is available to authenticated administrators for translating content, generating copy, and generating images. Providers and model selection live at /admin/settings/ai.

What administrators can do

TRANSLATE 20 / minTAXONOMY BATCH 10 / minGENERATE rate limited
  • POST /admin/translate translates arbitrary content, throttled to 20 requests per minute.
  • POST /admin/translate/taxonomy bulk-translates taxonomy in one pass, throttled to 10 per minute.
  • POST /admin/generate and POST /admin/generate-image produce copy and imagery, gated by products.manage|blogs.manage|pages.manage.

Translation runs through AiTranslationService, which chunks long fields and keeps every field that succeeded. A record with one untranslatable field is partially translated rather than rolled back, and the response lists what was skipped.

Vendors get metered access

Sellers reach the same tools from the vendor panel and the vendor API, but through VendorAiQuotaService, which enforces three separate pools:

POOL 1

Text generation

Tokens consumed by copy and description generation.

POOL 2

Translation

A separate token pool, because translation volume scales very differently from authoring.

POOL 3

Images

Counted per generated image rather than per token.

Vendor AI endpoints are throttled to 30 requests per minute each, and quota is checked before the call, not after. Credits are drawn down from the active package allowance, and unused allowances expire rather than rolling over: ai-credits:cancel-expired runs every minute.

Every AI call is recorded in ai_usage with its provider, model, token counts, and cost attribution, so you can reconcile spend against packages.

AI keys live in the database.

Provider credentials are stored through the settings store and edited from the admin panel. They are never written to .env, and they are never returned to the browser.

23 / Admin panel

Themes, app themes, and colours

Everything here sits behind themes.manage and lives under /admin/appearance.

SurfaceRouteWhat it does
Branding/admin/appearance/brandingSite logo, favicon, and brand marks.
Storefront themes/admin/appearance/themesInstall, activate, and remove storefront themes.
App themes/admin/appearance/app-themesActivate the mobile app's visual system.
Colours/admin/appearance/colorsEdit and reset the active palette.

Palette resolution

ThemePalette maps a manifest palette onto CSS custom properties. Each colour carries a label, a default, and a variable name, so an administrator sees "Button background" rather than --el-primary-500. The palette also carries shape radii, an isDark flag, and status colours for the seven order statuses, so a dark theme automatically gets readable status badges instead of inheriting light ones.

Storefront and app themes move independently

Switching the storefront theme does not change the mobile app's layout, and switching the app theme does not change the website. They share a colour palette, but each has its own activation state. Legacy /settings/branding, /settings/themes, and /settings/colors URLs redirect here, so old bookmarks and old documentation links keep working.

Removing a theme is destructive.

Uninstall drops every table the manifest declares. The mono theme is kept outside the removable tree for exactly this reason.

24 / Admin panel

Settings reference

/admin/settings redirects to the shop settings page. The rest of the group is a set of focused screens rather than one long form, so an operator changes one thing at a time.

PageRoutePermissionContains
Shop/admin/settings/shopsettings.manageSite name, tagline, contact details, currency, free-shipping threshold, flat shipping, product grid columns, pagination.
Shipping/admin/settings/shippingshipping.manageShipping rates, flat rate, free-shipping zone, and shipping method CRUD.
Locations/admin/settings/countrieslocations.manageCountries with nested states and cities.
Taxes/admin/settings/taxestaxes.manageTax classes and rates with priorities.
Payments/admin/settings/payment-gatewayspayments.manageAll fourteen gateway configurations.
Invoices/admin/settings/invoicessettings.manageEnable, triggers, number prefix, tax ID, footer.
SMTP/admin/settings/smtpsettings.manageMail server and a test-send button.
Firebase/admin/settings/firebasesettings.manageFCM web config and service-account upload.
Scheduled jobs/admin/settings/scheduled-jobssettings.manageJob table with frequency, enable/disable, and run-now.
Chat/admin/settings/chatsettings.manageAbly configuration.
Mobile app/admin/settings/mobile-appsettings.manageApp-facing configuration served to both Flutter clients.
AI/admin/settings/aisettings.manageProviders, model list, and translator, generator, and image model selection.
Cookie consent/admin/settings/cookiesettings.manageConsent banner copy and behaviour.
Settings are stored in the database.

Everything above is key and value rows in the settings table, not environment variables. That is deliberate: it lets an operator change a mail server or rotate a payment key without shell access. It also means backing up the database backs up your configuration.

25 / Admin panel

Administrators, roles, languages, and media

These four modules sit in the "Administration" group. They are the ones that change who can do what, in what language, and with what imagery.

Administrators

/admin/administrators creates, edits, and deletes admin accounts behind admins.manage. There is no public registration path: the installer creates the first account, and every account after that comes from this screen.

Roles

/admin/roles is where permissions are assigned. Each role is a name, a label, an is_super flag, and a set of permissions drawn from the 32 available. Changing a role's permission set changes it for every administrator holding that role, immediately.

Guard the last super administrator.

An is_super role bypasses hasPermission() entirely. If you remove super-admin status from every account, nobody can restore it through the panel and you will be editing the database by hand.

Languages

/admin/languages manages the three shipped languages plus any you add. Each has a web default and an independent mobile default, an enable/disable switch, and a per-language translation editor at /admin/languages/{language}/translations.

Media

/admin/media is the shared library behind every image picker in the panel, with a JSON listing endpoint, upload, delete, and bulk delete. Uploads land under storage/app/public in the owner-appropriate subdirectory and are served through PublicMedia::url().

The library is owner-isolated: a vendor only ever sees their own media, while an administrator sees everything. MediaIsolationTest guards that boundary.

26 / Backend

API version 1: shape, auth, and limits

Both Flutter clients talk to the same versioned JSON API. The prefix is set once in bootstrap/app.php as api/v1; there is no content negotiation, so the version is explicit in every path.

BASE URLhttps://your-domain.com/api/v1

Response envelope

Success

{ "success": true, "data": …, "meta": { … } }. Pagination and other collection metadata live in meta, never inside data.

Error

{ "success": false, "error": { "message": …, "code": …, "fields": { … } } }. fields is present only on validation failures. Rendered centrally by App\Support\ApiException.

Authentication

Sanctum personal access tokens, sent as Authorization: Bearer. Two middleware variants exist, and the difference matters when you add a public endpoint that still wants to know who is asking:

  • auth:sanctum requires a valid token and rejects without one.
  • auth.optional resolves the user when a token is present and never rejects. The public catalog runs on it so that wishlisted and collected flags can be resolved for a signed-in customer without turning browsing into a login wall.

Rate limits

There is no global limiter. Limits are declared per route:

RouteLimit
POST /contact/send10 / min
POST /products/{id}/reviews10 / min
POST /checkout/sessions, wallet-pay, cod-pay30 / min
Vendor AI generate, generate-image, translate30 / min
Vendor messages/start10 / min
Vendor messages/{id}/reply30 / min
Auth endpoints are not throttled in this build.

Login, register, and the OTP endpoints have no rate limit attached. If you are hardening a public deployment, add a limiter to those before launch; see the launch checklist.

Signed routes

Two download routes sit deliberately outside auth:sanctum and are guarded by signed URLs with expiry instead: chat attachments at one hour, and digital product files at thirty minutes. Signing replaces the bearer token in the query string so the URL can be handed to a browser or a download manager without leaking a session credential.

27 / Backend

Customer API surface

Roughly forty endpoint families serve the customer app. The shape below is grouped by what a user is doing rather than by controller, because that is how the apps consume it.

Session

  • POST /auth/login, /auth/register, /auth/google
  • GET /auth/google-config returns the client id
  • POST /auth/forgot-password/send-otp, /verify-otp, /reset
  • POST /auth/logout, /auth/change-password
  • GET and PUT /auth/profile

Discovery

  • GET /home, /home/discover, /home/featured
  • GET /categories, /products, /products/{id}, /products/{id}/related
  • GET /products/{id}/reviews, /flash-sale
  • GET /search/suggestions, /vendors/{id}
  • GET /vouchers — the public voucher pool

Cart and checkout

  • GET /cart, POST /cart/add, POST /cart/qty, DELETE /cart/{productId}, POST /cart/clear
  • POST /coupons/apply, DELETE /coupons
  • GET /checkout, POST /checkout/place
  • POST /checkout/paypal/create for native PayPal
  • POST /checkout/sessions opens a hosted web checkout; /checkout-sessions/{uuid}/status, /cancel, /wallet-pay, and /cod-pay complete it
  • POST /orders/{id}/payment-session retries payment for an existing order

Account

  • GET /orders, /orders/{id}, /orders/{id}/tracking, /orders/{id}/reorder
  • GET /invoices/{id}
  • GET /downloads, /downloads/{entitlement}
  • GET, POST, PUT, DELETE /addresses
  • POST /following, POST /vendors/{id}/follow

Money

  • GET /wallet, /wallet/transactions, /wallet/topup-methods, POST /wallet/topup
  • GET /loyalty, /loyalty/transactions, POST /loyalty/redeem
  • GET /vouchers/saved, POST /vouchers/{code}/save, GET /vouchers/{code}
  • Wishlist: GET, POST, and DELETE /wishlist

Engagement

  • GET /notifications, /notifications/unread-count, /notifications/read-all, and per-item read and delete
  • POST /notifications/register-token and unregister-token for FCM
  • GET and PUT /settings/notifications
  • GET /chat, /chat/conversations, /chat/{id}/poll, POST /chat/send, GET /chat/token
  • POST /contact/send for the contact form

Content and localisation

GET /content/about, /content/contact, and /content/page/{slug} return CMS content. GET /country, /country/states/{cc}, and /country/cities/{state} drive the address forms. GET /languages and GET /translations/{code} supply the app's interface strings, including the separate mobile namespaces.

Resources do the shaping.

Controllers return API Resources, not raw models: ProductCardResource, ProductDetailResource, OrderResource, WalletResource, and so on. Add a field to a Resource rather than to the model, or you will change the admin panel's own payloads by accident.

28 / Backend

Vendor API surface

Everything a seller needs is namespaced under /api/v1/vendor, served by sixteen controllers in Api/V1/Vendor/. Vendor auth, profile, and settings mirror the customer equivalents.

Store

  • GET /dashboard — the seller overview
  • GET, POST /products; GET, PUT, DELETE /products/{id}
  • GET /categories, /brands, /attributes, /languages as reference data
  • GET, POST /product-bundles and the item routes, with /product-bundles/candidates for the picker
  • GET, PUT /settings, PUT /settings/payouts, /settings/notifications

Orders and delivery

  • GET /orders, /orders/{id}
  • PATCH /orders/{id}/status for fulfilment
  • POST /orders/{order}/digital-deliver/{entitlement} for per-item digital delivery
  • GET, POST /delivery/requests and POST /delivery/requests/{id}/cancel
  • GET /delivery/history, /delivery/tracking, /delivery/tracking/orders/{id}
  • POST /delivery/shipments, PATCH /delivery/shipments/{id}/status

Growth

  • POST /categories/request and GET /categories/my-requests
  • GET, POST, PUT, DELETE /brands and /attributes, including /custom lists
  • GET /packages, POST /packages/{package}/checkout
  • GET /featured/packages, /featured/requests, POST /products/{id}/feature/checkout

Money and engagement

  • GET /wallet, /settlements, /withdrawals, POST /withdrawals
  • GET /ai/quota, /ai/credits and the three generation endpoints
  • GET, POST /faqs and GET /followers
  • GET /media, POST /media/upload, DELETE /media/{id}
  • The full notification set under /vendor/notifications
Vendor writes are owner-scoped.

Every mutation on a vendor route is scoped to the authenticated vendor's own records, independent of the client payload. If you add a vendor endpoint, keep that scoping in the controller or a policy; do not trust a vendor id from the request body.

29 / Backend

Schema overview

The application carries 147 migrations producing 94 tables, backed by 78 Eloquent models. Most of the volume is marketplace and delivery surface rather than framework scaffolding.

DomainTables
People and accessadmins, roles, permissions, role_permission, admin_role, customers, vendors, vendor_applications, addresses, password_otps, device_tokens
Catalogcategories, brands, attributes, attribute_values, products, product_images, product_variations, product_bundle_items
Commerceorders, order_items, carts, cart_items, wishlists, coupons, coupon_customer, customer_vouchers, reviews, invoices, shipping_methods, tax_classes, tax_rates
Moneywallets, wallet_transactions, vendor_payouts, vendor_packages, vendor_package_subscriptions, vendor_withdrawal_methods, vendor_withdrawal_presets, vendor_follows
Deliverydelivery_requests, delivery_request_events, shipments, shipment_tracking_events, delivery_personnel, delivery_locations, delivery_couriers
Digital goodsdigital_assets, digital_license_keys, digital_entitlements, digital_download_logs
Engagementconversations, conversation_messages, conversation_message_attachments, conversation_participants, customer_notifications, vendor_notifications, contact_messages, newsletter_subscriptions, newsletter_campaigns, ably_settings
Content and platformpages, posts, media, languages, settings, themes, countries, states, cities, scheduled_jobs, app_checkout_sessions, loyalty_transactions, loyalty_redemption_tiers, featured_packages, featured_product_requests
AIai_providers, ai_usage, ai_credit_packs, ai_credit_purchases, ai_pack_consumptions

Design decisions worth knowing

Orders denormalise their customer

customer_name and email are copied onto the order, so a historical invoice still prints correctly after the customer renames their account or changes their address book.

Subscriptions snapshot their terms

Price, duration, and commission are copied onto the subscription row at purchase time, so editing a package never rewrites what a vendor was actually sold.

Settings are key and value

Runtime configuration lives in one settings table rather than in files, which is what makes the admin panel able to change mail servers and payment keys without server access.

Demo seeders

DatabaseSeeder orchestrates the rest: RolePermissionSeeder creates the three roles and the 32 permissions, LocationSeeder fills countries, states, and cities, TaxSeeder sets tax classes, and PageSeeder creates the built-in pages. Beyond that, RealisticOrderAndReviewSeeder generates plausible orders and reviews, MonoDeskKitSeeder builds a full platform-vendor bundle catalogue, and DemoDigitalProductsSeeder and ScopedVoucherSeeder populate digital goods and vouchers per audience.

Never ship the demo credentials.

The seeders create admin@mono.test and jordan@example.com, both with the password password, and eight vendors on the same password. They also embed Firebase web keys. Run migrate without --seed on a real deployment, or remove these accounts before the site is public.

30 / Backend

Services, jobs, events, and commands

Business rules live in services, not in controllers. If a rule is about orders, money, delivery, or entitlement, it belongs in app/Services.

The services that matter most

COMMERCE

Order placement

OrderPlacementService is the canonical order path for web and API. OrderInventoryService is the only place stock moves.

MONEY

Billing

PaymentGatewayService and OnlineGatewayPaymentService own gateway availability, launch, and verification. VendorBillingService owns commission and settlement arithmetic.

DELIVERY

Fulfilment

DeliveryService validates requests from every entry point. ShipmentService runs the shipment lifecycle, TrackingService merges timelines, and DeliveryNotificationService fans out the alerts.

CONTENT

Documents

InvoiceService numbers and renders invoices. DigitalDeliveryService owns entitlement state. LoyaltyService and VoucherService own points and vouchers.

REALTIME

Messaging

ConversationService manages participants. AblyTokenService mints short-lived client tokens so the Ably secret never leaves the server.

PLATFORM

Infrastructure

ScheduledJobService registers the database-driven job table. MediaService, MenuService, and HomePageContent support the storefront editing surfaces.

Jobs and events

Three queued jobs: SendNewsletterCampaign (chunked, timeout = 1800), SendPushNotification, and SendVendorPushNotification (both tries = 2, both pruning dead device tokens). One observer, AiCreditPurchaseObserver, fires the vendor notification when a credit purchase becomes active from any flow and de-duplicates it.

Four broadcast events run through Ably: MessageSent, MessageRemoved, ChatReceiptUpdated, and VendorNotificationReceived.

Artisan commands

CommandPurpose
orders:cancel-expiredCancels unpaid orders past their window and restores stock.
ai-credits:cancel-expiredReverses unsettled AI credit purchases.
featured:expireClears lapsed featured placements.
loyalty:expire-pointsExpires stale point balances.
badges:expire-newRetires the "new" badge window.
vendor-packages:notify-expiryWarns vendors before a plan lapses.
media:backfillRe-resolves media records for the upload roots.
catalog:export-releaseExports a catalogue artifact for the installer.
release:install-catalog-themesInstalls catalogue theme packages on a release host.
release:backfill-catalog-mediaRestores catalogue imagery after an import.
release:verify-schemaVerifies the expected tables exist before go-live.
Run release:verify-schema after an update.

It checks that the expected tables are present, which catches a partially applied migration before it becomes a missing-column error in production.

31 / Backend

Going live: deployment and the pre-launch checklist

Production configuration

ENVIRONMENTAPP_ENV=production
DEBUGAPP_DEBUG=false
URLAPP_URL=https://your-domain.com
CRON* * * * * cd /path && php artisan schedule:run
QUEUEphp artisan queue:work --tries=2
SCHEMA CHECKphp artisan release:verify-schema
Serve everything over HTTPS.

Payment gateways reject non-HTTPS callbacks, Sanctum tokens must not travel in cleartext, and signed URLs are meaningless over an interceptable connection. Terminate TLS in front of the application and redirect HTTP permanently.

Video walkthrough available

For an end-to-end visual walkthrough of setting up production cPanel hosting, database creation, queue crons, and SSL verification, watch the cPanel Video Guide or open it directly on YouTube ↗.

Pre-launch checklist

  1. Remove the demo data.

    Delete the seeded admin, customer, and vendor accounts, or run migrate without --seed. Confirm no @mono.test account can still authenticate.

  2. Delete the installer.

    Remove public/install.php and confirm storage/app/installed.lock exists.

  3. Set the app key and rotate the seeded Firebase keys.

    Run key:generate for a fresh APP_KEY, and replace the Firebase web configuration that the seeders wrote.

  4. Configure only the gateways you need.

    Fill in real credentials at /admin/settings/payment-gateways and leave the rest disabled. A half-configured gateway is hidden from checkout automatically, which is the behaviour you want while you set it up.

  5. Verify mail actually sends.

    Use the test-send button at /admin/settings/smtp. Until mail works, order confirmations, invoices, OTPs, and password resets all fail silently from the customer's point of view.

  6. Confirm the cron and the queue worker are running.

    Unpaid orders will not be cancelled, featured placements will not expire, and newsletters and push will not be delivered without them.

  7. Add rate limiting to the auth endpoints.

    Login, register, and the OTP routes have no limiter in this build. Add one before exposing the site publicly.

  8. Review file upload handling.

    Confirm uploads land outside any executable path, that extensions and MIME types are validated server-side, and that chat attachments and digital downloads verify the requesting user before streaming a file.

  9. Set a real admin password policy and rotate shared logins.

    Particularly before granting vendors.impersonate to anyone.

Backups

Back up the database and storage/ together. The database holds every setting, every uploaded media record, and the entire wallet and settlement ledger; storage/ holds the actual files and the generated invoice PDFs. A backup of one without the other is not a backup.

Verify a restore before you need one.

An untested backup is a hypothesis. Restore into a scratch database and run release:verify-schema to confirm the result is usable.