Create a two-sided marketplace with user auth, listings, and split payments
A working marketplace where sellers can list products, buyers can purchase, and payments automatically split between you (platform fee) and the seller.
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.
Create a Next.js project and integrate Clerk for authentication.
npx create-next-app@latest marketplace --typescript --tailwind --appnpm install @clerk/nextjs.env.local in the root layoutnpx create-next-app@latest marketplace --typescript --tailwind --app
cd marketplace
npm install @clerk/nextjs
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.Create the database tables for listings, orders, and seller profiles.
@supabase/supabase-jssellers table (user_id, stripe_account_id, store_name, approved)listings table (seller_id, title, description, price, images, status)orders table (buyer_id, listing_id, amount, stripe_payment_id, status)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()
);
Enable Stripe Connect so sellers can receive payments with your platform taking a fee.
npm install stripeaccount.updated events to track seller verificationimport 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 })
}
Create a checkout flow that splits payments between your platform and the seller.
payment_intent_data.transfer_dataapplication_fee_amount to your platform fee (e.g., 10% of the listing price)transfer_data.destination to the seller's Stripe Connect account IDconst 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 your marketplace with all environment variables configured.
A working marketplace where sellers can list products, buyers can purchase, and payments automatically split between you (platform fee) and the seller.
Get a step-by-step checklist, setup order, and the exact config for every tool in this guide. Or let me build it for you.
Get the checklist → Want this built for you?