Case study
A tamper-proof raffle system for a gas station chain: 16,000 participants, no app to download
A gas station chain in Argentina came to us wanting to run customer raffles across its stations. We designed the whole system: attendants generate expiring QR codes, customers enter from their own phones without installing anything, and the owner runs auditable draws from a live dashboard.
At a glance
- Client
- Gas station chain, Argentina
- Our role
- Solution design, UX, development, deployment and ongoing support
- Type
- Client project
- Stack
- Next.js, React, TypeScript, Tailwind CSS, shadcn/ui, Supabase (PostgreSQL, Auth, Row Level Security), Zod, Vitest, Vercel
- Status
- In production
The brief
The client came to us with a goal, not a spec: reward customers with raffles across several stations. How it should work was up to us.
It sounded like a form: customers sign up, someone picks a winner. When we dug into it, the real requirements were harder:
- Only real customers should enter. An entry had to be tied to an actual visit to a station, not to a link shared in a group chat.
- Staff aren’t technical. The attendant’s part had to take a single tap.
- Customers won’t install anything. No app, no account.
- It had to be trusted. A raffle only works if people believe it’s fair, so it had to be hard to game and easy to audit.
This wasn’t a form. It was an integrity problem.
The solution we designed
One system, three roles.
- The attendant opens the app on their phone and taps once. A QR code appears, valid for five minutes. A daily counter shows how many entries they’ve generated.
- The customer scans it with their own phone and fills in a short form, validated in real time. No download, no sign-up.
- The admin manages stations, raffles and attendants, follows participation on a dashboard with KPIs, charts and a searchable participant base, and runs the draw.
Under the hood
- Expiring, single-use tokens. Each QR carries a SHA-256 token that expires after five minutes and is consumed atomically, so it can’t be reused or forged.
- Access control at two levels. Role-based routing for admins and attendants, plus PostgreSQL Row Level Security on an 8-table schema with triggers and functions.
- Hardened public endpoints. Rate limiting and Zod validation on every API route that receives customer data.
- Tested before launch. Unit and integration tests with Vitest.
After launch
When a single raffle passed 1,000 participants, the dashboard kept showing exactly 1,000. The cause was Supabase’s default limit on rows returned per query. We moved the count into a PostgreSQL function that aggregates on the server and returns one row per raffle instead of every participant. We validated it locally and then shipped it to production.
Delivery included an in-person demo, a user manual and a handover document.
Results
16,000
participants
Multi‑branch
raffles running across the client’s stations
In production
with ongoing support
Takeaway
Small-looking projects often hide the real requirement. Here it was trust. We gave a raffle app the same security we’d give a payments flow, because for the client, a raffle that feels rigged is worse than no raffle at all.