X402.Plug.PaymentGate (X402 v0.4.1)

Copy Markdown View Source

Plug middleware that gates configured routes behind x402 v2 payment verification.

For matching routes:

  1. Requests without a PAYMENT-SIGNATURE header receive 402 with a Base64-encoded PAYMENT-REQUIRED header (PaymentRequired v2 schema).
  2. Requests with PAYMENT-SIGNATURE are decoded as PaymentPayload v2.
  3. PaymentPayload.accepted and echoed extensions are matched against the complete requirements advertised by the route.
  4. Matched requirements are verified before the protected handler runs.
  5. Successful handler responses are settled immediately before they are sent. Successful settlements attach a PAYMENT-RESPONSE header and assign :x402_payment_payload / :x402_payment_requirements on 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

Types

Configuration map produced by init/1.

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

options()

@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

call(conn, map)

(since 0.1.0)
@spec call(Plug.Conn.t(), options()) :: Plug.Conn.t()

Gates matching requests behind x402 v2 payment verification.

init(opts)

(since 0.1.0)
@spec init(keyword()) :: options()

Validates and compiles X402.Plug.PaymentGate options.

Options

  • :facilitator (term/0) - Facilitator server pid/name used for verification and settlement. The default value is X402.Facilitator.

  • :hooks - Lifecycle hook module implementing X402.Hooks. The default value is X402.Hooks.Default.

  • :payment_identifier_cache - Optional ETSCache server pid/name used for idempotency. When set, the plug performs an atomic claim (via put_new) on the payment proof hash before settling, preventing concurrent requests from double-settling the same payment. The default value is nil.

  • :routes - Required. Route gate definitions (see route options below).

Route options

  • :method - Required. HTTP method for the route (:any matches all methods).

  • :path (String.t/0) - Required. Route path, supporting exact matches and * globs (for example /api/*).

  • :accepts (list of map/0) - Payment options advertised in PAYMENT-REQUIRED.accepts. When empty, a single option is built from the top-level :scheme, :price, :network, :asset, and :pay_to fields. The default value is [].

  • :scheme - Single-option scheme (used when :accepts is empty). The default value is "exact".

  • :price - Single-option amount (required when :accepts is empty).

  • :network (String.t/0) - Single-option CAIP-2 network (required when :accepts is empty).

  • :asset (String.t/0) - Single-option asset (required when :accepts is empty).

  • :pay_to (String.t/0) - Single-option payTo (required when :accepts is 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 is nil.

  • :tags (list of String.t/0) - ResourceInfo.tags (max 5 recommended). The default value is [].

  • :icon_url - ResourceInfo.iconUrl (absolute http(s) URL). The default value is nil.

  • :max_timeout_seconds (pos_integer/0) - Default maxTimeoutSeconds for single-option routes. The default value is 60.

  • :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 (exact or upto). The default value is "exact".

  • :price - Required. Payment amount in atomic token units (PaymentRequirements amount). For exact this is the required amount; for upto it is the maximum authorized amount.

  • :network (String.t/0) - Required. Blockchain network in CAIP-2 format (for example eip155:84532).

  • :asset (String.t/0) - Required. Token contract address or asset identifier.

  • :pay_to (String.t/0) - Required. Recipient wallet address (payTo in the PaymentRequirements schema).

  • :max_timeout_seconds (pos_integer/0) - Maximum time allowed for payment completion. The default value is 60.

  • :extra - Scheme-specific extra fields (string or atom keys). The default value is %{}.

put_settlement_amount(conn, amount)

(since 0.4.0)
@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}