1 min read
TechnologieMarketplace Platform Development
Multi-Vendor Marketplace bauen. Seller Management, Product Listings, Escrow Payments und Commission Handling.
MarketplaceMulti-VendorE-CommerceSeller ManagementCommissionEscrow

Marketplace Platform Development
Meta-Description: Multi-Vendor Marketplace bauen. Seller Management, Product Listings, Escrow Payments und Commission Handling.
Keywords: Marketplace, Multi-Vendor, E-Commerce, Seller Management, Commission, Escrow, Platform Business
Einführung
Marketplace Platforms verbinden Käufer und Verkäufer. Von Seller Onboarding bis Commission Splitting – die technischen Herausforderungen sind komplex. Dieser Guide zeigt die Architektur und Implementation eines Multi-Vendor Marketplaces.
Marketplace Architecture
┌─────────────────────────────────────────────────────────────┐
│ MARKETPLACE PLATFORM ARCHITECTURE │
├─────────────────────────────────────────────────────────────┤
│ │
│ Stakeholders: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Platform (You) │ │
│ │ ├── Commission on sales │ │
│ │ ├── Subscription fees │ │
│ │ └── Value-added services │ │
│ │ │ │
│ │ Sellers │ │
│ │ ├── List products │ │
│ │ ├── Receive payouts │ │
│ │ └── Manage inventory │ │
│ │ │ │
│ │ Buyers │ │
│ │ ├── Browse & purchase │ │
│ │ ├── Reviews & ratings │ │
│ │ └── Dispute resolution │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Payment Flow: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Buyer pays $100 │ │
│ │ ↓ │ │
│ │ Platform receives $100 (Stripe Connect) │ │
│ │ ↓ │ │
│ │ Platform takes 15% commission ($15) │ │
│ │ ↓ │ │
│ │ Seller receives $85 (after payout delay) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Core Features: │
│ ├── Seller Dashboard │
│ ├── Product Catalog │
│ ├── Shopping Cart │
│ ├── Checkout (Split Payments) │
│ ├── Order Management │
│ ├── Reviews & Ratings │
│ ├── Dispute Resolution │
│ └── Analytics & Reporting │
│ │
└─────────────────────────────────────────────────────────────┘Database Schema
// prisma/schema.prisma
model Seller {
id String @id @default(cuid())
userId String @unique
user User @relation(fields: [userId], references: [id])
// Business Info
businessName String
businessType String // individual, company
taxId String?
description String?
logo String?
// Stripe Connect
stripeAccountId String? @unique
stripeOnboarded Boolean @default(false)
payoutEnabled Boolean @default(false)
// Settings
commissionRate Float @default(0.15) // 15%
payoutSchedule String @default("daily") // daily, weekly, monthly
// Status
status SellerStatus @default(PENDING)
verifiedAt DateTime?
// Relations
products Product[]
orders OrderItem[]
payouts Payout[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum SellerStatus {
PENDING
ACTIVE
SUSPENDED
BANNED
}
model Product {
id String @id @default(cuid())
sellerId String
seller Seller @relation(fields: [sellerId], references: [id])
// Product Info
name String
slug String
description String
price Int // in cents
compareAtPrice Int?
currency String @default("EUR")
// Inventory
sku String?
inventory Int @default(0)
trackInventory Boolean @default(true)
// Categorization
categoryId String?
category Category? @relation(fields: [categoryId], references: [id])
tags String[]
// Media
images ProductImage[]
// Status
status ProductStatus @default(DRAFT)
publishedAt DateTime?
// Stats
viewCount Int @default(0)
salesCount Int @default(0)
rating Float?
reviewCount Int @default(0)
// Relations
variants ProductVariant[]
orderItems OrderItem[]
reviews Review[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([sellerId, slug])
@@index([categoryId])
@@index([status])
}
model Order {
id String @id @default(cuid())
orderNumber String @unique
// Buyer
userId String
user User @relation(fields: [userId], references: [id])
// Totals
subtotal Int // Sum of items
platformFee Int // Platform commission
shippingTotal Int
taxTotal Int
total Int
// Payment
stripePaymentId String?
paymentStatus PaymentStatus @default(PENDING)
paidAt DateTime?
// Shipping
shippingAddress Json
shippingMethod String?
// Status
status OrderStatus @default(PENDING)
// Items (can be from multiple sellers)
items OrderItem[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model OrderItem {
id String @id @default(cuid())
orderId String
order Order @relation(fields: [orderId], references: [id])
// Product
productId String
product Product @relation(fields: [productId], references: [id])
variantId String?
// Seller
sellerId String
seller Seller @relation(fields: [sellerId], references: [id])
// Pricing
price Int
quantity Int
subtotal Int // price * quantity
commission Int // Platform commission
sellerPayout Int // subtotal - commission
// Status (per-seller fulfillment)
status OrderItemStatus @default(PENDING)
shippedAt DateTime?
trackingNumber String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Payout {
id String @id @default(cuid())
sellerId String
seller Seller @relation(fields: [sellerId], references: [id])
amount Int
currency String @default("EUR")
status PayoutStatus @default(PENDING)
stripePayoutId String?
paidAt DateTime?
failedAt DateTime?
failureReason String?
// Payout period
periodStart DateTime
periodEnd DateTime
createdAt DateTime @default(now())
}Seller Onboarding
// lib/marketplace/seller-onboarding.ts
import { stripe } from '@/lib/stripe';
import { db } from '@/lib/db';
export async function createSellerAccount(
userId: string,
data: {
businessName: string;
businessType: 'individual' | 'company';
country: string;
email: string;
}
): Promise<string> {
// Create Stripe Connect account
const account = await stripe.accounts.create({
type: 'express',
country: data.country,
email: data.email,
business_type: data.businessType,
business_profile: {
name: data.businessName
},
capabilities: {
card_payments: { requested: true },
transfers: { requested: true }
}
});
// Create seller record
await db.seller.create({
data: {
userId,
businessName: data.businessName,
businessType: data.businessType,
stripeAccountId: account.id,
status: 'PENDING'
}
});
// Generate onboarding link
const accountLink = await stripe.accountLinks.create({
account: account.id,
refresh_url: `${process.env.NEXT_PUBLIC_URL}/seller/onboarding/refresh`,
return_url: `${process.env.NEXT_PUBLIC_URL}/seller/onboarding/complete`,
type: 'account_onboarding'
});
return accountLink.url;
}
export async function completeSellerOnboarding(
stripeAccountId: string
): Promise<void> {
// Check account status
const account = await stripe.accounts.retrieve(stripeAccountId);
if (account.charges_enabled && account.payouts_enabled) {
await db.seller.update({
where: { stripeAccountId },
data: {
stripeOnboarded: true,
payoutEnabled: true,
status: 'ACTIVE',
verifiedAt: new Date()
}
});
}
}
// Webhook handler for account updates
export async function handleAccountUpdated(
account: Stripe.Account
): Promise<void> {
await db.seller.update({
where: { stripeAccountId: account.id },
data: {
payoutEnabled: account.payouts_enabled || false,
stripeOnboarded: account.details_submitted || false
}
});
}Split Payments
// lib/marketplace/checkout.ts
import { stripe } from '@/lib/stripe';
import { db } from '@/lib/db';
import { nanoid } from 'nanoid';
interface CartItem {
productId: string;
quantity: number;
variantId?: string;
}
export async function createMarketplaceCheckout(
userId: string,
items: CartItem[],
shippingAddress: any
): Promise<string> {
// Group items by seller
const itemsBySeller = await groupItemsBySeller(items);
// Calculate totals
const { subtotal, platformFee, lineItems, transfers } =
await calculateOrderTotals(itemsBySeller);
// Create order
const order = await db.order.create({
data: {
orderNumber: `ORD-${nanoid(10).toUpperCase()}`,
userId,
subtotal,
platformFee,
shippingTotal: 0, // Calculate based on sellers
taxTotal: 0,
total: subtotal,
shippingAddress,
status: 'PENDING',
items: {
create: lineItems.map(item => ({
productId: item.productId,
sellerId: item.sellerId,
variantId: item.variantId,
price: item.price,
quantity: item.quantity,
subtotal: item.subtotal,
commission: item.commission,
sellerPayout: item.sellerPayout,
status: 'PENDING'
}))
}
}
});
// Create Stripe Checkout with transfers
const session = await stripe.checkout.sessions.create({
mode: 'payment',
customer_email: (await db.user.findUnique({ where: { id: userId } }))?.email,
line_items: lineItems.map(item => ({
price_data: {
currency: 'eur',
unit_amount: item.price,
product_data: {
name: item.productName,
images: item.images
}
},
quantity: item.quantity
})),
payment_intent_data: {
// Split payment to sellers
transfer_group: order.id,
metadata: {
orderId: order.id
}
},
success_url: `${process.env.NEXT_PUBLIC_URL}/orders/${order.id}/success`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/cart`,
metadata: {
orderId: order.id
}
});
return session.url!;
}
async function calculateOrderTotals(itemsBySeller: Map<string, CartItem[]>) {
const lineItems: any[] = [];
let subtotal = 0;
let platformFee = 0;
const transfers: any[] = [];
for (const [sellerId, items] of itemsBySeller) {
const seller = await db.seller.findUnique({
where: { id: sellerId }
});
for (const item of items) {
const product = await db.product.findUnique({
where: { id: item.productId }
});
if (!product) continue;
const itemSubtotal = product.price * item.quantity;
const commission = Math.round(itemSubtotal * (seller?.commissionRate || 0.15));
const sellerPayout = itemSubtotal - commission;
subtotal += itemSubtotal;
platformFee += commission;
lineItems.push({
productId: product.id,
productName: product.name,
sellerId,
variantId: item.variantId,
price: product.price,
quantity: item.quantity,
subtotal: itemSubtotal,
commission,
sellerPayout,
images: [] // Add product images
});
// Prepare transfer for seller
transfers.push({
sellerId,
stripeAccountId: seller?.stripeAccountId,
amount: sellerPayout
});
}
}
return { subtotal, platformFee, lineItems, transfers };
}
// After successful payment, create transfers
export async function createSellerTransfers(orderId: string): Promise<void> {
const order = await db.order.findUnique({
where: { id: orderId },
include: {
items: {
include: { seller: true }
}
}
});
if (!order) return;
// Group payouts by seller
const payoutsBySeller = new Map<string, number>();
for (const item of order.items) {
const current = payoutsBySeller.get(item.sellerId) || 0;
payoutsBySeller.set(item.sellerId, current + item.sellerPayout);
}
// Create transfers
for (const [sellerId, amount] of payoutsBySeller) {
const seller = order.items.find(i => i.sellerId === sellerId)?.seller;
if (!seller?.stripeAccountId) continue;
await stripe.transfers.create({
amount,
currency: 'eur',
destination: seller.stripeAccountId,
transfer_group: orderId,
metadata: {
orderId,
sellerId
}
});
}
// Update order status
await db.order.update({
where: { id: orderId },
data: {
paymentStatus: 'PAID',
paidAt: new Date()
}
});
}Seller Dashboard
// app/seller/dashboard/page.tsx
import { getServerSession } from 'next-auth';
import { db } from '@/lib/db';
export default async function SellerDashboard() {
const session = await getServerSession();
const seller = await db.seller.findUnique({
where: { userId: session!.user.id }
});
const [stats, recentOrders, topProducts] = await Promise.all([
getSellerStats(seller!.id),
getRecentOrders(seller!.id),
getTopProducts(seller!.id)
]);
return (
<div className="p-6 space-y-6">
<h1 className="text-2xl font-bold">Seller Dashboard</h1>
{/* Stats Cards */}
<div className="grid grid-cols-4 gap-4">
<StatCard
title="Revenue (30d)"
value={`€${(stats.revenue / 100).toFixed(2)}`}
change={stats.revenueChange}
/>
<StatCard
title="Orders"
value={stats.orderCount}
change={stats.orderChange}
/>
<StatCard
title="Products"
value={stats.productCount}
/>
<StatCard
title="Pending Payout"
value={`€${(stats.pendingPayout / 100).toFixed(2)}`}
/>
</div>
<div className="grid grid-cols-2 gap-6">
{/* Recent Orders */}
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-lg font-semibold mb-4">Recent Orders</h2>
<OrderList orders={recentOrders} />
</div>
{/* Top Products */}
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-lg font-semibold mb-4">Top Products</h2>
<ProductList products={topProducts} />
</div>
</div>
</div>
);
}
async function getSellerStats(sellerId: string) {
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
const [revenue, orders, products, pendingPayout] = await Promise.all([
db.orderItem.aggregate({
where: {
sellerId,
order: { paidAt: { gte: thirtyDaysAgo } }
},
_sum: { sellerPayout: true }
}),
db.orderItem.count({
where: {
sellerId,
createdAt: { gte: thirtyDaysAgo }
}
}),
db.product.count({ where: { sellerId } }),
db.orderItem.aggregate({
where: {
sellerId,
order: { paymentStatus: 'PAID' },
// Not yet paid out
},
_sum: { sellerPayout: true }
})
]);
return {
revenue: revenue._sum.sellerPayout || 0,
revenueChange: 0, // Calculate vs previous period
orderCount: orders,
orderChange: 0,
productCount: products,
pendingPayout: pendingPayout._sum.sellerPayout || 0
};
}Platform Economics
| Revenue Stream | Example |
|---|---|
| **Commission** | 10-20% per sale |
| **Listing Fee** | $0.20 per listing |
| **Subscription** | $29/mo for premium |
| **Promoted Listings** | $0.10 per click |
| **Transaction Fee** | 2.9% + $0.30 |
Fazit
Marketplace Platform Development erfordert:
- Multi-Party Payments: Stripe Connect für Split Payments
- Seller Management: Onboarding und Verification
- Trust & Safety: Reviews, Disputes, Moderation
- Scalability: Handling vieler Verkäufer und Produkte
Marketplaces sind komplex, aber hochprofitabel.
Bildprompts
- "Marketplace platform architecture diagram, buyers sellers and platform"
- "Seller dashboard interface, revenue charts and order list"
- "Payment flow visualization, split payment to multiple sellers"