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

Behaviour for resource-server extension adapters run by `X402.Plug.PaymentGate`.

An adapter packages one protocol extension's server-side lifecycle so the
gate can run it from a single option instead of the user wiring each step
by hand:

    plug X402.Plug.PaymentGate,
      routes: [...],
      extensions: [
        {X402.Extensions.PaymentIdentifier.Adapter, required: true},
        {X402.Extensions.BuilderCode.Adapter, app_code: "my_app"}
      ]

Every callback but `c:key/0` is optional:

* `c:init/1` validates the adapter's options once, when the gate's own
  options are validated; the returned options are what the remaining
  callbacks receive.
* `c:advertise/2` returns the value to advertise under the extension key
  in every `PaymentRequired.extensions` (merged over the route's static
  `extensions` map), or `nil` to advertise nothing for this request.
* `c:validate/3` runs after the generic extension echo check on every
  payment, with the value the client echoed (or `nil`) and the value that
  was advertised (or `nil`). An error rejects the payment with **400**
  `invalid_payload` and a `{:extension_invalid, key, reason}` telemetry
  reason.
* `c:after_verify/4` and `c:after_settle/4` are notified with the
  facilitator's result once verification succeeded and once settlement
  succeeded. Their return values are ignored and exceptions are logged.

`c:advertise/2` and `c:validate/3` are part of the request's control flow,
so an exception there propagates like any other programmer error.

The `sign-in-with-x` extension keeps its dedicated `:siwx` gate option:
its challenge is regenerated per response and it changes the request flow
(a proof can skip payment entirely), which the generic advertise/validate
shape does not express. Bazaar discovery metadata likewise has its own
`:bazaar` route option because it depends on the matched route pattern.

# `entry`

```elixir
@type entry() :: {module(), keyword()}
```

A normalized adapter entry.

# `spec`

```elixir
@type spec() :: module() | {module(), keyword()}
```

An adapter entry as accepted by the gate's `:extensions` option.

# `advertise`
*optional* 

```elixir
@callback advertise(
  keyword(),
  X402.Hooks.RequestContext.t()
) :: map() | nil
```

Builds the value advertised under the extension key, or `nil`.

# `after_settle`
*optional* 

```elixir
@callback after_settle(
  payload :: map(),
  requirements :: map(),
  result :: map(),
  keyword()
) :: term()
```

Notified after a successful facilitator settlement.

# `after_verify`
*optional* 

```elixir
@callback after_verify(
  payload :: map(),
  requirements :: map(),
  result :: map(),
  keyword()
) :: term()
```

Notified after a successful facilitator verification.

# `init`
*optional* 

```elixir
@callback init(keyword()) :: {:ok, keyword()} | {:error, String.t()}
```

Validates the adapter options at gate initialization.

# `key`

```elixir
@callback key() :: String.t()
```

Returns the extension key on the wire (for example `"payment-identifier"`).

# `validate`
*optional* 

```elixir
@callback validate(echoed :: term(), advertised :: term(), keyword()) ::
  :ok | {:error, term()}
```

Validates the client's echoed value against the advertised one.

# `advertise_all`
*since 0.9.0* 

```elixir
@spec advertise_all([entry()], X402.Hooks.RequestContext.t(), map()) :: map()
```

Merges every adapter's advertisement over a base extensions map.

Adapters without `c:advertise/2`, and those returning `nil`, leave the
map untouched.

## Examples

    iex> context = X402.Hooks.RequestContext.new(requirements: [%{"scheme" => "exact"}])
    iex> entries = [{X402.Extensions.PaymentIdentifier.Adapter, required: true}]
    iex> advertised = X402.Extension.advertise_all(entries, context, %{"other" => %{}})
    iex> {Map.keys(advertised) |> Enum.sort(), advertised["payment-identifier"]["info"]}
    {["other", "payment-identifier"], %{"required" => true}}

# `after_settle_all`
*since 0.9.0* 

```elixir
@spec after_settle_all([entry()], map(), map(), map()) :: :ok
```

Notifies every adapter defining `c:after_settle/4`.

Return values are ignored; an exception is logged and the remaining
adapters still run.

# `after_verify_all`
*since 0.9.0* 

```elixir
@spec after_verify_all([entry()], map(), map(), map()) :: :ok
```

Notifies every adapter defining `c:after_verify/4`.

Return values are ignored; an exception is logged and the remaining
adapters still run.

# `validate_all`
*since 0.9.0* 

```elixir
@spec validate_all([entry()], term(), map()) ::
  :ok | {:error, {:extension_invalid, String.t(), term()}}
```

Runs every adapter's `c:validate/3` over the echoed and advertised maps.

Stops at the first error, tagging it with the adapter's key.

## Examples

    iex> entries = [{X402.Extensions.BuilderCode.Adapter, app_code: "my_app"}]
    iex> advertised = %{"builder-code" => X402.Extensions.BuilderCode.extension("my_app")}
    iex> X402.Extension.validate_all(entries, %{"builder-code" => %{"a" => "my_app"}}, advertised)
    :ok

    iex> entries = [{X402.Extensions.BuilderCode.Adapter, app_code: "my_app"}]
    iex> advertised = %{"builder-code" => X402.Extensions.BuilderCode.extension("my_app")}
    iex> X402.Extension.validate_all(entries, %{"builder-code" => %{"a" => "other"}}, advertised)
    {:error, {:extension_invalid, "builder-code", :builder_code_mismatch}}

# `validate_spec`
*since 0.9.0* 

```elixir
@spec validate_spec(term()) :: {:ok, entry()} | {:error, String.t()}
```

Validates one `:extensions` entry and normalizes it to `{module, opts}`.

Designed for `NimbleOptions` custom validation. The module must define
`key/0`; when it defines `init/1` the options are passed through it.

## Examples

    iex> X402.Extension.validate_spec(X402.Extensions.PaymentIdentifier.Adapter)
    {:ok, {X402.Extensions.PaymentIdentifier.Adapter, [required: false]}}

    iex> X402.Extension.validate_spec({X402.Extensions.PaymentIdentifier.Adapter, required: true})
    {:ok, {X402.Extensions.PaymentIdentifier.Adapter, [required: true]}}

    iex> X402.Extension.validate_spec(:not_an_adapter)
    {:error, "expected a module implementing X402.Extension (key/0), got: :not_an_adapter"}

---

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