Plug middleware that gates configured routes behind x402 v2 payment verification.
For matching routes:
- Requests without a
PAYMENT-SIGNATUREheader receive 402 with a Base64-encodedPAYMENT-REQUIREDheader (PaymentRequiredv2 schema). - Requests with
PAYMENT-SIGNATUREare decoded asPaymentPayloadv2. PaymentPayload.acceptedand echoed extensions are matched against the complete requirements advertised by the route.- Matched requirements are verified before the protected handler runs.
- Successful handler responses are settled immediately before they are
sent. Successful settlements attach a
PAYMENT-RESPONSEheader and assign:x402_payment_payload/:x402_payment_requirementson the conn.
HTTP status mapping (HTTP transport v2):
- 402 — payment required, no matching requirements, or payment failed
- 400 — malformed / invalid payment payload (including wrong
x402Version) - 500 — facilitator transport failures or malformed facilitator responses
See the official x402 v2 specification and HTTP transport.
Summary
Functions
Gates matching requests behind x402 v2 payment verification.
Validates and compiles X402.Plug.PaymentGate options.
Stores the actual atomic amount to settle for an "upto" route.
Types
@type options() :: %{ facilitator: X402.Facilitator.server(), hooks: module(), payment_identifier_cache: X402.Extensions.PaymentIdentifier.ETSCache.server() | nil, routes: [compiled_route()] }
Configuration map produced by init/1.
Functions
@spec call(Plug.Conn.t(), options()) :: Plug.Conn.t()
Gates matching requests behind x402 v2 payment verification.
Validates and compiles X402.Plug.PaymentGate options.
Options
:facilitator(term/0) - Facilitator server pid/name used for verification and settlement. The default value isX402.Facilitator.:hooks- Lifecycle hook module implementingX402.Hooks. The default value isX402.Hooks.Default.:payment_identifier_cache- OptionalETSCacheserver pid/name used for idempotency. When set, the plug performs an atomic claim (viaput_new) on the payment proof hash before settling, preventing concurrent requests from double-settling the same payment. The default value isnil.:routes- Required. Route gate definitions (see route options below).
Route options
:method- Required. HTTP method for the route (:anymatches all methods).:path(String.t/0) - Required. Route path, supporting exact matches and*globs (for example/api/*).:accepts(list ofmap/0) - Payment options advertised inPAYMENT-REQUIRED.accepts. When empty, a single option is built from the top-level:scheme,:price,:network,:asset, and:pay_tofields. The default value is[].:scheme- Single-option scheme (used when:acceptsis empty). The default value is"exact".:price- Single-option amount (required when:acceptsis empty).:network(String.t/0) - Single-option CAIP-2 network (required when:acceptsis empty).:asset(String.t/0) - Single-option asset (required when:acceptsis empty).:pay_to(String.t/0) - Single-option payTo (required when:acceptsis empty).:description(String.t/0) - ResourceInfo.description. The default value is"Payment required".:mime_type(String.t/0) - ResourceInfo.mimeType. The default value is"application/json".:service_name- ResourceInfo.serviceName (printable ASCII, max 32 characters recommended). The default value isnil.:tags(list ofString.t/0) - ResourceInfo.tags (max 5 recommended). The default value is[].:icon_url- ResourceInfo.iconUrl (absolute http(s) URL). The default value isnil.:max_timeout_seconds(pos_integer/0) - Default maxTimeoutSeconds for single-option routes. The default value is60.:extra- Default extra map for single-option routes. The default value is%{}.:extensions- Protocol extensions advertised in PaymentRequired.extensions. The default value is%{}.
Accept option fields (inside :accepts)
:scheme- Payment scheme (exactorupto). The default value is"exact".:price- Required. Payment amount in atomic token units (PaymentRequirementsamount). Forexactthis is the required amount; foruptoit is the maximum authorized amount.:network(String.t/0) - Required. Blockchain network in CAIP-2 format (for exampleeip155:84532).:asset(String.t/0) - Required. Token contract address or asset identifier.:pay_to(String.t/0) - Required. Recipient wallet address (payToin the PaymentRequirements schema).:max_timeout_seconds(pos_integer/0) - Maximum time allowed for payment completion. The default value is60.:extra- Scheme-specific extra fields (string or atom keys). The default value is%{}.
@spec put_settlement_amount(Plug.Conn.t(), String.t() | non_neg_integer()) :: {:ok, Plug.Conn.t()} | {:error, :invalid_settlement_amount}
Stores the actual atomic amount to settle for an "upto" route.
Call this from the protected handler after resource consumption is known. When omitted, the route's advertised maximum is settled.
Examples
iex> conn = Plug.Test.conn(:get, "/paid")
iex> {:ok, conn} = X402.Plug.PaymentGate.put_settlement_amount(conn, "7500")
iex> conn.private[:x402_settlement_amount]
"7500"
iex> X402.Plug.PaymentGate.put_settlement_amount(Plug.Test.conn(:get, "/paid"), "1.5")
{:error, :invalid_settlement_amount}