Current section
Files
Jump to
Current section
Files
README.md
# X402[](https://hex.pm/packages/x402)[](https://hex.pm/packages/x402)[](https://hexdocs.pm/x402)[](https://github.com/cardotrejos/x402/actions/workflows/ci.yml)[](LICENSE)The Elixir SDK for the [x402](https://x402.org) HTTP payment protocol.X402 is a library, not an application. It provides protocol headers, a facilitatorclient, and optional Plug middleware without tying an application to a specificfacilitator, chain, or web framework.## Features- x402 v2 `PAYMENT-REQUIRED`, `PAYMENT-SIGNATURE`, and `PAYMENT-RESPONSE` headers- Complete v2 payment-requirement and extension-echo validation- Facilitator `/verify` and `/settle` client with retries, hooks, and telemetry- Plug/Phoenix payment gate that settles only after successful resource handling- `"exact"` and metered `"upto"` authorization flows- Optional payment-identifier idempotency cache and SIWX support- EVM and Solana wallet validation- Optional Finch, Plug, and cryptography dependencies## InstallationAdd the library and only the optional integrations your application uses:```elixirdef deps do [ {:x402, "~> 0.5.0"}, {:finch, "~> 0.19"}, # facilitator HTTP calls {:plug, "~> 1.14"} # PaymentGate ]end```Add `ex_secp256k1` and `ex_keccak` only when using the default EVM SIWXsignature verifier.## Phoenix quick startStart Finch, the facilitator client, and the idempotency cache in yourapplication supervision tree:```elixirchildren = [ {Finch, name: MyApp.Finch, pools: %{default: X402.Facilitator.HTTP.secure_pool_opts()}}, {X402.Facilitator, name: MyApp.Facilitator, url: "https://facilitator.example.com", finch: MyApp.Finch}, {X402.Extensions.PaymentIdentifier.ETSCache, name: MyApp.PaymentCache}]```Configure the Plug with the facilitator process, not a URL:```elixirplug X402.Plug.PaymentGate, facilitator: MyApp.Facilitator, payment_identifier_cache: MyApp.PaymentCache, routes: [ %{ method: :get, path: "/api/weather", scheme: "exact", price: "10000", # atomic units: 0.01 USDC when the asset has 6 decimals network: "eip155:8453", asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", pay_to: "0xYourWalletAddress", description: "Weather data API" } ]```An unpaid request receives HTTP 402 and a Base64-encoded v2`PAYMENT-REQUIRED` header. A paid request is decoded and matched against thecomplete advertised requirement, verified, passed to the protected handler,and settled immediately before a successful response is sent. Handler responseswith status 400 or greater are not settled.The verified payload and matched requirement are available to the handler as`conn.assigns.x402_payment_payload` and`conn.assigns.x402_payment_requirements`.## Metered `"upto"` paymentsFor an `"upto"` route, `price` is the maximum authorization in atomic tokenunits:```elixir%{ method: :post, path: "/api/generate", scheme: "upto", price: "1000000", # authorize up to 1 USDC for a 6-decimal asset network: "eip155:8453", asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", pay_to: "0xYourWalletAddress"}```After measuring resource use, store the actual amount on the connection beforebuilding the response:```elixirdef create(conn, params) do result = generate(params) actual_atomic_amount = billable_amount(result) {:ok, conn} = X402.Plug.PaymentGate.put_settlement_amount(conn, actual_atomic_amount) json(conn, %{result: result})end```The facilitator receives the advertised maximum during `/verify` and the actualamount during `/settle`. An amount above the authorized maximum fails closed.If no actual amount is supplied, the advertised maximum is settled.This release implements the post-handler `authorization` flow used by currentEVM `exact` and `upto` schemes. Route options declaring `paymentFlow: "upfront"`or `paymentFlow: "escrow"` are rejected because those flows require differenthandler and cancellation semantics.## Lifecycle hooksHooks receive an `X402.Hooks.Context` and must use the return contract defined by`X402.Hooks`:```elixirdefmodule MyApp.PaymentHooks do @behaviour X402.Hooks @impl true def before_verify(context, _metadata) do IO.inspect(context.payload, label: "Incoming payment") {:cont, context} end @impl true def after_verify(context, _metadata), do: {:cont, context} @impl true def on_verify_failure(context, _metadata), do: {:cont, context} @impl true def before_settle(context, _metadata), do: {:cont, context} @impl true def after_settle(context, _metadata), do: {:cont, context} @impl true def on_settle_failure(context, _metadata), do: {:cont, context}end```Pass the module with `hooks: MyApp.PaymentHooks`. Before hooks may return`{:halt, reason}`; failure hooks may return `{:recover, result}`.## Multiple payment optionsUse `accepts` to advertise more than one valid requirement:```elixir%{ method: :get, path: "/api/data", accepts: [ %{ scheme: "exact", price: "10000", network: "eip155:8453", asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", pay_to: "0xYourWalletAddress" }, %{ scheme: "exact", price: "5000", network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", asset: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", pay_to: "YourSolanaAddress" } ]}```Every core field must match exactly. Client-added metadata is allowed onlyunder `accepted.extra` and cannot remove or mutate values advertised by theserver.## Facilitator APIThe lower-level client can be called directly:```elixircase X402.Facilitator.verify( MyApp.Facilitator, payment_payload, payment_requirements ) do {:ok, %{status: 200, body: %{"isValid" => true} = result}} -> {:ok, result} {:ok, %{status: 200, body: %{"isValid" => false} = result}} -> {:error, result} {:error, reason} -> {:error, reason}end```Facilitator requests use the v2 wire object:`%{"x402Version" => 2, "paymentPayload" => payload,"paymentRequirements" => requirements}`.## HTTP outcomes`X402.Plug.PaymentGate` follows the v2 HTTP transport mapping:| Status | Meaning ||--------|---------|| 400 | Malformed or invalid payment input || 402 | Payment required, unmatched terms, or verification/settlement failure || 500 | Facilitator transport failure, malformed facilitator response, or internal payment-processing error |## Documentation- [Getting Started](https://hexdocs.pm/x402/getting-started.html)- [Plug/Phoenix Integration](https://hexdocs.pm/x402/plug-integration.html)- [Live Smoke Tests](https://hexdocs.pm/x402/live-smoke-tests.html)- [API Reference](https://hexdocs.pm/x402/api-reference.html)- [Official x402 v2 specification](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md)- [Official HTTP transport](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md)## LicenseMIT License — see [LICENSE](LICENSE) for details.