Paystack splits fail to apply for four main reasons: the split_code was not included in the transaction initialization call, the subaccount in the split is inactive or has invalid bank details, the split was created but not set as the default split for your integration, or the transaction was initialized before the split was created. Verify the split exists via the API, confirm the subaccount is active, and include the split_code in every transaction initialization request.
Paystack transaction splits let you automatically divide payment proceeds between multiple parties. A marketplace that takes a commission, a platform that pays vendors, a service that shares revenue with partners. Splits handle the accounting at the payment layer so you do not have to calculate and transfer funds manually.
The flow works like this:
split_code in the request. Paystack applies the split when the payment succeeds.When a split "does not apply," it means the full amount settled to your main account as if the split did not exist. The customer paid the correct amount, but the money was not divided. This is a configuration problem, not a payment problem.
This is the most common reason. You created the split in the dashboard, but you did not include the split_code when initializing the transaction. Paystack does not know to apply it.
Unless you have set a default split (more on that below), you must explicitly pass the split_code in every transaction initialization request.
const axios = require('axios');
// WRONG: No split_code - entire amount goes to main account
async function initializeWithoutSplit() {
const response = await axios.post(
'https://api.paystack.co/transaction/initialize',
{
email: 'customer@example.com',
amount: 1000000, // 10,000 NGN in kobo
},
{
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
'Content-Type': 'application/json',
},
}
);
return response.data;
}
// CORRECT: split_code included - payment will be split
async function initializeWithSplit() {
const response = await axios.post(
'https://api.paystack.co/transaction/initialize',
{
email: 'customer@example.com',
amount: 1000000, // 10,000 NGN in kobo
split_code: 'SPL_xxxxxxxxxxxxxxx', // Your split code
},
{
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
'Content-Type': 'application/json',
},
}
);
return response.data;
}
If you are using the Popup or Inline JS to initialize (not the server-side API), you cannot pass split_code from the frontend. You must initialize the transaction on your server, get the access_code, and pass that to the popup. The split_code goes in the server-side initialization call, not in the frontend JavaScript.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
Every subaccount in a split must be active and have valid, verified bank details. If a subaccount is inactive (because bank verification failed, or it was manually deactivated), the split will not apply.
Check the status of your subaccounts:
async function checkSubaccountStatus(subaccountCode) {
try {
const response = await axios.get(
`https://api.paystack.co/subaccount/${subaccountCode}`,
{
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
},
}
);
const sub = response.data.data;
console.log('Subaccount:', sub.business_name);
console.log('Active:', sub.is_verified); // Must be true
console.log('Bank:', sub.settlement_bank);
console.log('Account number:', sub.account_number);
console.log('Percentage charge:', sub.percentage_charge);
if (!sub.is_verified) {
console.warn('This subaccount is NOT verified. Splits using it will fail.');
}
return sub;
} catch (error) {
if (error.response?.status === 404) {
console.error('Subaccount not found. Check the subaccount code.');
}
throw error;
}
}
Common bank detail problems:
Even if you pass the split_code and the subaccounts are active, the split can fail if the configuration itself is invalid.
Percentage splits must add up correctly. If you create a percentage split where the shares exceed 100%, or where there is an unaccounted remainder, the split behavior may be unpredictable.
Flat splits must not exceed the transaction amount. If you have a flat split that takes 5,000 NGN from each transaction, but a customer pays only 3,000 NGN, the split cannot be applied because there is not enough money.
// Verify your split configuration
async function verifySplitConfig(splitCode) {
try {
const response = await axios.get(
`https://api.paystack.co/split/${splitCode}`,
{
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
},
}
);
const split = response.data.data;
console.log('Split name:', split.name);
console.log('Split type:', split.type); // "percentage" or "flat"
console.log('Currency:', split.currency);
console.log('Active:', split.active);
console.log('Bearer type:', split.bearer_type); // "all", "account", or "subaccount"
console.log('Bearer subaccount:', split.bearer_subaccount);
console.log('\nSubaccounts in this split:');
for (const sub of split.subaccounts) {
console.log(` - ${sub.subaccount.business_name}: ${sub.share} (${split.type})`);
}
// Check for issues
if (split.type === 'percentage') {
const totalShare = split.subaccounts.reduce((sum, s) => sum + s.share, 0);
console.log(`\nTotal percentage allocated: ${totalShare}%`);
if (totalShare > 100) {
console.warn('Total exceeds 100%. This split is misconfigured.');
}
}
return split;
} catch (error) {
console.error('Failed to fetch split:', error.response?.data || error.message);
throw error;
}
}
Also check the bearer_type. This determines who pays the Paystack transaction fee:
"all": The fee is split proportionally among all parties."account": Your main account absorbs the entire fee."subaccount": A specific subaccount absorbs the fee.If every transaction on your integration should use the same split, you can set a default split in the Paystack dashboard. Go to Settings > Preferences > Transaction Split, and select your split configuration. Once set, every transaction on that integration is automatically split without needing split_code in the API call.
This is convenient but comes with a catch: if you need different splits for different transactions (e.g., different vendor shares for different products), a default split will not work. You need per-transaction split_code values.
When to use a default split:
When to use per-transaction split_code:
If you have a default split set but also pass a split_code in the transaction, the per-transaction split_code takes precedence. This lets you override the default for specific transactions.
If your split is not working, sometimes the fastest fix is to create a new one from scratch, verifying each step.
const axios = require('axios');
const PAYSTACK_SECRET = process.env.PAYSTACK_SECRET_KEY;
const headers = {
Authorization: `Bearer ${PAYSTACK_SECRET}`,
'Content-Type': 'application/json',
};
async function setupSplitFromScratch() {
// Step 1: Create subaccount (or use existing one)
const subaccountResponse = await axios.post(
'https://api.paystack.co/subaccount',
{
business_name: 'Vendor Store',
bank_code: '057', // Zenith Bank
account_number: '2190209948',
percentage_charge: 80, // Vendor gets 80% (used if no split overrides)
},
{ headers }
);
const subaccountCode = subaccountResponse.data.data.subaccount_code;
console.log('Subaccount created:', subaccountCode);
// Step 2: Create the split
const splitResponse = await axios.post(
'https://api.paystack.co/split',
{
name: 'Platform 20/80 Split',
type: 'percentage',
currency: 'NGN',
subaccounts: [
{
subaccount: subaccountCode,
share: 80, // 80% to vendor
},
],
bearer_type: 'account', // Main account pays the fee
bearer_subaccount: '',
},
{ headers }
);
const splitCode = splitResponse.data.data.split_code;
console.log('Split created:', splitCode);
// Step 3: Test with a transaction
const txnResponse = await axios.post(
'https://api.paystack.co/transaction/initialize',
{
email: 'buyer@example.com',
amount: 1000000, // 10,000 NGN
split_code: splitCode,
},
{ headers }
);
console.log('Transaction initialized:', txnResponse.data.data.reference);
console.log('Checkout URL:', txnResponse.data.data.authorization_url);
return { subaccountCode, splitCode, reference: txnResponse.data.data.reference };
}
setupSplitFromScratch().catch(console.error);
After the test transaction completes (use test card 4084 0840 8408 4081 in sandbox), verify the split was applied by checking the transaction details.
After a transaction completes, verify the split through the API. Do not assume it worked just because the payment succeeded.
async function verifySplitApplied(reference) {
const response = await axios.get(
`https://api.paystack.co/transaction/verify/${reference}`,
{
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
},
}
);
const data = response.data.data;
console.log('Transaction status:', data.status);
console.log('Total amount:', data.amount / 100, 'NGN');
console.log('Fees:', data.fees / 100, 'NGN');
if (data.split) {
console.log('\nSplit applied: YES');
console.log('Split code:', data.split.split_code);
// Check each share
if (data.subaccount) {
console.log('Subaccount:', data.subaccount.business_name);
console.log('Subaccount share:', data.subaccount.share);
}
} else {
console.warn('\nSplit applied: NO');
console.warn('The full amount went to the main account.');
console.warn('Check that split_code was included in the initialization request.');
}
}
verifySplitApplied('your_transaction_reference').catch(console.error);
You can also check in the Paystack dashboard. Go to Transactions, find the transaction, and click on it. The transaction details page shows the split breakdown including each party's share and the fee allocation.
Run through this list before contacting support.
split_code is present and not undefined or null.GET /split/{split_code} and confirm it returns a valid response. A 404 means the code is wrong.GET /subaccount/{code} and verify is_verified is true.After fixing the split configuration, run this verification flow.
GET /split/{split_code} and confirm it is active with the correct subaccounts and shares.split_code included.GET /transaction/verify/{reference} and confirm the split field is present in the response.charge.success event payload includes split information.Once confirmed in sandbox, deploy the same configuration to live mode. Remember that live mode subaccounts and splits are separate from test mode ones. You need to recreate them (or create them via API) in live mode with real bank details.
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