# Embedded marketplace

The JavaScript library shows Beauty Router's interface inside your salon app: the marketplace, the cart, the checkout,
favorites, order history, one order, order tracking, order rating, the verification page, payment cards, the service
selector, supplier profiles, and product details. You decide where each one appears.

## Before you start

- **Web pages and web views.** The library runs on web pages. In a mobile app, open such a page in a web view. Native
  components are not available yet.
- **Add your origins.** Only origins registered for your salon app can embed the interface. Add the origins where your
  salon app runs, such as `https://app.example.com`, in the salon app admin area. Each one must use HTTPS. In the sandbox
  you can also add `http://localhost` with a port. Each environment has its own origins.
- **Get a user token on your server.** Your server logs in the user with `POST /v1/login` and passes the user token to
  your page, which hands it to the library. The app token stays on your server. See [Authentication](/authentication/).

## Load the library

Add the script to your page, then call `BeautyRouter.init`.

```html
<script src="https://js.beautyrouter.com/v1/beauty-router.js"></script>
```

## Initialize

```js
const beautyRouter = BeautyRouter.init({
  async getUserToken() {
    const response = await fetch("/beauty-router/login", { method: "POST" });
    const { userToken } = await response.json();
    return userToken;
  },
  onOrderCreated(orders) {},
});
```

| Option           | Description                                                                                                    |
| ---------------- | -------------------------------------------------------------------------------------------------------------- |
| `getUserToken`   | An async function your page provides. It asks your server for a user token and returns it. The library calls it when it starts and whenever the token expires. |
| `onOrderCreated` | Runs after a checkout. It gets an array of orders, because one checkout creates one order per brand or distributor. |

The user token reaches Beauty Router's frame through `postMessage`. It is never put in a URL.

### New orders

`onOrderCreated(orders)` gets an array of Order objects, one per brand or distributor in the checkout. Each one has the
same shape as in [Orders](/orders/). Your server also receives an `order.created` [webhook](/webhooks/) for each order.

### Token expiry

The user token lasts 24 hours. When it expires, the library calls `getUserToken` again and goes on with the new token. It
does the same once when a request fails with `invalid_user_token`. When `getUserToken` fails, the library shows a
try-again state.

You can call `show…` functions right after `init`. The library queues them until it has the first token.

Getting a token never needs the library. `POST /v1/login` is the same for a page that uses only the API, which logs in
again on a `401`.

### Another user

A user change needs a page reload, or a second `BeautyRouter.init`, which closes the earlier frames.

## Functions

The functions belong to the object that `BeautyRouter.init` returns. Every `show…` function takes the HTML element to
render into, and shows Beauty Router's interface inside it.

| Function                                                              | What it shows                                     |
| --------------------------------------------------------------------- | ------------------------------------------------- |
| [`showMarketplace(element)`](#marketplace)                            | The main page of the marketplace.                 |
| [`showCart(element)`](#cart)                                          | The cart.                                         |
| [`showCheckout(element, { lines })`](#checkout)                       | A checkout for a list of lines.                   |
| [`showFavorites(element)`](#favorites)                                | The user's favorite products.                     |
| [`showOrders(element)`](#orders)                                      | Order history, with all the company's orders.     |
| [`showOrder(element, orderId)`](#one-order)                           | One order, with its actions.                      |
| [`showOrderTracking(element, orderId)`](#order-tracking)              | The tracking page of one order.                   |
| [`showOrderRating(element, orderId)`](#order-rating)                  | The form to rate one delivered order.             |
| [`showVerification(element)`](#verification)                          | Company details, documents, and the payment card. |
| [`showPaymentCards(element)`](#payment-cards)                         | A small list of the company's payment cards.      |
| [`showSupplier(element, supplierId)`](#supplier)                      | The profile of one supplier.                      |
| [`showServiceSelector(element, { level })`](#service-selector)        | A form to select services or propose a new one.   |
| [`showProductDetails(element, productId, options)`](#product-details) | One product.                                      |

The pictures show the default design with sample data. A selected
[interface design](/features/#customize-the-interface) changes the colors and fonts.

### Marketplace

```js
beautyRouter.showMarketplace(element);
```

![The marketplace main page](../../assets/interfaces/marketplace.png)

The main page has a category menu, a search box, and rows of products, collections, and suppliers, such as From my
suppliers, Trending this month, and New arrivals. The user searches by product, brand, category, or concern, opens a product or a
supplier, and saves favorites.

The line under the menu shows the services behind the recommendations. **Change** lets the user select their own, at the
`user` level. See [Services](/companies/#services).

The price on each product depends on the company's [verification status](/companies/#verification-statuses):

| Status                     | What the user sees        |
| -------------------------- | ------------------------- |
| `prequalified`, `verified` | The wholesale price.      |
| `in_review`                | Waiting for verification. |
| `unverified`, `declined`   | Unlock wholesale price.   |

### Cart

```js
beautyRouter.showCart(element);
```

![The cart with products from two suppliers](../../assets/interfaces/cart.png)

The cart groups products by brand or distributor. Each group shows its products amount, shipping, and subtotal. The user
changes quantities, removes products, and moves them to Saved for later.

**Review and checkout** opens the [checkout](#checkout) in the same element. With two or more groups, the user can select
the ones to check out. When the user places the orders, `onOrderCreated` runs.

Shared product lists holds the lists that other users of the company shared with this user, who can add a list to their
cart.

| State                                | What the user sees                                                                     |
| ------------------------------------ | -------------------------------------------------------------------------------------- |
| Empty cart                           | A message and some categories to browse.                                               |
| Company is `unverified`              | A disabled button that asks for the company data.                                      |
| Company is `in_review`               | A disabled button that asks the user to wait for the review.                           |
| Company is `declined`                | A disabled button that asks the user to fix the company details and submit again.      |
| `canOrder: false`                    | A button to share the cart with a user who can order, in place of the checkout button. |

### Checkout

```js
beautyRouter.showCheckout(element, {
  lines: [
    { productId: "prod-4Fz9QbX2kL", quantity: 6 },
    { productId: "prod-2Df7gHj4Kl", supplierId: "sup-8Wq2eRt5Yu", quantity: 12 },
  ],
});
```

![The checkout with two orders, the totals, the delivery address, and the payment card](../../assets/interfaces/checkout.png)

`showCheckout` shows a checkout for a list of lines, apart from the cart. Use it when your salon app builds the list on
its own screen, such as a reorder list.

Each line has `productId`, `quantity`, and an optional `supplierId`. Without a `supplierId`, Beauty Router picks the
best offer.

Beauty Router shows the prices, discounts, sales tax, shipping, and the payment card. The user can change quantities,
remove products, and change the delivery address and the card. With no address or no card, the page offers to add one.

When the user places the orders, Beauty Router creates one order per brand or distributor and charges the card at once.
An order is `placed` when the charge succeeds, and `payment_failed` otherwise. An order from a `prequalified` company is
`pending_verification`, with no charge yet. Then `onOrderCreated` runs with the new orders.

The page can also save a draft, and `showOrders` lists the drafts. These are the same
[drafts](/create-orders/#drafts) as in the API.

To build your own page instead, see [Create orders](/create-orders/).

### Favorites

```js
beautyRouter.showFavorites(element);
```

![The favorites page with filters and product cards](../../assets/interfaces/favorites.png)

The page lists the user's favorite products, with the latest first. The user filters them, for example by brand or price,
sorts them by date added, newest, or price, opens a product, and removes it with the heart button. A product that is no
longer for sale stays in the list and shows as unavailable.

### Orders

```js
beautyRouter.showOrders(element);
```

![The order list with drafts and orders in several statuses](../../assets/interfaces/orders.png)

The page lists all the company's orders, newest first, with [drafts](/create-orders/#drafts). The user searches by order
number, brand, or supplier, and picks a date range. It opens on the Open tab, for the last six months. Each order opens
the same page as [`showOrder`](#one-order). An order in `payment_failed` also has buttons in the list to change the card
and to pay again.

| Tab       | Statuses                                                                                   |
| --------- | ------------------------------------------------------------------------------------------ |
| Open      | `draft`, `pending_verification`, `payment_failed`, `placed`, `ready_to_ship`, `in_transit` |
| Completed | `delivered`                                                                                |
| Canceled  | `delivery_failed`, `canceled`                                                              |
| All       | Every status.                                                                              |

See [Statuses](/orders/#statuses).

### One order

```js
beautyRouter.showOrder(element, "order-8Jd3kFs6Gh");
```

![One order in the payment_failed status, with its details and products](../../assets/interfaces/order.png)

The page shows the order's status, amounts, payment card, supplier, delivery address, and products. The picture shows an
order in `payment_failed`. The actions depend on the status:

| Status                 | What the salon can do                                              |
| ---------------------- | ------------------------------------------------------------------ |
| `draft`                | Continue in the [checkout](#checkout), or delete the draft.        |
| `pending_verification` | Change the delivery address or the card, or cancel.                |
| `payment_failed`       | Pay again when a retry is allowed, choose another card, or cancel. |
| `placed`               | Cancel.                                                            |
| `ready_to_ship`        | View the order.                                                    |
| `in_transit`           | Track the order.                                                   |
| `delivered`            | Track the order and rate it.                                       |
| `delivery_failed`      | Track the order and report a problem.                              |
| `canceled`             | Report a problem.                                                  |

A `placed` order can no longer change its delivery address. A payment that waits for 3-D Secure is also finished here.
**Track order** opens the [tracking page](#order-tracking) in the same element.

See [Change an order](/orders/#change-an-order) for the rules behind each action.

### Order tracking

```js
beautyRouter.showOrderTracking(element, "order-8Jd3kFs6Gh");
```

![The tracking page of one package](../../assets/interfaces/order-tracking.png)

The page shows the tracking page of the order, with a tab for each package. The content comes from the carrier's tracking
data, so it can look different from the picture.

| State                              | What the user sees                               |
| ---------------------------------- | ------------------------------------------------ |
| Not shipped yet                    | A message that tracking is not available yet.    |
| Carrier with its own tracking site | A button that opens the carrier's tracking page. |
| Not found                          | A message that the order was not found.          |
| Load error                         | A try-again state.                               |

To build your own tracking screen, see [Order tracking](/orders/#order-tracking).

### Order rating

```js
beautyRouter.showOrderRating(element, "order-8Jd3kFs6Gh");
```

![The form to rate a delivered order](../../assets/interfaces/order-rating.png)

`showOrderRating` shows a form to rate a delivered order. The salon rates the products and the delivery from one to five
stars. A rating of one or two stars adds a comment box. To report a problem with a `delivery_failed` or `canceled` order, the salon uses `showOrder`.

| State             | What the user sees                            |
| ----------------- | --------------------------------------------- |
| Not delivered yet | A message that the order cannot be rated yet. |
| Already rated     | The rating.                                   |
| Not found         | A message that the order was not found.       |
| Load error        | A try-again state.                            |
| `canOrder: false` | The form, read only.                          |

### Verification

```js
beautyRouter.showVerification(element);
```

![The verification form with company details, documents, the delivery address, and the payment card](../../assets/interfaces/verification.png)

The page collects the company details, the delivery address, the documents, and the payment card, and it submits the
company for review. Each field saves when the user leaves it, so the user can come back later.

A `salon` enters the legal name and the EIN, and adds a business or professional license. A `professional` adds a
professional license. Exemption certificates are optional. See [Companies and verification](/companies/).

| Status         | What the user sees                                                              |
| -------------- | ------------------------------------------------------------------------------- |
| `unverified`   | The form.                                                                       |
| `in_review`    | A message that the data is being verified, and a button to check the sent data. |
| `prequalified` | A message that wholesale prices are on, and a button to check the sent data.    |
| `verified`     | The details, documents, and card. Some changes go to [review](/companies/#changes-after-verification). |
| `declined`     | The reason, and the form to submit again.                                       |

### Payment cards

```js
beautyRouter.showPaymentCards(element);
```

![The list of payment cards](../../assets/interfaces/payment-cards.png)

The page is a small list of the company's payment cards. The user adds a card, selects the default card for orders, or
disables or deletes a card. The default card cannot be disabled or deleted until another card is the default. An expired
card shows as expired.

The card form runs inside the frame, including 3-D Secure. Only cards issued in the US are accepted. See
[Payment card](/companies/#payment-card).

### Supplier

```js
beautyRouter.showSupplier(element, "sup-8Wq2eRt5Yu");
```

![The profile of one supplier, with the archive action](../../assets/interfaces/supplier.png)

The page shows the supplier's logo, brands, and description, and an Orders tab with the company's orders from this
supplier.

The menu has **Send to archive**, and **Return from archive** on an archived supplier. The salon can still order from a
supplier it archived. See [Archive and restore](/suppliers/#archive-and-restore).

### Service selector

```js
beautyRouter.showServiceSelector(element, { level: "company" });
```

![The form to select services](../../assets/interfaces/service-selector.png)

A form where the user selects services or proposes a new one. **My service isn't listed** sends the new service to Beauty
Router's team. See [Services](/companies/#services).

| `level`     | What the form saves                                      |
| ----------- | -------------------------------------------------------- |
| `"company"` | The company's selection. This is the default.            |
| `"user"`    | The user's own selection, which overrides the company's. |

It is optional. Call it in your onboarding, or skip it. When you skip it, the marketplace shows all products, and users
personalize it in the marketplace.

### Product details

```js
beautyRouter.showProductDetails(element, "prod-4Fz9QbX2kL", { marketplaceFeatures: true });
```

![One product with marketplace features on](../../assets/interfaces/product-details.png)

`showProductDetails` shows the full product, with several images, benefits, and ingredients, so you need no screen of
your own. To build one, use [`GET /v1/catalog/products/{productId}`](/catalog/#product-details).

`options` is optional. Its `marketplaceFeatures` option turns the marketplace features on or off, and it defaults to `true`:

- `marketplaceFeatures: true` shows the product with the add-to-cart button, the other marketplace buttons, and recommended
  products.
- `marketplaceFeatures: false` shows the product without them. Use it to show product information on your own screens,
  such as an inventory screen.

```js
beautyRouter.showProductDetails(element, "prod-4Fz9QbX2kL", { marketplaceFeatures: false });
```

The product ID comes from the [catalog](/catalog/) or from a [product match](/product-matching/).

### Users who cannot order

A user logged in with [`canOrder: false`](/authentication/#users-who-cannot-order) can view the marketplace and fill a
cart, but sees no order button. `showOrder` does not let them change or cancel an order, and `showVerification` and
`showPaymentCards` do not let them change the payment card.

### Users who cannot change company settings

A user logged in with [`canManageCompany: false`](/authentication/#users-who-cannot-change-company-settings) sees the
company details and documents in `showVerification`, but cannot change them or submit the company for review. The
payment card still follows `canOrder`.

## Example page

This page shows the marketplace. Your server has an endpoint, here `/beauty-router/login`, that logs in the current
user with `POST /v1/login` and returns the user token.

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Marketplace</title>
    <script src="https://js.beautyrouter.com/v1/beauty-router.js"></script>
  </head>
  <body>
    <p id="order-message" hidden></p>
    <div id="beauty-router"></div>

    <script type="module">
      async function getUserToken() {
        const response = await fetch("/beauty-router/login", { method: "POST" });
        const { userToken } = await response.json();
        return userToken;
      }

      const beautyRouter = BeautyRouter.init({
        getUserToken,
        onOrderCreated(orders) {
          const message = document.getElementById("order-message");
          message.textContent = `Orders placed: ${orders.length}`;
          message.hidden = false;
        },
      });

      beautyRouter.showMarketplace(document.getElementById("beauty-router"));
    </script>
  </body>
</html>
```

Other screens of your salon app use the same setup and call another function, such as
`beautyRouter.showCart(element)` on a cart page or `beautyRouter.showOrders(element)` on an order history page.

## Beauty Router mark

The screens where a user buys, pays, or sends company documents show a small "Powered by Beauty Router" mark. Beauty Router
charges the card and processes the order, so your users can see who handles their purchase.

The mark appears on `showMarketplace`, `showCart`, `showCheckout`, `showOrder`, `showVerification`, and `showPaymentCards`.
It also appears on `showProductDetails` with `marketplaceFeatures: true`.

When you build such a screen yourself with the API, such as your own New order page or verification page, show "Powered by
Beauty Router" there too, with the [Beauty Router logo](/beauty-router-mark.svg) in front of the text, about 16 pixels high.
The mark has no link.

## Terms

The interface asks each company to accept [Beauty Router's terms](https://beautyrouter.com/terms/) on first use. When you
build your own screens with the API, link to the terms there.

## Customize the interface

Beauty Router can create a design for your salon app, and you select it in the salon app admin area. It applies to every
embedded interface session, so your code does not change. See
[Customize the interface](/features/#customize-the-interface).