A provider adapter translates one PSP or operator's API into the billing system's internal payment-attempt contract.
What the adapter should own
- Initiating the provider-specific payment request.
- Storing provider request identifiers.
- Parsing callbacks or status-query responses.
- Mapping raw provider states into normalized states.
- Preserving raw evidence for support and reconciliation.
- Exposing provider-specific verification or refund capability.
What the adapter should not own
- Whether the invoice exists.
- Merchant grace-period policy.
- Whether service should be suspended.
- How many reminders the customer receives.
Those belong to the recurring billing layer.
A small internal interface
A useful adapter contract can expose operations such as initiateAttempt, queryAttempt, parseEvent and mapStatus, plus capability metadata for the merchant route.
Keep country configuration outside the adapter class
The same wallet brand can have different operator or provider setups across countries. Pass market, currency and merchant-account configuration into the adapter rather than treating the wallet name as globally sufficient.
Why this matters
When a merchant adds a second PSP, the billing engine should not need a second set of invoice, recovery and service rules. Only the provider boundary should change.
Make capabilities explicit
An adapter should expose more than a method name. The billing layer may need to know whether the current merchant route supports status query, refund, reversal, partial refund, customer-initiated authorization or another capability.
Keep that information in configuration rather than hiding it in conditional code spread through the recovery engine.
Preserve correlation identifiers
Each provider has its own request and transaction identifiers. The adapter should return them in a stable internal structure while keeping the provider-native values intact. Those identifiers are essential when a callback arrives late or finance investigates a mismatch.
Test adapters with contract fixtures
- successful initiation and final success;
- customer cancellation or final failure;
- missing or delayed final event;
- duplicate callback;
- out-of-order event;
- provider response with an unknown/new status code.
A new provider should pass the same billing-contract tests before it is exposed to merchants. That is how the system avoids giving each PSP its own operational behavior.
Do not hide irreversible provider differences
Normalization is useful, but an adapter should not pretend every provider supports the same customer authorization, refund or settlement behavior. Expose those differences through capability metadata so product and operations can make safe decisions.
Log adapter decisions
When the adapter maps a raw provider result into a normalized state, record the mapping version or rule used. That becomes valuable when a provider introduces a new status or the team changes its interpretation later.



