# Build a marketplace

Source: https://developers.swell.is/guides/build-a-marketplace

A marketplace's distinct set of functionalities revolves around the differentiation between user groups: buyers and third-party vendors selling on the marketplace. Swell can accommodate an entire marketplace by building off of one store's data models.

> **Note:** The examples in this series are using swell-node, but you can accomplish all examples below through any HTTP client.

## Create account hierarchies

To begin, define and establish the account hierarchies based on the user groups within your marketplace. You can use **customer groups** to differentiate each customer base within the Swell dashboard.

##### To add a customer group:

1. From the dashboard, navigate to **Settings > Customers**
1. Select **Add customer group** under the Groups section
1. Provide the desired group name *(edit the group ID—optional)*
1. Click **Save**

Once created, you can further manage customer groups in the dashboard in the Customer section.

> **Note:** You will need the group's `id` when creating various accounts within your marketplace for a particular group.

##### Customer groups

For a marketplace, you'll want to create at least two specific customer groups:

- **Vendors:** the customer group intending to add and sell products through your marketplace and fulfill orders.
- **Buyers:** the customer group looking to only purchase products from the vendors in your marketplace.

Once you've created your customer groups, your Customers section in the dashboard should look something like this:

![Swell customer groups: vendors and buyers](https://cdn.sanity.io/images/ecf33d4l/production/6dbba3c2442d988f89099ec9e38e97a62028ce66-1754x310.png)

You can create additional groups as well depending on the needs of your marketplace—and designate them however you'd like.

##### Adding new accounts

With the customer groups created, you can now add new accounts by posting to the `/accounts` endpoint and associating them to either the `buyers` or `vendors` group. Passwords entered here encrypted immediately through `bcrypt`.

Assigning an account to the `vendor` customer group:

**Assigning account to vendor customer group**

**swell.js**

```javascript
await swell.post('/accounts', {
	email: 'vendor@mail.com',
	first_name: 'Vendor',
	last_name: 'Swell',
	password: 'password123',
	group: 'vendors' // Or buyers
});
```

In the response, you'll retrieve the account's `id`. This can be used as a `vendor_id`. Use this to create relationships between the vendor and their products and orders, as illustrated below.

#### Next steps

With the various accounts for the marketplace configured, we'll look at expanding upon the base Swell model functionality to accommodate marketplace logic.


## Extend Swell models

Leverage Swell's flexible model system to expand upon their base functionalities. In this case, you can implement additional business logic to accommodate your marketplace. By building off of the `/products` and `/orders` models, you can add associations for vendors.

##### Associating products to vendors

You can edit the `/products` model to include `vendor_id` as a field to establish an association between a vendor and a product. This will also allow querying for a vendor's specific products.

You can accomplish this through either the API or the model editor within the Swell dashboard.

**1. Use the following:**

**Adding vendor_id to /products**

**swell.js**

```javascript
await swell.put('/:models/products/fields', {
	vendor_id: {
		type: 'objectid',
		required: true // Optional
	}
});
```

**1. Add directly from the dashboard**

You can also accomplish this through the Swell dashboard through the Developer tools. To do so, navigate to the Products model under your Models tab, and add a field.

![Adding vendor_id to the Swell product model](https://cdn.sanity.io/images/ecf33d4l/production/20ccb1809f2796797afcf4dfa39924d888c907b0-800x449.png)

1. Go to **Developer > Models**
1. Select the **Products** model
1. Select **Add Field**; then choose **Lookup**
1. Under **Label** type `Vendor`
1. Select **Customers** for **Lookup collection**
1. Select **Details** for the **Admin field location**. Specify this to your preference for displaying (or hiding) the field in the desired location.
1. Save.

#### Creating new products

With the addition of the `vendor_id`, you can now create new products with an association to a vendor.

**Associate product with a vendor**

**swell.js**

```javascript
await swell.post('/products', {
	name: 'Swell T-Shirt',
	price: 120,
	type: 'standard',
	active: true,
	vendor_id: '609590e5d0ef5f42c5e1ce01' // Notes the id of the vendor account
});
```

##### Fetch products associated to a vendor

You can also fetch all of a vendor's products by referencing the vendor's `vendor_id`.

**Fetch product associated to a vendor_id**

**swell.js**

```javascript
await swell.get('/products', {
	where: { vendor_id: '609590e5d0ef5f42c5e1ce01'},
	limit 25 // Optional, can be up to 1000
});
```

This will return all the products belonging to a vendor, which can then be rendered with a vendor-specific dashboard.

#### Associating order to vendors

Similarly to products, it is also necessary to associate orders to vendors. Instead of adding the relation to the order itself, you can add the relation within an order's `items` array.

Update the `orders` model to include the `vendor_id` of each item added to the `items` array since an order might include items from multiple vendors. The `vendor_id` is retrievable from inputted products automatically using the formula.

**Associate order to a vendor**

**swell.js**

```javascript
await swell.put('/:models/orders/fields/items/fields', {
	vendor_id: {
		type: 'objectid',
		formula: 'product.vendor_id'
	}			
});
```

##### Fetch orders associated with a vendor

The following query will fetch orders containing products related to a particular vendor. Swell offers native support for most MongoDB query operators.

**Fetch orders associated to a vendor**

**swell.js**

```javascript
await swell.get('/orders', {
	where: {
		items: {$elemMatch: {'vendor_id': '609590e5d0ef5f42c5e1ce01'}}
	}
});
```

On your vendor dashboard, you'll want to filter the `items` array to only show the item that belongs to the specific vendor, as the query will return the entire order.

#### Shipments and order fulfillment

Vendors can fulfill order items by adding shipments onto orders. To create a shipment and fulfill items, simply post to the `/shipments` endpoint.

**Adding shipments to orders**

**swell.js** `Adding shipments to orders`

```javascript
await swell.post('/shipments', {
	order_id: '60a3cd3ac2c46f30b6cb3b5d', // Required
	items: [ // Required
		{
			order_item_id: '60a3cd3ac2c46f30b6cb3b2a', // This is the id in the items array within an order
			product_id: '60a3cc9f23ead02dff477b89',
			quantity: 1
		}
	],
	tracking_code: '787374451654' // Optional
});
```

Swell allows you to assign multiple shipments to one order so that each vendor can fulfill their items separately with individual tracking numbers. When viewing orders from the Swell dashboard, orders that have unfilled items will display the `partially fulfilled` status until the order has associated all items to shipments.

#### Next steps

Now that we've laid the foundation for the marketplace logic, we can move on to the next step: creating payouts for vendors on the marketplace. We'll need to ensure that they are able to set up and receive their payments by implementing payout logic specifically for a marketplace.


## Vendor onboarding

In the previous guide, we looked at customizing Swell to lay the foundations for creating a marketplace. Here we will be looking at onboarding for vendors, platform fees, and lay the foundation for creating vendor payouts.

> **Note:** This guide uses **Stripe Connect** and **Netlify** to accomplish its objective and is oriented around these particular tools.

#### Getting started

In order to set up payouts to vendors, you will need to have both a Stripe account and a Netlify account. Aside from creating the accounts, please follow the steps below to ensure you have the proper configurations:

#### Stripe setup

**1. Create a stripe account**

If you don't already have a Stripe account, you will need to signup with Stripe.

**2. Complete the following setup in your Connected accounts tab in Stripe**

1. Provide your **Business details**.
1. Complete your **Platform profile**.

#### Netlify setup

**1. Create a Netlify account**

If you don't have a Netlify account, you will need to signup with Netlify.

**2. Create a new local repo and a new remote repo**

1. Perform a `git init.`
1. Push an initial commit to the remote repo.

**3. From the Netlify home page, select New site from Git**

1. Connect to your preferred provider.
1. Select your repo from the list.

##### Configuring Netlify functions

After creating your repos and site from git, you will also need to install Netlify on your local machine.

**1. Install netlify-cli**

**Netlify cli install**

**swell.js**

```txt
npm install netlify-cli -g
```

**2. Link your repo with Netlify**

**Link your repo with Netlify**

**swell.js**

```txt
netlify link // Choose current repo
```

**3. Create the following folder structure**

Ensure that your files and folder names match otherwise they won't work correctly when using the Netlify Functions.

**Netlify folder structure**

**swell.js**

```txt
// Create the following structure in your repo. 
-- .netlify
	-- functions
		-- stripe-create-acc
			-- stripe-create-acc.js
		-- stripe-link-acc
			-- stripe-link-acc.js
```

**4. Run netlify functions locally**

This spins up a static server running on localhost which will allow us to test and debug our functions before deploying.

**Run Netlify functions locally**

**swell.js**

```txt
netlify dev
```

Now that the logistics are in place, let's get to the fun stuff.

#### Vendor onboarding

The first step is to get your vendors onboarded with Stripe so that they can receive payouts.

Install the `stripe` library:

**Stripe library install**

**cURL**

```txt
npm install stripe --save
```

##### Vendor stripe accounts

Use the following function to create an `express` account for each vendor. Stripe provides the onboarding flow for these accounts. You can also modify the below formula to create `custom` or `standard` accounts.

In `stripe-create-acc.js`:

**Creating custom or standard accounts**

**swell.js**

```javascript
const stripe = require('stripe')('STRIPE_TEST_KEY'); // TODO: Change to env

exports.handler = async function (event, context) {
  const { identity, user } = context.clientContext
  
  if (event.httpMethod !== "POST") {
    return { statusCode: 405, body: "Method Not Allowed" };
  }

  const account = await stripe.accounts.create({
    type: 'express',
  });

  const data = await account;
  return {
    statusCode: 200,
    body: JSON.stringify({data})
  }
}
```

##### This can be tested locally with the following script—replace the localhost port with your own.

**Test Netlify functions locally**

**swell.js**

```javascript
POST 'localhost:8888/.netlify/functions/stripe-create-acc'
```

You'll need the `id` since you have to pass this in the body of the next API call:

**id**

**swell.js**

```json
{"data":{"id":"acct_1Iu1bu2SHSa0FZJ3","object":"account",...}}
```

##### Onboarding URL for vendors

Create the `stripe-link-acc.js` function. This establishes an onboarding URL that your vendors need to complete in order to enter their verification and bank info:

**stripe-link-acc.js**

**swell.js**

```javascript
const stripe = require('stripe')('STRIPE_TEST_KEY'); //TODO: Change to env

exports.handler = async function (event, context) {
  
  if (event.httpMethod !== "POST") {
    return { statusCode: 405, body: "Method Not Allowed" };
  }

  const body = JSON.parse(event.body);

  const accountLinks = await stripe.accountLinks.create({
    account: body.accountId,
    refresh_url: 'https://example.com/reauth', // Change to actual URL
    return_url: 'https://example.com/return', // Change to actual URL
    type: 'account_onboarding',
  });

  const data = await accountLinks;
  return {
    statusCode: 200,
    body: JSON.stringify({data})
  }
}
```

Test this locally like the function above with the `accountId` returned with the previous call:

**Testing with the accountID**

**swell.js**

```json
POST 'localhost:8888/.netlify/functions/stripe-link-acc', {
	"accountId": "acct_1Iu1bu2SHSa0FZJ3"
}
```

This returns the following `url`:

**Response**

**swell.js**

```json
{"data":{"object":"account_link","created":1621715261,"expires_at":1621715561,"url":"https://connect.stripe.com/express/onboarding/eSLptzRHZTDc" ... }}
```

Return the url to the vendor so they can start their onboarding through Stripe Connect.

![Swell marketplace onboarding for vendor payouts](https://cdn.sanity.io/images/ecf33d4l/production/0442491f20bab4dec3266d137d88cee41298ccad-1005x663.png)

##### Onboard completion verification

A vendor will have to complete their onboarding for their account to be fully functional. Vendors redirected to your `return_url` may have completed the onboarding process. For these instances, use the Stripe `/v1/accounts` endpoint to retrieve the user’s account and check for `charges_enabled`.

If the vendor does not complete their onboarding, you'll need to provide UI prompts to allow them to continue onboarding later. The user can complete their account activation through a new account link (generated by the API call above). You can check the state of the `details_submitted` parameter on their Stripe Connect account to see if they have completed the onboarding process.

> **Note:** Continue with the onboarding flow to create a Stripe Connect Test account. Ensure you have Viewing test data enabled in the toolbar. After a successful onboarding, you should see a new account under Connected accounts.

#### Adding the Stripe connect id in Swell

Assuming that a vendor has completed their onboarding, you can update Swell's `account` model to include their Stripe id to manage payouts and transfers:

**Updating the Swell account with the Stripe id**

**swell.js**

```json
POST '/:models/accounts/fields', {
	"stripe_connect_id": {
		"type": "string",
		"label": "Stripe Connect ID"
	}
}
```

Let's now create a relation between the vendor and their Stripe connect account ID in Swell:

**Relate Stripe Connect id to vendor in Swell**

**swell.js**

```json
PUT '/accounts/{id}', {
	"stripe_connect_id": "acct_1Iu1PE2R8FdXytPe",
}
```

##### Creating a platform fee

Now that we have vendors associated to the Stripe account, we need to establish the transaction logic in Swell to charge platform fees to vendors:

**Charge platform fees to vendors**

**swell.js**

```json
POST ':/models/orders/fields/items/fields',
{
  "platform_fee": {
		"type": "float",
		"default": 0.2
	}
```

We can accomplish this by creating a `platform_item_field` to capture a portion of each item's total price towards the platform:

**Create a platform item fee**

**swell.js**

```json
POST '/:models/orders/fields/items/fields', 
{
	"platform_item_fee": {
		"type": "float",
		"formula": "platform_fee * price_total"
	}
}
```

This example calculates 20% of each item's total price as the platform fee. This will deduct the fee from vendor payouts and allocate it as the platform's revenue.

#### Up next

With vendors successfully onboarded, the last step will be setting up the payout logic to ensure vendors can receive payment for their goods purchased on the marketplace.


## Create vendor payouts

#### Payouts to vendors

Create another folder labeled `stripe-payout` in your `functions` folder, and include a `stripe-payout.js` file. The following function takes in `amount` and `accoundId` body parameters. This function is triggered by a webhook that we will configure on Swell to fire after every order.

We'll be issuing Stripe Transfers with the function below. Transfers accumulate in a Connected Account and are then paid out to the associated bank account/debit card. You can change the interval by visiting the Stripe dashboard's settings and modifying the payout schedule.

We'll need `swell-node` in order to query our Vendor's account and retrieve their `stripe-connect-id` to perform the payout:

**Install swell-node**

**cURL**

```txt
npm install swell-node --save
```

The following is an example of fetching the `stripe_connect_id` for one order item and transferring funds. For orders containing multiple order items, loop through the order to handle multiple transfers.

**Fetch the stripe_connect_id**

**swell.js**

```javascript
const stripe = require('stripe')('STRIPE_TEST_KEY');
const swell = require('swell-node');

swell.init('SWELL_STORE_ID', 'SWELL_SECRET_KEY');

exports.handler = async function (event, context) {
    // Only allow POST
    if (event.httpMethod !== "POST") {
        return { statusCode: 405, body: "Method Not Allowed" };
    }

    const body = JSON.parse(event.body);

    // Fetch stripe_connect_id from the vendor_id. 
    const account = await swell.get('/accounts', {
        where: {
            id: body.data.items[0].vendor_id
        }
    });

    const stripeId = account.results[0].stripe_connect_id;
  
    // Payout amounts must be in cents.
    const truncAmt = (body.data.items[0].price_total - body.data.items[0].item_platform_fee).toFixed(2);
    const payoutAmt =
        truncAmt.toString().split('.')[0] + truncAmt.toString().split('.')[1];

    const transfer = await stripe.transfers.create({
        amount: payoutAmt,
        currency: 'cad', // Change to your Stripe currency
        destination: stripeId
    });

    const data = await transfer;
    return {
        statusCode: 200,
        body: JSON.stringify({ data })
    }
}
```

##### Deploy functions to Netlify

Now that we have the necessary functions created, we can deploy them to Netlify. The last step is creating Swell webhook to test our functions.

> **Tip:** Be sure to commit and push the latest changes to your remote repo—this triggers a new deployment in Netlify every time you push changes to the master branch.

If your folder structure and function naming is correct, select your site in your Netlify account and navigate to the **Functions** tab. You should see the following functions:

![Swell Netlify functions](https://cdn.sanity.io/images/ecf33d4l/production/12d4a49ee9fb55e3cadd1bde83fb01006d5b6ce3-2000x1314.png)

When selecting a function from this list, you can monitor for any function invocations and `console.log` commands. You can also see the function **endpoint**—which we'll use to trigger them via webhook.

##### Create a Swell webhook

Now that we have the endpoints, we can use them to create webhooks within Swell:

In your Swell store, select **Developer > Webhooks**

1. Under Webhooks, select **Add new webhook**
1. In the **webhook url,** add the endpoint of your `stripe-payout` function. You can see this by clicking on the function from the Netlify Functions screen.
1. Under **events**, select `order.created`, this will trigger our `stripe-payout` function after every order.
1. Select **Save**

##### Top up Stripe Account

In order to test our live integration, you'll need to load funds in your Stripe account to make transfers to another Stripe Connect account. You can do this in two separate ways:

1. By the Stripe dashboard by selecting: **Balances > Add to balance**. This functionality is only available to US Stripe accounts.
1. By creating a manual charge using a specific card number, You can do this by selecting: **Payments > Create payment >** enter $5000 (or other) under **Amount** > enter **4000 0000 0000 0077** as the card number, any future date as the expiration date, and any 3 digit number for CVC.

Now that we have funds to transfer with our payout functionality, we can test our payout functions.

##### Testing

Create a test order from the **Swell Dashboard** in order to verify everything we've built in this guide. If successful, you should not see any errors in the function log for the `stripe-payout` function.

![Swell netlify function testing](https://cdn.sanity.io/images/ecf33d4l/production/7f3a7dc2eaf274ea98d48fccfd168a29f845564a-1686x100.png)

Let's check our Stripe connect account to see that the transfer was successful. Select the test Connect account that you created under **Connected accounts.** If you have a sufficient balance in your Stripe account, you will see a transfer similar to the one below:

![Confirming transfer on Stripe](https://cdn.sanity.io/images/ecf33d4l/production/74cb587ea5bd70992b468da4a84c02884d01ef0f-2000x514.png)

#### Conclusion

Well done—with everything showing properly, you have now successfully implemented everything you needed to establish a marketplace, onboard vendors for payouts, and facilitate the payouts to the vendors.

