"""Payment gateway abstraction with idempotent callbacks. Payments never talk to a concrete gateway directly; the gateway is selected from configuration and isolated behind an interface. """ from typing import Protocol from django.conf import settings class PaymentGatewayInterface(Protocol): def request_payment(self, amount, currency, order_number: str, callback_url: str) -> dict: """Return {'authority': str, 'redirect_url': str}.""" ... def verify(self, authority: str, amount) -> dict: """Return {'transaction_id': str, 'status': 'success'|'failed'|'cancelled'}.""" ... class MockGateway: def request_payment(self, amount, currency, order_number: str, callback_url: str) -> dict: return { "authority": f"MOCK-AUTH-{order_number}", "redirect_url": f"{callback_url}?authority=MOCK-AUTH-{order_number}", } def verify(self, authority: str, amount) -> dict: return {"transaction_id": f"MOCK-TXN-{authority}", "status": "success"} def get_gateway() -> PaymentGatewayInterface: if settings.PAYMENT_GATEWAY_PROVIDER == "mock": return MockGateway() # Real gateways (GatewayA, GatewayB, ...) are selected here. return MockGateway()