- PHP · Symfony / Laravel
- PostgreSQL
- Redis
- Docker / Podman
- GitLab CI/CD
Challenge
Taking payments internationally hinges on a single provider: an outage, a daily cap or a refusal in one region stops collection outright. Adding another PSP usually means editing payment logic — a change in the one place where the cost of a mistake equals the value of the transactions that did not go through.
There is also nothing to compare channels on: each provider's statistics stay in its own dashboard, in its own format.
Solution
Providers sit behind one routing layer: a payment goes to a channel by rule — region, method, currency, provider availability — and on a failure or a cap it is retried through the fallback. Provider responses are normalized to a single status model, so the product talks to one payment interface instead of five.
Adding a PSP means writing its adapter and a routing rule, not changing payment logic. Channel statistics are collected in one place: approval rate, acquiring cost and confirmation time, per provider and side by side.
Outcome
Collection no longer depends on the state of one provider: a failing channel moves traffic to the fallback instead of stopping sales. A new PSP is added by configuration, with payment logic untouched. Choosing a channel became a decision about approval rate and cost rather than about whichever contract was signed first.