Card 04/ 08
Adapter Example: A Legacy Payment Gateway Behind a New Interface
The checkout expects pay(amount); the old gateway wants make_transaction(cents, currency).
A legacy payment gateway is a natural Adapter, and this example maps the template onto it. The checkout is written against a PaymentProcessor with pay(amount), taking a decimal amount in pounds and returning whether it succeeded. The gateway offers make_transaction(cents, currency) and returns a dictionary with a status, so the adapter must convert the units and the result as well as the name.
| Template | Payment example |
|---|---|
| Target | PaymentProcessor, with pay(amount) |
| Adaptee | LegacyGateway, with make_transaction(cents, currency) |
| Adapter | LegacyGatewayAdapter, which holds the gateway and a currency |
| Client | Checkout, which calls pay and nothing else |
from abc import ABC, abstractmethod
from decimal import Decimal
class PaymentProcessor(ABC):
@abstractmethod
def pay(self, amount: Decimal) -> bool: ...
class LegacyGateway:
def make_transaction(self, cents: int, currency: str) -> dict:
print(f"legacy gateway: charging {cents} {currency} cents")
return {"status": "OK", "reference": "TX-1042"}
class LegacyGatewayAdapter(PaymentProcessor):
def __init__(self, gateway: LegacyGateway, currency: str) -> None:
self._gateway = gateway
self._currency = currency
def pay(self, amount: Decimal) -> bool:
cents = int(amount * 100)
response = self._gateway.make_transaction(cents, self._currency)
return response["status"] == "OK"
class Checkout:
def __init__(self, processor: PaymentProcessor) -> None:
self._processor = processor
def complete(self, total: Decimal) -> str:
return "paid" if self._processor.pay(total) else "declined"
checkout = Checkout(LegacyGatewayAdapter(LegacyGateway(), "GBP"))
print(checkout.complete(Decimal("24.99")))legacy gateway: charging 2499 GBP cents
paidThree translations happen in pay. The amount is converted from pounds to pence, which is a change of meaning and not only of name. The currency, which the checkout never mentions, is supplied by the adapter from its constructor. The gateway's dictionary is reduced to the boolean the target promises. The output shows the gateway charged 2499 pence and the checkout saw "paid".
The amount is a Decimal, because multiplying a binary floating-point price by 100 can land just below a whole number: in Python 3.12, 0.29 * 100 is 28.999999999999996, and int turns it into 28 pence. The adapter holds that conversion in one place, where it can be tested once.
← → move through sections and cards