x402-axum
Axum middleware for protecting routes with x402 protocol payments.
This crate provides a drop-in tower::Layer that intercepts incoming requests,
validates X-Payment headers using a configured x402 facilitator,
and settles the payment before responding.
If no valid payment is provided, a 402 Payment Required response is returned with details about accepted assets and amounts.
Features
- Built for Axum
- Fluent builder API for composing payment requirements and prices
- Enforces on-chain payment verification before executing protected handlers
- Configurable settlement timing (before or after request execution)
- Returns standards-compliant
402 Payment Requiredresponses - Emits rich tracing spans with optional OpenTelemetry integration (
telemetryfeature) - Compatible with any x402 facilitator (remote or in-process)
Installation
Add to your Cargo.toml:
= "0.6"
If you want to enable tracing and OpenTelemetry support, use the telemetry feature (make sure to register a tracing subscriber in your application):
= { = "0.6", = ["telemetry"] }
Specifying Prices
Prices in x402 are defined using the PriceTag struct. A PriceTag includes:
- Asset (
asset) — the ERC-20 token used for payment - Amount (
amount) — the required token amount, either as an integer or a human-readable decimal - Recipient (
pay_to) — the address that will receive the tokens
You can construct PriceTags directly or use fluent builder helpers that simplify common flows.
Asset
Bring Your Own Token
If you're integrating a custom token, define it using TokenDeployment. This includes token address, decimals, the network it lives on, and EIP-712 metadata (name/version):
use ;
use Network;
let asset = TokenDeployment ;
Known tokens (like USDC)
For common stablecoins like USDC, you can use the convenience struct USDCDeployment:
use ;
let asset = by_network;
Amount
Human-Readable Amounts
Use .amount("0.025") on asset to define a price using a string or a number.
This will be converted to the correct on-chain amount based on the asset’s decimals:
usdc.amount // → 25000 for 0.025 USDC with 6 decimals
Raw Token Amounts
If you already know the amount in base units (e.g. 25000 for 0.025 USDC with 6 decimals), use .token_amount(...):
usdc.token_amount
This will use the value onchain verbatim.
Recipient
Use .pay_to(...) to set the address that should receive the payment.
let price_tag = usdc.amount.pay_to.unwrap;
Integrating with Middleware
Once you’ve created your PriceTag, pass it to the middleware:
let x402 = try_from.unwrap;
let usdc = by_network;
let app = new.route;
You can extract shared fields like the payment recipient, then vary prices per route:
let x402 = try_from.unwrap;
let asset = by_network
.pay_to; // Both /vip-content and /extra-vip-content are paid to 0xYourAddress
let app: Router = new
.route
.route;
Settlement Timing
By default, the middleware settles payments after request execution. You can control this behavior with settle_before_execution.
let x402 = try_from.unwrap
.settle_before_execution;
This is useful when you want to:
- Avoid failed settlements requiring external retry mechanisms
- Prevent payment authorization expiration before final settlement
- Ensure payment is settled before granting access to the resource
Example
use ;
use X402Middleware;
use IntoPriceTag;
use ;
use StatusCode;
use json;
async
async
HTTP Behavior
If no valid payment is included, the middleware responds with:
// HTTP/1.1 402 Payment Required
// Content-Type: application/json
{
"error": "X-PAYMENT header is required",
"accepts": [
// Payment Requirements
],
"x402Version": 1
}
Configuring Input and Output Schemas
You can provide detailed metadata about your API endpoints using with_input_schema() and with_output_schema(). These schemas are embedded in the PaymentRequirements.outputSchema field and can be used by discovery services, documentation generators, or clients to understand your API.
Input Schema
The input schema describes the expected request format, including HTTP method, query parameters, headers, and whether the endpoint is publicly discoverable:
use json;
let x402 = try_from.unwrap;
let app = new.route;
Output Schema
The output schema describes the response format:
let app = new.route;
Discoverable vs Private Endpoints
You can control whether your endpoint appears in public discovery services by setting the discoverable flag:
// Public endpoint - will appear in x402 Bazaar
x402.with_input_schema
// Private endpoint - direct access only
x402.with_input_schema
The combined input and output schemas are automatically embedded in PaymentRequirements.outputSchema as:
Optional Telemetry
If the telemetry feature is enabled, the middleware emits structured tracing spans such as:
x402.handle_request,x402.verify_payment,x402.settle_payment,
You can connect these to OpenTelemetry exporters like Jaeger, Tempo, or Otel Collector.
To enable:
[]
= { = "0.6", = ["telemetry"] }
Related Crates
- x402-rs: Core x402 types, facilitator traits, helpers.