WTB Market logo

WTB Market

SaaS platform for resellers

WTB Market is a reseller community born in 2022 on Discord. Before the platform, resellers were managing inventory in spreadsheets or manually despite existing tools. I decided to build a platform to tailor their needs, letting them manage their inventory, sales and daily operations, which quickly evolved into a full ecosystem.
Note: I own the full technical stack while the community is operated by a business partner.

1700+ Paying users
800K+ Page views
240K+ Visitors
2022 In production since
50 Active storefronts
35+ Shared UI components
80+ REST endpoints

Highlights

Freemium quota system

Server-enforced limits with real-time validation.

WTB Market freemium quota dialog

Limit Reached dialog shown to users when they exceed their quota, explaining the available plans and how to upgrade.

Dual-view architecture

Two distinct product experiences (user & store) under the same shell.

Same nav, auth context & component library

User view
  • - Analytics
  • - Inventory
  • - List
  • - Inbox
Store view
  • - Management
  • - Listings
  • - API keys
View context stored in a cookie view=user | view=store

Stripe & Discord integration

Subscription events trigger automated multi-step workflows.

Stripe events

checkout.completedinvoice.paidcustomer.subscription.deleted
Webhook received
POST /webhooks/stripe
Signature verified
HMAC-SHA256 · Stripe secret
Express handler

Triggered actions

Discord role
assign / revoke
JWT refresh
roles re-encoded
Admin alert
sent to admins
Referral +
period extended if applicable

Multi-layer authentication

Discord OAuth2 combined with JWT sessions and real-time validation.

Login

Discord OAuth2
identity proof
JWT signed
roles + plan encoded
MongoDB session
record created
HttpOnly cookie
returned to browser

Every request

Cookie extracted
JWT verified
signature + expiry
Session lookup
MongoDB check
Roles validated

Architecture

WTB Market is not a single app, it's a set of interconnected products.

Next.js Frontend App Router · React Query · Zustand · Tailwind CSS
Express Backend Service layer architecture · Public & Private endpoints
MongoDB Mongoose · Data-driven pipelines
Cloudflare R2 User files storage
Discord OAuth2 · Roles · DMs
Stripe Checkout · Subscriptions
APIs Integrations StockX · CardMarket · Ebay

Key Challenges

Discord notifications

Backend

Matching thousands of freeform WTB posts against user keywords in near-real-time without hitting Discord rate limits.

Approach

Each message is normalized before matching (punctuation, regional suffixes, special characters stripped), then split into rows and tokenized. Matching fans out with p-limit (10 parallel workers) inside a serialized PQueue, matching one message at a time while processing ten users in parallel. Notifications go through a Bottleneck rate limiter at 40 req/s.

Edge cases
  • ·Unknown guild members are migrated to an archive collection.
  • ·Zero-match senders receive a DM with formatting tips.
  • ·Webhook failures fall back to a direct DM.
  • ·All ping events are persisted to a log collection for audit.

Multi-tier product lookup

Backend

Ensuring reliable product search results even when external APIs fail or return incomplete data.

Approach

External APIs often return incomplete or inconsistent data. I handle lookups through a fallback chain: provider API → MongoDB cache → provider slug lookup. The result is saved in the cache to prevent repeated misses.

Edge cases
  • ·Items without a retail price are defaulted to 0 rather than null to keep the response shape consistent.
  • ·Identifier is sanitized before all comparisons to avoid mismatches from scraped data.
  • ·Cache auto-population is logged with the query string so new items added at runtime are auditable.

Deduplication at scale

Backend

Aggregating 4300+ items across stores and user lists into a single unified view without quadratic growth in lookup latency.

Approach

Stores and user lists are fetched in parallel, then merged in a single pass using a Map keyed by normalized slugs. This replaced a previous Array.find loop that scanned ~6.9M entries at 4300 items.

Edge cases
  • ·Different size are normalized before the merge.
  • ·Freemium restrictions are enforced at the data layer, not on the client.
  • ·The count field reflects total sources (stores + lists) so sorting stays consistent.

Reversible mutations

Frontend

Supporting undo actions for every data operation without a dedicated undo stack.

Approach

Before each mutation, a reverse payload is created from the original data and time bounded. The undo action re-enters the same mutation pipeline, reusing the same validation, error handling and API normalization.

Edge cases
  • ·Undo is blocked if the user has hit their quota limit between the original action and the reversal attempt.
  • ·If the undo fails, a new second error toast surfaces without replacing or dismissing the original.
  • ·Bulk operations generate individual reverse payloads per row, so partial undos remain possible.

What I'd Do Differently

Standardize environments earlier

Early deployments were messy, too many branches and ad-hoc deployments lead to config problems and confusion. Dev, beta and production improved stability and consistency. I'd set up this flow from the start.

Define data before the schema

MongoDB's flexibility worked well when I started, but as time passed, aggregations and multi-field filters became harder to manage. I'd define a clearer data model upfront, considering a relational approach where it fits instead of pushing everything into the document model.

Normalize the keyword store

Matching all users keywords means iterating the entire collection (~7 million operations per message). I'd normalize the keyword store with proper indexing and enforce structured input: users pick from known SKUs on the dashboard instead of typing freeform text, making matches fast and exact.

Complete Stack

Frontend
Next.jsTypeScriptTanStack QueryZustandTailwind CSSNextUIShadcn UI
Next.jsTypeScriptTailwind CSS
Backend
ExpressMongoDBStripe APIOAuth2JWTCloudflare R2
ExpressMongoDBStripe
Infrastructure
VercelCloudflareCaddySentryPM2Posthog
VercelCloudflareSentry