App Launcher
← All apps
LP
Not publicly hosted

Lottery Pools

Group lottery pools with payments and OCR ticket import

Go Gin GORM Next.js TypeScript MySQL Stripe Google Vision OCR JWT
Not publicly hosted

What it is

A platform for running group lottery pools: people join a pool, contribute money, and own a fractional share of the tickets the pool buys. It tracks contributions, computes each participant’s ownership percentage, handles payments and refunds, and can even import physical paper tickets by scanning them. Not publicly hosted — it runs locally — but it’s the most feature-complete full-stack project in this set.

Architecture

A Go + Gin API (versioned /api/v1) with GORM over MySQL in production and SQLite in tests, paired with a Next.js 15 (App Router) + React 19 + TypeScript frontend. Auth is JWT; a centralized Axios client attaches the token and redirects on 401.

Next.js + TS
Go + Gin API /api/v1
MySQL GORM
Typed full-stack with external payment + OCR services

Engineering highlights

  • Payment-provider abstraction — Stripe’s PaymentIntent flow (SCA / 3-D Secure ready) sits behind a PaymentProvider interface with a mock implementation, so the whole contribution → payment → confirmation workflow is testable without hitting Stripe.
  • OCR with a human in the loop — physical tickets are scanned via Google Cloud Vision, extracting serial numbers, barcodes, draw dates, and the numbers on multi-line plays with confidence scores. Extracted data is returned for review before it’s saved — never blindly trusted.
  • Pool economics — fractional ticket shares, configurable contribution deadlines and refund windows, platform-fee accounting, and a clear pool lifecycle state machine (active → closed → completed/cancelled).
  • Transactional integrity — multi-step operations (payment + ticket writes) are wrapped in GORM transactions that roll back as a unit, so a failure never leaves a pool in a half-updated state.
  • End-to-end type safety — Go structs mirror TypeScript interfaces, with Zod validating forms on the client and matching the server’s rules.
  • Tested both sides — service layer covered with testify + sqlmock (the same models run against SQLite in tests); React flows with Jest + Testing Library.

What I learned

Splitting thin HTTP handlers from a service layer kept the payment, OCR, and pool-economics logic independently testable — which mattered a lot once real money (Stripe) and unreliable input (OCR) entered the picture and “mostly works” stopped being good enough.