# `X402.ExtensionResponses`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/extension_responses.ex#L1)

Encodes and decodes the `EXTENSION-RESPONSES` facilitator sidechannel.

Facilitators may report extension-specific processing outcomes on
`/verify` and `/settle` responses through a transport sidechannel that is
not part of the JSON body (x402 v2 §7.2.1). On HTTP that sidechannel is
the `EXTENSION-RESPONSES` header: Base64-encoded JSON keyed by extension
name, each value being that extension's outcome object — for example the
bazaar extension reports `{"bazaar": {"status": "success"}}`.

The sidechannel is for the resource server only and is **never forwarded
to the buyer**: `X402.Facilitator` surfaces it as the `:extension_responses`
key of verify/settle results, and `X402.Plug.PaymentGate` keeps it out of
the `PAYMENT-RESPONSE` header.

Decoding is lenient by design at the call sites: the sidechannel is
advisory, so a malformed header is logged and dropped rather than failing
the payment. The functions here still report structured errors so callers
can make that choice.

# `decode`
*since 0.9.0* 

```elixir
@spec decode(String.t()) :: {:ok, t()} | {:error, decode_error()}
```

Decodes a Base64 `EXTENSION-RESPONSES` value to a map of outcomes.

Returns `{:error, :payload_too_large}` above 8 KB, `{:error, :invalid_base64}`
or `{:error, :invalid_json}` for undecodable values, and
`{:error, :invalid_responses}` when the JSON is not an object of objects.

## Examples

    iex> X402.ExtensionResponses.decode("eyJiYXphYXIiOnsic3RhdHVzIjoic3VjY2VzcyJ9fQ==")
    {:ok, %{"bazaar" => %{"status" => "success"}}}

    iex> X402.ExtensionResponses.decode(Base.encode64(~s({"bazaar":"success"})))
    {:error, :invalid_responses}

    iex> X402.ExtensionResponses.decode("%%%")
    {:error, :invalid_base64}

# `encode`
*since 0.9.0* 

```elixir
@spec encode(t()) :: {:ok, String.t()} | {:error, encode_error()}
```

Encodes extension outcomes to a Base64 header value.

The argument must be a map from extension name to an outcome map.

## Examples

    iex> {:ok, value} = X402.ExtensionResponses.encode(%{"bazaar" => %{"status" => "success"}})
    iex> value
    "eyJiYXphYXIiOnsic3RhdHVzIjoic3VjY2VzcyJ9fQ=="

    iex> X402.ExtensionResponses.encode(%{"bazaar" => "success"})
    {:error, :invalid_responses}

# `from_headers`
*since 0.9.0* 

```elixir
@spec from_headers([{String.t(), String.t()}]) ::
  {:ok, t() | nil} | {:error, decode_error()}
```

Extracts and decodes the sidechannel from a list of response headers.

Header names are matched case-insensitively. Returns `{:ok, nil}` when
the header is absent.

## Examples

    iex> X402.ExtensionResponses.from_headers([{"content-type", "application/json"}])
    {:ok, nil}

    iex> X402.ExtensionResponses.from_headers([
    ...>   {"extension-responses", "eyJiYXphYXIiOnsic3RhdHVzIjoic3VjY2VzcyJ9fQ=="}
    ...> ])
    {:ok, %{"bazaar" => %{"status" => "success"}}}

    iex> X402.ExtensionResponses.from_headers([{"EXTENSION-RESPONSES", "%%%"}])
    {:error, :invalid_base64}

# `from_headers_lenient`
*since 0.9.0* 

```elixir
@spec from_headers_lenient([{String.t(), String.t()}]) :: t() | nil
```

Decodes the sidechannel from response headers, dropping malformed values.

The advisory nature of the sidechannel means a malformed header must not
fail the payment: this returns the decoded outcomes, or `nil` when the
header is absent or invalid, emitting a
`[:x402, :extension_responses, :decode]` telemetry event with
`status: :error` and the `:reason` in the latter case.

## Examples

    iex> X402.ExtensionResponses.from_headers_lenient([{"extension-responses", "%%%"}])
    nil

# `header_name`
*since 0.9.0* 

```elixir
@spec header_name() :: String.t()
```

Returns the canonical sidechannel header name.

## Examples

    iex> X402.ExtensionResponses.header_name()
    "EXTENSION-RESPONSES"

# `decode_error`

```elixir
@type decode_error() ::
  :invalid_base64 | :invalid_json | :invalid_responses | :payload_too_large
```

# `encode_error`

```elixir
@type encode_error() :: :invalid_responses | :invalid_json
```

# `t`

```elixir
@type t() :: %{optional(String.t()) =&gt; map()}
```

Extension outcomes keyed by extension name.

---

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