Migrating from Manual Payouts to Paystack Splits
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.
Create Subaccounts for Existing Vendors
// 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
}
}
Parallel Running and Validation
Before decommissioning manual payouts, run one full cycle in parallel:
- Enable Paystack splits for all new transactions
- Continue running manual payouts for that cycle (paying for pre-migration orders)
- After cycle ends, compare Paystack settlement report against your manual payout ledger
- Verify each vendor received the correct amount via both channels
- Decommission manual payout process once confirmed
// 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);
Learn More
This guide is part of the Paystack split payments guide.
Key Takeaways
- ✓Migration is not a switch-flip — run parallel for one cycle to validate amounts before decommissioning manual transfers.
- ✓Create subaccounts for all existing vendors using stored bank details. Validate each with the Paystack resolve account endpoint.
- ✓Update checkout to include subaccount: vendorSubaccountCode on every transaction.
- ✓Vendor communication matters: tell them the payout cycle is changing from manual (Friday) to automated (T+1 or T+2 via Paystack).
- ✓Paystack's settlement cycle is typically T+1 for cards. This may be faster or slower than your current manual schedule.
- ✓Keep manual payout records for the transition period in case of disputes about missing payments.
Frequently Asked Questions
- Will Paystack splits change my vendors' settlement timing?
- Likely yes. Paystack settles subaccounts on their normal settlement cycle (typically T+1 for cards). If you were previously paying vendors every Friday manually, they will now receive funds automatically on T+1 after each transaction — which may actually be faster.
- Do I need vendors' consent to create subaccounts for them?
- Yes, in principle. Paystack subaccounts route settlement to the vendor's bank account. You need their consent to associate their bank details with your Paystack account. Include subaccount creation in your vendor onboarding terms of service.
- What if a vendor changed their bank details between when you stored them and the migration?
- The resolve account check during migration will flag invalid accounts. For each failure, send the vendor a link to update their bank details in your platform before creating their subaccount. Do not create subaccounts with unverified bank details.
- Can I migrate mid-month without disrupting in-flight orders?
- Yes. Orders placed before migration still show the old payment flow in your database. Pay out those orders manually as usual. Enable Paystack splits only for new orders placed after the migration date. This gives a clean cutover without mixing flows.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon