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.

  1. 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.
  2. The customer scans it with their own phone and fills in a short form, validated in real time. No download, no sign-up.
  3. The admin manages stations, raffles and attendants, follows participation on a dashboard with KPIs, charts and a searchable participant base, and runs the draw.
ScreenshotAttendant QR screen
ScreenshotCustomer form on mobile
ScreenshotAdmin dashboard

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.

Next step

Have a process that’s stuck?

Tell us about it and we’ll design the tool that fixes it.