For each existing vendor, create a Paystack subaccount with POST /subaccount using their stored bank details. Store the returned subaccount_code. Update your checkout code to include the subaccount in transaction initialization. Run both systems in parallel for one payout cycle — verify Paystack settlement matches your manual calculation — then decommission manual payouts.
// Migration script: create subaccounts for all vendors
async function migrateVendorsToSubaccounts() {
var vendors = await db.vendors.findAll({ where: { subaccount_code: null } });
for (var vendor of vendors) {
try {
// Validate bank account first
var resolveRes = await fetch(
'https://api.paystack.co/bank/resolve?account_number=' + vendor.account_number + '&bank_code=' + vendor.bank_code,
{ headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY } }
);
var resolved = await resolveRes.json();
if (!resolved.status) {
console.error('Invalid account for vendor', vendor.id, resolved.message);
continue;
}
// Create subaccount
var subRes = await fetch('https://api.paystack.co/subaccount', {
method: 'POST',
headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({
business_name: vendor.business_name,
settlement_bank: vendor.bank_code,
account_number: vendor.account_number,
percentage_charge: 90, // Vendor gets 90%, platform keeps 10%
}),
});
var subData = await subRes.json();
await db.vendors.update(
{ subaccount_code: subData.data.subaccount_code },
{ where: { id: vendor.id } }
);
console.log('Migrated vendor', vendor.id, '→', subData.data.subaccount_code);
} catch (e) {
console.error('Failed for vendor', vendor.id, e.message);
}
await new Promise(r => setTimeout(r, 200)); // Rate limit: 5 req/sec
}
}
Before decommissioning manual payouts, run one full cycle in parallel:
// Validation query: compare expected vs actual payouts
var validationReport = await db.query(`
SELECT
v.id as vendor_id,
v.business_name,
SUM(o.vendor_share) as expected_payout,
v.paystack_settled_this_cycle as actual_payout,
SUM(o.vendor_share) - COALESCE(v.paystack_settled_this_cycle, 0) as discrepancy
FROM orders o
JOIN vendors v ON v.id = o.vendor_id
WHERE o.paid_at BETWEEN '2026-07-01' AND '2026-07-31'
GROUP BY v.id, v.business_name, v.paystack_settled_this_cycle
HAVING ABS(SUM(o.vendor_share) - COALESCE(v.paystack_settled_this_cycle, 0)) > 1
`);
console.log('Discrepancies:', validationReport);
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
This guide is part of the Paystack split payments 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