Adapter
Your checkout service needs to charge a credit card. You integrate Stripe and write
stripe_client.charge(amount_cents=5000, source_token="tok_abc") wherever a payment happens.
Six months later the business adds PayPal. The PayPal SDK has a completely different method
signature: paypal.execute_payment(amount_usd=50.0, payment_id="PAY-xyz"). Now every payment
call site needs to know which vendor is in use and call the right method with the right arguments.
Add a third processor and the problem compounds.
The deeper issue is that your business logic — charge the customer — is tightly coupled to a vendor SDK's interface. Each time you switch vendors, extend to new markets, or write a test that should not hit the real payment network, you have to change the code that should not care about which processor is underneath.
How the Adapter solves it
The Adapter wraps a third-party class behind your own interface. Your code always calls your interface. The adapter translates those calls into whatever the vendor SDK actually expects. Adding a new vendor means adding a new adapter class — nothing in your business logic changes.
The target is PaymentMethod — the interface your code depends on. The adaptees are
StripeClient and PayPalClient — the third-party classes you cannot change. The adapters sit
between them, translating every method call.
At runtime, CheckoutService calls charge on the adapter, which translates the call into
whatever the underlying SDK actually expects.
Bad → Good
Without an adapter, every call site is aware of vendor details and cannot be easily swapped.
1# Bad: business logic is coupled directly to the Stripe SDK.
2# Switching to PayPal means changing every payment call site.
3class CheckoutService:
4 def __init__(self, stripe_client: StripeClient) -> None:
5 self._stripe = stripe_client
6
7 def complete_order(self, order: Order, token: str) -> None:
8 # This only works with Stripe. PayPal uses dollars, not cents,
9 # and expects a payment_id, not a source token.
10 charge = self._stripe.charge(
11 amount_cents=order.total_cents,
12 source_token=token,
13 )
14 order.mark_paid(charge.id)