Case study · 02 AP Enterprises

Commerce is more than a catalogue.
It is a connected operational system.

Building one platform—live on Google Play and the web—that carries a customer from browsing to a delivered order, and gives the staff and owners behind it the tools to run every step.

SHOP↓ORDER↓OPERATEAP
Company

Electrical and appliance supplier in Lucknow, and an authorised IFB service partner

Role

End-to-end: app, backend, data, payments, and operations

Platform

One app for customers, delivery staff, and admins

Live

Google Play ↗
apelko.com ↗

The problem 01

Connect the customer journey to the work that fulfils it.

AP Enterprises sells cables, switches, fans, appliances, and commercial equipment from brands like Polycab, Crompton, IFB, and Voltas. As the business grew, products, stock, orders, and deliveries were being handled through manual and disconnected processes—with little visibility into inventory and growing administrative effort.

The goal was a single platform where customers could shop and pay, and where every order flowed into the work behind it: stock movements, GST invoices, packing, delivery to the customer's door, cash collection, returns, and refunds—with the owner able to see and manage all of it.

Constraints 02

Ship a real store that handles real money—built, released, and run by one engineer.

  • One developer across app, backend, infrastructure, and release
  • Android, web, and three roles from a single codebase
  • Online payments and cash on delivery, with no room for double charges
  • GST-compliant invoices on every order
  • Delivery only within Lucknow, by the business's own staff
  • Low running costs, without giving up backups, logs, or alerts

The system 03

Flutter · Android + web→Go API→PostgreSQL→Cloudflare R2

One business layer, multiple customer and operational surfaces.

A single Flutter codebase builds the Android app and the web app. On a phone it's an app; in a desktop browser customers get a website and staff get an admin console. Behind it, one Go API owns every business rule—pricing, stock, coupons, payments, delivery, returns—over a PostgreSQL schema of 43 tables, with images in Cloudflare R2 and invoices in a private bucket served only by short-lived links. Razorpay handles online payments, MSG91 sends email, and Firebase delivers push notifications.

FlutterGoPostgreSQLsqlcRazorpayCloudflare Pages + R2RailwayFirebaseGitHub ActionsGoogle Play

Under the hood 04

One pattern per domain

Nineteen domains—catalog, inventory, cart, orders, payments, deliveries, invoices, returns, reviews, notifications, and more—each live in their own package with the same shape: routes, handlers, a service holding the rules, and hand-written SQL compiled into typed Go. Dependencies point one way, so the order flow can grow without the codebase tangling.

Payments that assume failure

Every payment is a state machine the server controls. A timed-out gateway call is “ambiguous,” not “failed”; webhooks are signature-verified and de-duplicated; a reconciler polls the gateway for anything left pending; and online orders are only created once the money has actually settled—so an abandoned checkout leaves no order and no missing stock behind.

Read the full engineering deep-dive ↗

Key decisions 05

One Flutter app for everything

Android and web, customers, delivery staff, and admins—one codebase with role-based screens, so one developer can ship every surface together.

Trade-off: every screen has to work at phone and desktop widths.
PostgreSQL + sqlc

Hand-written SQL compiled into typed Go, with no ORM and no repository layer. Multi-step writes run in one transaction, and the database enforces what the code relies on.

Trade-off: more SQL to write by hand.
Orders after payment

Online orders are created only once a payment settles, from a checkout snapshot stored on the payment—abandoned checkouts no longer leave pending orders and decremented stock behind.

Trade-off: the checkout snapshot has to carry everything an order needs.
Managed hosting, owned operations

Railway for the API and database, Cloudflare Pages and R2 for the web app and files—with nightly encrypted backups, log shipping, and email alerts built on GitHub Actions and Axiom.

Trade-off: backups and monitoring are ours to build and keep working.

Commerce detail 06

Inventory

Stock has a ledger, not just a number.

Every movement—sale, return, purchase, damage, correction—is written to an append-only ledger in the same transaction that changes the balance, so the two can never disagree. Bundles derive their stock from their parts.

Delivery

Handover is confirmed with a code.

Dispatch sends the customer a six-digit code by email and push; the delivery person enters it on handover to mark the order delivered, and records cash collected for cash-on-delivery orders.

Reviews

Reviews are tied to verified purchases.

A customer must have a delivered order containing the product before they can review it at all, and other customers can report a review for moderation.

Account lifecycle

Deletion respects retention needs.

Customers can delete their account in the app or on the web. After a 14-day grace period it's anonymized, while order and invoice records are kept as tax and consumer-protection law requires.

Foundation created 07

A live platform for the customer journey and the operations behind it.

  • Android app published on Google Play
  • Website and admin console on the web
  • Online payments, cash on delivery, and refunds
  • GST invoices, emailed and downloadable
  • Delivery assignment, handover codes, and returns
  • Nightly encrypted backups, centralized logs, and alerts

Lessons 08

Business workflows need explicit domain models.

Client state cannot own critical transactional rules.

A payment timeout is not a payment failure.

Inventory needs a history, not just a number.

Shipping is the start of operating, not the end of building.

Building connected workflows? 09

Let’s make the
business logic dependable.

If your product needs more than isolated features, let’s talk about the system that will hold the workflow together.

Start a conversation ↗