# `MPP.Client.Transport`
[🔗](https://github.com/ZenHive/mpp/blob/v0.10.0/lib/mpp/client/transport.ex#L1)

Behaviour for client-side transports that bridge a protocol response/request pair
and the MPP payment flow.

A `Transport` implementation knows how to:

  1. Detect whether a response signals "payment required" (HTTP 402, JSON-RPC
     error code `-32042`, etc.)
  2. Extract the challenges carried on that response
  3. Attach a decoded credential to a new request for retry

This is the transport-shaped glue that sits between the provider-side
`MPP.Client.PaymentProvider` abstraction and whatever concrete client (Req,
an MCP JSON-RPC client, etc.) is actually issuing requests.

## Callbacks

  * `c:payment_required?/1` — plain boolean check on a response
  * `c:get_challenges/1` — parse the response into a challenge list
  * `c:set_credential/2` — return a new request with the credential attached
    in a transport-specific way

## Selection

`select_challenge/2` is a small helper (not a callback) that picks the first
challenge whose `method`/`intent` is supported by a `MPP.Client.MultiProvider`.
This matches the baseline "first supported in server offer order" behaviour
shared with the reference SDKs; ranking via `Accept-Payment` is layered on top
via the optional `:accept_payment` keyword to `select_challenge/3`.

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `select_challenge` | 3 | Pick the first challenge whose method+intent is supported by a MultiProvider. | `challenges: value`, `multi: value`, `opts: value` |

# `get_challenges`

```elixir
@callback get_challenges(response :: term()) ::
  {:ok, [MPP.Challenge.t()]} | {:error, term()}
```

Extract the `MPP.Challenge` list carried on a payment-required response.

Returning `{:ok, []}` is not a valid outcome — an empty or unparseable
challenge set should surface as `{:error, reason}` so callers can distinguish
"402 with no Payment challenges" from "402 with N challenges".

# `payment_required?`

```elixir
@callback payment_required?(response :: term()) :: boolean()
```

Return `true` if the response signals that a payment is required.

For HTTP, this is `status == 402`. For MCP/JSON-RPC, this is
`error.code == -32042`.

# `set_credential`

```elixir
@callback set_credential(request :: term(), credential :: MPP.Credential.t()) :: term()
```

Return a new request with the given credential attached in transport-specific
form.

For HTTP, this sets `Authorization: Payment <base64url>`. For MCP, this
injects the credential into `params._meta["org.paymentauth/credential"]`.
Each transport owns its wire format — the credential struct is passed in
decoded form so the transport is free to serialise it however it needs to.

# `select_challenge`

```elixir
@spec select_challenge([MPP.Challenge.t()], MPP.Client.MultiProvider.t(), keyword()) ::
  {:ok, MPP.Challenge.t()} | {:error, :no_supported_challenge}
```

Pick the first supported challenge, optionally ranked by `Accept-Payment`.

When `:accept_payment` is provided, challenges are reordered by client
preferences before the first `MultiProvider`-supported match is chosen.
Malformed preference lists are ignored (baseline server-offer order).

Returns `{:error, :no_supported_challenge}` if no challenge matches any
provider, including the empty-list case.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
