Learn how to split payments between two users in Stripe using Connect, direct charges, destination charges, and transfers. Step-by-step guide for developers.
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 Split Payments Between Two Users in Stripe
In this tutorial, I'll walk you through the process of implementing payment splitting between two users using Stripe. This is commonly needed for marketplace platforms, affiliate systems, or any application where funds need to be distributed between multiple parties.
Step 1: Set Up Your Stripe Account
Before you can split payments, you need to have a Stripe account and the necessary API keys:
Step 2: Install the Stripe SDK
Install the Stripe library in your project using npm, pip, or another package manager depending on your programming language:
For Node.js:
npm install stripe
For Python:
pip install stripe
Step 3: Set Up Connect Accounts for Recipients
Stripe Connect allows you to split payments by creating accounts for each recipient:
For Node.js, creating a Connect account looks like this:
const stripe = require('stripe')('sk_test_YOUR_SECRET_KEY');
async function createConnectAccount(email, name) {
const account = await stripe.accounts.create({
type: 'express',
country: 'US',
email: email,
business\_type: 'individual',
capabilities: {
card\_payments: {requested: true},
transfers: {requested: true},
},
business\_profile: {
name: name,
},
});
return account.id;
}
Step 4: Implement Direct Charges with Automatic Transfers
This method charges the customer directly and automatically transfers a portion to another account:
const stripe = require('stripe')('sk_test_YOUR_SECRET_KEY');
async function splitPayment(amount, customerPaymentMethodId, connectedAccountId, platformFeePercentage) {
// Convert amount to cents for Stripe
const amountInCents = amount \* 100;
// Calculate platform fee (your commission)
const platformFeeAmount = Math.round(amountInCents \* (platformFeePercentage / 100));
// Create a payment intent with automatic transfer
const paymentIntent = await stripe.paymentIntents.create({
amount: amountInCents,
currency: 'usd',
payment\_method: customerPaymentMethodId,
confirmation\_method: 'manual',
confirm: true,
application_fee_amount: platformFeeAmount,
transfer\_data: {
destination: connectedAccountId,
},
});
return paymentIntent;
}
Step 5: Implement Destination Charges
An alternative approach is to use destination charges, where your platform is the merchant of record:
const stripe = require('stripe')('sk_test_YOUR_SECRET_KEY');
async function destinationCharge(amount, customerPaymentMethodId, destinationAccountId, platformFeePercentage) {
// Convert amount to cents
const amountInCents = amount \* 100;
// Calculate platform fee
const platformFeeAmount = Math.round(amountInCents \* (platformFeePercentage / 100));
// Create a payment intent with destination
const paymentIntent = await stripe.paymentIntents.create({
amount: amountInCents,
currency: 'usd',
payment\_method: customerPaymentMethodId,
confirmation\_method: 'manual',
confirm: true,
application_fee_amount: platformFeeAmount,
transfer\_data: {
destination: destinationAccountId,
},
on_behalf_of: destinationAccountId,
});
return paymentIntent;
}
Step 6: Split Payments Using Transfers
If you want more control over the payment flow, you can first charge the customer and then distribute the funds:
const stripe = require('stripe')('sk_test_YOUR_SECRET_KEY');
async function chargeAndSplit(amount, customerPaymentMethodId, recipientAccountId, recipientPercentage) {
// Convert amount to cents
const amountInCents = amount \* 100;
// Calculate recipient amount
const recipientAmount = Math.round(amountInCents \* (recipientPercentage / 100));
const platformAmount = amountInCents - recipientAmount;
try {
// 1. Charge the customer (funds go to your platform account)
const paymentIntent = await stripe.paymentIntents.create({
amount: amountInCents,
currency: 'usd',
payment\_method: customerPaymentMethodId,
confirmation\_method: 'manual',
confirm: true,
});
// 2. Transfer the recipient's share
if (paymentIntent.status === 'succeeded') {
const transfer = await stripe.transfers.create({
amount: recipientAmount,
currency: 'usd',
destination: recipientAccountId,
transfer\_group: `order_${paymentIntent.id}`,
source\_transaction: paymentIntent.charges.data[0].id,
});
return {
paymentIntent,
transfer,
platformAmount,
recipientAmount
};
}
} catch (error) {
console.error('Error processing payment split:', error);
throw error;
}
}
Step 7: Implement a Checkout Flow with Payment Splitting
Here's how to integrate payment splitting with Stripe Checkout:
const stripe = require('stripe')('sk_test_YOUR_SECRET_KEY');
const express = require('express');
const app = express();
app.use(express.json());
app.use(express.static('public'));
app.post('/create-checkout-session', async (req, res) => {
const { amount, recipientId, recipientPercentage, orderId } = req.body;
// Calculate the application fee amount
const amountInCents = amount \* 100;
const applicationFeeAmount = Math.round(amountInCents \* (1 - recipientPercentage / 100));
try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line\_items: [
{
price\_data: {
currency: 'usd',
product\_data: {
name: 'Order #' + orderId,
},
unit\_amount: amountInCents,
},
quantity: 1,
},
],
mode: 'payment',
success_url: 'https://yourwebsite.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel\_url: 'https://yourwebsite.com/cancel',
payment_intent_data: {
application_fee_amount: applicationFeeAmount,
transfer\_data: {
destination: recipientId,
},
}
});
res.json({ id: session.id });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000, () => console.log('Server running on port 3000'));
Step 8: Handle Frontend Integration
Add this JavaScript to your frontend to redirect customers to the Stripe Checkout page:
// Include Stripe.js
Step 9: Implement Stripe Webhooks for Payment Events
Set up webhooks to track payment statuses and handle notifications:
const express = require('express');
const stripe = require('stripe')('sk_test_YOUR_SECRET_KEY');
const app = express();
// Use JSON parser for webhook events
app.use('/webhook', express.raw({type: 'application/json'}));
app.use(express.json());
app.post('/webhook', async (req, res) => {
const sig = req.headers['stripe-signature'];
const endpointSecret = 'whsec_YOUR_WEBHOOK\_SECRET';
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
} catch (err) {
console.error(`Webhook Error: ${err.message}`);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle specific event types
switch (event.type) {
case 'payment\_intent.succeeded':
const paymentIntent = event.data.object;
console.log(`PaymentIntent ${paymentIntent.id} succeeded. Amount: ${paymentIntent.amount}`);
// Here you can update your database, notify users, etc.
// If using the transfer approach, you could trigger your transfer here
break;
case 'transfer.created':
const transfer = event.data.object;
console.log(`Transfer ${transfer.id} created. Amount: ${transfer.amount}`);
break;
// Add more event handlers as needed
}
res.json({received: true});
});
Step 10: Handle Reporting and Reconciliation
Create functions to track and report on the payment splits:
const stripe = require('stripe')('sk_test_YOUR_SECRET_KEY');
async function getTransactionReport(startDate, endDate) {
// Get all payments in the date range
const paymentIntents = await stripe.paymentIntents.list({
created: {
gte: Math.floor(startDate.getTime() / 1000),
lte: Math.floor(endDate.getTime() / 1000)
},
limit: 100
});
// Get all transfers in the date range
const transfers = await stripe.transfers.list({
created: {
gte: Math.floor(startDate.getTime() / 1000),
lte: Math.floor(endDate.getTime() / 1000)
},
limit: 100
});
// Process and format the data for reporting
const report = {
totalProcessed: 0,
platformRevenue: 0,
transferredToPartners: 0,
transactions: []
};
// Process payment intents
for (const pi of paymentIntents.data) {
report.totalProcessed += pi.amount;
// Find matching transfers
const relatedTransfers = transfers.data.filter(t =>
t.source\_transaction === (pi.charges.data[0] ? pi.charges.data[0].id : null)
);
const transferAmount = relatedTransfers.reduce((sum, t) => sum + t.amount, 0);
const platformAmount = pi.amount - transferAmount;
report.platformRevenue += platformAmount;
report.transferredToPartners += transferAmount;
report.transactions.push({
id: pi.id,
amount: pi.amount,
platformAmount,
transferAmount,
date: new Date(pi.created \* 1000),
transfers: relatedTransfers.map(t => ({
id: t.id,
amount: t.amount,
destination: t.destination
}))
});
}
return report;
}
Step 11: Test Your Implementation
Before going live, test your payment splitting implementation using Stripe's test mode:
Step 12: Go Live with Production
When you're ready to go live:
Conclusion
You now have a comprehensive system for splitting payments between two users in Stripe. This implementation supports several approaches including direct charges with automatic transfers, destination charges, and manual transfers. The approach you choose will depend on your specific business requirements and user experience needs.
Remember to keep your Stripe libraries updated and regularly check Stripe's documentation for any API changes or new features that might improve your payment splitting implementation.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.