Testing Split Payment Logic Safely
Test split payment logic at three levels: (1) unit test the commission calculation function in isolation, (2) mock the Paystack API in integration tests to verify the correct percentage_charge or transaction_charge is passed, and (3) end-to-end test using Paystack test mode — create real test subaccounts and verify the settlement breakdown in the Paystack test dashboard.
Unit Testing Commission Calculation
// commission.ts
export function calculateSplit(totalAmount: number, commissionRate: number) {
const vendorShare = Math.round(totalAmount * (1 - commissionRate) * 100); // kobo
const platformShare = Math.round(totalAmount * commissionRate * 100); // kobo
return { vendorShare, platformShare };
}
// commission.test.ts
describe('calculateSplit', () => {
it('splits 10% commission correctly', () => {
var result = calculateSplit(10000, 0.10);
expect(result.vendorShare).toBe(900000); // NGN 9,000 in kobo
expect(result.platformShare).toBe(100000); // NGN 1,000 in kobo
});
it('handles non-round amounts without floating point errors', () => {
var result = calculateSplit(999.99, 0.10);
// Should not produce fractional kobo
expect(result.vendorShare % 1).toBe(0);
expect(result.platformShare % 1).toBe(0);
});
it('returns full amount to vendor at 0% commission', () => {
var result = calculateSplit(5000, 0);
expect(result.vendorShare).toBe(500000);
expect(result.platformShare).toBe(0);
});
});
Integration Test with Mocked Paystack API
// Mock Paystack API in Jest
jest.mock('../lib/paystack', () => ({
initializeTransaction: jest.fn().mockResolvedValue({
status: true,
data: {
authorization_url: 'https://checkout.paystack.com/test',
access_code: 'test_access',
reference: 'test_ref_001',
}
}),
}));
describe('Checkout with vendor split', () => {
it('passes correct percentage_charge to Paystack', async () => {
var paystack = require('../lib/paystack');
var vendor = { subaccount_code: 'SUB_test123', commission_rate: 0.15 };
await checkoutService.initializeSplitPayment({
email: 'buyer@test.com',
amount: 10000,
vendor,
});
expect(paystack.initializeTransaction).toHaveBeenCalledWith(
expect.objectContaining({
subaccount: 'SUB_test123',
percentage_charge: 85, // 1 - 0.15 = 0.85 = 85%
bearer: 'account',
})
);
});
});
Learn More
This guide is part of the Paystack split payments guide.
Key Takeaways
- ✓Unit test commission calculation functions independently before testing the API integration.
- ✓Mock the Paystack API in unit and integration tests — do not hit the real API in CI pipelines.
- ✓Create dedicated test subaccounts in Paystack test mode for end-to-end testing.
- ✓Test the full split: initialize transaction, simulate webhook, verify subaccount share is correct in your DB.
- ✓Test edge cases: 0% commission, 100% to subaccount, rounding on non-round amounts (NGN 999.99).
- ✓Verify bearer configuration in tests — a wrong bearer setting shifts who absorbs Paystack fees.
Frequently Asked Questions
- Can I create test subaccounts with fake bank details in Paystack test mode?
- Yes. In test mode, Paystack does not validate bank account numbers. You can use any numeric string as an account number. Create test subaccounts with dummy bank codes and account numbers. They will work for testing the split API without needing real bank details.
- How do I verify the actual settlement breakdown in a test transaction?
- After completing a test transaction, call GET /transaction/:id to see the transaction details. The subaccount field in the response shows how much was allocated to the subaccount. Compare this against your expected split calculation.
- Should I test percentage splits or flat splits?
- Test both — they use different Paystack parameters (percentage_charge vs transaction_charge). Percentage splits can have rounding issues at certain amounts. Flat splits can fail if the transaction_charge exceeds the transaction amount. Have dedicated tests for each model.
- How do I test the webhook handler for a split payment?
- Send a mock charge.success event to your webhook endpoint in tests. Include the metadata you set during transaction initialization (vendor_id, order_id). Verify that your handler correctly updates the order status and records the vendor's earnings. Use the Paystack webhook simulator from the test dashboard for end-to-end testing.
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