Skip to content
All Case Studies
Backend
2024-056 min read

Integrating Multiple Payment Gateways

A unified payment abstraction for M-Pesa, Stripe, and PayPal

Problem

The platform needed to accept payments through M-Pesa (local), Stripe (international cards), and PayPal (global). Each gateway had different APIs, error handling, and webhook patterns. Hardcoding each integration created maintenance burden and made adding new gateways difficult.

Requirements

  • Unified API for all payment gateways
  • Adding a new gateway should be a configuration change, not a code change
  • Idempotent payment processing
  • Webhook handling for all gateways
  • Automatic retry for transient failures
  • Dead-letter queue for permanent failures

Architecture

Client
Web + Mobile
API Gateway
Nginx
Payment Service
FastAPI unified API
Gateway Adapters
M-Pesa, Stripe, PayPal
RabbitMQ
Async task queue
Workers
Payment processors
PostgreSQL
Transaction records
Dead Letter Queue
Failed transactions

Trade-offs

The adapter pattern adds a layer of abstraction, which means some gateway-specific features are not directly accessible. But the benefit of a unified interface far outweighs the cost of occasionally needing to bypass the adapter for a specific gateway feature.

Implementation

Built a payment service with the adapter pattern. Each gateway has an adapter that implements a common interface: initiatePayment, checkStatus, handleWebhook, refund. A factory selects the adapter based on configuration. Payment processing is async via RabbitMQ, with idempotency keys to prevent duplicate charges. Failed payments go to a dead-letter queue for manual review.

Scaling Considerations

Payment workers scale independently on RabbitMQ consumer count. The adapter pattern means new gateways require no changes to the core payment flow. PostgreSQL handles transaction records with appropriate indexing for status queries.

Testing Strategy

Unit tests for each adapter with mocked gateway responses. Integration tests with gateway sandbox environments. Idempotency tests to verify no duplicate charges. Webhook signature verification tests. Chaos tests for gateway timeout scenarios.

Performance Optimization

Async processing via RabbitMQ reduced payment API latency by 60%. Idempotency keys prevent duplicate processing. Redis caching for gateway configuration reduces database lookups. Connection pooling for gateway HTTP clients prevents connection exhaustion.

Lessons Learned

The adapter pattern is the right choice for multiple integrations with similar interfaces. Idempotency is not optional for payment systems. Dead-letter queues are essential for handling permanent failures without losing data. Always test with gateway sandboxes before production.

Tech Stack

PythonFastAPIRabbitMQPostgreSQLRedisDocker