Skip to content
Projects
4 min read

World Cup 2026 Shopify Reward Game

A World Cup 2026 promotional game built for a Shopify storefront where mini-game scores unlock unique, single-use, time-limited discount codes, backed by a Node/Express and PostgreSQL service running behind a signed Shopify App Proxy.

Role
Sole engineer
Year
2026
Status
prototype
shopifyliquidnode.jspostgresqlgamificatione-commerce

// Architecture flow

Overview

For Mike Sport's FIFA World Cup 2026 campaign, I built a two-part promotional game system: a set of Liquid storefront mini-games (a keepy-uppy ball-juggling game, a penalty shootout, and a "predict the streak" tournament picker) themed around the tournament, paired with a standalone Node/Express and PostgreSQL backend designed to turn game performance into real, single-use Shopify discount codes. The backend is deployed with Docker Compose behind nginx and is designed to sit behind a Shopify App Proxy so the storefront can call it as a trusted first-party endpoint.

Problem

Mike Sport wanted a World Cup-themed engagement mechanic that felt like an arcade game rather than a plain "spin to win" popup, while still producing something commercially useful: real, redeemable, abuse-resistant discount codes tied to Shopify's native discount system rather than a static universal code shared across all players.

Approach

I split the work into a storefront experience and a reward service:

  • On the storefront side, I built three self-contained Liquid mini-game sections (Keepy-Uppy, Penalty Shootout, Predictor Streak), each with its own canvas/SVG-based physics or animation, score tracking, local best-score persistence, and an end-of-game reward panel, all assembled into a single page template alongside a hero, predictor, bundle, product, and nations section.
  • On the backend side, I built a dedicated Express API (session start, reward claim, admin claims, health) that validates a signed Shopify App Proxy request, enforces a set of anti-abuse rules, and calls the Shopify Admin GraphQL API to mint a real, time-limited, single-use discount code, persisting every session and claim in PostgreSQL for auditability.
  • Reward tiers are score-gated, with higher scores unlocking better rewards (free shipping, then percentage discounts), each code expiring a couple of hours after issuance and usable once.

The mini-game sections currently compute their own local score thresholds and display a static demo code with an on-site email capture, so the score-to-live-code path is the backend's designed integration point (via the App Proxy) rather than something wired end-to-end in the Liquid files reviewed. The backend itself is fully built and deployed to generate and track real codes.

Architecture

  • Storefront (Shopify Liquid): three independent mini-game sections, each with inline CSS/JS, canvas or SVG rendering, and an email-capture reward panel; a separate WebGL/Three.js cinematic scroll landing page; a shared scroll-driven pitch/ball background snippet; a page template assembling hero, predictor, the three mini-games, bundle, products, and nations sections into one page.
  • Backend (Node/Express + PostgreSQL): src/app.js wires up helmet, CORS, rate limiting, and routes; src/middleware/shopifyProxy.js verifies the Shopify App Proxy HMAC signature (sha256 over sorted query params) with a dev escape hatch; src/routes/session.js and src/routes/reward.js implement session issuance and reward claiming; src/services/discount.js maps score to reward tier and calls a thin wrapper around the Shopify Admin GraphQL discount-code mutations.
  • Data: a SQL migration defines sessions (one per game play, session id plus IP plus expiry) and claims (one per successful reward, score, reward type, generated code, Shopify discount node id, expiry), intentionally not foreign-keyed so claim history survives session cleanup.
  • Deployment: Dockerized app plus Postgres via docker-compose, migrations run automatically on first container start, fronted by nginx/certbot for HTTPS, with a test console and an admin claims endpoint for operational visibility.

Tech stack

  • Shopify Liquid, HTML5 Canvas/SVG, vanilla JS, Three.js (landing page only)
  • Node.js, Express 4, express-rate-limit, helmet, morgan, cors
  • PostgreSQL (pg driver, raw SQL migrations)
  • Shopify Admin GraphQL API (discount code mutations), Shopify App Proxy (HMAC-signed requests)
  • Docker / Docker Compose, nginx plus certbot for TLS

Engineering highlights

  • HMAC-SHA256 verification of Shopify App Proxy requests using a constant-time compare to prevent timing attacks, with sorted-query-param message construction matching Shopify's signing scheme exactly.
  • Layered anti-abuse controls: one reward claim per session (409 on repeat), a DB-level rolling 24-hour IP claim cap checked inside the transaction, and separate HTTP-level rate limits for session start and reward claims.
  • Score-tiered reward mapping that always resolves to the highest tier earned, with codes generated via cryptographically strong random bytes and prefixed per tier for easy identification in Shopify admin.
  • Every discount code is created with a usage limit of one and a starts-at/ends-at window directly in the Shopify Admin GraphQL mutation, so enforcement lives in Shopify's own discount engine rather than the app.
  • Full claim/session audit trail in PostgreSQL, exposed through a paginated, admin-secret-gated claims endpoint and a browser-based test console for manual QA of the discount-generation flow.
  • Three distinct mini-game implementations (physics-based ball juggling, an aim-and-power penalty shootout with a reactive goalkeeper, and a probabilistic tournament predictor with ranking-weighted outcomes) built as self-contained Liquid sections that degrade gracefully under prefers-reduced-motion.

Want to dig deeper?

Ask my AI agent anything about how this was built, what tradeoffs I made, or how it could fit your team.

Ask my AI →