A comprehensive multi-tenant e-commerce platform where creators can set up their own storefronts, sell digital products, and get paid through Stripe Connect. Built with Next.js 15, Payload CMS, and modern web technologies.
This platform uses a shared database with tenant isolation approach, not separate databases per tenant:
π Single MongoDB Database
βββ π₯ Users Collection (shared)
βββ πͺ Tenants Collection (shared)
βββ π Books Collection (tenant-filtered)
βββ πΌοΈ Media Collection (tenant-filtered)
βββ π Orders Collection (tenant-filtered)
βββ β Reviews Collection (tenant-filtered)
β Single Frontend + Single Backend + Single Database
- One Next.js application serves all tenants
- One PayloadCMS backend handles all data
- One MongoDB database stores everything
π Tenant Isolation Through Data Fields
Instead of separate databases, each tenant-specific record includes a tenant field:
// Example book record
{
id: "book123",
name: "My Awesome Book",
price: 29.99,
tenant: "creator-store-id", // π Links to specific tenant
// ... other fields
}π PayloadCMS Multi-Tenant Plugin
The @payloadcms/plugin-multi-tenant plugin automatically:
- Adds
tenantfields to specified collections - Filters queries to show only current tenant's data
- Handles access control based on user-tenant relationships
// In payload.config.ts
multiTenantPlugin<Config>({
collections: {
books: {}, // Tenant-specific
media: {}, // Tenant-specific
},
});π Subdomain Routing Middleware handles subdomain routing seamlessly:
creator1.yourdomain.comβ Shows only Creator1's productscreator2.yourdomain.comβ Shows only Creator2's products- Same codebase, different data context!
// middleware.ts - Extracts tenant from subdomain
if (hostname.endsWith(`.${rootDomain}`)) {
const tenantSlug = hostname.replace(`.${rootDomain}`, "");
return NextResponse.rewrite(
new URL(`/tenants/${tenantSlug}${url.pathname}`, req.url)
);
}When someone visits john.yourdomain.com:
- Middleware extracts "john" from subdomain
- Database Query finds tenant where
slug = "john" - PayloadCMS Plugin automatically filters all queries:
// Transforms: "Find all books" // Into: "Find all books WHERE tenant = 'john-tenant-id'"
Users can be associated with multiple tenants:
// User record structure
{
id: "user123",
email: "[email protected]",
tenants: [
{ tenant: "john-store-id" } // User owns this store
]
}- π° Cost Effective: Single database to maintain
- π§ Simple Infrastructure: No per-tenant database provisioning
- π Easy Scaling: Add tenants without new databases
- π€ Resource Sharing: Categories/tags shared across tenants
- π Platform Analytics: Easy cross-tenant reporting
- PayloadCMS plugin ensures complete tenant data separation
- Access control prevents unauthorized cross-tenant access
- Super admins can access all tenant data when needed
- Regular users only see their own tenant's data
This "shared database, shared schema" model is the most efficient approach for SaaS multi-tenancy!
- Vendor Subdomains: Each creator gets their own subdomain (e.g.,
creator.yourdomain.com) - Custom Storefronts: Personalized merchant pages with custom branding
- Isolated Product Catalogs: Each tenant manages their own products independently
- Stripe Connect Integration: Seamless payment processing for multiple vendors
- Automatic Platform Fees: Configurable commission system
- Secure Checkout: PCI-compliant payment handling
- Digital Product Delivery: Automated file delivery after purchase
- Payload CMS Backend: Powerful headless CMS for content management
- Category & Product Filtering: Advanced search and filtering capabilities
- Image Upload Support: Integrated media management
- Rich Content Editing: WYSIWYG editor for product descriptions
- Personal Libraries: Users can access their purchased products
- Product Reviews & Ratings: Community-driven product feedback
- Advanced Search: Multi-faceted search functionality
- Responsive Design: Mobile-first responsive interface
- Role-Based Access Control (RBAC): Granular permission system
- Authentication System: Secure user registration and login
- Admin Dashboard: Platform administration interface
- Merchant Dashboard: Vendor management tools
- Frontend: Next.js 15 (App Router)
- Styling: TailwindCSS V4 + ShadcnUI Components
- Backend: Payload CMS
- Database: MongoDB (configurable)
- Payments: Stripe Connect
- Type Safety: TypeScript
- State Management: Zustand
- API Layer: tRPC
- Deployment: Vercel (recommended)
lexi/
βββ src/
β βββ app/ # Next.js App Router
β β βββ (app)/ # Main application routes
β β β βββ (auth)/ # Authentication pages
β β β βββ (home)/ # Public homepage & categories
β β β βββ (library)/ # User library pages
β β β βββ (tenants)/ # Multi-tenant storefront pages
β β βββ (payload)/ # Payload CMS admin
β β βββ api/ # API routes (tRPC, Stripe webhooks)
β β
β βββ collections/ # Payload CMS collections
β β βββ Books.ts # Digital products schema
β β βββ Categories.ts # Product categories
β β βββ Orders.ts # Order management
β β βββ Reviews.ts # Product reviews
β β βββ Tenants.ts # Multi-tenant configuration
β β βββ Users.ts # User management
β β
β βββ modules/ # Feature modules
β β βββ auth/ # Authentication logic
β β βββ books/ # Product management
β β βββ checkout/ # Shopping cart & checkout
β β βββ home/ # Homepage components
β β βββ library/ # User library
β β βββ reviews/ # Review system
β β βββ tenants/ # Multi-tenant logic
β β
β βββ components/ui/ # Reusable UI components
β βββ lib/ # Utility functions
β βββ trpc/ # tRPC configuration
β
βββ public/ # Static assets
βββ media/ # Uploaded media files
- Node.js 18+
- Bun (recommended for faster installs and builds) or npm/yarn/pnpm
- MongoDB database
- Stripe account (for payments)
# Clone the repository
git clone <your-repo-url>
cd lexi
# Install dependencies (Bun recommended)
bun install
# Alternative package managers
# npm install
# yarn install
# pnpm installCreate a .env.local file in the root directory:
# Database
DATABASE_URI=mongodb://localhost:27017/lexi
# Payload CMS
PAYLOAD_SECRET=your-payload-secret-key
# Stripe
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
# App Configuration
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_ROOT_DOMAIN=localhost:3000
NEXT_PUBLIC_ENABLE_SUBDOMAIN_ROUTING=false
# File Storage (Vercel Blob)
BLOB_READ_WRITE_TOKEN=vercel_blob_rw_...
# Development
NODE_ENV=development- DATABASE_URI: MongoDB connection string. Use a local MongoDB instance or MongoDB Atlas
- PAYLOAD_SECRET: A secure random string used by Payload CMS for JWT tokens
- STRIPE_SECRET_KEY: Your Stripe secret key (use test key for development)
- STRIPE_WEBHOOK_SECRET: Webhook endpoint secret from your Stripe dashboard
- NEXT_PUBLIC_APP_URL: The base URL of your application (includes protocol)
- NEXT_PUBLIC_ROOT_DOMAIN: Your root domain for subdomain routing (without protocol)
- NEXT_PUBLIC_ENABLE_SUBDOMAIN_ROUTING: Set to "true" in production for multi-tenant subdomains
- BLOB_READ_WRITE_TOKEN: Vercel Blob storage token for file uploads
- NODE_ENV: Set to "development" for local development, "production" for deployment
# Seed the database with initial data
bun run db:seed
# Or reset and seed the database completely
bun run db:reset# Start development server (Bun recommended)
bun dev
# Alternative package managers
# npm run dev
# yarn dev
# pnpm devOpen http://localhost:3000 to see the application.
Each creator gets their own subdomain with:
- Custom product catalog
- Branded storefront design
- Independent inventory management
- Separate analytics and reporting
- Upload & Organize: Easy product upload with rich descriptions
- Categories & Tags: Hierarchical organization system
- Pricing Controls: Flexible pricing options
- Inventory Tracking: Stock management for limited releases
- Onboarding: Streamlined merchant onboarding flow
- Split Payments: Automatic platform fee deduction
- Payout Management: Direct payouts to merchant accounts
- Webhook Handling: Real-time payment status updates
- Star Ratings: 5-star rating system
- Written Reviews: Detailed customer feedback
- Review Moderation: Admin controls for content quality
- Aggregate Scores: Automatic rating calculations
The CMS is configured in src/payload.config.ts with:
- Custom collections for products, users, orders
- File upload handling
- Access control policies
- Admin interface customization
Subdomain routing is handled through:
- Next.js middleware for subdomain detection
- Dynamic tenant resolution
- Isolated data contexts per tenant
- Create a Stripe Connect platform account
- Configure webhook endpoints
- Set up platform fee structure
- Implement merchant onboarding flow
- Connect your repository to Vercel
- Configure environment variables
- Set up custom domain with wildcard subdomain support
- Deploy with automatic CI/CD
# Build the application
bun run build
# Start production server
bun startFor multi-tenant subdomains:
- Configure DNS with wildcard subdomain (
*.yourdomain.com) - Set up SSL certificates for subdomains
- Update environment variables for production URLs
- User management and role assignment
- Platform-wide analytics and reporting
- Content moderation tools
- System configuration
- Product management interface
- Order tracking and fulfillment
- Revenue analytics
- Customer communication tools
- Role-Based Access Control: Granular permissions system
- Secure File Handling: Protected media uploads
- Payment Security: PCI-compliant payment processing
- Data Validation: Comprehensive input sanitization
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
For support and questions:
- Create an issue in the repository
- Check the documentation
- Review the code examples in the modules
Built with modern web technologies:
- Next.js - React framework
- Payload CMS - Headless CMS
- Stripe - Payment processing
- TailwindCSS - Utility-first CSS
- ShadcnUI - Component library