# 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

```typescript
// 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

```typescript
// 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

```typescript
// 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

```tsx
// 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:

1. **Multi-Party Payments**: Stripe Connect für Split Payments
2. **Seller Management**: Onboarding und Verification
3. **Trust & Safety**: Reviews, Disputes, Moderation
4. **Scalability**: Handling vieler Verkäufer und Produkte

Marketplaces sind komplex, aber hochprofitabel.

---

## Bildprompts

1. "Marketplace platform architecture diagram, buyers sellers and platform"
2. "Seller dashboard interface, revenue charts and order list"
3. "Payment flow visualization, split payment to multiple sellers"

---

## Quellen

- [Stripe Connect](https://stripe.com/docs/connect)
- [Marketplace Business Models](https://a16z.com/marketplace-100/)
- [Sharetribe Marketplace Guide](https://www.sharetribe.com/academy/)
- [Building Marketplaces](https://www.nfx.com/post/marketplace-handbook)
