{
  "id": 13353720,
  "title": "Building a Provider-Agnostic Payout Architecture",
  "url": "https://urgent.news/2026/10/10/building-a-provider-agnostic-payout-architecture",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-10-10T07:23:18.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/parksontano/building-a-provider-agnostic-payout-architecture-5hd2"
  },
  "original_language": "en",
  "account": "In order to maintain a stable customer experience amidst shifting payment systems, banks, fees, and status definitions, a provider-agnostic payout architecture was developed. Integrating with a payment provider often leads to a tight coupling of the product to the provider. This results in provider-specific field names in database models, universal bank codes, and direct technical errors reaching customers. To avoid this, a provider-agnostic architecture is implemented, recognizing that providers are not all the same. It provides the application with a stable domain contract, allowing each adapter to maintain the relevant differences.\n\nThe product should separate its intent from the provider's commands. The product aims to perform operations like listing supported banks, resolving an account, initiating a bank payout, retrieving a transaction status, and creating deposit instructions. The provider adapter translates these operations into the external API's authentication, paths, payloads, signatures, and response structure. A class called PayoutProvider is defined with methods for listing banks, resolving an account, sending, and getting status.\n\nApplication services should depend on this contract rather than importing a provider client directly. Collection and disbursement should be configured separately, allowing for independent changes without forcing a release or altering the customer flow. The chosen provider should be recorded on every transaction, but the current configuration should not rewrite history. Bank directories should be treated as provider-owned, as names and codes differ across providers. Each account should retain its provider context throughout the process.\n\nWhen switching providers, accounts created with another provider should maintain their identifiers and not be silently submitted using incompatible ones. The product can filter saved recipients by provider or ask the customer to validate a new provider-specific destination, ensuring safety over guessing. Bank lists are cached frequently, and the cache key must include the provider to prevent serving the previous provider's list. The cache key follows the format: cache_key = f\"bank-directory: {provider}:v2\". A controlled force-refresh option is also available.\n\nProviders have different status vocabularies, so the adapter should expose a small internal state while preserving the raw response for staff diagnostics. Errors should also be preserved for technical diagnostics, while customer-facing responses should use stable product language. Technical messages, provider account restrictions, and internal liquidity information must remain inaccessible to customers. Provider-specific fee behavior should be modeled explicitly, distinguishing between the amount the customer sends, the platform fee charged to the customer, the amount requested from the provider, the provider fee, the expected recipient amount, and the actual recipient amount when known. This allows the transaction to construct the correct payout amount without altering how the application quotes the transfer. The principle of provider independence stems from isolating differences without erasing them. The domain layer defines the stable customer and transaction contract, while adapters handle provider authentication and semantics. Transactions retain the rail used, and provider-owned reference data remains scoped to that rail. This approach turns provider switching into an operational decision rather than a complete rewrite of the payment product.",
  "summary": "Keeping the customer experience stable while payment rails, bank directories, fees, and status semantics change underneath it. The first integration with a payment provider often becomes tightly coupled to the product. Provider-specific field names enter database models, bank codes are assumed to be universal, and technical errors flow directly to customers. The second provider exposes every one…",
  "key_points": [],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}