> ## Documentation Index
> Fetch the complete documentation index at: https://vlyai-1c28d863.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Marketplace with Stripe

> A two-sided marketplace — buyers, sellers, listings, and money flowing through Stripe Connect. The complete walkthrough.

export const PromptBlock = ({children, variant}) => <div className={`vly-prompt ${variant || ""}`}>{children}</div>;

export const Prerequisites = ({items}) => <Note>
    <strong>Before you start.</strong>
    <ul>
      {(items || ["A vly.ai account (free tier is enough)", "A modern browser (Chrome, Firefox, Safari, Edge — all current versions)", "About 10 minutes"]).map((item, i) => <li key={i}>{item}</li>)}
    </ul>
  </Note>;

A marketplace is a step harder than a regular SaaS — money has to flow from buyers to sellers, with you as the platform taking a cut. **Stripe Connect** handles the hard parts (KYC, payouts, compliance). vly wires it into your app.

This recipe builds an end-to-end two-sided marketplace.

**Time:** \~90 minutes. **Difficulty:** Advanced.

## What you'll build

A marketplace where:

* **Sellers** create listings with a price.
* **Buyers** browse listings, click "Buy", and pay via Stripe.
* **Money flows**: 90% to the seller, 10% to the platform.
* **Sellers onboard via Stripe Connect Express** (KYC, bank account).
* **Payouts are automatic** — Stripe sends the money.

## Prerequisites

<Prerequisites
  items={[
"A vly project (start fresh or follow on from the multi-tenant SaaS recipe)",
"A Stripe account with Connect enabled (Settings → Connect)",
"Stripe test mode keys (`sk_test_`, `pk_test_`)",
"About 90 minutes"
]}
/>

## Step 1 — Project foundation

Use **Plan mode**.

<PromptBlock>
  **Goal**: a two-sided marketplace where sellers list things and buyers purchase them.

  **User**:

  * Sellers (create listings, get paid out)
  * Buyers (browse, purchase)
  * Both groups are just signed-up users; sellers are users who completed Stripe Connect onboarding

  **Data**:

  * `users` (built-in auth)
  * Add `stripeAccountId` and `stripeOnboardingComplete` (boolean) to users
  * `listings`: `{ sellerId, title, description, priceUsdCents, imageUrl, active, createdAt }`. Index by\_seller, by\_active.
  * `purchases`: `{ buyerId, sellerId, listingId, amountUsdCents, platformFeeUsdCents, stripePaymentIntentId, status, createdAt }`. Index by\_buyer, by\_seller.

  **Behavior**:

  * Public homepage `/` shows all active listings, paginated.
  * Listing detail page `/listing/{id}` shows the listing and a "Buy" button.
  * Logged-in buyers can purchase. Logged-out users are redirected to sign-in first.
  * Sellers have a dashboard at `/sell/dashboard` showing their listings, revenue, and payouts.
  * "Become a seller" button initiates Stripe Connect Express onboarding.
  * New listing form is at `/sell/new` (sellers only, must have completed onboarding).

  **Constraints**:

  * Use Stripe Connect (Express accounts) for seller payouts.
  * Platform takes a 10% fee on every transaction.
  * Listings can be active or inactive (sellers can toggle).
  * Use Stripe test mode for development.
</PromptBlock>

Submit, review the plan, approve. Build takes \~2 minutes.

vly will prompt you to provide Stripe credentials. Paste your `sk_test_...` and `pk_test_...` keys.

## Step 2 — Test buyer flow

Once built, sign up as a buyer:

1. Browse the (empty) homepage — no listings yet.
2. We need to create a seller and a listing first.

## Step 3 — Test seller onboarding

Sign up as a different user (incognito). Click **Become a seller**.

vly redirects to Stripe's hosted Connect Express onboarding. In test mode, you can fast-forward through verification with Stripe's test data:

<Tabs>
  <Tab title="Test bank">
    Routing: `110000000`
    Account: `000123456789`
  </Tab>

  <Tab title="Test SSN (US)">
    SSN: `000-00-0000`
  </Tab>

  <Tab title="Test address">
    Address line 1: `address_full_match`
    City: `San Francisco`
    State: `CA`
    ZIP: `94103`
  </Tab>

  <Tab title="Test ID verification">
    Document: upload any image; test mode auto-passes.
  </Tab>
</Tabs>

After Stripe redirects back to your app, the user's `stripeOnboardingComplete` should be `true`.

## Step 4 — Create a listing

Still as the seller, go to `/sell/new`:

* Title: "Vintage typewriter"
* Description: "1965 Olivetti, fully working"
* Price: \$50.00
* Image: paste any image URL

Submit. The listing appears on the homepage.

## Step 5 — Test buyer flow end-to-end

Switch back to the buyer browser:

1. Open the homepage. The listing appears.
2. Click into it. Click **Buy**.
3. Redirected to Stripe Checkout. Use test card `4242 4242 4242 4242`, any future expiry, any CVC.
4. After successful payment, redirected back to a success page.

In your Stripe dashboard:

* The payment shows as completed.
* \$50 charged to the buyer.
* Connected account (the seller) receives \$45 (90%).
* Platform receives \$5 (10%) as `application_fee_amount`.

The transfer happens automatically via Stripe Connect.

## Step 6 — Webhook for purchase confirmation

vly already wired up the webhook. To verify:

1. Stripe dashboard → Webhooks → find the endpoint.
2. Send a test `checkout.session.completed` event.
3. The vly app receives it, marks the purchase as `succeeded` in the DB.

In production, you'll need to register a real webhook endpoint and set the signing secret. vly will guide you through this.

## Step 7 — Refunds (admin path)

For disputes or seller-initiated refunds:

<PromptBlock>
  **Goal**: sellers can refund a purchase from their dashboard.

  **Behavior**:

  * On the seller's dashboard, each purchase has a "Refund" button.
  * Clicking it shows a confirmation modal.
  * On confirm, calls Stripe's refund API and creates a `application_fee.refund` so the platform fee is also refunded.
  * Updates the purchase status to "refunded" in the DB.

  **Constraints**:

  * Only the seller of the listing can refund.
  * Refunds within 60 days of purchase only.
</PromptBlock>

Test with a real charge and refund.

## Going to production

<Steps>
  <Step title="Activate your Stripe account">
    Provide business details, bank account, identity. Required for live mode.
  </Step>

  <Step title="Switch keys">
    Replace `sk_test_...` and `pk_test_...` with `sk_live_...` and `pk_live_...` in vly's production environment.
  </Step>

  <Step title="Register a production webhook">
    Use your custom domain URL. Copy the new signing secret to production env.
  </Step>

  <Step title="Set up your platform's payout schedule">
    In Stripe → Settings → Payouts. Daily, weekly, or manual.
  </Step>

  <Step title="Customer support">
    Set up a `disputes@yourapp.com` inbox; Stripe forwards customer dispute notifications here.
  </Step>
</Steps>

## Common variations

<AccordionGroup>
  <Accordion title="Subscriptions instead of one-time" icon="repeat">
    Replace `checkout.sessions.create({ mode: "payment" })` with `mode: "subscription"`. Listings become "Subscribe" with a recurring price.
  </Accordion>

  <Accordion title="Escrow / hold funds until delivery" icon="shield">
    Use Stripe's [delayed transfer](https://stripe.com/docs/connect/separate-charges-and-transfers). Charge the buyer immediately, transfer to the seller after a manual confirmation step.
  </Accordion>

  <Accordion title="Multi-item cart" icon="shopping-cart">
    Replace single-listing checkout with a cart that aggregates `line_items` across multiple sellers. Use Stripe's destination charges with multiple transfers.
  </Accordion>

  <Accordion title="International payouts" icon="globe">
    Stripe Connect Express supports payouts in 40+ countries. Each seller's account is regional; the marketplace just sends the transfer.
  </Accordion>

  <Accordion title="Fee on top of price (vs. taken from)" icon="plus">
    Buyer-pays-fee: present the price as `$50 + $5 platform fee = $55`. Use Stripe's `application_fee_amount` either way; UI presentation differs.
  </Accordion>
</AccordionGroup>

## Things to watch

<AccordionGroup>
  <Accordion title="Onboarding incomplete states" icon="alert-triangle">
    Sellers can drop out partway through Stripe onboarding. Periodically check `account.charges_enabled` and prompt them to finish.
  </Accordion>

  <Accordion title="Disputes and chargebacks" icon="alert-triangle">
    These hit your platform balance, not the seller's. Set up a reserve or insurance for high-risk categories.
  </Accordion>

  <Accordion title="Tax handling" icon="percent">
    Stripe Tax can calculate and remit. For complex cross-border, talk to a tax advisor.
  </Accordion>

  <Accordion title="Fraud" icon="shield-alert">
    Stripe Radar (built into Stripe) handles most fraud. For high-risk categories, add manual review on first-time sellers.
  </Accordion>
</AccordionGroup>

## Related

<CardGroup cols={3}>
  <Card title="Stripe integration" icon="credit-card" href="/integrations/stripe">
    Full reference for the Stripe integration.
  </Card>

  <Card title="Subscription billing" icon="repeat" href="/recipes/subscription-billing">
    Subscription marketplaces.
  </Card>

  <Card title="Multi-tenant SaaS" icon="building" href="/recipes/multi-tenant-saas">
    The base pattern for B2B variants.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.