Case Study

Brew-ha Cafe & Bar

A mobile ordering, payment and loyalty system for an independent café in Siquijor, Philippines. Built end to end: the customer app, the staff tools, and the backend that keeps money and data correct.

Role
Sole developer: product design, mobile app, backend, deployment
Mobile
React Native, Expo, Expo Router, TypeScript, Zustand
Backend
Supabase: PostgreSQL, Row Level Security, Auth, Storage, Edge Functions
Delivery
EAS Build and over-the-air updates, Firebase Cloud Messaging, Google sign-in
Key features
Order ahead and walk-in counter mode, GCash receipt verification, stamp-card vouchers, push notifications, daily cash reports, store open/closed switch, in-app account deletion

The problem

Like many small cafés, Brew-ha ran on paper and messages:

  • Orders were written by hand, so nothing connected what was ordered to what was paid.
  • Customers paid by GCash and showed a screenshot, which staff had to trust or check by hand.
  • There was no loyalty program to bring customers back.
  • At closing, nobody could say how much cash the drawer should hold, so shortages went unexplained.

The owner wanted one system for app orders and walk-ins, without paying for a point-of-sale subscription or a payment gateway.

What I built

For customers

  • A menu with sizes, milk/temperature options, add-ons, a cart, and checkout for pickup
  • Pay in cash at pickup, or by bank or e-wallet transfer with a receipt upload
  • Live order tracking with a daily pickup number, plus push notifications when the order is ready, a payment is confirmed or rejected, or the order is cancelled
  • A stamp card: 10 drinks earn a free-drink voucher, applied automatically at checkout
  • Google or email sign-in, profile editing, password change and in-app account deletion
  • Order history with the full details of every past order, its payment status and the rewards it earned, plus one-tap reorder that puts a past order back in the cart

For staff

  • A real-time order panel with a vibration alert and a push notification for each new order
  • Payment verification: view the receipt, approve, or reject with a reason the customer sees
  • Counter mode for walk-in customers, using the same menu and checkout
  • Cash & Reports: open the drawer, log expenses, and close the day with the expected cash, the counted cash and the difference
  • A menu builder the owner runs without a developer: sizes, milk, sweetness and add-ons set up per category or per product, each with its own price, and items marked sold out instantly
  • Home banners, and an “accepting orders” switch with a custom closed message

Engineering highlights

Prices are decided by the database, never the phone

The app sends only product IDs, quantities and option IDs. A single Postgres function, place_order, looks up real prices, validates every option against the product, applies vouchers under a row lock, and writes the order in one transaction. A modified app cannot change what anything costs, and a voucher cannot be spent twice.

The same principle runs through the whole app: every order, payment, stamp and voucher passes through rules enforced by the Supabase database itself, not by the phone. So a tampered phone or a modified copy of the app gains nothing: it can only ask, and the database decides.

Payment verification without a payment gateway

Customers upload a transfer receipt to a private storage bucket via Supabase. Staff view it through links that expire within minutes, then approve or reject it. Every attempt is a row in a payments ledger with its status, reviewer and timestamp, so a rejected and re-uploaded payment keeps its full history. A database trigger refuses to start preparing any unpaid transfer order, whatever screen tries.

Loyalty as a ledger

Stamps are never stored as a single number. Each change is a row: earned on order completion, and converted into a voucher when it reaches 10 stamps, then the count resets to 0. The balance is their sum, like a bank statement. Triggers award stamps when an order completes and restore a voucher if its order is cancelled.

Security by default

Row Level Security is on for every table, so customers can only read their own orders, receipts and rewards. Staff actions run through functions that check the caller’s role, and functions meant only for the server have execute permission revoked from app users. Webhooks and scheduled jobs authenticate with their own secrets.

A notification pipeline with no polling

A database webhook calls an Edge Function on every order change. The function compares the old and new row to decide whether anything worth a notification happened, finds the right recipients, sends through Expo’s push service, and deletes tokens for uninstalled apps.

Daily operations

Order numbers restart at #001 each day using an atomic counter, so two simultaneous orders never share a number. The cash report calculates sales from the orders themselves and combines them with logged expenses, so sales are never typed in twice. At closing, the expected cash is frozen as a snapshot, so later changes cannot rewrite a closed day.

Privacy built in, not bolted on

Account deletion removes the person’s files, anonymizes their past orders for the café’s records, and then deletes the login, in an order that is safe to retry. A nightly scheduled job deletes payment receipts after one year. The published privacy policy describes exactly what the system does.

Timeline

The system is complete and in live testing at the café. This timeline is updated as results come in.

  • Oct 22, 2026: Google sign-in and push notifications live on Android.
  • Oct 29, 2026: In-app account deletion and automatic one-year receipt cleanup shipped.
  • Nov 7, 2026: Automatic image compression for uploads; test data cleared; privacy policy and terms published; Google Play developer account created.
  • Nov 8, 2026: First live day at the café. Real walk-in orders ran through counter mode, and a bug found mid-shift was fixed in the database the same day, with no app update needed.
  • Nov 9, 2026: Preparing a Google Play closed test with café customers.

What I learned

  • Adoption is the hard part, not the code.A working system only matters once staff use it during a real shift, so the counter flow was designed to be faster than writing on paper.
  • Scope decides whether anything ships. A feature list with no end means an app that never launches. Every idea went onto a “later” list unless the first version needed it.
  • Some secondary features earn their place by driving adoption. The stamp card isn’t essential to taking an order, but it gives customers a reason to order through the app instead of messaging the café, so it got shipped in the first version.
  • Put the rules where they cannot be skipped. Pricing, payment checks and stamps live in the database, so every screen, present or future, follows the same rules.
  • Store facts, calculate totals. Ledgers for stamps, payments and cash make every number traceable to the rows behind it, a habit from my data engineering background.

Brew-ha app policies