Guides

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.

💡

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

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.

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

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

💡

You will need the group's id when creating various accounts within your marketplace for a particular group.

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:

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

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
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.

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

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.

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.

Use the following:
Adding vendor_id to /products
await swell.put('/:models/products/fields', {
	vendor_id: {
		type: 'objectid',
		required: true // Optional
	}
});
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 product model
  1. Go to Developer > Models
  2. Select the Products model
  3. Select Add Field; then choose Lookup
  4. Under Label type Vendor
  5. Select Customers for Lookup collection
  6. Select Details for the Admin field location. Specify this to your preference for displaying (or hiding) the field in the desired location.
  7. Save.

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

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

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

Fetch product associated to a vendor_id
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.

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
await swell.put('/:models/orders/fields/items/fields', {
	vendor_id: {
		type: 'objectid',
		formula: 'product.vendor_id'
	}			
});

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
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.

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
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.

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.

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.

💡

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

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:

01
Create a stripe account

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

02
Complete the following setup in your Connected accounts tab in Stripe
  1. Provide your Business details.
  2. Complete your Platform profile.
01
Create a Netlify account

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

02
Create a new local repo and a new remote repo
  1. Perform a git init.
  2. Push an initial commit to the remote repo.
03
From the Netlify home page, select New site from Git
  1. Connect to your preferred provider.
  2. Select your repo from the list.

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

01
Install netlify-cli
Netlify cli install
npm install netlify-cli -g
02
Link your repo with Netlify
Link your repo with Netlify
netlify link // Choose current repo
03
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
// Create the following structure in your repo. 
-- .netlify
	-- functions
		-- stripe-create-acc
			-- stripe-create-acc.js
		-- stripe-link-acc
			-- stripe-link-acc.js

04
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
netlify dev

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

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

Install the stripe library:

Stripe library install
npm install stripe --save

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
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})
  }
}
Test Netlify functions locally
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
{"data":{"id":"acct_1Iu1bu2SHSa0FZJ3","object":"account",...}}

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
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
POST 'localhost:8888/.netlify/functions/stripe-link-acc', {
	"accountId": "acct_1Iu1bu2SHSa0FZJ3"
}

This returns the following url:

Response
{"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.

Stripe onboarding url for vendors

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.

💡

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.

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
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
PUT '/accounts/{id}', {
	"stripe_connect_id": "acct_1Iu1PE2R8FdXytPe",
}

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
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
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.

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 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
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
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 })
    }
}

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.

👉

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:

Netlify functions

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.

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
  2. 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.
  3. Under events, select order.created, this will trigger our stripe-payout function after every order.
  4. Select Save

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.
  2. 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.

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.

Netlify function testing

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

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.