Inspiration

What it does## Inspiration

Group-buying is an old idea — the more people who buy, the cheaper it should get — but almost no consumer marketplace actually lets buyers feel that mechanic in real time. We wanted to build a store where the price visibly drops as more people commit, and where everyone who joined pays the lowest tier reached by the deadline. Fair, transparent, and a little addictive to watch.

What it does

Pindrop is a drop-pricing group-buy marketplace. Each product is a live "drop" with a tier ladder: as committed buyers accumulate, the unit price falls to the next tier. When a drop closes, every committed buyer pays the single lowest price reached — not the price at the moment they joined. You can browse 200+ real product drops across 115 categories, watch the price update live (it polls every 2s and flashes on each drop), pick a quantity, and commit.

How we built it

  • Frontend: Next.js 16 + React 19 + Tailwind v4, deployed on Vercel. Editorial design system, live price-drop panel, a slide-in search drawer, a /drops catalog with filter + search + pagination, and a /presentation deck.
  • Backend: Python FastAPI + SQLAlchemy 2.0 + psycopg3, running on EC2 behind a Caddy reverse proxy (HTTPS via Let's Encrypt).
  • Database: Amazon Aurora DSQL — IAM-token auth via boto3, UUID primary keys, no foreign keys, and optimistic concurrency control.
  • Catalog: ~200 real products imported from an Amazon dataset, filtered to discount-only items, with price→tier ladders derived from list vs. sale price and seeded commitments scaled from real "bought last month" signals.

Challenges we ran into

  • Overselling under concurrency. The last unit in a batch is a hot row. We guard it with SELECT ... FOR UPDATE on a per-campaign control row inside an OCC-retried transaction. DSQL surfaces write conflicts as SQLSTATE 40001, which we retry with exponential backoff + jitter — so the cap never oversells even under bursts of simultaneous commits.
  • DSQL is not vanilla Postgres. No foreign keys, no SAVEPOINT, one DDL statement per transaction, no ALTER COLUMN ... TYPE, and a 10,000-row transaction limit. We modeled around all of it: app-enforced refs, append-only commitments (live count = a conflict-free COUNT(*)), per-table DDL in autocommit, and settlement chunked into batches.
  • Performance. The homepage was doing N+1 queries (~2.9s). We collapsed it into two set-based queries and computed pricing in Python, dropping it to ~0.5s.
  • A VARCHAR(40) category column truncated real catalog categories; since DSQL can't alter a column type, we changed the model to TEXT and recreated the table.

What we learned

Designing for Aurora DSQL's distributed, OCC model pushes you toward append-only, conflict-free data patterns — and those turn out to be a great fit for a live counter like "how many people have committed." The constraint made the design better.

What's next

Stripe live capture at settlement (the adapter seam is already built), seller-facing drop creation, and shareable drop links to recruit buyers and push the price down.

Built With

Share this project:

Updates