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
/dropscatalog with filter + search + pagination, and a/presentationdeck. - 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 UPDATEon a per-campaign control row inside an OCC-retried transaction. DSQL surfaces write conflicts as SQLSTATE40001, 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, noALTER 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-freeCOUNT(*)), 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 toTEXTand 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
- amazon-aurora-dsql
- aws-ec2
- boto3
- caddy
- fastapi
- next.js
- postgresql
- python
- react
- sqlalchemy
- stripe
- tailwindcss
- typescript
- vercel
Log in or sign up for Devpost to join the conversation.