Skip to main content
RapidDev - Software Development Agency
v0-integrationsNext.js API Route

How to Integrate Stripe Connect with V0

To integrate Stripe Connect with V0 by Vercel, generate a Next.js app with V0, create API routes for Connect OAuth onboarding and payment splitting, add your Stripe secret key and webhook secret to Vercel environment variables, and deploy. Stripe Connect lets your platform onboard sellers and route payments between buyers, sellers, and your platform — all secured server-side via API routes.

What you'll learn

  • How to generate a marketplace payment UI with V0 and connect it to Stripe Connect API routes
  • How to implement the Stripe Connect OAuth flow to onboard sellers in a Next.js app
  • How to split payments between your platform and connected seller accounts using transfer_data
  • How to verify Stripe webhook signatures in the App Router using request.text()
  • How to configure Stripe Connect environment variables in Vercel Dashboard for production
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate15 min read45 minutesPaymentMarch 2026RapidDev Engineering Team
TL;DR

To integrate Stripe Connect with V0 by Vercel, generate a Next.js app with V0, create API routes for Connect OAuth onboarding and payment splitting, add your Stripe secret key and webhook secret to Vercel environment variables, and deploy. Stripe Connect lets your platform onboard sellers and route payments between buyers, sellers, and your platform — all secured server-side via API routes.

Build Marketplace Payments with Stripe Connect and V0

Stripe Connect is the backbone of marketplace and platform payments — it powers Lyft driver payouts, Shopify merchant payments, and thousands of other multi-sided platforms. Unlike standard Stripe, Connect lets your app act as the intermediary: buyers pay through your platform, Stripe routes funds to connected seller accounts, and your platform takes a fee automatically. V0 accelerates the front-end work by generating polished seller onboarding flows, marketplace dashboards, and payment confirmation UIs in minutes.

The integration follows a clear server-side pattern. V0 generates your React components and Next.js page structure. You then add two or three API routes: one to initiate the Connect OAuth flow (redirecting sellers to Stripe to authorize your platform), one to complete the OAuth exchange and store the resulting account ID, and one to create payment intents that specify both the destination account and your platform fee. All Stripe SDK calls live in these route handlers where your secret key stays server-side and never appears in the browser bundle.

Deploying on Vercel makes secret management straightforward — environment variables set in the Vercel Dashboard are automatically injected into your serverless functions at runtime. The Stripe Marketplace integration on Vercel can also auto-provision your publishable key and secret key if you prefer the one-click path. Once deployed, your marketplace can onboard sellers in minutes and process split payments on their first sale.

Integration method

Next.js API Route

Stripe Connect integrates with V0-generated Next.js apps through server-side API routes that handle the OAuth onboarding flow, payment intent creation with transfer splits, and webhook signature verification. The Stripe Node.js SDK runs exclusively in route handlers so your secret key never reaches the browser. After generating your UI with V0, you wire these API routes and configure environment variables in Vercel Dashboard.

Prerequisites

  • A V0 account at v0.dev to generate the Next.js application code
  • A Stripe account with Connect enabled — go to stripe.com/connect and apply for platform access
  • Node.js 18+ installed locally if you plan to run the project before deploying to Vercel
  • A Vercel account to deploy the generated Next.js app and manage environment variables
  • Basic familiarity with Next.js App Router file structure (app/ directory, route.ts files)

Step-by-step guide

1

Generate the Marketplace UI with V0

Start by opening V0 at v0.dev and describing the marketplace interface you need. Be specific about the seller onboarding flow, the payment confirmation screens, and any dashboards. V0 will generate a complete Next.js App Router project with React components, Tailwind styling, and placeholder API calls. When V0 finishes generating, review the preview — you should see your seller dashboard, a 'Connect Stripe Account' CTA button, and a checkout form. V0 will likely generate mock fetch calls to API endpoints like /api/stripe-connect/authorize and /api/stripe-connect/payment — these are the routes you'll build in the next steps. The generated code establishes your component structure; now you need to wire in real Stripe logic. Use the Git panel in V0's sidebar to push the generated project to a new GitHub repository, which Vercel will use for automatic deployments. If V0 hasn't generated API route files yet, note the component file locations (typically app/page.tsx and app/components/) since you'll add route files alongside them.

V0 Prompt

Create a two-sided marketplace with a seller onboarding section featuring a 'Connect with Stripe' button linked to /api/stripe-connect/authorize, a dashboard showing connected account status and recent earnings, and a buyer checkout page with a payment form that posts to /api/stripe-connect/payment. Include a navigation bar and professional Tailwind styling with a clean white and indigo color scheme.

Paste this in V0 chat

Pro tip: Ask V0 to include loading states and error messages in the UI — for example, 'Show a loading spinner while the Stripe connection is processing and display an error banner if the connection fails.' This saves you from adding those states manually later.

Expected result: A fully styled marketplace UI in V0's preview with seller onboarding components, a buyer checkout form, and a dashboard layout — all referencing the API endpoints you'll create next.

2

Create the Stripe Connect OAuth API Routes

After exporting your V0 project to GitHub, open the code locally (or edit via GitHub) and create the API route files Stripe Connect needs. The first route handles the OAuth redirect — it constructs the Stripe Connect authorization URL with your client ID and a redirect URI, then sends the user to Stripe to authorize your platform. When Stripe redirects back to your app with a code parameter, the second route exchanges that code for a connected account ID using stripe.oauth.token(). You store this account ID (typically in your database, though for simplicity you can use a cookie or session for testing). These two routes together complete the seller onboarding flow. Create the files at app/api/stripe-connect/authorize/route.ts and app/api/stripe-connect/callback/route.ts. The authorize route reads your Stripe Connect client ID from process.env.STRIPE_CONNECT_CLIENT_ID and constructs the OAuth URL. The callback route reads the code from the URL query string and calls the Stripe SDK to exchange it. Make sure to handle errors gracefully — Stripe will include an error parameter in the redirect URL if the seller declines or something goes wrong. The connected account ID you receive (starting with 'acct_') is what you'll reference in every subsequent payment for that seller.

app/api/stripe-connect/authorize/route.ts
1// app/api/stripe-connect/authorize/route.ts
2import { NextResponse } from 'next/server';
3
4export async function GET() {
5 const clientId = process.env.STRIPE_CONNECT_CLIENT_ID!;
6 const redirectUri = `${process.env.NEXT_PUBLIC_APP_URL}/api/stripe-connect/callback`;
7
8 const params = new URLSearchParams({
9 response_type: 'code',
10 client_id: clientId,
11 scope: 'read_write',
12 redirect_uri: redirectUri,
13 });
14
15 return NextResponse.redirect(
16 `https://connect.stripe.com/oauth/authorize?${params.toString()}`
17 );
18}
19
20// app/api/stripe-connect/callback/route.ts
21import { NextRequest, NextResponse } from 'next/server';
22import Stripe from 'stripe';
23
24const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
25
26export async function GET(request: NextRequest) {
27 const { searchParams } = new URL(request.url);
28 const code = searchParams.get('code');
29 const error = searchParams.get('error');
30
31 if (error) {
32 return NextResponse.redirect(
33 `${process.env.NEXT_PUBLIC_APP_URL}/onboarding?error=${error}`
34 );
35 }
36
37 if (!code) {
38 return NextResponse.json({ error: 'Missing authorization code' }, { status: 400 });
39 }
40
41 try {
42 const response = await stripe.oauth.token({
43 grant_type: 'authorization_code',
44 code,
45 });
46
47 const connectedAccountId = response.stripe_user_id;
48 // TODO: Save connectedAccountId to your database linked to the seller's user ID
49
50 return NextResponse.redirect(
51 `${process.env.NEXT_PUBLIC_APP_URL}/dashboard?connected=true`
52 );
53 } catch (err) {
54 console.error('Stripe Connect OAuth error:', err);
55 return NextResponse.redirect(
56 `${process.env.NEXT_PUBLIC_APP_URL}/onboarding?error=oauth_failed`
57 );
58 }
59}

Pro tip: In production, save the connected account ID (acct_xxx) to your database linked to the seller's user record. For testing, you can temporarily log it to console.error and copy it for the next step.

Expected result: Two new route files exist in your project. Navigating to /api/stripe-connect/authorize in your browser redirects you to Stripe's OAuth authorization page where a test seller can connect their account.

3

Create the Payment Intent Route with Transfer Split

The payment route is where Stripe Connect's split-payment magic happens. When a buyer checks out, your app creates a PaymentIntent on your platform's Stripe account, specifying the seller's connected account ID and your platform's application fee. Stripe automatically routes the funds: after the payment succeeds, the application_fee_amount stays with your platform and the remainder transfers to the seller's account. Create this route at app/api/stripe-connect/payment/route.ts. The route receives the amount, currency, and the seller's connected account ID from the request body. It calls stripe.paymentIntents.create() with a transfer_data object specifying the destination account. It then returns the client_secret from the created PaymentIntent back to the browser, which uses it with Stripe.js to confirm the payment on the client side. Be precise with amounts — Stripe uses the smallest currency unit (cents for USD, so $10.00 = 1000). The application_fee_amount should also be in cents. For example, a 10% platform fee on a $50 sale would be: amount: 5000, application_fee_amount: 500, with the remaining 4500 (minus Stripe's processing fee) going to the seller. This entire calculation and PaymentIntent creation happens server-side, so your Stripe secret key and the financial logic never touches the client.

app/api/stripe-connect/payment/route.ts
1// app/api/stripe-connect/payment/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import Stripe from 'stripe';
4
5const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
6
7export async function POST(request: NextRequest) {
8 try {
9 const { amount, currency = 'usd', connectedAccountId, platformFeePercent = 10 } =
10 await request.json();
11
12 if (!amount || !connectedAccountId) {
13 return NextResponse.json(
14 { error: 'Missing required fields: amount, connectedAccountId' },
15 { status: 400 }
16 );
17 }
18
19 const amountInCents = Math.round(amount * 100);
20 const platformFee = Math.round(amountInCents * (platformFeePercent / 100));
21
22 const paymentIntent = await stripe.paymentIntents.create({
23 amount: amountInCents,
24 currency,
25 application_fee_amount: platformFee,
26 transfer_data: {
27 destination: connectedAccountId,
28 },
29 });
30
31 return NextResponse.json({
32 clientSecret: paymentIntent.client_secret,
33 paymentIntentId: paymentIntent.id,
34 });
35 } catch (error) {
36 console.error('Payment intent creation failed:', error);
37 return NextResponse.json(
38 { error: 'Failed to create payment intent' },
39 { status: 500 }
40 );
41 }
42}

Pro tip: Use Stripe's test connected accounts during development. In test mode, you can create test connected accounts at dashboard.stripe.com/test/connect/accounts without real sellers needing to go through OAuth.

Expected result: POSTing to /api/stripe-connect/payment with a valid amount and connectedAccountId returns a clientSecret that Stripe.js can use to confirm the payment on the client side.

4

Add Webhook Handling for Payment Events

Webhooks are essential for marketplace reliability — your app needs to know when a payment actually succeeds, not just when the client-side confirmation returns. Stripe sends POST requests to your webhook endpoint with signed payloads whenever payment events occur. Create a webhook handler at app/api/webhooks/stripe/route.ts. The most critical implementation detail for Next.js App Router is using await request.text() instead of request.json() to read the raw request body. Stripe signs the payload using your webhook secret, and that signature is computed against the raw bytes. If you parse the body to JSON first (even just by calling request.json()), the raw body changes and signature verification always fails. The stripe.webhooks.constructEvent() method verifies the signature and returns a typed Stripe event object. You then switch on event.type to handle the events you care about. For Connect, the key events are payment_intent.succeeded (payment completed), account.updated (seller's connected account details changed), and transfer.created (funds routed to seller). After processing, always return a 200 response quickly — Stripe will retry unacknowledged webhooks for up to 72 hours, and long-running webhook handlers can cause duplicate processing. Set up your webhook endpoint in the Stripe Dashboard under Developers → Webhooks, pointing to your deployed Vercel URL.

app/api/webhooks/stripe/route.ts
1// app/api/webhooks/stripe/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import Stripe from 'stripe';
4
5const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
6
7export async function POST(request: NextRequest) {
8 const body = await request.text(); // CRITICAL: use text(), not json()
9 const signature = request.headers.get('stripe-signature');
10
11 if (!signature) {
12 return NextResponse.json({ error: 'Missing stripe-signature header' }, { status: 400 });
13 }
14
15 let event: Stripe.Event;
16
17 try {
18 event = stripe.webhooks.constructEvent(
19 body,
20 signature,
21 process.env.STRIPE_WEBHOOK_SECRET!
22 );
23 } catch (err) {
24 console.error('Webhook signature verification failed:', err);
25 return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
26 }
27
28 switch (event.type) {
29 case 'payment_intent.succeeded': {
30 const paymentIntent = event.data.object as Stripe.PaymentIntent;
31 console.log('Payment succeeded:', paymentIntent.id);
32 // Update order status in your database
33 break;
34 }
35 case 'account.updated': {
36 const account = event.data.object as Stripe.Account;
37 console.log('Connected account updated:', account.id);
38 // Update seller's verification status in your database
39 break;
40 }
41 case 'transfer.created': {
42 const transfer = event.data.object as Stripe.Transfer;
43 console.log('Transfer to seller:', transfer.destination, transfer.amount);
44 break;
45 }
46 default:
47 console.log('Unhandled event type:', event.type);
48 }
49
50 return NextResponse.json({ received: true });
51}

Pro tip: Test webhooks locally using the Stripe CLI: run 'stripe listen --forward-to localhost:3000/api/webhooks/stripe' to forward Stripe events to your development server before deploying.

Expected result: Your webhook endpoint receives Stripe events, verifies signatures successfully, and returns 200 responses. In the Stripe Dashboard under Developers → Webhooks, you'll see green checkmarks for delivered events.

5

Configure Environment Variables in Vercel and Deploy

With all API routes created and pushed to GitHub, the final step is adding your Stripe credentials to Vercel so your serverless functions can authenticate with Stripe. Open the Vercel Dashboard, navigate to your project, click Settings in the top navigation, then Environment Variables in the left sidebar. You need to add four variables. STRIPE_SECRET_KEY is your Stripe platform secret key — find it at dashboard.stripe.com under Developers → API Keys. This key should never have the NEXT_PUBLIC_ prefix since it must stay server-side only. STRIPE_CONNECT_CLIENT_ID is your Connect application's client ID — find it at dashboard.stripe.com/connect/settings under 'Your Platform's client_id'. STRIPE_WEBHOOK_SECRET is the webhook signing secret — Stripe generates this when you create a webhook endpoint in the Dashboard. NEXT_PUBLIC_APP_URL is your deployed Vercel URL (e.g., https://your-app.vercel.app) — this is safe to expose since it's just your domain. Set each variable for Production, Preview, and Development environments. After saving, trigger a new deployment by pushing a small change to GitHub or clicking 'Redeploy' in the Vercel Dashboard. Vercel will rebuild your project with the new environment variables injected. For complex Connect configurations, RapidDev's team can help set up the complete marketplace payment flow including database integration for account storage. Once deployed, test the full flow by going through the OAuth onboarding with a Stripe test account and making a test payment using Stripe's test card 4242 4242 4242 4242.

Pro tip: In Vercel, you can also set different Stripe keys per environment — use your Stripe test keys (sk_test_...) for Preview deployments and live keys (sk_live_...) for Production only. This prevents accidental real charges during testing.

Expected result: Your Vercel deployment builds successfully, the Stripe Connect OAuth flow works end-to-end in the deployed app, and test payments correctly split funds between the platform and connected accounts.

Common use cases

Freelance Marketplace with Seller Onboarding

A platform where freelancers sign up, connect their Stripe accounts via OAuth, and receive payment automatically when clients hire them. The platform takes a 10% fee on every transaction, with the remaining 90% routed to the freelancer's connected account.

V0 Prompt

Create a freelancer marketplace dashboard with a 'Connect Stripe' button that links to /api/stripe-connect/authorize, a seller profile card showing connected account status, and a payment confirmation page. Use a clean professional design with Tailwind.

Copy this prompt to try it in V0

Event Ticket Marketplace with Promoter Payouts

Event promoters list tickets on a marketplace platform. When buyers purchase, the platform collects payment, takes a 5% platform fee, and transfers the remaining amount to the promoter's Stripe connected account automatically via transfer_data.

V0 Prompt

Build an event ticketing marketplace UI showing a list of events with 'Buy Ticket' buttons, a checkout form that calls /api/stripe-connect/payment, and a promoter dashboard showing earnings and payout status. Include a banner for promoters to connect their Stripe account.

Copy this prompt to try it in V0

SaaS Platform with Vendor Revenue Sharing

A SaaS platform with a vendor marketplace where third-party vendors sell add-ons. Revenue is automatically split: 70% goes to the vendor's connected Stripe account and 30% stays with the platform, all handled server-side without manual invoicing.

V0 Prompt

Design a vendor marketplace page showing plugin/add-on cards with prices and 'Purchase' buttons, a vendor onboarding section with Stripe Connect status, and a revenue dashboard showing platform earnings. Call /api/stripe-connect/checkout for purchases.

Copy this prompt to try it in V0

Troubleshooting

Webhook signature verification fails with 'No signatures found matching the expected signature for payload'

Cause: The request body was parsed as JSON before being passed to stripe.webhooks.constructEvent(). Stripe verifies signatures against the raw bytes of the payload, and any transformation — even JSON.parse followed by JSON.stringify — changes the bytes and breaks verification.

Solution: Replace request.json() with request.text() in your webhook route handler. This is a Next.js App Router-specific requirement. The parsed body bytes must match exactly what Stripe signed.

typescript
1// Wrong:
2const body = await request.json();
3
4// Correct:
5const body = await request.text();
6const event = stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);

OAuth callback returns 'No such authorization code: ac_xxx' error from Stripe

Cause: The authorization code from Stripe's OAuth redirect has already been used or has expired. Codes are single-use and expire quickly. This often happens when the callback route is called twice (e.g., due to browser refresh, redirect loop, or duplicate request).

Solution: Ensure the callback route redirects the user after exchanging the code, preventing the callback URL from being hit a second time. Store the code in a processed-codes set to reject duplicates. Check for redirect loops in your Next.js middleware.

Payment intent creation returns 'The `transfer_data[destination]` parameter of a PaymentIntent must be a connected account'

Cause: The connectedAccountId passed to the payment route is invalid, missing, or belongs to a different Stripe platform account. In test mode, this also occurs if you're using live-mode account IDs or if the connected account hasn't completed Stripe's onboarding requirements.

Solution: Verify the connected account ID starts with 'acct_' and was created under your platform account. In the Stripe Dashboard under Connect → Accounts, confirm the account exists and its details_submitted field is true. Use Stripe's test mode connected accounts for development.

STRIPE_SECRET_KEY is undefined in production but works locally

Cause: The environment variable was set in the local .env.local file but not added to Vercel's Environment Variables configuration. Vercel does not read .env files from your repository during deployment.

Solution: Go to your Vercel project → Settings → Environment Variables and add STRIPE_SECRET_KEY with its value. Select all three environments (Production, Preview, Development) and click Save. Then redeploy your project from the Deployments tab.

Best practices

  • Always use request.text() (never request.json()) in webhook handlers — Stripe signature verification requires the raw unparsed body bytes
  • Store connected account IDs (acct_xxx) in your database linked to seller user records — never hardcode them or store in cookies long-term
  • Use separate Stripe API keys per Vercel environment: test keys for Preview deployments and live keys for Production only
  • Set application_fee_amount explicitly on every PaymentIntent rather than relying on platform-level fee settings — this makes each transaction's economics transparent and auditable
  • Handle the account.updated webhook to track connected account verification status — payments to unverified accounts may be held by Stripe
  • Implement idempotency keys (stripe.paymentIntents.create({ idempotencyKey: orderId })) to safely retry payment creation on network errors without double-charging
  • Never put STRIPE_SECRET_KEY or STRIPE_CONNECT_CLIENT_ID in client-side code or Next.js NEXT_PUBLIC_ variables — these must remain server-side only

Alternatives

Frequently asked questions

What is the difference between Stripe and Stripe Connect?

Standard Stripe handles payments for a single business — money flows from buyer directly to your merchant account. Stripe Connect adds a layer for platforms: buyers pay through your platform, Stripe splits the funds between your platform (which takes a fee) and the seller's connected account. Connect is required any time you need to pay out to third parties.

Can I use Stripe Connect in V0's preview environment?

V0's preview sandbox can run your API routes in Vercel Sandbox VMs, but Stripe OAuth redirects require a stable HTTPS URL with a matching redirect URI registered in your Stripe Dashboard. For testing Connect OAuth, deploy to Vercel first and register your deployed URL. You can test payment intent creation in preview using Stripe test connected account IDs.

Do sellers need their own Stripe account to receive payments?

Yes. Stripe Connect's Standard and Express account types require sellers to create or link a Stripe account through the OAuth flow. With Custom accounts, your platform creates and manages accounts on sellers' behalf, but this requires more compliance responsibility and a more complex integration. For most marketplaces, Express accounts (shown in the OAuth flow as a guided onboarding) are the right choice.

How do I test Stripe Connect without real sellers going through OAuth?

In Stripe's test mode, you can create test connected accounts directly in the Dashboard at dashboard.stripe.com/test/connect/accounts without going through the OAuth flow. Copy the test account ID (acct_test_xxx) and use it directly in your API routes during development. You can also use the Stripe CLI's stripe test-helpers command to simulate Connect events.

What happens if a connected seller's Stripe account gets restricted?

Stripe will send an account.updated webhook event with the account's payouts_enabled or charges_enabled field set to false. Your webhook handler should catch this event, update the seller's status in your database, and prevent new transactions from being routed to that account until the restriction is resolved. The Stripe Dashboard under Connect → Accounts shows restriction reasons.

Is there a Vercel Marketplace one-click integration for Stripe Connect?

The Vercel Marketplace Stripe integration (generally available March 2026) auto-provisions your platform's STRIPE_SECRET_KEY and NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY. It does not automatically configure Stripe Connect — you still need to manually add STRIPE_CONNECT_CLIENT_ID and set up the Connect OAuth flow. The Marketplace integration primarily helps with basic payment acceptance.

What are Stripe Connect's fees for marketplace payments?

Stripe Connect charges standard processing fees (2.9% + 30¢ per transaction for cards) plus a Connect fee (0.25% + 25¢ per payout) for Express and Custom accounts. These fees come out of the seller's portion — your platform fee set via application_fee_amount is separate. Standard connected accounts pay their own Stripe fees directly without any Connect surcharge to your platform.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.