Client
Handle 402 responses automatically
The Client class wraps httpx and intercepts 402 responses—it parses the Challenge, signs a stablecoin transfer, and retries with the Credential.
Quick start
client.py
from mpp.client import Client
from mpp.methods.tempo import tempo, TempoAccount, ChargeIntent
account = TempoAccount.from_key("0x...")
async with Client(methods=[tempo(account=account, intents={"charge": ChargeIntent()})]) as client:
response = await client.get("https://api.example.com/resource")
print(response.json())Event handling
Register event handlers to record the automatic 402 flow. Use helper methods for named events, event constants for shared wiring, and * for every client event.
client.py
from mpp.client import Client
from mpp.events import PAYMENT_RESPONSE
from mpp.methods.tempo import tempo, TempoAccount, ChargeIntent
account = TempoAccount.from_key("0x...")
async with Client(methods=[tempo(account=account, intents={"charge": ChargeIntent()})]) as client:
def log(event: str, data: dict[str, object]) -> None:
print(event, data)
# Record the selected Challenge before the client creates a Credential.
client.on_challenge_received(
lambda payload: log(
"payment.challenge.received",
{
"challenge_id": payload["challenge"].id,
"intent": payload["challenge"].intent,
"method": payload["challenge"].method,
},
)
)
# Record the retried response after payment.
client.on(
PAYMENT_RESPONSE,
lambda payload: log(
"payment.response",
{"status": payload["response"].status_code},
),
)
# Catch every client payment event in one handler.
client.on("*", lambda event: log("payment.event", {"name": event.name}))
response = await client.get("https://api.example.com/resource")on_challenge_received can return a Credential to override the default Credential creation path. Other handlers only record payment handling. Each registration returns an unsubscribe function.
Common methods
| Method | Description |
|---|---|
delete(url, **kwargs) | DELETE request with payment handling |
get(url, **kwargs) | GET request with payment handling |
post(url, **kwargs) | POST request with payment handling |
put(url, **kwargs) | PUT request with payment handling |
request(method, url, **kwargs) | Generic request |
One-off requests
client.py
from mpp.client import get
from mpp.methods.tempo import tempo, TempoAccount, ChargeIntent
response = await get(
"https://api.example.com/resource",
methods=[tempo(account=TempoAccount.from_key("0x..."), intents={"charge": ChargeIntent()})],
)Custom httpx transport
PaymentTransport wraps any httpx.AsyncBaseTransport, so you can compose it with custom transports:
transport.py
import httpx
from mpp.client import PaymentTransport
from mpp.methods.tempo import tempo, TempoAccount, ChargeIntent
account = TempoAccount.from_key("0x...")
transport = PaymentTransport(
methods=[tempo(account=account, intents={"charge": ChargeIntent()})],
inner=httpx.AsyncHTTPTransport(retries=3),
)
async with httpx.AsyncClient(transport=transport) as client:
response = await client.get("https://api.example.com/resource")For manual Challenge/Credential handling, see Core types.