Project

Preem — a major Swedish fuel brand — runs self-service car washes and wanted a fast, on-brand way to hand new customers a free trial code, captured as a marketing lead. We designed and built a mobile-first landing page and a full admin portal that turns a simple Excel list of wash codes into a tracked, GDPR-compliant acquisition funnel the client operates entirely on their own.

The Challenge

The brief looked simple — “hand out a discount code” — but hid real complexity underneath.

01

Exactly One Code, Ever

Codes live in an Excel/CSV file. The site must hand out exactly one per person and never the same code twice — even under simultaneous traffic at the pump.

02

A Campaign, Not a Form

Every step had to feed Meta and TikTok ads for retargeting and conversion optimisation — but under strict Swedish/EU GDPR, so nothing could fire without consent.

03

A Non-Technical Client

They needed to upload new code batches, see how many are left/used, and change pixel IDs or the email provider themselves — without touching code or redeploying.

04

It Had to Feel Like an App

A sub-30-second flow, on mobile, at the station — locked zoom, no rubber-band scroll, resume-on-return, app-like OTP entry.

What We Built

One Next.js application in two halves — a three-step customer journey and a self-serve control room — faithfully matched to Preem's brand (deep green / amber, custom Preem Sans).

PUBLIC · 01

Email Capture

A mobile-first, single-field entry with step animations, back-button memory and localStorage resume — the lead is captured the moment they start.

PUBLIC · 02

6-Digit Email Verification

A confirmation code is emailed and verified on screen with an app-like OTP input — paste support, auto-advance, hashed + expiring codes.

PUBLIC · 03

Instant One-Time Wash Code

A unique code is issued (and emailed as backup) with a “screenshot this” prompt, confetti, and directions to the nearest station.

ADMIN · 04

Code Management

Drag-and-drop CSV or Excel upload with automatic de-duplication and per-batch reporting — no developer in the loop.

ADMIN · 05

Live Dashboard

Real-time counts of registrations, delivered codes and codes remaining, plus a registration list with CSV export.

ADMIN · 06

Self-Serve Tracking & Email

Paste Meta / TikTok / Cookiebot IDs (injected live, no redeploy) and configure any SMTP provider with one-click presets + a real “Test connection” handshake.

Engineering Highlights

The parts that make a “simple code hand-out” actually production-safe.

Guaranteed One-Time Codes, Concurrency-Safe

The core risk was two people claiming the same code at once. We solved it with a single atomic Postgres claim inside a transaction — FOR UPDATE SKIP LOCKED means simultaneous requests each grab a different row. No duplicates, no double-delivery, ever. Each email maps to exactly one code, and the result is idempotent — a refresh returns the same code.

UPDATE wash_codes SET status='delivered', delivered_to_email=$1
WHERE id = (SELECT id FROM wash_codes WHERE status='available'
            ORDER BY id LIMIT 1 FOR UPDATE SKIP LOCKED)
RETURNING code;

Consent-Gated Tracking

Rather than trusting cookie auto-blocking (which misses dynamically-injected scripts), pixels are explicitly withheld until the visitor grants marketing consent via Cookiebot — and the server-side Meta CAPI respects that same flag, de-duplicated against the browser pixel via a shared event_id.

Provider-Agnostic Infra

Tracking IDs and SMTP settings live in the database, editable from the admin UI. The client swapped email providers (Brevo → Microsoft 365 → one.com) themselves, guided by the built-in connection tester — without a single deploy.

Secure by Default

Hashed, expiring OTPs with send-rate limiting and attempt caps, bcrypt admin passwords, JWT sessions in httpOnly cookies, middleware-protected admin routes, and a first-run setup that locks itself after the first account is created.

Challenges → Solutions

Challenge

No duplicate / no double-spent codes under load

Solution

Atomic FOR UPDATE SKIP LOCKED claim inside a transaction

Challenge

Track for ads and stay GDPR-compliant

Solution

Consent-gated pixel injection + consent-aware server-side CAPI

Challenge

Non-technical client autonomy

Solution

DB-backed, admin-editable tracking + SMTP with a live connection tester

Challenge

Lost browser session losing the code

Solution

Code emailed at delivery + idempotent re-issue on refresh

Challenge

“App” feel on mobile Safari

Solution

Viewport + CSS + iOS gesture handlers to lock zoom and rubber-banding

Outcome

A production-ready, self-managed acquisition funnel: the client uploads a spreadsheet of codes and instantly has a branded, trackable, GDPR-compliant campaign that captures leads, delivers unique offers and feeds Meta/TikTok — all observable from a live dashboard and adjustable without engineering. Containerised with Docker for one-click deployment to Vercel or a self-hosted Coolify server.

// Redemption & lead-volume metrics to be layered in once the campaign has run.

Stack & Tooling

Front endNext.js 15 (App Router), React 19, TypeScript
Back end / dataNode runtime · Neon Postgres · Drizzle ORM
Auth & emailjose (JWT) · bcryptjs · Nodemailer (SMTP)
FilesPapaParse (CSV) · SheetJS (Excel)
MarketingMeta Pixel + Conversions API · TikTok Pixel · Cookiebot · dataLayer
DevOpsDocker (standalone) · Vercel / Coolify · GitHub

Have a campaign to build?

Let's build something great together

Start Your Project