Simulate Paystack payment failures using: (1) test decline cards (4084080000000409 for Do Not Honor, 4084080000000005 for Insufficient Funds) — these fire charge.failed webhooks; (2) mock the Paystack initialize endpoint returning 5xx — test your server error handling; (3) use an expired access_code — test session timeout handling; (4) send a charge.failed webhook payload manually with a valid HMAC — test your failed payment webhook handler.
| Failure Type | How to Simulate | Expected Behavior |
|---|---|---|
| Card declined (Do Not Honor) | Use test card 4084080000000409 | charge.failed webhook fires; order stays pending; user sees decline message |
| Insufficient funds | Use test card 4084080000000005 | charge.failed webhook; user told to check balance or try different card |
| Paystack API 500 on initialize | Mock fetch to return 500 response | Checkout returns 503 with friendly message; no order created |
| Paystack API timeout | Mock fetch to hang for 30+ seconds | Timeout after configured limit; retry logic fires; user sees "try again" |
| Expired access_code / session | Use an already-processed reference | Paystack returns error; user is redirected to restart checkout |
| Duplicate reference | Send same reference to initialize twice | Paystack returns error on second call; your code handles it without crashing |
// test: charge.failed webhook handler
const crypto = require('crypto');
describe('charge.failed webhook handling', () => {
it('marks order as payment_failed without fulfilling it', async () => {
var orderId = 77;
await db.orders.create({ id: orderId, status: 'pending', amount: 5000 });
var payload = {
event: 'charge.failed',
data: {
reference: 'ref_fail_001',
amount: 500000,
status: 'failed',
gateway_response: 'Insufficient Funds',
metadata: { order_id: orderId },
customer: { email: 'test@example.com' },
},
};
var body = JSON.stringify(payload);
var sig = crypto.createHmac('sha512', process.env.PAYSTACK_SECRET_KEY).update(body).digest('hex');
await request(app)
.post('/webhook/paystack')
.set('x-paystack-signature', sig)
.send(body)
.expect(200);
var order = await db.orders.findByPk(orderId);
expect(order.status).toBe('payment_failed');
expect(order.failure_reason).toBe('Insufficient Funds');
// Critically: the order should NOT be fulfilled
expect(order.fulfilled_at).toBeNull();
});
});
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 testing 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