One API in front, any gateway behind: a gateway manager with retries and circuit breakers, and a state machine that blocks impossible moves.
The challenge
Payments are where small bugs get expensive. A network hiccup makes your app retry and the customer is charged twice. A fake webhook marks an unpaid order as paid. Adding or switching a provider means rewriting checkout code.
The solution
I built a provider-agnostic payment API. Your app integrates once; behind it, adapters talk to Stripe or a sandbox provider, and a new gateway is a new adapter file.
How it works
Idempotency keys: a retried create or refund returns the original result instead of charging again, and the same key with a different body is refused.
State machine: seven payment states with only allowed moves, so a payment can't be refunded before capture or past its amount.
Resilience: retries with jittered backoff and a circuit breaker per provider, which tells infrastructure failures apart from real card declines.
Money as integers: amounts in minor units (4999 = 49.99), never floating point.
Real attacks against the running API: forged webhooks, a missing login and the wrong role were all refused with precise errors.
Security, tested against the running API
In the real run shown, every attack attempt was refused:
Forged webhook, old timestamp: replay_detected (300-second window).
Behind that: provider secrets under AES-256-GCM envelope encryption with key rotation, Argon2id passwords, API keys stored only as SHA-256 hashes, SSRF guards, rate limiting, strict headers and secrets redacted from logs.
19 documented endpoints, 105 passing tests at 95% coverage, and a Docker setup with PostgreSQL, Redis and NGINX.
API and quality
19 REST endpoints with interactive docs: payments, captures, cancels, refunds, webhooks and API keys.
Log in with a JWT or an API key; roles separate admins, merchants and viewers.
105 automated tests at 95% coverage, with ruff, black and mypy in CI.
Docker Compose with PostgreSQL, Redis and NGINX, Alembic migrations, Prometheus metrics and JSON logs.