Skip to content

Running the platform

Platform configuration

Most operational settings can be managed from the admin panel after the backend is installed.

Platform settings

The platform settings area includes:

  • Platform name.
  • Public website URL.
  • Primary color.
  • City name.
  • City Google Place ID.
  • City latitude and longitude.
  • Country.
  • Default currency.
  • Default locale.
  • Platform logo URL.
  • Light logo URL.
  • Dark logo URL.

The admin login page supports light and dark theme handling, so provide logo assets that work on both backgrounds.

Currency

A deployment runs in exactly one currency: every order, parcel booking, wallet and payout is stamped with the platform currency, and the mobile app reads it from GET /api/v1/platform. Set it before going live. Only currencies with two decimal places are accepted, and the setting locks as soon as any order, booking or wallet exists, because every stored amount is in that currency. The country setting is also used for Apple Pay and Google Pay.

Where every setting lives

Settings are grouped into screens under Settings in the admin: General (platform), Payments, Commission (fees, rider pay, payout minimum), Tax (pricing mode and rates), Delivery (dispatch timing, proof of delivery, ETA defaults), Courier onboarding (required documents, minimum age, expiry warnings), Parcel pricing, Notifications, Storage and Integrations. Amounts marked "cents" are in the currency's smallest unit (500 = 5.00). Rates accept decimals (5.5).

Every setting the platform reads has a row with a sensible default; installs and updates create missing ones automatically. To check or repair them by hand, run php bin/console app:settings:sync (add --dry-run to preview). It never changes a saved value.

Sign-in security

Settings → Sign-in security: ask for an emailed code when someone signs in with a password on a new device (off, staff = admins, stores and couriers, or everyone), and how many days a verified device is remembered. Off by default. Details in Admin and vendor panels.

App review accounts (security.two_factor_exempt): the demo accounts the app stores' reviewers sign in with, as comma-separated emails. They are never asked for a code, since reviewers can't receive one; an admin account never is exempt.

Storage settings

Sharkhex Delivery supports local and S3-compatible storage configuration.

Storage settings include:

  • Storage driver: local or s3.
  • S3 region.
  • S3 bucket.
  • S3 key.
  • S3 secret.
  • Public address of uploaded files (storage.public_url): a CDN in front of the bucket, e.g. https://cdn.example.com. New uploads get links through it. To move images uploaded before, run php bin/console app:storage:use-public-url (it shows how many links it would change), then again with --apply; only links to the bucket are changed.

Local storage is simpler for development. S3 is recommended for production deployments that need durable and scalable media storage.

Courier identity and vehicle documents never get a public URL. With local storage they are written to var/private/kyc/ (outside the web root; keep it on persistent disk and back it up); with S3 they go under the private/ prefix with a private ACL, so the bucket policy must not make private/* public. Admins open them through Courier documents → View file.

Integration settings

Integration settings include:

  • Google Maps API key.
  • Pusher app ID.
  • Pusher key.
  • Pusher secret.
  • Pusher cluster.
  • Expo push access token (only when the client's Expo project has Enhanced Security for Push Notifications on).
  • Portal readiness key: set by the portal's connection (Build your apps); clear it to disconnect the portal.

Build your apps. When PORTAL_URL is set in the environment (packs from the portal set it), the installer's last step and the admin's Build your apps open the portal with a one-time link (30 minutes). The portal fetches this platform's address, name, logo, currency, country, language and support email for the app's brand. It receives a readiness key, which lets it read counts and flags (GET /api/v1/platform/readiness) to check, before submission, what the stores' reviewers will meet. It never gets an account here.

Google Maps is used for location and place-related functionality. Pusher is used for real-time rider delivery offers. Push notifications go through Expo to the client's own Expo project. To check them, sign in on the app on a phone, allow notifications, then run php bin/console app:push:test someone@example.com (an email or phone number). It sends a test notification and prints Expo's answer if something is missing. For example, an Android build whose Expo project has no FCM credentials, or a missing access token.

Payment settings

Payment settings include:

  • Stripe publishable key.
  • Stripe secret key.
  • Stripe webhook secret.

These settings are used by the backend payment service. The mobile app must also use the matching Stripe publishable key.

Commission and tax settings

The platform includes configurable commission and tax-style settings, such as:

  • Default vendor commission percent.
  • Service fee percent.
  • Minimum service fee amount.
  • Rider base pay.
  • Rider per-kilometer pay.
  • Surge multiplier.
  • Offer timeout.
  • Maximum delivery offer attempts.
  • Payout schedule.
  • Minimum payout amount.
  • Pricing and tax modes.

Review these values before using the platform in production. Marketplace tax and fee handling can be legally sensitive, so production deployments should be reviewed by the business owner or accountant.

Delivery zones

Zones are the areas the platform delivers in: a city, or part of one. Draw them in Admin → Locations → Delivery zones with the polygon or rectangle tool; several shapes can make one zone.

  • Until the first zone is active, nothing is restricted: any address can order from any store.
  • Once a zone is active, customers outside every zone see "We don't deliver here yet". Store lists only show stores that deliver to the selected address, and an order to an address outside the store's area is refused with the reason.
  • Stores: a store delivers to the zones it is located in. Add it to more zones under "Also delivers to" on the store (or on the zone), and limit how far it delivers with "Delivery radius (km)".
  • Parcels: the pickup and every drop-off must be in the same zone.
  • Couriers are only offered jobs in the zone they are in, by their live location.

Delivery fees

The customer's delivery fee is worked out on the server for each address, in this order:

  1. A store's delivery offer (store form: "Delivery fee offer"), e.g. 0 for free delivery.
  2. The store's own fee ("Delivery fee"), when set.
  3. Otherwise a base fee plus a price per km from the store: the zone's when it has one, else Settings → Delivery & Dispatch (delivery.base_fee, delivery.per_km_fee, in cents; both 0 by default).
  • Free delivery for every store when the basket reaches delivery.free_over_amount (0 = off). The platform pays for it.
  • A store's free-delivery promotion (set up by the store, optionally above a minimum order): the customer pays no delivery fee and the store pays it instead, out of its earning on the order. The platform's own free delivery comes first.
  • Stores setting their own fee in the app is off by default: turn on Stores can set their own delivery fee in the app (vendors.can_set_delivery_fee) to allow it.
  • Surge (on a zone): Off, On now, or On a schedule (weekly hours in the platform's time zone). While it applies, the delivery fee and the courier's pay are multiplied, and customers see that the area is busy. A store's delivery offer isn't raised.
  • Below a store's minimum order (store form): either the order is refused, or it goes through with a small-order fee equal to what the basket is short of the minimum. The fee is the store's: it is charged at the product tax rate and the store's commission applies to it.

Store cards show the fee for the customer's selected address, and the checkout shows the full breakdown the order is charged.

Website

Every instance includes a customer ordering website on its own address (platform.public_url), the domain the app's links already use. It works like the app: customers enter their address and see the stores that deliver there, fill a basket, sign in with a code sent to their phone (or email and password), and check out with a card, a saved card or cash. Prices, fees, promo codes, credit and membership savings are worked out by the same rules as the app.

  • Home page: Settings → Platform → Website home page (web.home_page). storefront (the default) shows the ordering site at /; platform shows the platform presentation page there instead, and the ordering site stays at /stores.
  • Brand: the site takes the platform name, logos (Logo URL (light theme)) and Primary colour (platform.primary_color, e.g. #32c194; use the app's brand 500 shade). Text on the colour turns dark for very light colours.
  • Address search: goes through the server with the Google Maps key, so the key is never shown to visitors. Enable Places API (New) on that key. Without a key, visitors can still use their current location, and type addresses in at checkout.
  • Card payments: need the Stripe publishable key (Payments). Customers pay with Stripe's card form (it also offers Link and the local methods switched on in the Stripe dashboard), and 3-D Secure opens in a Stripe window. Cash on delivery is offered as in the app.
  • Languages: the 16 platform languages, chosen from the browser and switchable in the footer; Arabic is shown right to left.
  • Maps: the courier map on the tracking page uses OpenStreetMap tiles, like the admin's live map.

Branding checklist

Before publishing a buyer build:

  • Replace admin platform name.
  • Replace light and dark logos.
  • Replace mobile app icon.
  • Replace mobile splash image.
  • Update app package identifiers.
  • Update colors to match the buyer brand.
  • Update email sender and mail templates if included in your release.
  • Update terms, privacy, help, and support content.

Found a mistake? Tell us.