Build a Marketplace
Create a two-sided marketplace with user auth, listings, and split payments
What You'll Build
A working marketplace where sellers can list products, buyers can purchase, and payments automatically split between you (platform fee) and the seller.
- Multi-user auth with seller and buyer roles
- Product listing creation and management
- Stripe Connect split payments
- Deployed and production-ready
Prerequisites
- Node.js 18+ installed
- Experience with React and TypeScript
- A Stripe account with Connect enabled
- About half a day of focused time
Architecture
Next.js handles the frontend and API layer. Clerk provides user authentication with role-based access (buyer vs seller). Supabase stores listings, orders, and user profiles. Stripe Connect handles payments - buyers pay, Stripe splits the funds between seller and your platform automatically.
Plan checks and sources
Plan-specific access matters more than a tool’s “free” label. Read the dated profile linked above, then confirm your seat count, usage, billing basis and required export or publishing feature.
No new vendor price or free-plan allowance is asserted in this edit. Linked profiles retain their own source dates and unknowns.
Before launch: use a synthetic record, verify permissions and output, test a failed action, and document how to reverse it. A successful checkout preview or API response does not prove a paid order, delivery or confirmed subscription.
Scaffold the Next.js app with Clerk
Create a Next.js project and integrate Clerk for authentication.
- Create a new Next.js project:
npx create-next-app@latest marketplace --typescript --tailwind --app - Install Clerk:
npm install @clerk/nextjs - Create a Clerk application at clerk.com and get your API keys
- Add Clerk keys to
.env.local - Wrap your app with
in the root layout - Add Clerk middleware to protect routes
Illustrative code, not executed in this review. Check current SDK documentation, authentication, input validation and error handling before use.
npx create-next-app@latest marketplace --typescript --tailwind --app
cd marketplace
npm install @clerk/nextjs
Illustrative code, not executed in this review. Check current SDK documentation, authentication, input validation and error handling before use.
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...
CLERK_SECRET_KEY=sk_...
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
and components instead of building forms from scratch.Set up the Supabase database schema
Create the database tables for listings, orders, and seller profiles.
- Create a Supabase project and install
@supabase/supabase-js - Create a
sellerstable (user_id, stripe_account_id, store_name, approved) - Create a
listingstable (seller_id, title, description, price, images, status) - Create an
orderstable (buyer_id, listing_id, amount, stripe_payment_id, status) - Enable Row Level Security on all tables
- Write RLS policies: sellers can CRUD their own listings, buyers can read all active listings
Illustrative code, not executed in this review. Check current SDK documentation, authentication, input validation and error handling before use.
CREATE TABLE sellers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id TEXT UNIQUE NOT NULL,
stripe_account_id TEXT,
store_name TEXT NOT NULL,
approved BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE listings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
seller_id UUID REFERENCES sellers(id),
title TEXT NOT NULL,
description TEXT,
price INTEGER NOT NULL,
images TEXT[],
status TEXT DEFAULT 'active',
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
buyer_id TEXT NOT NULL,
listing_id UUID REFERENCES listings(id),
amount INTEGER NOT NULL,
stripe_payment_id TEXT,
status TEXT DEFAULT 'pending',
created_at TIMESTAMPTZ DEFAULT now()
);
Set up Stripe Connect for split payments
Enable Stripe Connect so sellers can receive payments with your platform taking a fee.
- Enable Connect in your Stripe Dashboard under Connect → Settings
- Choose "Express" onboarding (Stripe handles seller KYC)
- Install Stripe:
npm install stripe - Create an API route to generate seller onboarding links
- Create a webhook handler for
account.updatedevents to track seller verification - Set your platform fee percentage (e.g. 10%)
Illustrative code, not executed in this review. Check current SDK documentation, authentication, input validation and error handling before use.
import Stripe from 'stripe'
import { NextResponse } from 'next/server'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(req: Request) {
const { sellerId } = await req.json()
const account = await stripe.accounts.create({ type: 'express' })
const link = await stripe.accountLinks.create({
account: account.id,
refresh_url: `${process.env.NEXT_PUBLIC_URL}/seller/onboard`,
return_url: `${process.env.NEXT_PUBLIC_URL}/seller/dashboard`,
type: 'account_onboarding'
})
// Save account.id to the seller's record in Supabase
return NextResponse.json({ url: link.url })
}
Build the marketplace checkout
Create a checkout flow that splits payments between your platform and the seller.
- Create a checkout API route that creates a Stripe Checkout Session with
payment_intent_data.transfer_data - Set the
application_fee_amountto your platform fee (e.g. 10% of the listing price) - Set
transfer_data.destinationto the seller's Stripe Connect account ID - Build a product detail page with a "Buy Now" button that calls your checkout API
- Create success and cancel pages for post-checkout redirects
Illustrative code, not executed in this review. Check current SDK documentation, authentication, input validation and error handling before use.
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: [{
price_data: {
currency: 'usd',
product_data: { name: listing.title },
unit_amount: listing.price
},
quantity: 1
}],
payment_intent_data: {
application_fee_amount: Math.round(listing.price * 0.10),
transfer_data: { destination: seller.stripe_account_id }
},
success_url: `${process.env.NEXT_PUBLIC_URL}/orders/{CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/listings/${listing.id}`
})
Deploy to Vercel
Deploy your marketplace with all environment variables configured.
- Push to GitHub and import into Vercel
- Add all environment variables (Clerk, Supabase, Stripe keys)
- Configure your Stripe webhook endpoint for production
- Test a full buyer flow: browse → buy → seller gets paid
- Test a full seller flow: onboard → list product → receive payment
Validate before launch
Target outcome: A working marketplace where sellers can list products, buyers can purchase, and payments automatically split between you (platform fee) and the seller. Check the actual workflow, failure and recovery paths before publishing.
Need help building?
I help founders set up, connect, and launch their tool stacks. Skip the guesswork.
Work with me →