Collect the full project amount from the client. Hold the freelancer's share using a subaccount with manual settlement enabled. When the client approves the work, call POST /settlement/initiate (or trigger transfer from your balance) to release funds to the freelancer. Your platform commission is the percentage not allocated to the subaccount. Handle disputes by withholding settlement until resolved.
// Client pays for a project
app.post('/api/projects/:id/pay', async (req, res) => {
var project = await db.projects.findById(req.params.id);
var freelancer = await db.freelancers.findById(project.freelancer_id);
var commissionRate = 0.10; // 10% platform commission
var freelancerShare = Math.round((1 - commissionRate) * 100); // 90%
var reference = 'proj_' + project.id + '_' + Date.now();
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: req.user.email,
amount: project.budget * 100,
subaccount: freelancer.subaccount_code,
percentage_charge: freelancerShare,
bearer: 'account',
reference,
metadata: { project_id: project.id, action: 'escrow_hold' },
}),
});
var data = await response.json();
res.json({ authorization_url: data.data.authorization_url });
});
Important: For true delayed settlement, create the freelancer subaccount with settle_interval: "manual". This holds the funds until you explicitly trigger settlement. Contact Paystack support to enable manual settlement on your account.
// Client approves the delivered work
app.post('/api/projects/:id/approve', async (req, res) => {
var project = await db.projects.findById(req.params.id);
await db.projects.update({ status: 'approved', approved_at: new Date() }, { where: { id: project.id } });
// If using manual settlement: initiate settlement for the subaccount
// Or: transfer from your balance to the freelancer recipient
await fetch('https://api.paystack.co/transfer', {
method: 'POST',
headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({
source: 'balance',
recipient: project.freelancer_recipient_code,
amount: Math.round(project.freelancer_earnings * 100),
reason: 'Project payment: ' + project.title,
reference: 'release_' + project.id,
}),
});
res.json({ released: true });
});
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