Decodes and validates x402 PAYMENT-SIGNATURE header values.
The header value is Base64-encoded JSON. This module supports both the legacy
v1 signature fields and the v2 PaymentPayload envelope.
For v2, accepted must be a complete X402.PaymentRequirements object and
payload must contain the scheme-specific signed data. When requirements are
passed to validate/2, every core field is matched and advertised extra
values must be preserved.
Summary
Header Encoding
Decodes a Base64 PAYMENT-SIGNATURE value to a map.
Returns the canonical x402 header name.
Payment Verification
Decodes and validates a PAYMENT-SIGNATURE header in one step.
Decodes and validates a PAYMENT-SIGNATURE header against requirements.
Validates a decoded v1 or v2 PAYMENT-SIGNATURE payload.
Validates a decoded PAYMENT-SIGNATURE payload against payment requirements.
Header Encoding
@spec decode(String.t()) :: {:ok, map()} | {:error, decode_error()}
Decodes a Base64 PAYMENT-SIGNATURE value to a map.
Examples
iex> payload = %{"x402Version" => 2, "accepted" => %{"scheme" => "exact"}, "payload" => %{}}
iex> value = payload |> Jason.encode!() |> Base.encode64()
iex> X402.PaymentSignature.decode(value)
{:ok, payload}
iex> X402.PaymentSignature.decode("not-base64")
{:error, :invalid_base64}
@spec header_name() :: String.t()
Returns the canonical x402 header name.
Examples
iex> X402.PaymentSignature.header_name()
"PAYMENT-SIGNATURE"
Payment Verification
@spec decode_and_validate(String.t()) :: {:ok, map()} | {:error, decode_and_validate_error()}
Decodes and validates a PAYMENT-SIGNATURE header in one step.
Examples
iex> payload = %{"x402Version" => 2, "accepted" => %{"scheme" => "exact", "network" => "eip155:8453", "amount" => "1", "asset" => "asset", "payTo" => "receiver", "maxTimeoutSeconds" => 60, "extra" => %{}}, "payload" => %{}}
iex> value = payload |> Jason.encode!() |> Base.encode64()
iex> X402.PaymentSignature.decode_and_validate(value)
{:ok, payload}
@spec decode_and_validate(String.t(), map()) :: {:ok, map()} | {:error, decode_and_validate_error()}
Decodes and validates a PAYMENT-SIGNATURE header against requirements.
@spec validate(map()) :: {:ok, map()} | {:error, validate_error()}
Validates a decoded v1 or v2 PAYMENT-SIGNATURE payload.
Payloads with x402Version: 2 are validated against the v2
PaymentPayload structure. Payloads with version 1, or without an explicit
version, retain the legacy field validation used by x402 v1.
Examples
iex> payload = %{
...> "x402Version" => 2,
...> "accepted" => %{
...> "scheme" => "exact",
...> "network" => "eip155:8453",
...> "amount" => "10000",
...> "asset" => "0xasset",
...> "payTo" => "0xreceiver",
...> "maxTimeoutSeconds" => 60,
...> "extra" => %{}
...> },
...> "payload" => %{"signature" => "0xsignature"}
...> }
iex> X402.PaymentSignature.validate(payload)
{:ok, payload}
iex> X402.PaymentSignature.validate(%{"x402Version" => 2})
{:error, :invalid_payload}
@spec validate(map(), map()) :: {:ok, map()} | {:error, validate_error()}
Validates a decoded PAYMENT-SIGNATURE payload against payment requirements.
For v2, this matches the complete accepted object. For the "upto" scheme,
it also ensures the signed maximum does not exceed the advertised amount.
Types
@type decode_and_validate_error() :: decode_error() | validate_error()
@type decode_error() :: :invalid_base64 | :invalid_json | :payload_too_large
@type upto_validation_error() ::
:missing_max_price
| :missing_payment_value
| :invalid_max_price
| :invalid_payment_value
| :payment_value_exceeds_max_price
@type validate_error() :: :invalid_payload | :invalid_payment_requirements | :invalid_x402_version | :no_matching_requirements | {:missing_fields, [String.t()]} | {:invalid_fields, [String.t()]} | {:invalid_upto_payment, upto_validation_error()} | {:invalid_format, [{field :: String.t(), reason :: atom()}]}