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.
- From the dashboard, navigate to Settings > Customers
- Select Add customer group under the Groups section
- Provide the desired group name (edit the group ID—optional)
- 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:
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:
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.
- Go to Developer > Models
- Select the Products model
- Select Add Field; then choose Lookup
- Under Label type Vendor
- Select Customers for Lookup collection
- Select Details for the Admin field location. Specify this to your preference for displaying (or hiding) the field in the desired location.
- Save.
With the addition of the vendor_id, you can now create new products with an association to 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.
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.
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.
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.
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:
Create a stripe account
If you don't already have a Stripe account, you will need to signup with Stripe.
Complete the following setup in your Connected accounts tab in Stripe
- Provide your Business details.
- Complete your Platform profile.
Create a Netlify account
If you don't have a Netlify account, you will need to signup with Netlify.
Create a new local repo and a new remote repo
- Perform a git init.
- Push an initial commit to the remote repo.
From the Netlify home page, select New site from Git
- Connect to your preferred provider.
- 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.
Install netlify-cli
npm install netlify-cli -g
Link your repo with Netlify
netlify link // Choose current repo
Create the following folder structure
Ensure that your files and folder names match otherwise they won't work correctly when using the Netlify Functions.
// Create the following structure in your repo.
-- .netlify
-- functions
-- stripe-create-acc
-- stripe-create-acc.js
-- stripe-link-acc
-- stripe-link-acc.js
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.
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:
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:
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})
}
}
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:
{"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:
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:
POST 'localhost:8888/.netlify/functions/stripe-link-acc', {
"accountId": "acct_1Iu1bu2SHSa0FZJ3"
}
This returns the following url:
{"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.
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:
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:
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:
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:
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:
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.
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:
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
- Under Webhooks, select Add new webhook
- 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.
- Under events, select order.created, this will trigger our stripe-payout function after every order.
- 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:
- By the Stripe dashboard by selecting: Balances > Add to balance. This functionality is only available to US Stripe accounts.
- 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.
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:
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.