Case Study

Payment Orchestration: Provider Routing with Recoverable Follow-Up Work

A shared payment interface with provider selection, status tracking and scheduled recovery work.

This payment orchestration case study covers a checkout that works with several providers through one integration layer. Connectors translate the authorisation, capture and cancellation requests. Routing rules then pick a provider by conditions, volume splits or fallback settings.

We kept the payment's later work in scope as well, since it reaches well past the checkout. Pending checks, refunds and merchant notifications all outlive it. Each one needed a home of its own.

The challenge

A provider may confirm a payment before the merchant app has heard about it. Treating both as one status hides a real difference. The payment may still need a check, or the merchant update may just need another try. The integration therefore kept two records apart. One holds the financial operation, and the other holds the work still waiting to go out.

What we built

Confirmation validates the request and loads the payment and customer context. It then works out which authentication the payment requires and selects the connector to use. Optional fraud checks can run before the provider call.

A verified incoming notification updates the payment. A separate signed webhook then tells the merchant, with its own delivery tracking and retries. Refunds and disputes also pass through the same integration layer that handles the original payment. Saved payment details can sit in a separate vault.

Technical approach

Typed Rust interfaces define the operations each connector supports. Conversion functions map the shared requests and responses to a provider's format, error codes included. Type checking catches a mismatched shape, though provider behaviour still needs checking at runtime. Adding a connector means a new application build.

Make unfinished work durable

PostgreSQL holds the jobs for payment checks, refund-status requests and outbound webhooks. Schedulers pick the due records and send the work through Redis to consumers. A retry raises the attempt count and records the next run time, while completion stores the finished status and business result. Stopping a process leaves its outstanding database job ready for later.

Tenant settings keep PostgreSQL schemas and Redis prefixes apart. Merchant-sensitive fields use merchant-specific keys, and the key store sits behind a master key or an external KMS.

Payment state can live in PostgreSQL directly. An optional mode writes through Redis first and drains the changes to PostgreSQL afterward. That mode allows a brief disagreement between the two stores, and it leans on Redis durability and the drainer's progress.

Project success

Checkout can switch provider routes while keeping the shared payment model. More useful for follow-up work, an operator can tell a pending payment apart from an undelivered merchant webhook. The job record shows the attempts and the next scheduled run. An accepted provider update no longer vanishes inside a failed notification.

Technology stack

Application

Rust, Actix-web

Transaction and job storage

PostgreSQL

Scheduling transport

Redis

Integration contracts

Typed Rust connector interfaces

Key management

Merchant key store, external KMS options

Recognize the pattern?

If one of these looks like your situation, the discovery call is where we find out how close.