Wager Up

A real-time skill-based wagering platform for golf, esports, basketball, football, darts, and bowling — with live game state sync, integrated payments, and a modern animated UI.

6
Game Categories
18
Game Types
Real-Time
WebSocket Sync

The Concept

Wager Up is a platform where players compete head-to-head in skill-based competitions with real money on the line. Unlike gambling, every outcome is determined by player skill — you're betting on your own performance.

Players create matches, set wager amounts, and compete across 18 different game types in 6 categories. The platform handles match creation, opponent matching, real-time chat, wallet management, and automatic payout to winners.

Key Features

  • 6 game categories: Golf, Video Games, Basketball, Football, Darts, Bowling
  • Real-time match updates via WebSocket (Socket.IO)
  • Digital wallet with hold/capture/release pattern for wagers
  • Stripe Checkout for deposits with webhook verification
  • Google OAuth authentication
  • In-match chat with message persistence
  • Leaderboard with win/loss tracking
  • Age verification and terms acceptance gate

Architecture & Technology

⚛️

React 19

Modern frontend with Tailwind CSS and Framer Motion

🐍

FastAPI

Async Python backend with REST API

🔌

Socket.IO

Real-time bidirectional communication

💳

Stripe

Deposits, webhook verification, idempotent processing

🔐

Google OAuth

Secure authentication with session cookies

☁️

AWS ECS Fargate

Containerized deployment with CI/CD

How the Wallet System Works

Hold / Capture / Release

When a player creates or joins a match, the wager amount is placed on hold — deducted from their available balance but not yet spent. This prevents double-spending even under concurrent requests.

When the match completes, holds are captured (money leaves the wallet) and the winner receives the total pot. If a match is cancelled, holds are released and funds return to available balance.

Race Condition Protection

Financial operations use SQLite's BEGIN IMMEDIATE to acquire write locks before balance checks. This prevents two concurrent requests from both passing a balance check before either deducts.

Match joining uses atomic UPDATE statements with WHERE conditions — if two players try to join the same match simultaneously, only one succeeds. The other gets a clean error message.

Security & Reliability

Google OAuth 2.0

Secure authentication with proxy header support behind ALB

Atomic Wallet Operations

BEGIN IMMEDIATE transactions prevent double-spend race conditions

Rate Limiting

Per-endpoint limits on auth, deposits, match creation, and result reporting

Security Headers

HSTS, X-Frame-Options, CSP, Referrer-Policy on all responses

Locked CORS

Origin-restricted CORS on both HTTP and WebSocket connections

Input Validation

Wager amount limits, message length caps, and Pydantic request validation

The Build Journey

Wager Up came together in focused phases — each one shipped something usable.

Phase 1 — Shipped

Auth + Match Creation

Google OAuth, user profiles, and the core match creation flow. Users could create a match, set a wager, and pick a game type. No real money yet.

Phase 2 — Shipped

Wallet + Stripe Deposits

Added the digital wallet with hold/capture/release, Stripe Checkout for deposits, and webhook-driven balance updates. This was the riskiest phase — we spent extra time on concurrency and idempotency.

Phase 3 — Shipped

Real-Time Match Play

Socket.IO integration for live match state, in-match chat, and opponent matching. The frontend transitioned from polling to event-driven updates, cutting latency dramatically.

Phase 4 — Shipped

Game Expansion + Polish

Grew from 2 game types to 18, added leaderboards, age verification, terms gate, and the animated UI using Framer Motion. Full deploy on AWS with CI/CD.

Key Technical Challenges

Challenge

Preventing double-spend under real concurrency

Two players joining the same match at the same moment could each pass a balance check before either was deducted. We solved this with SQLite's BEGIN IMMEDIATE transactions and atomic UPDATE-WHERE statements. Either one request wins cleanly or it errors out — there's no state where both succeed.

Challenge

Stripe webhooks that are actually trustworthy

Webhook events arrive late, out of order, and sometimes twice. We verify every webhook signature, store the event ID for idempotency, and only apply state changes once per event. Balance reconciliation pulls from Stripe on read if there's any ambiguity, so wallet balances are always accurate.

Challenge

WebSockets behind an AWS load balancer

Socket.IO behind ALB is fiddly — sticky sessions, CORS, and proxy headers all have to line up or nothing connects. We locked CORS to known origins, configured ALB for WebSocket upgrades, and made the Python backend honor X-Forwarded-* headers so OAuth redirects still work in production.

See It Live

Wager Up is live. Check out the platform and see the UI in action.

Visit Wager Up →

Need a Real-Time Platform?

We build interactive, real-time applications with WebSockets, payments, and modern UI. Let's talk about your project.

Get a Free Consultation