Multi-vendor marketplaces require complex payment flows—splitting payments between platform and vendors, handling refunds, and managing payouts. Stripe Connect makes this manageable. At ZIRA Software, we've built marketplaces processing millions in transactions.
Stripe Connect Account Types
Stripe Connect Options
├── Standard
│ ├── Stripe-hosted onboarding
│ ├── Stripe handles compliance
│ └── Best for: Simple marketplaces
├── Express
│ ├── Simplified onboarding
│ ├── Some customization
│ └── Best for: Most marketplaces
└── Custom
├── Full UI control
├── Platform handles compliance
└── Best for: Enterprise platforms
Vendor Onboarding
// app/Http/Controllers/VendorController.php
use Stripe\StripeClient;
class VendorController extends Controller
{
protected $stripe;
public function __construct()
{
$this->stripe = new StripeClient(config('services.stripe.secret'));
}
public function createConnectAccount(Request $request)
{
$vendor = auth()->user()->vendor;
// Create Express account
$account = $this->stripe->accounts->create([
'type' => 'express',
'country' => 'US',
'email' => $vendor->email,
'capabilities' => [
'card_payments' => ['requested' => true],
'transfers' => ['requested' => true],
],
'business_type' => 'individual',
'metadata' => [
'vendor_id' => $vendor->id,
],
]);
$vendor->update(['stripe_account_id' => $account->id]);
// Generate onboarding link
$link = $this->stripe->accountLinks->create([
'account' => $account->id,
'refresh_url' => route('vendor.onboarding.refresh'),
'return_url' => route('vendor.onboarding.complete'),
'type' => 'account_onboarding',
]);
return redirect($link->url);
}
public function checkOnboardingStatus()
{
$vendor = auth()->user()->vendor;
$account = $this->stripe->accounts->retrieve($vendor->stripe_account_id);
if ($account->charges_enabled && $account->payouts_enabled) {
$vendor->update(['stripe_onboarding_complete' => true]);
return redirect()->route('vendor.dashboard')
->with('success', 'Account setup complete!');
}
return back()->with('warning', 'Please complete your account setup.');
}
}
Payment Flow
// app/Services/MarketplacePaymentService.php
class MarketplacePaymentService
{
protected $stripe;
protected $platformFeePercent = 10; // 10% platform fee
public function __construct()
{
$this->stripe = new StripeClient(config('services.stripe.secret'));
}
public function createPaymentIntent(Order $order)
{
$vendor = $order->vendor;
$amount = $order->total * 100; // Convert to cents
$platformFee = $amount * ($this->platformFeePercent / 100);
$paymentIntent = $this->stripe->paymentIntents->create([
'amount' => $amount,
'currency' => 'usd',
'payment_method_types' => ['card'],
'application_fee_amount' => $platformFee,
'transfer_data' => [
'destination' => $vendor->stripe_account_id,
],
'metadata' => [
'order_id' => $order->id,
'vendor_id' => $vendor->id,
],
]);
$order->update(['stripe_payment_intent_id' => $paymentIntent->id]);
return $paymentIntent;
}
public function confirmPayment(Order $order, $paymentMethodId)
{
$paymentIntent = $this->stripe->paymentIntents->confirm(
$order->stripe_payment_intent_id,
['payment_method' => $paymentMethodId]
);
if ($paymentIntent->status === 'succeeded') {
$order->update(['status' => 'paid']);
event(new OrderPaid($order));
}
return $paymentIntent;
}
}
Split Payments (Multiple Vendors)
// Order with items from multiple vendors
public function createMultiVendorPayment(Order $order)
{
$amount = $order->total * 100;
// Create payment intent without automatic transfer
$paymentIntent = $this->stripe->paymentIntents->create([
'amount' => $amount,
'currency' => 'usd',
'payment_method_types' => ['card'],
'metadata' => ['order_id' => $order->id],
]);
return $paymentIntent;
}
// After successful payment, create transfers
public function distributePayment(Order $order)
{
foreach ($order->items->groupBy('vendor_id') as $vendorId => $items) {
$vendor = Vendor::find($vendorId);
$vendorTotal = $items->sum('total');
$platformFee = $vendorTotal * 0.10;
$vendorAmount = ($vendorTotal - $platformFee) * 100;
$this->stripe->transfers->create([
'amount' => $vendorAmount,
'currency' => 'usd',
'destination' => $vendor->stripe_account_id,
'transfer_group' => 'ORDER_' . $order->id,
'metadata' => [
'order_id' => $order->id,
'vendor_id' => $vendor->id,
],
]);
}
}
Webhook Handling
// app/Http/Controllers/StripeWebhookController.php
class StripeWebhookController extends Controller
{
public function handle(Request $request)
{
$payload = $request->getContent();
$sigHeader = $request->header('Stripe-Signature');
try {
$event = \Stripe\Webhook::constructEvent(
$payload,
$sigHeader,
config('services.stripe.webhook_secret')
);
} catch (\Exception $e) {
return response('Invalid signature', 400);
}
switch ($event->type) {
case 'payment_intent.succeeded':
$this->handlePaymentSucceeded($event->data->object);
break;
case 'account.updated':
$this->handleAccountUpdated($event->data->object);
break;
case 'payout.paid':
$this->handlePayoutPaid($event->data->object);
break;
}
return response('OK', 200);
}
protected function handlePaymentSucceeded($paymentIntent)
{
$order = Order::where('stripe_payment_intent_id', $paymentIntent->id)->first();
if ($order) {
$order->update(['status' => 'paid']);
$this->distributePayment($order);
}
}
}
Refunds
public function refundOrder(Order $order, $amount = null)
{
$refundAmount = $amount ? $amount * 100 : null;
// Refund customer
$refund = $this->stripe->refunds->create([
'payment_intent' => $order->stripe_payment_intent_id,
'amount' => $refundAmount,
'reverse_transfer' => true, // Reverse vendor transfer
'refund_application_fee' => true, // Refund platform fee
]);
$order->update(['status' => 'refunded']);
return $refund;
}
Conclusion
Stripe Connect transforms complex marketplace payments into manageable flows. Proper vendor onboarding, payment splitting, and webhook handling create reliable multi-vendor platforms.
Building a marketplace? Contact ZIRA Software for Stripe Connect integration.