Payment Gateway Integration: Stripe, Refunds & Webhooks by Mojtaba KarimiabdarPayment Gateway Integration: Stripe, Refunds & Webhooks by Mojtaba Karimiabdar

Payment Gateway Integration: Stripe, Refunds & Webhooks

Mojtaba Karimiabdar

Mojtaba Karimiabdar

One API in front, any gateway behind: a gateway manager with retries and circuit breakers, and a state machine that blocks impossible moves.
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.
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).
Forged webhook, fake signature: signature_invalid (HMAC-SHA256).
Payment request without a login: 401.
Merchant calling an admin endpoint: 403.
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.
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.
Tech stack: Python, FastAPI, async SQLAlchemy, PostgreSQL, Redis, Stripe and Docker.
Need payments, refunds or webhooks wired into your app safely? Send me a message with your provider and what should happen when a customer pays.
Like this project

Posted Oct 14, 2026

One API in front of Stripe and other gateways: payments and refunds that are safe to retry, verified webhooks and encrypted keys. 105 tests.