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

Transport-agnostic payer client for x402 v2 payments.

Pure functions covering the client half of the protocol: selecting a
payment option from a server's `PAYMENT-REQUIRED` response, signing it
through the `X402.Signer` behaviour, assembling the v2 `PaymentPayload`
envelope, and encoding it to a `PAYMENT-SIGNATURE` header value. Bring your
own HTTP client, or use `X402.Client.Finch` for a ready-made
402 → sign → retry flow.

Signing dispatches through `X402.Scheme.Registry`: out of the box this
client signs the `exact` scheme on EVM (`eip155:*`) networks via EIP-3009
(`X402.Scheme.ExactEVM`), on Solana (`solana:*`) networks via a
partially signed v0 transaction (`X402.Scheme.ExactSVM`), and the
`upto` scheme on EVM networks via Permit2 (`X402.Scheme.UptoEVM`);
other scheme/network combinations return
`{:error, {:unsupported_kind, scheme, network}}` unless a matching
`X402.Scheme` module is passed with the `:schemes` option.

## Example

    {:ok, signer} = X402.Signer.LocalKey.new(System.fetch_env!("PAYER_KEY"))

    with {:ok, payment_required} <- X402.PaymentRequired.decode(header_value),
         {:ok, payload} <- X402.Client.build_payment(payment_required, signer),
         {:ok, header} <- X402.Client.encode_payment(payload) do
      # retry the request with {"payment-signature", header}
    end

# `build_error`

```elixir
@type build_error() ::
  select_error()
  | X402.Client.Hooks.hook_error()
  | {:unsupported_kind, term(), term()}
  | X402.EIP3009.domain_error()
  | X402.EIP3009.encode_error()
  | term()
```

# `select_error`

```elixir
@type select_error() ::
  :no_acceptable_requirements
  | :invalid_payment_required
  | {:invalid_policy_result, term()}
  | term()
```

# `select_opts`

```elixir
@type select_opts() :: [
  network: String.t(),
  scheme: String.t(),
  asset: String.t(),
  max_amount: String.t() | non_neg_integer(),
  policies: [X402.Client.Policy.t()],
  schemes: [module()]
]
```

Selection options — see `select_requirements/2`.

# `build_payment`
*since 0.6.0* 

```elixir
@spec build_payment(map() | [map()], X402.Signer.t(), keyword()) ::
  {:ok, map()} | {:error, build_error()}
```

Builds a complete v2 `PaymentPayload` for a payment-required response.

Accepts either a decoded `PaymentRequired` map — in which case one entry is
chosen via `select_requirements/2` and the server's `resource` and
`extensions` are echoed — or a single requirements map, which skips
selection (and carries no `resource`/`extensions` echo).

The chosen requirements are echoed in full (including `extra`) as
`accepted`, and server-advertised `extensions` are echoed unchanged,
following the spec's append-only rule: the client must preserve every
advertised value and may only add to them. `:extensions` enrichers run
after assembly and may add extension data on top of the echo — for
example `X402.Extensions.EIP2612GasSponsoring.enricher/2` for
gas-sponsored Permit2 approvals.

Signing dispatches on the scheme and network of the chosen requirements
through `X402.Scheme.Registry`; out of the box this supports `exact` on
`eip155:*` networks (EIP-3009) and on `solana:*` networks (partially
signed v0 transactions), plus `upto` on `eip155:*` networks (Permit2).
Other combinations return
`{:error, {:unsupported_kind, scheme, network}}` unless a matching
module is passed with the `:schemes` option. Scheme modules receive the
validated build options, so options like `:valid_after_buffer` (EVM) and
`:svm_blockhash` (SVM) reach `c:X402.Scheme.sign/3`.

## Lifecycle hooks

The `:hooks` module (`X402.Client.Hooks`, default
`X402.Client.Hooks.Default`) runs `before_payment/2` once requirements
are selected — it may replace them or halt — `after_payment/2` once the
payload is built — it may replace the payload — and
`on_payment_failure/2` when signing or enrichment fails — it may replace
the error or recover with a payload. Selection failures return before
any hook runs.

## Options

* `:network` (`t:String.t/0`) - Only select requirements on this CAIP-2 network. A trailing `*` acts as
  a prefix wildcard (for example `"eip155:*"`).

* `:scheme` (`t:String.t/0`) - Only select requirements using this scheme (for example `"exact"`).

* `:asset` (`t:String.t/0`) - Only select requirements paying with this asset (compared case-insensitively).

* `:max_amount` - Only select requirements whose `amount` (in atomic units) does not
  exceed this value — the budget guard for automated payers.

* `:policies` (list of function of arity 2) - Selection policies, each a function of the candidate requirements and
  the `PaymentRequired` map (`nil` for a bare list) returning `true` to
  accept, `false` to skip the entry, or `{:error, reason}` to abort
  selection with that error. Every policy must accept an entry for it
  to be selected — see `X402.Client.Policy` for ready-made ones. The default value is `[]`.

* `:schemes` - Additional `X402.Scheme` modules consulted (before the built-ins) when
  deciding which requirements this client can sign and how to sign
  them — see `X402.Scheme.Registry`. The default value is `[]`.

* `:auth_capture` - Auth-capture signing options (`:now`, `:salt`, `:salt_nonce`). The default value is `[]`.

* `:hooks` - Module implementing `X402.Client.Hooks`, run around
  payment creation. The default value is `X402.Client.Hooks.Default`.

* `:valid_after_buffer` (`t:non_neg_integer/0`) - Seconds subtracted from the current time for the EVM
  authorization's `validAfter` (clock-skew tolerance). The default value is `60`.

* `:extensions` (list of function of arity 2) - Client extension enrichers applied, in order, to the
  assembled payload. Each function receives the payload
  and the original `PaymentRequired` map (`nil` when
  building from a bare requirements map) and returns
  `{:ok, payload}` or `{:error, reason}` — see
  `X402.Extensions.EIP2612GasSponsoring.enricher/2` and
  `X402.Extensions.ERC20ApprovalGasSponsoring.enricher/1`. The default value is `[]`.

* `:svm_blockhash` (`t:String.t/0`) - Base58 recent blockhash for SVM (Solana) payments,
  used when the server's `extra.recentBlockhash` hint
  is absent — see `X402.Scheme.ExactSVM`.

* `:svm_blockhash_fetcher` (function of arity 1) - 1-arity fun receiving the CAIP-2 network and
  returning `{:ok, blockhash}` for SVM payments (for
  example a wrapper around an RPC client's
  `getLatestBlockhash`).

* `:svm_decimals` (`t:non_neg_integer/0`) - The SVM asset's decimals for `TransferChecked`,
  for mints outside `X402.Scheme.ExactSVM`'s
  known-asset table.

* `:svm_token_program` (`t:String.t/0`) - The SVM asset's owning token program (SPL Token or
  Token-2022 address), for mints outside
  `X402.Scheme.ExactSVM`'s known-asset table.

# `encode_payment`
*since 0.6.0* 

```elixir
@spec encode_payment(map()) ::
  {:ok, String.t()} | {:error, :invalid_payload | :invalid_json}
```

Encodes a `PaymentPayload` map to a `PAYMENT-SIGNATURE` header value.

The header value is Base64-encoded JSON, compatible with
`X402.PaymentSignature.decode/1` on the validation side.

## Examples

    iex> payload = %{"x402Version" => 2, "accepted" => %{"scheme" => "exact"}, "payload" => %{}}
    iex> {:ok, header} = X402.Client.encode_payment(payload)
    iex> X402.PaymentSignature.decode(header)
    {:ok, payload}

    iex> X402.Client.encode_payment(nil)
    {:error, :invalid_payload}

# `select_requirements`
*since 0.6.0* 

```elixir
@spec select_requirements(map() | [map()], select_opts()) ::
  {:ok, map()} | {:error, select_error()}
```

Selects one payment requirements entry from a `PAYMENT-REQUIRED` payload.

Accepts a decoded `PaymentRequired` map (its `accepts` list is used) or a
bare list of requirements maps. Returns the first entry that passes the
option filters **and** that this client can sign: structurally valid per
`X402.PaymentRequirements.validate/1` and resolved by
`X402.Scheme.Registry` to a scheme module with a sign callback — by
default `exact` on an `eip155:*` network via `X402.Scheme.ExactEVM`
(which additionally requires the EIP-712 domain fields `extra.name` /
`extra.version`) and `upto` on an `eip155:*` network via
`X402.Scheme.UptoEVM` (which requires `extra.facilitatorAddress`). Pass
additional schemes with the `:schemes` option.

Entries whose `extra.paymentFlow` names a flow this client does not
recognize are skipped: the protocol requires clients never to construct
a payment for an unknown flow (spec §6.1). Only the default
`"authorization"` flow (explicit or omitted) is recognized; `upfront` and
`escrow` entries settle before the resource executes and are not selected.

`:policies` run last, on the signable candidates in order: the first entry
every policy accepts is selected. A policy returning `{:error, reason}`
aborts with `{:error, reason}` (for example a budget policy refusing to
continue); any other non-boolean return aborts with
`{:error, {:invalid_policy_result, value}}`.

The selected entry is returned exactly as the server sent it, so it can be
echoed verbatim as the payload's `accepted` value.

## Options

* `:network` (`t:String.t/0`) - Only select requirements on this CAIP-2 network. A trailing `*` acts as
  a prefix wildcard (for example `"eip155:*"`).

* `:scheme` (`t:String.t/0`) - Only select requirements using this scheme (for example `"exact"`).

* `:asset` (`t:String.t/0`) - Only select requirements paying with this asset (compared case-insensitively).

* `:max_amount` - Only select requirements whose `amount` (in atomic units) does not
  exceed this value — the budget guard for automated payers.

* `:policies` (list of function of arity 2) - Selection policies, each a function of the candidate requirements and
  the `PaymentRequired` map (`nil` for a bare list) returning `true` to
  accept, `false` to skip the entry, or `{:error, reason}` to abort
  selection with that error. Every policy must accept an entry for it
  to be selected — see `X402.Client.Policy` for ready-made ones. The default value is `[]`.

* `:schemes` - Additional `X402.Scheme` modules consulted (before the built-ins) when
  deciding which requirements this client can sign and how to sign
  them — see `X402.Scheme.Registry`. The default value is `[]`.

## Examples

    iex> requirements = %{
    ...>   "scheme" => "exact",
    ...>   "network" => "eip155:84532",
    ...>   "amount" => "10000",
    ...>   "asset" => "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    ...>   "payTo" => "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
    ...>   "maxTimeoutSeconds" => 60,
    ...>   "extra" => %{"name" => "USDC", "version" => "2"}
    ...> }
    iex> {:ok, selected} =
    ...>   X402.Client.select_requirements(%{"x402Version" => 2, "accepts" => [requirements]})
    iex> selected == requirements
    true

    iex> X402.Client.select_requirements(%{"x402Version" => 2, "accepts" => []})
    {:error, :no_acceptable_requirements}

    iex> requirements = %{
    ...>   "scheme" => "exact",
    ...>   "network" => "eip155:84532",
    ...>   "amount" => "10000",
    ...>   "asset" => "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    ...>   "payTo" => "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
    ...>   "maxTimeoutSeconds" => 60,
    ...>   "extra" => %{"name" => "USDC", "version" => "2"}
    ...> }
    iex> X402.Client.select_requirements([requirements], policies: [X402.Client.Policy.max_amount("100")])
    {:error, :no_acceptable_requirements}

---

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