Promotion Platform — Design Document
1.Summary
The Promotion Platform modernizes CAMEX Restaurants' promotional draws without changing how
customers order and without changing the receipt. After purchasing, the customer
scans a static QR code displayed in the store, which opens the promotion web app
with the store's location already set. They enter the order number printed on their
receipt; the system validates that the order was really placed at that location on that
day and hasn't been claimed; the customer identifies with their phone number
(returning customers are recognised); and they select from the promotions their purchase
qualifies for. Each purchase can be claimed exactly once.
This is an extension of the existing camex-oms platform, reusing its backend
services rather than integrating with a foreign POS.
2.Business objectives
- Eliminate physical receipt-collection boxes.
- Reduce operational cost and manual administration of draws.
- Provide immediate, digital promotion registration.
- Reduce staff involvement.
- Prevent duplicate and fraudulent entries.
- Build a verified customer database (name, phone, address tied to real purchases).
- Establish a reusable promotions platform for future marketing (loyalty, coupons, surveys,
instant-win).
3.Context and constraints
Sample receipts reviewed: Mario's Pizza and Church's Chicken (CAMEX Restaurants, Guyana; GYD
currency, +592 numbers).
- The receipt cannot be modified. Client confirmed we have no control over
receipt printing — no QR, barcode, or token can be added. This is the constraint that shaped the
v2 design.
- The check number (
CHK 40847) is per-store and recyclable. Two
stores can share a check number and numbers reset over time. It is never sufficient alone;
validation uses a composite key (§6).
- The customer phone number is not reliably on the receipt — it only prints
for call-center orders. The customer always enters their own details.
- Store connectivity is reliable. No offline validation mode is required.
4.Scope decisions (agreed)
| Decision | Choice | Rationale |
| Platform | Extension of camex-oms; reuse existing backend | Backend is owned; no foreign POS integration |
| QR code | Static, displayed in-store (poster/sticker per location) | Receipt cannot be modified; QR identifies the location |
| Scan time | Captured server-side at scan | Never trusted from the customer's device or receipt |
| Order validation | Location + date + order number looked up live in camex-oms | The composite key that works with an unmodified receipt |
| Receipt date entry | Optional customer input, business-configurable | Off = same-day receipts only; on = older receipts accepted |
| Identity | Phone-first lookup, before promotions are shown | Returning customers skip the form; builds the customer DB |
| New-customer data | First name, last name, street address, town/city | One-time capture |
| Promotion eligibility | Rules on order amount and items (for now) | Future: business-defined rules on any order detail |
| Entries per purchase | 1 entry per promotion per purchase, business-defined | Allowance is a business decision |
| Phone verification | None for pilot; SMS OTP is the Phase-2 upgrade | Fastest, no SMS cost; upgrade path preserved |
| Configuration | Dev team configures for pilot; business self-service later | Admin screens are a platform goal, not a pilot deliverable |
5.Architecture overview
Four logical pieces on top of the existing camex-oms backend:
- In-store QR codes — one static code per location, printed on
posters/stickers. Encodes the promotion-app URL with the location identity. No per-order
data.
- Promotion web app — mobile-first web page opened by the QR. Runs equally on
a customer's phone (primary) or an optional in-store kiosk tablet (same app).
- Promotion Service — validates orders against camex-oms, looks up/creates
customers, evaluates promotion eligibility, enforces the single-claim rule, records
entries.
- camex-oms order lookup — the authoritative source for "did this order
happen here, on this day, and what was on it."
Customer purchases (nothing changes at POS; receipt unchanged)
│
Customer scans the in-store QR ──▶ app opens; location set by QR;
│ scan time stamped server-side
▼
Customer enters order number (CHK) [+ receipt date, if business allows]
│
▼
Promotion Service validates against camex-oms
1. order exists at THIS location on THAT day
2. totals/items pulled from backend records (never typed)
3. purchase not already claimed
│
▼
Customer enters phone number ──▶ lookup
├─ returning customer → recognised, details on file, no form
└─ new customer → first name, last name, street, town/city (one time)
│
▼
Eligible promotions listed (rules on amount/items; ineligible shown greyed with reason)
│
Customer selects draws ──▶ entries recorded (1 per promotion per purchase)
│
▼
Purchase marked CLAIMED (atomic; permanent; single-use)
6.Order validation design
Composite key: location + date + order number.
- Location comes from the QR that was scanned — the customer never picks or
types it.
- Date defaults to the server's current day. If the business enables
receipt-date entry, the customer may set an earlier date to register an older receipt.
- Order number is the
CHK number the customer types from the
receipt.
The Promotion Service queries camex-oms for an order matching all three. On a match it pulls
the authoritative order data (total, items, time) from the backend — nothing
about the purchase is trusted from user input.
Why not the check number alone: check numbers are per-store
and recycle over time. Scoping by location (from the QR) and day makes the key practically unique.
Residual ambiguity (the same store issuing the same check number twice on the same day) is
considered negligible but should be confirmed against real OMS data during integration.
Production behaviour: failure responses are generic
("we couldn't match that order") — they must not reveal whether an order exists at another store
or on another date. The demo shows detailed reasons for teaching purposes only, and labels them as
demo-only.
7.Identity: phone-first, before promotions
The customer identifies before any promotions are shown:
- Enter phone number.
- Backend lookup:
- Known customer → their stored details are returned; they're greeted by
name and go straight to the promotions list.
- New customer → one-time form: first name, last name, street address,
town/city. Saved on their first successful claim; never asked again.
The entered phone number is not verified in the pilot (§14). Manual entry also
acts as friction against bulk/automated abuse.
8.Promotions and eligibility
- All active promotions are shown; each is marked qualified or
not qualified for this specific purchase, with the reason (e.g. "order is
$3,200 — needs $5,000+"). Showing near-misses doubles as an upsell nudge.
- Current rule types: minimum spend; required item/category on the
order.
- Platform goal: eligibility rules defined by the business per promotion,
over any order detail (order type, time of day, combos, store set…), through an admin screen.
For the pilot, rules are implemented by the dev team from business direction.
- The customer selects any subset of the promotions they qualify for; each selection produces
one entry with its own reference number.
- Allowance: 1 entry per promotion per purchase. The exact allowance is a
business-defined, configurable setting.
9.Claims and integrity
Three layers, each with a distinct job:
- Single-claim ledger — keyed on
location + date + order number. Writing a claim is atomic (unique
constraint on the key), eliminating the race where two people submit the same purchase
simultaneously. One purchase → one claim, by one customer, permanently.
- Real-order validation — every claim is backed by an order that camex-oms
confirms existed at that store on that day; totals and items come from the backend.
- Abuse controls — order numbers are short and typed, therefore
guessable. Production mitigations: rate limiting per phone/session/IP, generic
failure messages (§6), and manual-entry friction. These are launch requirements, not
nice-to-haves.
Trade-off vs the v1 design: v1's signed receipt-QR made
forgery cryptographically hard. Because the receipt can't be modified, v2 accepts guessability as
a residual risk and manages it with the controls above plus the single-claim rule (a guessed
number only ever pays out once, and only for a real purchase).
10.Surfaces
- Customer's own phone (primary) — scans the in-store QR; the whole flow runs
in their browser. Hygienic, no hardware to maintain, naturally rate-limited (one person, one
phone).
- In-store kiosk (optional) — a mounted tablet running the exact same web app
for customers without smartphones. Same backend, same flow; the kiosk build decision can be
taken per store.
11.Configuration ownership
Pilot: all settings designed and configured by the
development team from business direction. Platform goal: business self-service
through a promotion-platform admin screen.
| Setting | Controls |
| Promotions & prizes | Which draws run, names, prizes, start/end dates |
| Eligibility rules | Min spend, required items — later any order detail |
| Entries per purchase | Allowance per promotion per purchase |
| Receipt date entry | Same-day only vs older receipts accepted |
| Participating stores | Which locations display QRs and accept entries |
12.Data captured
Each claim records: location, order number, receipt date, promotion entries (with reference
numbers), and the customer (first name, last name, phone, street, town/city). Customers are stored
once and recognised on every later claim. Byproduct: a clean
identity-plus-real-purchase dataset — the foundation for loyalty, coupons,
surveys, and direct marketing with no extra collection work.
13.Backend integration — open questions
To resolve against the camex-oms codebase before implementation:
- Order lookup — an endpoint (or query) by
store + business day + check number, returning total, items, order type, and
timestamps. Confirm check-number uniqueness within a store-day.
- Customer store — where promotion customers live (new collection/table vs
existing customer records), keyed on normalized phone.
- Claims + entries storage — new tables/collections with a unique constraint
on the claim key; kept separate from order records.
- Rate limiting — per phone/session/IP throttling for order-number
attempts.
- Location identity in the QR — stable store IDs to encode in each poster
URL.
14.Future upgrade: phone verification (Phase 2+)
SIM-based verification (Google Phone Number Hint / SMS Retriever / carrier "verify with SIM")
only works on the customer's own device with their SIM present, so it can't serve a kiosk. The
device-agnostic equivalent is SMS OTP: enter a number, receive a code, type it
back — proving SIM possession from any surface. Deferred from the pilot for cost and friction;
documented as the intended verification path.
15.Phasing
- Phase 1 — Pilot (one store). In-store QR, order-number validation,
phone-first identity, eligibility list, single-claim ledger. Settings configured by the dev
team. No OTP.
- Phase 2 — Multi-store rollout. Store scoping, reporting/admin views,
business self-service configuration, SMS OTP verification, production rate
limiting hardened.
- Phase 3 — Loyalty & advanced campaigns. Loyalty integration, digital
coupons, surveys, instant-win — built on the captured customer/purchase dataset.
16.Revision history
- v1 (2026-07-01) — assumed a signed QR printed on each receipt (order-ID
pointer + HMAC), kiosk-primary, name+phone entry only. Superseded: client
confirmed the receipt cannot be modified.
- v2 (2026-07-06, current) — static in-store location QR; composite-key order
validation (location + date + order number); phone-first identity with returning-customer
recognition; address capture for new customers; eligibility-based promotion selection;
configuration dev-managed for pilot with business self-service as the platform goal.
17.Non-goals (for the pilot)
- Modifying the receipt or the ordering/POS workflow.
- Offline validation (connectivity is reliable).
- Phone verification / OTP (Phase 2).
- Business self-service configuration screens (Phase 2).
- Loyalty, coupons, surveys, instant-win (Phase 3).