Integrate Stripe and Stripe Connect
Build your own marketplace from scratch using Medusa
In this last part of the series, we’ll be setting up Stripe and Stripe Connect to collect payments from our customers and trigger payments for our vendors.
Before starting this part, make sure you have a valid Stripe account and have set up Stripe Connect
Updating the Store entity
First, we’ll add two new properties to the Store entity :
-
The first property will be the Stripe account ID linked to a store
-
The second property will indicate whether the Stripe account is active and ready to receive payments.
Creating the Migration
We can now apply our data to the database by creating our migration :
We can now add our changes in the up
and down
functions :
Don’t forget to apply the migrations once you have updated the file by executing this in your terminal :
Whenever you add a new column / property to an entity that have been extended don’t forget to update your index.d.ts
in case you are going to use that property, or it will throw TypeScript errors.
Configuring Medusa
In your medusa-config.js
at the root of your backend directory, we’ll setup the medusa-payment-stripe
so that we can use it later :
Adding the medusa-payment-stripe
dependency
Before continuing, make sure you have the medusa-payment-stripe
package available in your project, otherwise you can install it this way:
Creating the Service
With all this groundwork done, we’re finally going to create the StripeConnectService
. I invite you to create a new file in the /src/services
folder with the name stripe-connect.ts
.
Once the file has been created, here’s what you’ll need to paste inside it for now :
This service contains all the logic we need to carry out the following process:
-
When creating a store, we’ll use the
StripeConnectService.createAccount
function, which will allow us to create a Stripe account for the store in question. -
Once the Stripe account has been created, you can then create an onboarding link for the Store using the
StripeConnectService.createOnboarding
. This link will be saved directly in the store metadata in our case, but you can also add a new property to your Store entity that will store the onboarding url if you want. -
Finally, once the Stripe account of a store is active, we’ll be able to trigger payments using the
StripeConnect.createTransfer
function once an order meets our conditions. In this example, we’ll trigger a transfer when an order has been delivered and paid, of course you can adapt to your needs.
Don’t forget to change the YOUR_COUNTRY_CODE
value in the StripeConnectService.createAccount
function
Use the service
As mentioned above, we’re going to use our new service, when a new store is created.
To do this, we’ll update our Subscriber responsible for monitoring the StoreService.Events.CREATED
event :
Here we’ve refactored our code a little bit, to have three different functions triggered in order when a store is created, we also have a logger that will allows us to understand easily what’s going on. First it will create the default shipping profile for the new store, then it will create a Stripe account for that store and finally the onboarding link will be created and saved into the Store metadata, so we’ll use it inside our Admin UI.
Update (again) our Medusa config
As you may have noticed, in our service we’re extending Medusa’s configuration and we’ve added a new property: server_url
In fact, this property doesn’t exist by default, so you can add it directly to your configuration in this way to point to the url of your Medusa backend so that Stripe can redirect you back to this URL in the case of a success or error following their onboarding process.
Testing our service flow
If you try to create a new user, you should see something like this in your terminal after a few seconds :
Perfect, if you take a look at your store
table in your database, you should see a store with not null metadata column, which contains the link to start Stripe’s onboarding process.
On the other hand, before starting a process, we need to add the two routes that will handle the success/leave and error cases
Adding the Stripe API routes
Create the Success / Leave route
For this section, I invite you to create a new file in the /src/api/stripe/onboarding/return/route.ts
folder :
Here, in the case of a process success we retrieve the storeId
from the query parameters, and then set the store.stripe_account_enabled
value to true
for that store. Once the value has been updated, we can redirect the user to the admin UI
But in case the user has just leaved the onboarding process, we do nothing and just redirect.
Of course, you can use a constant here or even the config to make sure it’s not hardcoded like this, here, it’s just for the example.
Create the Error route
For this route, in the same /src/api/stripe/onboarding
folder, I invite you to create a refresh
folder containing this route.ts
file :
Here, in the case of an error such as an expired URL, we can generate a new onboarding URL and redirect the user directly to it.
You can learn more here in the official Stripe docs.
Update the ProductService
I suggest that you make sure that the storefront can’t access products from stores that haven’t activated their Stripe account, to make sure that all stores are “activated” before they can proceed with sales on our platform.
Transfers
We will initiate money transfers only when an order has been marked as shipped and its overall status is set to completed.
However, Medusa does not automatically update an order’s status to completed when the order is shipped and the payment is captured.
To address this, we need to create a new subscriber that monitors the payment status and fulfillment status. Based on these statuses, we will update the overall order status to completed
.
By implementing this new subscriber, we can specifically track when an order is truly completed. Once the order is marked as completed, we can then trigger the money transfer process using our StripeConnectService.createTransfer
Create the Child Order Subscriber
This subscriber will monitor the events of the child orders (to be clearer, it will monitor the changes made by the stores), if an order has the status of payment captured, and its order has been marked as shipped, then we can update the child order’s status to completed
and create a transfer for the order’s store :
Please note that this is purely for educational purposes. In a real marketplace, you wouldn’t make transfers directly like this, it implies more thoughts. For example, a transfer could be triggered after a certain time to make sure that no refund could be requested from the customer. Take a look at the Scheduled Job with Medusa
Creating an Admin Widget
Now that the backend is in place, we’re going to tackle the Admin UI, so that our vendors can begin the process of onboarding with Stripe to be able to receive transfers later on.
You can create a new widget file at the following path /src/admin/widgets/stripe-onboarding-widget.tsx
:
Here we’re injecting this widget in several places, the product and order pages.
The orders page, in particular, is the one that appears following a login, perfect for attracting our vendor’s attention!
Other areas are available for injection, you can find them all “here”
You should see the widget like this when you log in with a Store that do not have activated it’s account.
And by click on the Activate Now button, it should redirect you directly to the Stripe page.
In order to be displayed in this way at the end of the process :
Recap
Throughout this series, we’ve explored the process of extending and customizing Medusa.
Leveraging Medusa’s flexibility, we’ve enabled users/vendors to create products, define shipping options, manage orders individually and integrate with Stripe for payouts.
While our course has been a rewarding one, it’s important to recognize that we’ve only scratched the surface of Medusa’s capabilities.
Platforms such as Amazon and Etsy offer a wide range of features and functionality to meet a variety of “business requirements”, however, the knowledge and skills you’ve gained in this series have provided you with the foundation you need to further develop Medusa’s core functionality and apply advanced concepts to your project!
GitHub Branch
You can access the complete part’s code here.
You can also have access to the storefront code here.
Support
If you’ve enjoyed this series, or if you’d like to see other guides, please consider supporting me on GitHub.
Support my work
Your support helps me dedicate more time to creating high-quality content and building tools that benefit the community.
Was this page helpful?