# Design a Mobile Loyalty App: Starbucks-Style Rewards

Source: https://www.techinterview.org/post/3233475136/design-mobile-loyalty-rewards-app/
Updated: 2026-07-26 · techinterview.org

Mobile loyalty apps (Starbucks, Chipotle, Dunkin) are unusually rich mobile [system design](/system-design-interview-guides/) topics. They combine payment processing, loyalty points, mobile ordering, geofencing, and the realities of physical retail integration. The Starbucks app in particular has been studied for its size and engagement — over 30M monthly active users.

## Functional requirements

- Earn points (stars) for purchases — the earning rule engine should be server-side and configurable, so a purchase event comes in and the server applies current rules and writes the new balance. Expect follow-ups on how you claw back points when a purchase is later refunded.

- Pay with stored loyalty balance via QR/barcode — the QR encodes a short-lived token, not the balance itself. Be ready to explain why the client never holds the authoritative number and how you stop a screenshotted code from being reused.

- Mobile order ahead at a specific store — each store has its own menu, inventory, and prep queue, so orders are scoped to a store ID. A common probe: what happens when an item sells out between the client loading the menu and submitting the order.

- Customize drinks/items with options — this is where the data model gets tested. Options nest (a latte has size, milk, shots, syrups), each with its own price and availability, so a rigid column-per-option schema falls apart fast.

- Send/receive gift cards to friends — gift cards move stored value between accounts, so treat the transfer as a two-sided ledger entry with an idempotency key. Interviewers may ask how you prevent double-spending if the sender and recipient act at the same time.

- Find nearby stores with hours and inventory — a geospatial query (geohash or a [spatial index](/post/3233461821/database-indexing-interview-guide/)) returns stores near the user's coordinates, joined with per-store hours and live inventory. Cache slow-changing store metadata separately from fast-changing stock counts.

## Architecture

Three modules: **store / catalog**, **order**, **loyalty**.

## Loyalty mechanics

Each transaction adds points. Points have:

- Earning rules (purchase amount, item type, time of day) — model these as data, not code, so marketing can change "earn 2 stars per dollar" without a deploy. Rules key off purchase amount, item category, and time, evaluated server-side at transaction time.

- Tier system (free → reward levels) — tiers unlock rewards at thresholds (e.g., 150 stars for a free drink) and often decay over time. Be clear about whether tier status and redeemable balance are the same counter or two separate ones.

- Expiration (points expire after N months) — track earn dates per lot and expire oldest-first. A nightly batch job or lazy evaluation at read time both work; interviewers like hearing the trade-off between the two.

- Promotion events (double-points day, bonus stars) — these are time-boxed multipliers layered on the base rules. The gotcha is targeting: some promos apply to everyone, others to a specific user segment, so the rule engine needs a scope condition.

Server is authoritative — never let the client decide point balance.

## Mobile payment

The Starbucks model: prepaid stored value. User loads $X via credit card; in-store, present QR code; system deducts from stored value.

Benefits to the company: float (held customer cash), reduced credit card fees per transaction, increased switching cost.

Implementation:

- User reloads stored value via credit card (real Stripe/Adyen call)

- Balance updates server-side

- Client shows balance

- In-store: app generates QR with token tied to the user account + brief expiry

- POS scans QR; server deducts

## Mobile order ahead

User picks a store, customizes order, pays, app provides estimated pickup time.

Architecture:

- Client requests menu for the chosen store

- User customizes order

- Client submits order with idempotency key

- Server validates inventory and pricing

- Charges loyalty balance + reload if needed

- Server pushes order to in-store POS

- Server returns estimated ready time

Geofencing: when user is N miles from the store, app pushes notification. When they arrive, the order moves to the front of the queue.

## Customization engine

Drinks have many options. Modeling:

- Base item (Latte) — the base carries a default price and default modifiers. Everything a customer changes is a delta against this base.

- Modifiers (size, milk, syrup, espresso shots) — store these as typed attributes with allowed values per item, not free text, so validation and pricing stay deterministic. Some modifiers are single-choice (size), others multi (extra shots), which the schema has to encode.

- Per-modifier price adjustments — each option carries its own price delta, and size often re-prices the whole drink. Compute the total server-side so the client can never submit a stale or tampered price.

- Calorie / nutrition impact — nutrition recalculates as modifiers change, so it's a derived field the server owns. Some regions require showing it at point of sale, which makes correctness matter, not just display.

This is data modeling — custom orders need a [flexible schema](/post/3233459967/sql-vs-nosql/) with strong validation.

## Offline behavior

- Show last-known balance with "as of [time]"

- QR code generation works offline (cached token with longer expiry)

- Mobile order requires network (cannot place an order to a store without confirmation)

## Push notifications

- Order ready — triggered by the kitchen marking the order complete, delivered fast because it's time-sensitive. This is transactional, not marketing, so it bypasses any promotional throttle.

- Promotional offers — marketing-driven and the main source of opt-outs, so respect user preferences and quiet hours. These are the pushes you rate-limit.

- Nearby store reminders — fired by geofence entry, useful but easy to overdo. Cap frequency so a user who passes the same store daily isn't pinged every time.

- Reward milestone reached — sent when a balance crosses a tier threshold, reinforcing the loyalty loop. Tie it to the same server event that updates the tier so the message and the state never disagree.

Push tokens stored per device. [Throttle promotional pushes](/post/3233474159/system-design-rate-limiter-token-bucket-sliding-window-leaky-bucket-distributed-rate-limiting-api-gateway/) to avoid annoyance.

## Frequently Asked Questions

### How does the QR for in-store payment work?

Token-based. App generates a short-lived (5–10 minute) token tied to the user. Cashier scans QR; POS calls server to validate and deduct. Token cannot be replayed.

### Why do loyalty apps push so many notifications?

Engagement metric. App teams are typically scored on DAU/MAU. Aggressive push tends to inflate metrics short-term but creates churn long-term. Balance is hard.

### How is mobile order ahead synchronized with the in-store kitchen?

Orders push to a kitchen display system over a real-time channel. Status updates flow back as orders progress.
