Learn how to create a Stripe Connect Express account step-by-step using Node.js and Express, including onboarding, webhooks, and dashboard integration.
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
How to Create an Express Account with Stripe Connect
Step 1: Set Up Your Stripe Account
Before you can create Express accounts, you need to have a Stripe platform account. If you don't have one yet, go to Stripe's website (https://stripe.com) and sign up. Make sure to complete the verification process to unlock all features.
Step 2: Install Required Dependencies
For a Node.js project, install the Stripe SDK and other necessary packages:
npm install stripe express body-parser dotenv
Step 3: Configure Your Environment
Create a .env file in your project root to store your Stripe API keys:
# .env file
STRIPE_SECRET_KEY=sk_test_your_test_key
STRIPE_PUBLISHABLE_KEY=pk_test_your_test_key
STRIPE_WEBHOOK_SECRET=whsec_your_webhook\_secret
Step 4: Set Up Your Express Server
Create a server.js file with the basic Express configuration:
const express = require('express');
const bodyParser = require('body-parser');
const dotenv = require('dotenv');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
dotenv.config();
const app = express();
const port = process.env.PORT || 3000;
// Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public'));
// Start server
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Step 5: Create an Express Account Creation Endpoint
Add an endpoint to create a Stripe Connect Express account:
app.post('/create-express-account', async (req, res) => {
try {
// Extract user information from request
const { email, name, country } = req.body;
// Create an Express account with Stripe Connect
const account = await stripe.accounts.create({
type: 'express',
country: country || 'US',
email: email,
capabilities: {
card\_payments: { requested: true },
transfers: { requested: true },
},
business\_type: 'individual',
business\_profile: {
url: req.body.website || 'https://example.com',
mcc: '5734', // Computer Software Stores
product\_description: 'Software services',
},
metadata: {
user_id: req.body.userId || 'user_123',
},
});
// Return the account ID
res.status(200).json({
success: true,
accountId: account.id
});
} catch (error) {
console.error('Error creating Express account:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
Step 6: Create an Account Link for Onboarding
Add an endpoint to generate an account link for the onboarding process:
app.post('/create-account-link', async (req, res) => {
try {
const { accountId } = req.body;
if (!accountId) {
return res.status(400).json({ error: 'Account ID is required' });
}
// Create an account link for the user to onboard
const accountLink = await stripe.accountLinks.create({
account: accountId,
refresh\_url: `${req.headers.origin}/onboarding-refresh`,
return\_url: `${req.headers.origin}/onboarding-success`,
type: 'account\_onboarding',
collect: 'eventually\_due',
});
// Return the account link URL
res.status(200).json({
success: true,
url: accountLink.url
});
} catch (error) {
console.error('Error creating account link:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
Step 7: Handle Onboarding Refresh and Success Routes
Create routes to handle the redirect URLs from the onboarding process:
// Route for handling onboarding refresh
app.get('/onboarding-refresh', (req, res) => {
res.send(\`
Onboarding session expired
Please refresh the page to continue with the onboarding process.
\`);
});
// Route for handling successful onboarding
app.get('/onboarding-success', (req, res) => {
res.send(\`
Onboarding Successful!
Your Stripe Connect Express account has been set up successfully.
Return to Dashboard
\`);
});
Step 8: Create a Simple Frontend Interface
Create a public folder with an index.html file to provide a user interface:
Stripe Connect Express Onboarding
Create Stripe Connect Express Account
Step 9: Add Routes to Check Account Status
Create an endpoint to check the status of an Express account:
app.get('/account-status/:accountId', async (req, res) => {
try {
const { accountId } = req.params;
// Retrieve the account from Stripe
const account = await stripe.accounts.retrieve(accountId);
// Return the account details and onboarding status
res.status(200).json({
success: true,
account: {
id: account.id,
email: account.email,
chargesEnabled: account.charges\_enabled,
payoutsEnabled: account.payouts\_enabled,
requirements: account.requirements,
detailsSubmitted: account.details\_submitted,
}
});
} catch (error) {
console.error('Error retrieving account:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
Step 10: Set Up a Webhook Handler
Add a webhook endpoint to handle Stripe events related to Connect accounts:
app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
// Verify webhook signature
event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
console.error('Webhook signature verification failed:', err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle specific events
switch (event.type) {
case 'account.updated':
const account = event.data.object;
console.log('Account updated:', account.id);
// Check if account is now fully onboarded
if (account.charges_enabled && account.payouts_enabled) {
console.log('Account fully onboarded:', account.id);
// Update your database or notify your systems
}
break;
case 'account.application.deauthorized':
// Handle when a user disconnects from your platform
const accountId = event.data.object.id;
console.log('Account disconnected:', accountId);
// Update your database to mark the account as disconnected
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.json({received: true});
});
Step 11: Complete the Setup with Error Handling
Add global error handling to your Express application:
// Global error handler
app.use((err, req, res, next) => {
console.error('Global error:', err);
res.status(500).json({
success: false,
error: process.env.NODE\_ENV === 'production'
? 'An error occurred'
: err.message
});
});
// Handle 404 errors
app.use((req, res) => {
res.status(404).json({
success: false,
error: 'Resource not found'
});
});
Step 12: Run and Test Your Application
Start your application and test the Express account creation flow:
// Run the application
node server.js
Navigate to http://localhost:3000 in your browser, fill out the form, and follow the onboarding process.
Step 13: Setting Up Stripe CLI for Webhook Testing
For local development, install the Stripe CLI to forward webhook events to your local server:
# Install Stripe CLI (Mac with Homebrew)
brew install stripe/stripe-cli/stripe
# Login to your Stripe account
stripe login
# Forward webhook events to your local server
stripe listen --forward-to localhost:3000/webhook
The Stripe CLI will provide a webhook signing secret. Add this to your .env file as STRIPE_WEBHOOK_SECRET.
Step 14: Implementing Custom Connect Account Dashboard
Add an endpoint to create a dashboard link for Express account users:
app.post('/create-dashboard-link', async (req, res) => {
try {
const { accountId } = req.body;
if (!accountId) {
return res.status(400).json({ error: 'Account ID is required' });
}
// Create a login link for the Express account
const loginLink = await stripe.accounts.createLoginLink(accountId);
// Return the login link URL
res.status(200).json({
success: true,
url: loginLink.url
});
} catch (error) {
console.error('Error creating dashboard link:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
Add a button to your frontend to generate and navigate to the dashboard link when needed.
Step 15: Deploying to Production
When deploying to production, follow these best practices:
Before going live, test the entire flow in Stripe's test mode, then switch to live mode when ready for production.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.