Gateway Failover: Designing for Provider Outages
Gateway failover architecture: 1) Health check endpoint — poll the primary gateway's health URL every 30 seconds. 2) Circuit breaker — after N consecutive failures, trip the breaker and route to secondary. 3) Payment provider abstraction layer — your checkout code calls an interface, not Paystack directly. 4) Secondary gateway — same interface implementation for a different provider. 5) Automatic recovery — re-check primary after 5 minutes; restore when healthy. What failover cannot solve: active subscriptions (tokens are gateway-specific), stored card authorizations, in-flight transactions when the outage started.
Circuit Breaker Implementation
class GatewayCircuitBreaker {
constructor({ primaryGateway, fallbackGateway, failureThreshold = 5, resetTimeoutMs = 300000 }) {
this.primary = primaryGateway;
this.fallback = fallbackGateway;
this.failures = 0;
this.failureThreshold = failureThreshold;
this.state = 'closed'; // closed = primary active, open = using fallback
this.resetTimeout = resetTimeoutMs;
this.lastFailureTime = null;
// Health check every 30 seconds
setInterval(() => this.healthCheck(), 30000);
}
async healthCheck() {
if (this.state !== 'open') return;
// Check if timeout has elapsed since last failure
if (Date.now() - this.lastFailureTime > this.resetTimeout) {
try {
await this.primary.ping(); // implement a lightweight health check
this.reset(); // primary healthy — close the breaker
console.log('Circuit breaker reset: primary gateway restored');
} catch {
this.lastFailureTime = Date.now(); // extend timeout
}
}
}
async initializePayment(params) {
var gateway = this.state === 'open' ? this.fallback : this.primary;
try {
var result = await gateway.initialize(params);
if (this.state !== 'open') this.failures = 0; // reset on success
return result;
} catch (err) {
if (this.state !== 'open') {
this.failures++;
this.lastFailureTime = Date.now();
if (this.failures >= this.failureThreshold) {
this.state = 'open';
console.error('Circuit breaker opened: switching to fallback gateway');
}
}
throw err; // re-throw so caller handles
}
}
reset() {
this.state = 'closed';
this.failures = 0;
}
}
// Usage
var payments = new GatewayCircuitBreaker({
primaryGateway: new PaystackGateway(process.env.PAYSTACK_SECRET_KEY),
fallbackGateway: new FlutterwaveGateway(process.env.FLUTTERWAVE_SECRET_KEY),
failureThreshold: 5,
resetTimeoutMs: 5 * 60 * 1000, // 5 minutes
});
Learn More
See building a payment provider abstraction layer for the prerequisite design that makes failover possible.
Key Takeaways
- ✓Build the abstraction layer first — failover only works if your code is not directly coupled to one gateway.
- ✓Health check the primary gateway independently from transaction attempts — do not let failed transactions trip the breaker.
- ✓Circuit breaker: open on N failures, half-open after timeout, close when health check passes.
- ✓Failover only helps new transactions — it cannot recover in-flight transactions from before the outage.
- ✓Subscription tokens and stored card authorizations are not portable between gateways.
Frequently Asked Questions
- How often does Paystack have outages that would trigger failover?
- Paystack has historically been very reliable — major outages affecting checkout are rare, typically a few times per year and usually brief (under 1 hour). For most businesses, the engineering cost of building and maintaining a second gateway integration exceeds the revenue protected. Calculate: if Paystack is down for 2 hours/year, and you do NGN 10M/month, you lose ~NGN 28,000/year to outages. Is that worth a second integration that costs 2-3 weeks of engineering time to build and ongoing maintenance?
- What does "health check" look like for Paystack specifically?
- Paystack has a status page at status.paystack.com — you can programmatically check this. A lightweight approach: make a GET /bank call (list banks) — it is cheap, read-only, and exercises the Paystack API. If it returns 200, the API is up. If it fails or times out, the breaker records a failure. Do not use a transaction initialize for health checks — you would be creating real pending transactions.
- How do I handle webhooks from two gateways at the same time?
- Each gateway sends webhooks to its own configured URL. Use separate webhook endpoints: /webhooks/paystack and /webhooks/flutterwave. Each handler validates its own HMAC signature and processes events with its own field mapping. Store the source gateway in your order/transaction records so you know which gateway's verify endpoint to call when confirming payment.
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