Back to index
03Work

Roleta Online

Quota-based draw with limited seats

My role
Full stack developer
Client
Catsuc Labs
Sector
Consumer product · web
Status
In production

Proprietary code. Private client system — the repository isn’t public.

Numbers

20/40/100

wheel capacities

The draw fires the instant the final seat is taken.

1

chip per seat

Guaranteed by a unique constraint in the database, not a check in the code.

0

seats sold twice

Transactional allocation — the database refuses the second sale.

A

Context

A quota pool is an old, well-known format: a group splits a prize into equal parts, each person buys one, and a draw decides. Moving it to the web looks trivial until you notice the interesting part isn’t the wheel spinning — it’s what happens when two people click to buy the last chip in the same millisecond.

B

The problem

Seats are finite and money is involved. That turns three things into hard requirements: no seat may be sold twice, no repeated payment may grant an extra chip, and the draw has to be explainable after the fact — because whoever lost is going to want to understand how.

C

The approach

I put the guarantees in the database, not in application code. Each seat is a row with a unique constraint, and a purchase happens inside a transaction that either reserves the seat or fails — the database refuses the race instead of the app hoping it won’t happen. Payment confirmation is idempotent by key, so a re-delivered webhook never becomes an extra chip. And the draw records its seed and its result, so it can be reconstructed later.

D

Modules

01Capacity and state

Roletas

Each wheel has capacity, chip price and state — open, full, drawn. State lives on the server: the client displays it, never decides it.

02Purchase and allocation

Fichas

Buying a chip means reserving a numbered seat inside a transaction. If the wheel filled up mid-flow, the purchase fails cleanly and no money is captured.

03Trigger and record

Sorteio

Runs the instant the final seat is taken, exactly once per wheel, with seed and result persisted.

04Audit trail

Histórico

Who bought which seat, when, and what the result was. Without it there is no trust in a product where somebody loses money.

E

The hard parts

The last chip and two people clicking

This is the core problem, and it isn’t solved by “check whether a seat is free before writing” — another request fits between the check and the write. The seat is a row with a unique constraint and the reservation happens inside the transaction: the second sale is simply not accepted by the database. Pushing the guarantee to the lowest possible level is what keeps the rest of the code simple.

The webhook arrives twice

Payment providers redeliver notifications — that is normal behaviour, not an exception. Without idempotency by key, the second delivery mints a chip nobody paid for. Treating redelivery as the expected case rather than an error is the difference between a product whose books balance and one whose books don’t.

The result has to be defensible

In a draw product, suspicion is the default. The draw runs exactly once per wheel, stores its seed and its result, and cannot be re-run — not even by me. Being able to reconstruct the draw afterwards is worth more than any spinning-wheel animation.

F

What’s mine

  • Modeling wheels, seats and chips, with uniqueness guarantees in the database
  • Transactional purchase flow and idempotent payment confirmation
  • Draw triggered on the final seat, with an auditable record
  • The participation UI, live fill tracking and history
G

Outcome

A pool format that worked on paper now runs itself on the web, with limited seats, real money and a reconstructible result. It’s the project that taught me the most about concurrency — not the theory, the version where getting it wrong costs somebody money.

What I took from it

  1. 01Integrity guarantees belong in the database. An application check is a suggestion; a constraint is a law.
  2. 02Webhook redelivery is the normal case. Idempotency isn’t polish, it’s a requirement.
  3. 03In a product where someone loses, auditability is a trust feature — not a compliance one.

Stack