# Zero to first sale - storefront

From your Stripe keys to a seller paid out: the storefront shape end to end, where buyers pay on the seller's own Stripe account and your fee comes to you.

This guide takes an app from no selling at all to a paid, fulfilled and
refunded sale in the **storefront** shape, then live. In the storefront
shape the buyer pays the seller: Stripe charges on the seller's own Stripe
account (a direct charge) and sends your fee to you. The seller owns their
refunds and disputes, and Stripe's processing fee is the seller's unless you
choose to pay it.

Running a marketplace instead, where your app takes the money and pays
sellers out? Follow
[Zero to first sale: marketplace](/billing/first-sale-marketplace). The two
guides share their steps; the differences are called out in each.

> **Note:**

**What you can use today.** Every console step and server route below
works now. The SDK screens and hooks (`<SellerHome>`, `<SellerItems>`,
`<BuyButton>`, `<SellerSales>` and the rest) are in
`@buildbase/sdk`
1.2.0 and later. You can also call the routes named on each module's page
from your own screens.

> **Note:**

- A BuildBase app with the SDK installed ([installation](/quick-start/installation))
  and two test users you can sign in as: one seller, one buyer.
- A Staging environment to test in ([step 0](#0-open-staging)). It takes
  Stripe test keys only, so nothing real is charged.
- A Stripe account. In the Stripe Dashboard, in test mode (the mode of your
  test keys), turn Connect on and complete your platform profile.
- A webhook endpoint on your server for BuildBase events, if your server
  acts on sales ([webhooks](/webhooks/overview)).
- Selling open for your app. If **Billing**, **Selling** says **Selling is
  not open for this app yet**, contact BuildBase support.

## Storefront and marketplace side by side

|                      | Storefront (this guide)                              | Marketplace                              |
| -------------------- | ---------------------------------------------------- | ---------------------------------------- |
| Who the buyer pays   | The seller, on the seller's Stripe account           | Your app, which sends the seller's share |
| Your fee             | Sent to you by Stripe from each charge               | Kept from the charge on your account     |
| Refunds and disputes | The seller's (you can refund too)                    | Yours only                               |
| Stripe's fees        | The seller's, or yours if you choose                 | Yours                                    |
| Stripe Tax           | On each seller's account, which each seller sets up  | On your account                          |
| Carts across sellers | One Stripe page per seller, in turn                  | One Stripe page for the whole cart       |
| Seller's countries   | Countries Stripe supports for sellers in your region | A narrower list Stripe can pay out to    |
| Seller gives email   | No                                                   | Yes                                      |

## 0. Open Staging

Everything up to step 12 happens in a Staging environment. In the console,
choose **Staging** in the environment switcher at the top, or add it there
with **Add environment** (owners and admins). Staging keeps its own users,
settings and Stripe keys apart from Production, and takes Stripe test keys
only. Point your app at it with its environment key
([environments](/quick-start/environments)), so the seller and buyer you sign
in as in steps 5 and 7 are Staging users.

**No Staging environment yet?** Environments are switched on per app as the
platform rolls them out: yours has them once the console shows
**Environments** under **Settings > General**. Until then, your
Production environment accepts Stripe test keys too. Make your first test
sale there with your test keys (steps 1 to 12, with a **Test keys** badge on
**Selling**), then switch to your live keys there in step 13.

## 1. Save your Stripe test keys in Staging

In the console, in Staging, open **Billing**, **Credentials** and save your Stripe test
secret and publishable keys. **Selling** and **Item types** appear in the
**Billing** menu, under **Selling**, once they are saved. Staging refuses a
live key: "Staging accepts Stripe test keys only (`sk_test_` and `pk_test_`).
Live keys belong in production."

![Billing, Credentials with a Stripe test secret key and publishable key saved, and Selling and Item types listed under Selling in the Billing menu](/images/billing/selling/console-credentials-test-keys.webp)

## 2. Set selling up as a storefront

Open **Billing**, **Selling**. The **Overview** tab is a checklist of seven
steps, each saved on its own:

![The Selling Overview tab in Staging, the header reading Staging: test keys: Let your users sell at 1 of 7 done, Your Stripe account is ready ticked, steps 2 to 7 still to do, and the Go live step open with a Test keys badge, saying nothing real is charged in Staging and going live happens from Production](/images/billing/selling/console-selling-checklist.webp)

1. **Your Stripe account is ready.** Press **Check my Stripe account**: your
   secret key works,
   Connect is on and your platform profile is complete.
2. **How selling works.** Choose **Storefront**: the buyer pays the seller.
   The choice locks once the first seller joins, because each seller's
   Stripe account is made for it.
3. **Money.** Set your fee (a percent, a fixed part and a minimum) and who
   pays Stripe's fee. Choose what happens to your fee on a refund, and
   whether Stripe Tax works out tax at checkout. In the storefront shape,
   Stripe Tax runs on each seller's account, so each seller completes Stripe
   Tax's settings in Stripe; leave it off for a first sale.
4. **Who can sell.** People, workspaces or both; their countries; your
   currencies; whether you approve each seller by hand.
5. **Receive your sellers' Stripe events.** Press **Set it up for me**, then
   **Create the endpoint**. In
   the storefront shape every sale happens on a seller's account, so this
   Connect endpoint is how payments, refunds, disputes and subscription
   renewals reach your app.
6. **Your terms and policies.** Link your seller terms and refund policy
   (both required), and your fee disclosure, buyer terms and privacy policy,
   each a full `https://` address, and set your **Order page** so buyers get
   a receipt email.
7. **Turn on selling.** In Staging it runs on your test keys: nothing real
   is charged.

![How selling works with Storefront chosen: the buyer pays the seller on their own Stripe account, and refunds and disputes are the seller's](/images/billing/selling/console-selling-shape-storefront.webp)

![The Money step: a 10% fee, the live preview of what a seller keeps at $5, $10, $50, $100 and $500, keep or return the fee on a refund, and Tax at checkout off](/images/billing/selling/console-selling-money.webp)

## 3. Design an item type

Open **Billing**, **Item types**, choose **New item type** and start from
the ready-made **Service or commission** type: a fixed price, a message for
the buyer and work the seller delivers by hand. Name it **Coaching
session** (key `coaching-session`), work through the sections and press
**Publish v1**. Item types never touch Stripe, so they are the same in both
shapes.

![The item type builder on the Pricing section of Coaching session, with the seller's form previewed on the right and Publish enabled](/images/billing/selling/console-item-type-builder.webp)

More in [item types](/billing/item-types).

## 4. Add the seller screens to your app

Install Stripe's Connect loader and pass it to the provider:

```bash
npm install @stripe/connect-js
```

```tsx title="app/providers.tsx"
import { ApiVersion } from '@buildbase/sdk';
import { SaaSOSProvider } from '@buildbase/sdk/react';
import { loadConnectAndInitialize } from '@stripe/connect-js/pure';

import '@buildbase/sdk/css';

export function BuildBaseProvider({ children }: { children: React.ReactNode }) {
  return (
    <SaaSOSProvider
      serverUrl="https://api.console.buildbase.app"
      version={ApiVersion.V1}
      orgId="your-org-id"
      loadConnectAndInitialize={loadConnectAndInitialize}
      // auth and your other props as in the installation guide
    >
      {children}
    </SaaSOSProvider>
  );
}
```

Allow `https://connect-js.stripe.com` and `https://js.stripe.com` in your
Content-Security-Policy's `script-src` and `frame-src`. Then give sellers a
page with their account, their items and their sales:

```tsx title="app/selling/page.tsx"
'use client';

import {
  SellerHome,
  SellerItems,
  SellerSales,
  useSaaSAuth,
} from '@buildbase/sdk/react';

export default function SellingPage() {
  const { user } = useSaaSAuth();
  return (
    <>
      <SellerHome subject={{ user: true }} defaultDisplayName={user?.name} />
      <SellerItems
        subject={{ user: true }}
        itemTypeKeys={['coaching-session']}
      />
      <SellerSales subject={{ user: true }} />
    </>
  );
}
```

## 5. Onboard a seller

Sign in to your app as the seller and open the selling page. **Start
selling** shows your fee and who pays Stripe's fee; the seller picks their
country and the name buyers see (no email: a storefront seller's Stripe
account is their own contact). Stripe's onboarding opens inside your app;
in Staging, on your test keys, it accepts Stripe's test values. Back in your app the seller
reads **Can sell** and **Can get paid**.

![SellerHome once the seller is active: Can sell and Can get paid, and Payouts and balance open on the balance and two payouts](/images/billing/selling/sdk-seller-home-active.webp)

The seller shows under **Billing**, **Selling**, **Sellers**. If you approve
sellers by hand, open the row's menu (**...**) and choose **Approve**.

![The Sellers tab: one seller Active, one Onboarding and one Awaiting your approval, with the row menu open on Refresh from Stripe, Approve, Pause selling and Close account](/images/billing/selling/console-sellers-tab.webp)

## 6. Publish an item

As the seller, press **New item** in `<SellerItems>` and fill in an item of
**Coaching session**: a title, a price of $45.00, under **Add a
message** the message the buyer reads after paying, and, under **Add
something you deliver**, what the seller will do. Press **Publish**
(**Send for review** when you approve items). If you approve items, approve
it from its row's menu under **Billing**, **Selling**, **Items**, **Needs
review**; the seller gets the **Item Approved** email.

![SellerItems with the item form open: title, summary, a USD price of $45.00, and the message a buyer gets after paying](/images/billing/selling/sdk-seller-items-form.webp)

## 7. A buyer pays

The shop page is the same in both shapes:

```tsx title="app/shop/page.tsx"
'use client';

import {
  BuyButton,
  formatMinorAmountIntl,
  usePublishedItems,
} from '@buildbase/sdk/react';

export default function ShopPage() {
  const { items } = usePublishedItems({ typeKey: 'coaching-session' });
  return items.map((item) => {
    const variant = item.variants[0];
    const price = variant.prices[0];
    return (
      <div key={item._id}>
        <h2>{item.title}</h2>
        {/* some prices have no fixed amount */}
        {price.amount !== undefined && (
          <p>{formatMinorAmountIntl(price.amount, price.currency)}</p>
        )}
        <BuyButton
          itemId={item._id}
          variantId={variant.id}
          priceId={price.id}
        />
      </div>
    );
  });
}
```

Sign in as the buyer and press the button. Stripe's page opens on the
**seller's** Stripe account; in Staging, pay with Stripe's test card
`4242 4242 4242 4242`, any future date and any CVC.

![Stripe's hosted checkout in test mode for Coaching session at $45.00, on the seller's own Stripe account](/images/billing/selling/stripe-checkout-storefront.webp)

Back on your page, `useMyOrder(orderId, { waitForPayment: true })` shows
the order once paid. Your server gets `order.created`, then `grant.created`
and `order.paid`; the buyer gets the **Order Receipt** email.

![The Order Receipt email: the app's name, Coaching session at $45.00, the seller, and a button to the order page](/images/billing/selling/email-order-receipt.webp)

## 8. Where the money went

Stripe charged the buyer on the seller's account and sent your fee to your
account as an application fee. The order page under **Billing**,
**Selling**, **Orders** shows the total, your fee and the seller's share.

![An order page in the storefront shape: $45.00 charged on the seller's Stripe account, your fee $4.50 sent to you, the seller's share $40.50, and the cart it was bought in](/images/billing/selling/console-order-storefront.webp)

## 9. Fulfil the order

A line whose item includes a benefit the seller delivers by hand (`kind:
'manual'`) waits on the seller (`fulfilment: 'due'`). `<SellerItems>`
adds it under **Add something you deliver** when the item type allows it,
as it does a licence key and something your app delivers (`webhook_only`);
your own screen or server can save it with the item too. An item with only
a message reads **Nothing to send**. In `<SellerSales>` the
seller opens the sale and presses **Mark as sent** with a note the buyer
reads. You can do the same with **Mark fulfilled** on the order page. Your
server gets `order.fulfilled`.

![SellerSales on one sale in the storefront shape: what the seller receives, Mark as sent on the line, and Refund](/images/billing/selling/sdk-seller-sales.webp)

## 10. Refund it

In the storefront shape **the seller** can refund from `<SellerSales>`, and
so can you, from the order page or your server
(`POST /api/v1/server/commerce/orders/:id/refund`). The seller's Stripe
balance pays the refund, and your fee on the refunded part goes back to
them unless you chose to keep it under **Money**. Your server gets
`order.refunded`.

![The refund dialog on an order: the Coaching session line, $45.00 of $45.00 left, a reason, and Refund $45.00](/images/billing/selling/console-order-refund.webp)

A buyer who disputes the charge is answered in the seller's own Stripe
Dashboard; the order shows the dispute until the bank decides.

## 11. Payouts

Stripe pays the seller's balance to their bank on their payout schedule.
The seller sees **Payouts and balance** inside `<SellerHome>`; you see each
seller's balance and latest payouts on their page under **Sellers**. Your
server gets `payout.created`, `payout.paid` and `payout.failed`. Your fees
land in your own Stripe balance and are paid out on your schedule.

![A seller's page in the console: Active, Can sell and Can get paid, the Stripe balance, and the latest payouts with their arrival dates](/images/billing/selling/console-seller-payouts.webp)

## 12. Tick off the go-live list

In Staging, the **Overview** tab's **Before going live** list checks the
setup steps, selling on, policy pages, tax on sales, the Connect endpoint's
events, a clean Reconcile, a seller who can sell, a test sale and a test
refund. The last item, **Going live**, reads **In Production**: going live
happens in your Production environment.

![The Overview tab in Staging with selling on: seller counts, the Go live card with a Test keys badge, and Test a sale in Staging with every check Done except Tax on sales, which is Worth a look, and Going live, which reads In Production](/images/billing/selling/console-selling-go-live.webp)

## 13. Go live in Production

Switch to **Production** with the environment switcher at the top of the
console. Production keeps its own settings and item types, so set selling up
there as you did in Staging (steps 2 and 3). Save your live keys under
**Billing**, **Credentials** (until you do, **Selling** shows a **Test
keys** badge; with live keys it reads **Live**), complete your platform
profile in Stripe live mode, set up the live Connect webhook endpoint
(**Set it up for me**), run the Stripe check again and turn selling on.
**Live keys** reads **Not yet open** until live selling is open for your
app; contact BuildBase support to open it. Each seller finishes Stripe's
onboarding again in live mode, and with Stripe Tax on, completes its
settings on their own live account. More in
[test in Staging, then go live](/billing/selling#test-in-staging-then-go-live).

![The Overview tab in Production with selling on and Stripe test keys: the header and the Go live card read Test keys, and Before going live lists Platform profile in live mode, A seller can sell and Live keys to do, with a first sale and a refund recommended](/images/billing/selling/console-selling-go-live-production.webp)

## When something stops you

| What you see                                                                 | Why                                                          | What to do                                                                     |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------ |
| **Selling is not open for this app yet**                                     | Selling is off for your app on the platform                  | Contact BuildBase support                                                      |
| `SELLER_COUNTRY_NOT_ALLOWED`                                                 | A country you do not allow                                   | Allow it under **Who can sell**                                                |
| `CHECKOUT_REFUSED`, `tax_not_ready`                                          | Stripe Tax is on but not set up on the seller's account      | The seller completes Stripe Tax's settings in Stripe, or turn tax off          |
| `SELLER_CANNOT_SELL`                                                         | The seller is paused, or Stripe needs more from them         | The seller finishes what `<SellerHome>` asks; or resume them under **Sellers** |
| Payments or renewals never arrive                                            | The Connect endpoint is missing events                       | **Selling**, **Settings**, "Your sellers' Stripe events": **Set it up for me** |
| `REFUND_FAILED`                                                              | Stripe made no refund                                        | Read the reason in the seller's Stripe account; nothing was returned           |
| Live keys refused under **Billing**, **Credentials** in Staging              | Staging takes Stripe test keys only (`sk_test_`, `pk_test_`) | Save your live keys in Production                                              |
| `SELLING_UNAVAILABLE`: **This environment sells with Stripe test keys only** | A live key in a non-production environment such as Staging   | Use test keys there; go live in Production                                     |
| `SELLING_UNAVAILABLE` with live keys                                         | Live selling is not open for your app yet                    | Contact BuildBase support, or save your test keys again                        |

## Next steps

- [Carts across sellers](/billing/orders#storefront-carts-in-steps) - how a
  storefront cart is paid one seller at a time.
- [Coupons](/billing/coupons), [bundles](/billing/bundles),
  [options](/billing/options), [bookings](/billing/bookings),
  [affiliates](/billing/affiliates) and [shipping](/billing/shipping).
- [Zero to first sale: marketplace](/billing/first-sale-marketplace).
