This article breaks down payment orchestration into its core engineering components: a normalized payment model, a routing layer, and a reconciliation spine. It discusses when building such a system is necessary, the common pitfalls of a DIY approach, and the critical architectural decisions involved in abstracting multiple payment providers.
Read original on Dev.to #systemdesignAs a system integrates with multiple payment service providers (PSPs) or payment methods, the codebase often becomes riddled with conditional logic (e.g., `if provider == "X"`) to handle provider-specific nuances. This complexity extends to refunds, webhooks, reconciliation, and fraud checks, making new integrations cumbersome and error-prone. Payment orchestration addresses this by introducing an abstraction layer.
Recognizing the Problem
You likely need payment orchestration when your codebase frequently branches on payment provider logic, or when financial reporting struggles to reconcile transactions across different providers due to inconsistent data models.
A crucial architectural decision is designing a simple, money-shaped interface that all provider adapters must implement. This ensures a consistent interaction model regardless of the underlying payment gateway. Key design rules include using minor units (integers/bigints) for all monetary values to avoid floating-point issues, and maintaining an internal, unified decline-code taxonomy that provider-specific codes are mapped to.
type Money = { amountMinor: bigint; currency: string };
interface PaymentProvider {
readonly id: string;
authorize(req: AuthorizeRequest): Promise<AuthorizeResult>;
capture(authId: string, amount: Money, idemKey: string): Promise<CaptureResult>;
refund(captureId: string, amount: Money, idemKey: string): Promise<RefundResult>;
parseWebhook(raw: HttpRequest): NormalizedEvent | null;
}Build vs. Buy Considerations
While integrating a second adapter might seem simple, the true engineering complexity lies in components like the reconciliation spine and webhook fan-in. These often require significant effort to build robustly, making the build-vs-buy decision for a full orchestration platform critical for long-term scalability and operational stability.