A Provider-Agnostic Client

Step 3: A Provider-Agnostic Client

Here is the payoff. Because both adapters honour the same target interface, client code can be written once and reused with any gateway. Write a checkout function that takes any PaymentProcessor and an amount, calls charge, and returns the result, with zero knowledge of whether it holds a Stripe or a PayPal adapter.

This is Adapter's promise: incompatible APIs made interchangeable behind one contract, so business logic depends on the interface, not on any specific SDK.

  • checkout(processor, amount). Call processor.charge(amount) and return its dict
Hints
Starter code
class StripeApi:
    def create_charge(self, amount_cents):
        if amount_cents <= 0:
            return {"id": "ch_declined", "paid": False}
        return {"id": "ch_" + str(amount_cents), "paid": True}


class PayPalApi:
    def make_payment(self, dollars):
        if dollars <= 0:
            return {"success": False, "txn": "pp_declined"}
        return {"success": True, "txn": "pp_" + str(dollars)}


class PaymentProcessor:
    def charge(self, amount_dollars):
        raise NotImplementedError


class StripeAdapter(PaymentProcessor):
    def __init__(self, api):
        self._api = api

    def charge(self, amount_dollars):
        cents = int(round(amount_dollars * 100))
        resp = self._api.create_charge(cents)
        return {"ok": resp["paid"], "id": resp["id"]}


class PayPalAdapter(PaymentProcessor):
    def __init__(self, api):
        self._api = api

    def charge(self, amount_dollars):
        resp = self._api.make_payment(amount_dollars)
        return {"ok": resp["success"], "id": resp["txn"]}


def checkout(processor, amount):
    """Charge through any PaymentProcessor and return its result dict."""
    # TODO:
    #   return processor.charge(amount)
    pass
Write it on a laptop

The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.