Finch-backed payer client with an automatic 402 → sign → retry flow.
request/3 performs an HTTP request; when the server answers 402 with a
PAYMENT-REQUIRED header, it decodes the payment requirements, builds and
signs a payment via X402.Client.build_payment/3, and retries the request
once with the PAYMENT-SIGNATURE header. A request is never paid twice:
at most one payment retry is made, and requests that already carry a
payment-signature header are refused.
Requires the optional finch dependency; without it every call returns
{:error, :missing_dependency}. Start your own Finch pool (with TLS peer
verification — see X402.Facilitator.HTTP.secure_pool_opts/0) and pass its
name.
Example
{:ok, signer} = X402.Signer.LocalKey.new(System.fetch_env!("PAYER_KEY"))
{:ok, %{status: 200, body: body, payment_response: receipt}} =
X402.Client.Finch.request(MyApp.Finch, "https://api.example.com/paid",
signer: signer,
max_amount: "10000",
on_payment_required: fn payment_required ->
IO.inspect(payment_required["accepts"], label: "about to pay")
:ok
end
)Security
Like the facilitator client, URLs must use https:// — payment
authorizations must never travel in plaintext. Loopback hosts
(localhost, 127.0.0.1, ::1) are exempt for local development.
Summary
Functions
Performs an HTTP request, paying for the resource if it requires payment.
Types
A Finch pool name or pid.
@type request_error() :: :missing_dependency | :insecure_url | :payment_cancelled | :payment_already_attempted | {:transport_error, term()} | {:invalid_payment_required, term()} | X402.Client.build_error()
@type response() :: %{ status: non_neg_integer(), headers: [{String.t(), String.t()}], body: binary(), payment_response: map() | nil }
A completed HTTP response.
:payment_response holds the decoded PAYMENT-RESPONSE header (the
settlement receipt) when the server sent a valid one, otherwise nil.
Functions
@spec request(finch_name(), String.t(), keyword()) :: {:ok, response()} | {:error, request_error()}
Performs an HTTP request, paying for the resource if it requires payment.
Flow:
- Perform the request. Anything other than a
402with aPAYMENT-REQUIREDheader is returned as-is. - Decode the
PAYMENT-REQUIREDheader (X402.PaymentRequired.decode/1). - Invoke the
:on_payment_requiredhook, which may cancel. - Build and sign a payment (
X402.Client.build_payment/3) and retry the request once with thePAYMENT-SIGNATUREheader. - Return the retried response with the decoded
PAYMENT-RESPONSEsettlement receipt, when present. A second402is returned as-is — the payment is never re-signed or re-sent.
Options
:signer- Required. A struct implementingX402.Signer, used to sign the payment.:method- HTTP request method. The default value is:get.:headers- Additional{name, value}request headers. The default value is[].:body- Request body. The default value isnil.:receive_timeout_ms(non_neg_integer/0) - Finch receive timeout per attempt, in milliseconds. The default value is5000.:network(String.t/0) - Payment selection filter — seeX402.Client.select_requirements/2.:scheme(String.t/0) - Payment selection filter — seeX402.Client.select_requirements/2.:asset(String.t/0) - Payment selection filter — seeX402.Client.select_requirements/2.:max_amount- Maximumamount(atomic units) this client will pay — the budget guard for automated payers. Requirements above it are never selected.:valid_after_buffer(non_neg_integer/0) - Clock-skew buffer for the authorization'svalidAfter, in seconds. The default value is60.:extensions(list of function of arity 2) - Client extension enrichers forwarded toX402.Client.build_payment/3— see its:extensionsoption. The default value is[].:schemes- AdditionalX402.Schememodules forwarded toX402.Client.build_payment/3— see its:schemesoption. The default value is[].:on_payment_required- Budget/consent hook invoked with the decodedPaymentRequiredmap before any payment is signed. Return:cancelto abort with{:error, :payment_cancelled}; any other return value continues. The default value isnil.