1. What is Zippy Island?
Zippy Island is a food ordering and delivery platform for Port Blair. Its first service zone runs from Aberdeen Bazar to Sippighat, about 10 km of the town where most restaurants and most hungry people are. Customers order from local restaurants, see the full price before paying, track each step, and pay by UPI, card or cash. The site is live at zippyisland.in, and the Android and iPhone apps are listed there as coming soon.
I built it for the Zippy Island team as a lean, fixed-budget MVP. That shaped every decision. Anything that could be a setting became a setting rather than code, and anything that belonged in Phase 2 (live tracking, wallets, auto-dispatch, coupons) stayed there.
What each side gets:
- Customers: WhatsApp OTP login, restaurant and menu browsing with add-ons and variants, an itemised bill before paying, online or cash payment, order tracking and ratings
- Restaurants: accept or reject orders, mark them ready, manage the menu and packing charge, view earnings, and upgrade to a Premium listing
- Riders: go online, receive offers, pick up, collect cash, deliver, and see earnings
- Admin: partners, zones, fees, surge, dispatch fallback, refunds, payouts, reports and website content
2. The Stack
Flutter (Riverpod, go_router)Laravel · PHP 8.4PostgreSQL + PostGISRedis + HorizonFilament adminGoogle Maps PlatformRazorpayFirebase Cloud MessagingWhatsApp OTP via GeekAdsCloudflare R2I chose PostgreSQL over MySQL for two reasons. CHECK constraints and partial unique indexes protect the money and state data at the database level, and PostGIS handles service-zone polygons and “riders near this restaurant” queries natively. The admin panel and the landing site live in the same Laravel app, which keeps hosting to one deployable.
3. One App, Three Roles
Customers, restaurants and riders share one Flutter app with one store listing. The app shows each person their own experience depending on who signs in. For a fixed-budget MVP, that means one codebase to build, test and release instead of three.
Login follows the role. Customers sign in with their phone number and a 6-digit code sent on WhatsApp, with no password to remember. The code is hashed, expires in 5 minutes, allows 5 attempts and has resend limits, and all of those are admin settings. Restaurants and riders use email and password with self-service reset, and never OTP. Partners can apply from inside the app, confirming their email with a code, and they can sign in once an admin approves them.
4. Pricing the Customer Can See
Food apps lose trust over surprise charges, so the bill is itemised before checkout and the server is the only thing that calculates it. The app asks for a quote. When the order is placed the server recalculates, and if anything changed it rejects the order with a 409 instead of charging a different amount.
The rules (all editable in admin):
- Delivery: ₹30 up to 3 km, then +₹5 per started km. 100% goes to the rider.
- Platform fee: ₹15 including GST on food under ₹199, or ₹30 + 18% GST from ₹199 up
- Surge: switched on by admin as a flat amount or a percentage, and also paid to the rider
- Taxes: each on its own line, with configurable rates
- Minimum order: ₹100 of food
- Round-off: to the nearest rupee as its own line, so cash riders never handle paise
Every charge on an order is stored as a row with its base, tax rate, tax amount, who pays and who receives it. Changing a fee tomorrow never changes yesterday’s orders, and payouts and reports read those rows rather than recalculating. All money is integer paise in the database, in PHP, in Dart and in JSON.
The pricing rules were covered by tests before any screen was built. The tests check the boundaries at exactly 3.0 and 3.01 km, food subtotals of ₹198.99, ₹199.00 and ₹199.01, every tax toggle, packing and surge on and off, and round-off in both directions.
5. Maps, Zones & Distance
The service area is a polygon drawn in admin and checked with PostGIS, so whether an address is serviceable is a single indexed query. Delivery distance is the real road distance from Google Routes, cached so the same trip doesn’t cost a second API call.
If Google fails, checkout carries on. The system falls back to straight-line PostGIS distance multiplied by a road factor, and logs it. Address search and “drop a pin” also go through the backend rather than calling Google from the phone. The API key stays on the server, Android and iOS share one code path, and the same response can say whether that address is deliverable.
6. Rider Dispatch & Cash on Delivery
When a restaurant accepts an order, it’s offered to every approved, online rider within range who has recently sent a location and isn’t already on a delivery. The first rider to accept wins. Accepting locks the order row, and a second rider gets a clean “offer taken” response. If nobody accepts within three minutes, the order appears under “Needs rider” in admin, and an admin can assign, reassign (with a reason, even after pickup) or unassign.
7. State Machines, Payments & the Ledger
Order status and payment status are two separate state machines. Every change goes through them, illegal transitions throw an error, and every transition is written to a history table. That one rule removed a whole class of “how did this order get here?” bugs.
- An online-paid order reaches the restaurant only after the server verifies the payment, by Razorpay signature and webhook. Webhooks are idempotent, so a retry can’t double-count.
- Customers can cancel only before the restaurant accepts. After that, only admin can cancel. Restaurant rejections and auto-cancels (not accepted in 5 minutes) refund automatically.
- Every money movement writes a double-entry ledger row. Payouts to restaurants and riders are made offline and recorded in admin against that ledger, including a rider’s ₹100 emergency cash advance and any cash still in hand.
8. Calls Without Sharing Numbers
Customers, restaurants and riders need to call each other, but order data shouldn’t hand out personal phone numbers. So order responses carry can_call flags instead of numbers, and a single endpoint decides who may call whom and connects them, only while the order is active. Every attempt is logged and rate-limited.
Today it opens the phone dialer. Because the server returns a call instruction rather than a number, switching to masked numbers or in-app calling later is a server change, not an app release.
9. Admin, Push & the Landing Site
The operations team works in a Filament admin: partners and approvals, menus, zones, every fee and threshold, surge, dispatch fallback, refunds, payouts, reports, and website content. Restaurants can buy a Premium listing in the app through Razorpay. Premium puts them first in listings and lets them show admin-approved fliers in the home slider, and a daily job downgrades expired subscriptions automatically.
Push notifications use Firebase Cloud Messaging with Firebase Installation IDs rather than the registration tokens Google is deprecating. Making that switch before the first release means no installed app will ever depend on the old path.
The landing site is plain server-rendered HTML with no JavaScript. The FAQ and mobile menu are built from native <details> elements. FAQ answers pull fees from the live settings, so the site can never quote an old delivery charge.
10. Testing a Three-Sided App
Backend tests run on a real PostGIS database, because zone and distance logic can’t be faked convincingly. Every pricing rule, state transition, ledger posting and permission had a test before it had a screen.
There’s also an end-to-end script that places one cash order through all three roles: customer orders, restaurant accepts, rider picks up, collects and delivers. It runs against a real backend on a throwaway database, which catches the bugs that only show up when the three sides meet.
11. Engineering Lessons From the Build
12. Frequently Asked Questions
What is Zippy Island?
A food ordering and delivery platform for Port Blair, from Aberdeen Bazar to Sippighat. Customers, restaurants and riders use one Flutter app, and the operations team uses a web admin.
How is delivery charged?
₹30 up to 3 km of road distance, then ₹5 for every started kilometre beyond that. The rider receives the full delivery charge.
Why one app instead of three?
One codebase and one store listing is cheaper to build, test and release. The app shows each user the experience for their role when they sign in.
How long does an app like this take?
Zippy Island was planned as a 12–14 week MVP, with the first real order flowing end to end around week six. Similar platforms usually take three to five months, depending on scope.
Can you build a delivery app for my business?
Yes. Whether it’s food, grocery, pharmacy, parcels or any marketplace in Port Blair or the wider Andamans, I can build the apps, backend, admin and payments end to end.
Want a delivery or marketplace app for the Andamans?
Tell me what you want to deliver and where, and I’ll come back with a scope, timeline and price.