Paystack multi-split lets you divide a single customer payment among three or more parties. You create a Transaction Split object that lists subaccounts and their shares (percentage or flat), then pass the resulting split_code when initializing a transaction. Paystack settles each subaccount directly. Your main account receives whatever portion is not allocated to subaccounts.
Standard split payments on Paystack work between two parties: your main account and one subaccount. That covers the common marketplace case where a buyer purchases from a single seller and the platform takes a cut. But many real-world transactions involve more than two parties.
Here are the situations where multi-split becomes necessary:
If every transaction on your platform involves exactly two parties (vendor and platform), use single split. If any transaction involves three or more, you need multi-split. Do not use multi-split for two-party transactions. It adds an extra API call (creating the split group) for no benefit.
A Transaction Split group defines the participants and their shares. You create it with a POST to the /split endpoint, then use the returned split_code in transaction initialization.
// Create a multi-split group for a food delivery order
async function createOrderSplit(restaurantCode, riderCode) {
var response = await fetch('https://api.paystack.co/split', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Order Split - ' + Date.now(),
type: 'percentage',
currency: 'NGN',
subaccounts: [
{
subaccount: restaurantCode,
share: 65,
},
{
subaccount: riderCode,
share: 15,
},
],
bearer_type: 'account',
bearer_subaccount: '',
}),
});
var data = await response.json();
if (data.status) {
// data.data.split_code => "SPL_xyz789"
// Platform gets the residual: 100 - 65 - 15 = 20%
return data.data.split_code;
} else {
throw new Error('Split creation failed: ' + data.message);
}
}
The key fields in the creation request:
percentage or flat. This applies to all subaccounts in the group. You cannot mix percentage and flat within one split group.subaccount code and a share. For percentage type, shares are percentages. For flat type, shares are amounts in the smallest currency unit.account, subaccount, or all-proportional (fee is shared proportionally among the subaccounts). If subaccount, you must also set bearer_subaccount to specify which one.Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
Once you have a split_code, pass it when initializing the transaction instead of the subaccount parameter.
// Initialize a transaction with a multi-split group
async function chargeWithMultiSplit(customerEmail, amountInKobo, splitCode, orderId) {
var response = await fetch('https://api.paystack.co/transaction/initialize', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: customerEmail,
amount: amountInKobo,
split_code: splitCode,
reference: 'order_' + orderId + '_' + Date.now(),
metadata: {
order_id: orderId,
custom_fields: [
{
display_name: 'Order ID',
variable_name: 'order_id',
value: String(orderId),
},
],
},
}),
});
var data = await response.json();
if (data.status) {
return data.data.authorization_url;
} else {
throw new Error('Transaction initialization failed: ' + data.message);
}
}
Important: do not pass both subaccount and split_code in the same transaction. Use one or the other. If you pass split_code, the split group definition takes over completely. Any subaccount, transaction_charge, or bearer parameters are ignored in favor of the split group's configuration.
The customer experience is identical to a regular payment. They see one charge for the full amount. The multi-split happens entirely at the settlement layer.
The type field on the split group determines how shares are interpreted. The choice affects your margin math and how the split scales with transaction size.
Percentage type
Each subaccount's share is a percentage of the transaction amount. The residual (whatever is left after all subaccount shares) goes to your main account.
Example: customer pays 10,000 NGN. Restaurant share is 65%, rider share is 15%.
Flat type
Each subaccount's share is a fixed amount in the smallest currency unit. The residual goes to your main account.
Example: customer pays 10,000 NGN (1,000,000 kobo). Restaurant share is 650,000 kobo, rider share is 150,000 kobo.
The numbers look the same in this example, but they diverge at different price points. If the order were 5,000 NGN:
With flat type, you must ensure the sum of all subaccount shares does not exceed the transaction amount. Paystack will reject or miscalculate the split if flat shares exceed the total. Percentage type avoids this problem because percentages always produce amounts within the transaction total (as long as they do not sum above 100).
For most multi-party marketplaces, percentage type is safer and more flexible. Use flat type only when each party's share is a known fixed amount regardless of transaction size.
In theory, split groups are reusable. In practice, most multi-party platforms create a new split group for each order because the participant combination changes every time. A food delivery order at restaurant A with rider B is a different split than restaurant A with rider C.
// Full checkout flow with dynamic split group creation
async function processDeliveryCheckout(order) {
var restaurant = await db.restaurants.findById(order.restaurantId);
var rider = await db.riders.findById(order.riderId);
// Create a split group specific to this order
var splitResponse = await fetch('https://api.paystack.co/split', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Order ' + order.id,
type: 'percentage',
currency: 'NGN',
subaccounts: [
{
subaccount: restaurant.paystackSubaccountCode,
share: restaurant.splitPercentage || 65,
},
{
subaccount: rider.paystackSubaccountCode,
share: 15,
},
],
bearer_type: 'account',
bearer_subaccount: '',
}),
});
var splitData = await splitResponse.json();
if (!splitData.status) {
throw new Error('Could not create split for order: ' + splitData.message);
}
// Store the split code on the order for reconciliation
await db.orders.update(order.id, {
paystackSplitCode: splitData.data.split_code,
});
// Initialize the transaction with the new split code
var txResponse = await fetch('https://api.paystack.co/transaction/initialize', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: order.customerEmail,
amount: order.totalInKobo,
split_code: splitData.data.split_code,
reference: 'delivery_' + order.id + '_' + Date.now(),
}),
});
var txData = await txResponse.json();
return txData.data.authorization_url;
}
This adds one extra API call per checkout (creating the split group). For most platforms, that latency is acceptable since it happens before redirecting the customer to the payment page, not during the payment itself. If the extra call concerns you, consider pre-creating split groups when orders are placed (rather than at checkout) or caching split groups for common participant combinations.
Paystack lets you modify existing split groups by adding or removing subaccounts. This is useful for long-running split configurations that evolve over time, like a partnership structure where a new partner joins or an existing one leaves.
// Add a subaccount to an existing split group
async function addToSplitGroup(splitId, subaccountCode, share) {
var response = await fetch(
'https://api.paystack.co/split/' + splitId + '/subaccount/add',
{
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
subaccount: subaccountCode,
share: share,
}),
}
);
return await response.json();
}
// Remove a subaccount from a split group
async function removeFromSplitGroup(splitId, subaccountCode) {
var response = await fetch(
'https://api.paystack.co/split/' + splitId + '/subaccount/remove',
{
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
subaccount: subaccountCode,
}),
}
);
return await response.json();
}
When you add a subaccount, the new share comes out of the residual (your main account's portion). When you remove one, their share goes back to the residual. Make sure the total shares after modification still make sense for your business model.
Changes to a split group affect all future transactions that use its split_code. Transactions that were already processed with the old configuration are not affected. If you need to preserve the old configuration for audit purposes, create a new split group instead of modifying the existing one.
Multi-split introduces complexity that single split does not have. These are the edge cases that cause problems in production.
Rounding on percentage splits. When a percentage split produces a fractional amount (65% of 9,999 kobo = 6,499.35 kobo), Paystack rounds to the nearest whole unit. Over many transactions, rounding can create small discrepancies between your expected totals and actual settlements. Track these and reconcile periodically rather than investigating each one.
Sum of shares exceeding 100%. If your subaccount percentages add up to more than 100%, Paystack will reject the split group creation. Always validate the total before sending the request.
Flat shares exceeding transaction amount. If you use flat type and the sum of flat shares is greater than the transaction amount, the split cannot be fulfilled. Validate this on your server before initializing the transaction.
Rider not yet assigned. In food delivery, the customer might pay before a rider is assigned. You cannot create the multi-split without the rider's subaccount. Options: use a single split to the restaurant at checkout, then transfer the rider's share later via Paystack Transfers when the rider is assigned. Or, collect the full amount and use transfers for both restaurant and rider. The choice depends on your operational flow.
Refunds on multi-split transactions. Refunding a multi-split transaction refunds from your main Paystack balance. The subaccounts are not automatically debited for their shares. If the restaurant and rider have already been settled, you absorb the full refund amount and recover from them separately.
Split group limits. Paystack does not publicly document a hard limit on subaccounts per split group. In practice, multi-splits with 2 to 5 subaccounts are common. If you need to split among many more parties, test thoroughly and consider whether a different architecture (collect then transfer) might be more appropriate.
For the full context of how multi-split fits into marketplace payment architecture, see the Paystack split payments and marketplaces complete guide.
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course