
Comprehensive Paystack Payment Architecture
Master the unified integration guide covering standard checkouts, split payouts, and recurring billing.
Read Comprehensive Guide →
Building a modern, multi-vendor marketplace or service-matching ecosystem requires solving one of financial technology's trickiest challenges: managing multi-party payouts with legal precision and accounting accuracy. When a customer purchases a product or hires a designer on a digital platform, the platform operator must automatically split funds between seller earnings and marketplace commissions. Furthermore, in high-value digital labor environments, immediate payout creates significant dispute risk. True financial integrity requires an escrow-style hold mechanism before merchant settlement.
In this technical breakdown, we present the end-to-end architecture used by the Fort Engineering Team to implement Paystack Split Payments alongside custom escrow state management. By combining Paystack's Subaccounts and Multi-Split APIs with webhooks-driven application logic, platforms can seamlessly process customer cards, direct vendor payouts, hold operational funds safely, and handle dispute resolutions automatically.
To understand split payment integration, developers must distinguish between simple single-vendor checkouts and multi-party payment routing. Paystack facilitates automated distribution using three core primitives:
When an order is created, the application server generates a dynamic payment payload. Instead of routing all funds into the primary platform's main balance, the Paystack engine parses the assigned subaccount or split code, calculating splits on the fly at the precise instant the client's card or bank account is charged.
While Paystack handles payment collection and split calculation instantly, true escrow requires conditional payout logic. A native Paystack split transaction instantly sends funds to the merchant's subaccount according to standard settlement schedules (T+1 business days in Nigeria). However, for services requiring milestone approval—such as custom graphic design, software development, or physical freight delivery—instant payout can leave buyers vulnerable if work is incomplete or substandard.
Our engineering model implements an explicit state machine for every transaction processed across the network:
| State Name | Trigger Event | System Action | Financial Status |
|---|---|---|---|
INITIATED |
Client clicks "Pay Now" | Generates Paystack reference & locks order details | Unpaid |
ESCROW_HELD |
charge.success webhook received |
Validates signature; unlocks project workspace | Funds captured; payout locked |
MILESTONE_SUBMITTED |
Vendor uploads deliverables | Notifies buyer; starts 72-hour review timer | Escrow state active |
RELEASED |
Buyer approves or timer expires | Triggers automated payout transfer to subaccount | Funds disbursed |
DISPUTED |
Buyer opens support ticket | Freezes payout transfer; routes to human audit | Funds locked in dispute hold |
Below is a production-ready JavaScript implementation utilizing the Paystack Inline JS SDK. The payload configures dynamic platform fee absorption alongside targeted vendor subaccount attribution:
// Production Paystack Checkout Modal Initialization with Dynamic Split
function initializeEscrowCheckout(orderData) {
const handler = PaystackPop.setup({
key: 'pk_live_XXXXXXXXXXXXXXXXXXXXXXXX',
email: orderData.clientEmail,
amount: orderData.totalAmountInKobo, // e.g., 500000 NGN = 50000000 Kobo
currency: 'NGN',
subaccount: orderData.vendorSubaccountCode, // e.g., 'ACCT_xxxxxxxxx'
transaction_charge: orderData.platformCommissionInKobo,
bearer: 'subaccount', // Vendor absorbs the Paystack gateway fee
metadata: {
projectId: orderData.projectId,
orderId: orderData.orderId,
escrowHoldDays: 7,
custom_fields: [
{ display_name: "Project Reference", variable_name: "project_ref", value: orderData.projectId },
{ display_name: "Vendor Code", variable_name: "vendor_code", value: orderData.vendorSubaccountCode }
]
},
callback: function(response) {
console.log('Payment completed successfully. Reference:', response.reference);
// Notify frontend state; server-side verification remains mandatory via webhook
updateUItoEscrowProcessing(response.reference);
},
onClose: function() {
alert('Payment window closed. Your escrow order remains pending.');
}
});
handler.openIframe();
}
Client-side callbacks can be manipulated or interrupted by network failures. Therefore, system state mutations must strictly rely on secure server-side webhook processing. Paystack signs all incoming webhooks using a HMAC SHA512 hash created with your Secret Key. Your endpoint must recalculate this signature and verify parity before updating database records.
const crypto = require('crypto');
const express = require('express');
const app = express();
app.post('/api/webhooks/paystack', express.json(), async (req, res) => {
// 1. Verify Request Signature
const hash = crypto.createHmac('sha512', process.env.PAYSTACK_SECRET_KEY)
.update(JSON.stringify(req.body))
.digest('hex');
if (hash !== req.headers['x-paystack-signature']) {
return res.status(401).send('Unauthorized webhook signature');
}
const event = req.body;
// 2. Process Successful Charge
if (event.event === 'charge.success') {
const { reference, amount, metadata, subaccount } = event.data;
// Perform atomic database transaction
await db.orders.update({
where: { reference: reference },
data: {
status: 'ESCROW_HELD',
paidAt: new Date(),
paystackFee: event.data.fees,
amountCollected: amount / 100
}
});
// Trigger notifications and unlock vendor work environment
await notifyVendorToStartWork(metadata.projectId);
}
// Always respond with 200 OK immediately to acknowledge receipt
res.sendStatus(200);
});
In scenarios involving marketplace transactions with multiple sellers, affiliate marketers, and platform royalties, simple single-subaccount splitting is insufficient. Paystack's Split Code API allows up to 100 subaccounts to share proceeds from a single checkout transaction.
Consider an order where a client hires a lead designer, an editor, and pays an affiliate referrer. The API payload defines explicit percentage splits or flat splits:
// Node.js example: Creating a Dynamic Multi-Split Rule Group
const axios = require('axios');
async function createMultiVendorSplitGroup() {
const response = await axios.post('https://api.paystack.co/split', {
name: "Order #9041 Dynamic Split",
type: "percentage",
currency: "NGN",
subaccounts: [
{ subaccount: "ACCT_designer123", share: 70 }, // 70% to Primary Vendor
{ subaccount: "ACCT_editor456", share: 15 }, // 15% to Secondary Specialist
{ subaccount: "ACCT_affiliate789", share: 5 } // 5% to Affiliate Referrer
],
bearer_type: "all-proportional" // Gateway fees divided relative to share
}, {
headers: { Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}` }
});
return response.data.data.split_code; // Pass this split_code to transaction setup
}
Operating financial software in West Africa requires strict adherence to financial regulations and anti-money laundering frameworks. When engineering split and escrow payment workflows on Paystack, keep the following rules in place:
/transaction/totals API endpoints.