How to Add a New PSP

Step-by-step checklist for integrating any new payment provider

Next
Reference
1.

Overview

Thanks to the Adapter Pattern, adding a new PSP is an isolated task. No changes to the Orchestrator, Cascade Manager, checkout, or State Machine. All new code lives in one adapter + registration in the factory.
If adding a new PSP requires changing code outside the adapter -- that is a signal the IPaymentProvider contract is broken. Stop and fix the contract before proceeding.
IPaymentProvider
Interface contract
initiateDeposit(req: UnifiedDepositRequest) → Promise<UnifiedResponse>

Maps your unified request to PSP-specific API call. Returns unified response regardless of PSP format.

initiateWithdrawal(req: UnifiedWithdrawalRequest) → Promise<UnifiedResponse>

Same pattern as deposit.

getTransactionStatus(id: string) → Promise<UnifiedStatus>

Polling fallback. Maps PSP-specific status string to UnifiedStatus enum.

handleWebhook(payload: unknown, headers: Headers) → Promise<UnifiedEvent>

Validates signature, parses payload, emits a unified event for the State Machine.

getSupportedMethods(geo: string, currency: string) → Method[]

Returns methods this PSP supports for the given geo+currency. Used by Orchestrator to filter candidates.

2.

Integration Checklist

Step 1 -- Research & Decision

PM
  • Obtain PSP API documentation (sandbox + production)
  • Identify supported payment methods, countries, and currencies
  • Clarify integration type for each method: H2H (direct API) or Invoice Link (redirect)
  • Confirm webhook delivery: what events are sent, payload format, signing algorithm
  • Clarify settlement currency and payout schedule
  • Confirm sandbox credentials are available for development
  • Map PSP-specific transaction statuses to UnifiedStatus enum (see State Machine spec)

The status mapping must be complete and agreed before any code is written. Gaps here cause reconciliation bugs later.

Step 2 -- Admin Panel Configuration

PM
  • Create a new PSP record in Admin Panel: name, slug, base URL, timeout, retry policy
  • Enter API credentials (key/secret) -- stored encrypted, never visible after save
  • Enter webhook signing secret
  • Register each supported payment method under this PSP with correct integration type (H2H / Invoice Link)
  • Set PSP status to MAINTENANCE -- it will be excluded from routing until explicitly activated
  • Add routing rules for the new PSP (brand + country + method combinations)

Step 3 -- Adapter Implementation

BE
  • Create a new class implementing IPaymentProvider interface -- do not modify the interface itself
  • Implement initiateDeposit() -- maps UnifiedDepositRequest → PSP-specific request, returns UnifiedResponse
  • Implement initiateWithdrawal() -- same pattern
  • Implement getTransactionStatus() -- maps PSP status response → UnifiedStatus
  • Implement handleWebhook() -- validate signature, parse payload, emit UnifiedEvent
  • Implement getSupportedMethods() -- returns Method[] filtered by geo + currency
  • Register the new adapter in the adapter factory/registry (the only place that knows concrete PSP classes)

Zero changes to Orchestrator, Cascade Manager, or any other service. If you need to change something outside the adapter + registry, the interface contract is likely broken -- revisit before proceeding.

Step 4 -- Data & Configuration

BE
  • Add a migration that inserts the new PSP into psp_configs (if seeded from code, not Admin Panel)
  • Add new PSP-specific status codes to the status_mapping table (psp_slug + raw_status → UnifiedStatus)
  • Ensure reconciliation job handles this PSP's transaction format (date format, amount unit, currency field name)
  • Add PSP to the health-check scheduler configuration

Step 5 -- Testing (Sandbox)

BE
  • Unit tests for status mapping: every PSP status code maps to a UnifiedStatus without gaps
  • Unit tests for webhook signature validation: valid signature passes, tampered payload fails
  • Integration test: deposit flow end-to-end against PSP sandbox
  • Integration test: withdrawal flow end-to-end against PSP sandbox
  • Test cascade fallback: PSP #1 fails, traffic routes to new PSP successfully
  • Test MAINTENANCE mode: new PSP in MAINTENANCE is never selected by Orchestrator
  • Test reconciliation: a sample PSP settlement file is parsed correctly and matched to transactions

Step 6 -- Frontend (if new methods added)

FE
  • Add method icon/logo asset to the checkout UI asset registry
  • If integration type is Invoice Link: ensure redirect + return URL flow is handled (the checkout already supports this generically)
  • If integration type is H2H with a custom form (e.g. crypto address input): add the method-specific input component
  • No changes to checkout routing, state machine, or method list ordering logic -- these are driven by backend response

In most cases (card PSPs, standard redirects) zero frontend changes are needed. The checkout UI is data-driven from the backend method list.

Step 7 -- Production Launch

Both
  • Enter production API credentials in Admin Panel (sandbox credentials must not reach production)
  • Set routing rules to send a small % of traffic (1-5%) to the new PSP -- A/B split in Admin Panel
  • Monitor success rate and error rate for the new PSP in real time for 30 minutes
  • If success rate is comparable to existing PSPs -- increase traffic split gradually
  • If success rate drops below threshold -- switch PSP back to MAINTENANCE in Admin Panel (no code deploy needed)
  • Update the routing rules to full traffic once stability is confirmed over 24h

Never go straight to 100% traffic for a new PSP. The A/B split + MAINTENANCE toggle combination means rollback takes seconds, not a deploy.

DEPO44 | PAYMENT MODULE v1 | OUT OF SCOPE -- ADD NEW PSP