A plug-and-play SDK for HTTP 402 on-chain payment workflows, supporting USDC/ERC20 offline signing, EVM chains, and AI Agent integration.
Project description
Quick Start
x402-mock is an implementation of a payment protocol based on HTTP 402 status code, supporting token payments on EVM blockchains. This guide will help you get started quickly.
Installation
This project uses uv as the package manager.
uv add x402-mock
uv sync
Environment Configuration
Create a .env file in the project root directory to configure your private key and RPC service keys:
# Required: EVM private key (for signing and receiving payments)
EVM_PRIVATE_KEY=your_private_key_here
# Optional: API keys for Infura or Alchemy (for accessing blockchain networks)
EVM_INFURA_KEY=your_infura_key_here
EVM_ALCHEMY_KEY=your_alchemy_key_here
Core Concepts
Payment Flow Overview
x402-mock implements a payment flow with separation of responsibilities, similar to a cinema's ticketing system:
- Server (Payment Receiver): Provides services and accepts payments, similar to a cinema
- Client (Payment Payer): Requests services and completes payments, similar to an audience member
- Payment Process:
- Client requests a protected resource
- Server verifies the Client's access token (similar to ticket checking)
- If the token is invalid, returns 402 status code + payment information (similar to directing to ticket office)
- Client completes signed payment based on payment information (similar to buying a ticket)
- Client obtains access token and retries the resource request (similar to entering with ticket)
Separation of Responsibilities with Status Code 402
The HTTP 402 "Payment Required" status code implements separation of responsibilities in this project:
- Server side: Only responsible for verifying the validity of access tokens, not handling payment logic
- Payment verification: Handled by an independent
/tokenendpoint, receiving payment signatures and issuing access tokens - Client side: Automatically handles 402 responses, completes payment flow and retries requests
This design decouples payment logic from business logic, improving system maintainability and security.
Server (Payment Receiver)
Server is the party that provides services and accepts payments. Main responsibilities include:
- Defining accepted payment methods (chain, network, token)
- Verifying the authenticity and validity of payment signatures
- Completing on-chain transfer settlement
- Issuing access tokens
Creating a Server Instance
from x402_mock.servers import Http402Server, create_private_key
from x402_mock.adapters.evm.schemas import EVMPaymentComponent
# Generate access token signing key
token_key = create_private_key()
# Create Server instance (inherits from FastAPI)
app = Http402Server(
token_key=token_key, # Access token signing key
token_expires_in=300 # Token validity period (seconds)
)
Http402Server Parameter Description
| Parameter | Type | Required | Description |
|---|---|---|---|
token_key |
str | Yes | Access token signing key, can be generated using create_private_key() |
token_expires_in |
int | No | Access token validity period (seconds), default 3600 |
enable_auto_settlement |
bool | No | Whether to automatically settle payments, default True |
token_endpoint |
str | No | Token exchange endpoint path, default "/token" |
Adding Payment Methods
# Add EVM payment method
app.add_payment_method(
EVMPaymentComponent(
amount=0.5, # Payment amount (human-readable units)
currency="USDC", # Token symbol
caip2="eip155:11155111", # CAIP-2 chain identifier
token="0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238" # Token contract address
)
)
EVMPaymentComponent Parameter Description
| Parameter | Type | Required | Description |
|---|---|---|---|
amount |
float | Yes | Payment amount (human-readable units, e.g., 0.5 USDC) |
currency |
str | Yes | Token symbol (e.g., "USDC", "USDT", "ETH") |
caip2 |
str | Yes | CAIP-2 chain identifier (format: eip155:<chain_id>) |
token |
str | Recommended | Token contract address (0x prefix, 42 characters) |
pay_to |
str | No | Payment receiving address, defaults to address corresponding to environment variable private key |
rpc_url |
str | No | RPC node URL, defaults to public nodes or automatically configured based on environment variables |
token_name |
str | No | Token name, can be omitted if automatically retrieved |
token_decimals |
int/str | No | Token decimals, can be omitted if automatically retrieved |
token_version |
int/str | No | Token version, can be omitted if automatically retrieved |
Note: If token, token_name, token_decimals, token_version are not provided, the system will automatically query them based on caip2 and currency.
Protecting API Endpoints
Use the @app.payment_required decorator to protect endpoints that require payment:
@app.get("/api/protected-data")
@app.payment_required
async def get_protected_data(payload):
"""Endpoint that requires payment to access"""
return {
"message": "Payment verified successfully",
"user_address": payload["address"]
}
Event Handling
Server provides an event system where you can listen to various events during the payment process:
from x402_mock.engine.events import SettleSuccessEvent
@app.hook(SettleSuccessEvent)
async def on_settle_success(event, deps):
"""Processing logic when payment succeeds"""
print(f"✅ Payment succeeded: {event.settlement_result}")
# You can log, send notifications, etc. here
Available event types:
RequestInitEvent: Request initializationRequestTokenEvent: Token requestTokenIssuedEvent: Token issuedVerifyFailedEvent: Verification failedAuthorizationSuccessEvent: Authorization succeededHttp402PaymentEvent: Payment requiredSettleSuccessEvent: Settlement succeeded
Client (Payment Payer)
Client is the party that requests services and completes payments. Main responsibilities include:
- Registering supported payment methods
- Automatically handling 402 responses
- Generating payment signatures
- Exchanging signatures for access tokens
Creating a Client Instance
from x402_mock.clients.http_client import Http402Client
from x402_mock.adapters.evm.schemas import EVMPaymentComponent
async with Http402Client() as client:
# Register payment method
client.add_payment_method(
EVMPaymentComponent(
caip2="eip155:11155111",
amount=0.8, # Maximum payment amount limit
currency="USDC",
token="0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"
)
)
# Send request (automatically handles 402 payment flow)
response = await client.get("http://localhost:8000/api/protected-data")
print(response.json())
Special Notes on Client-side EVMPaymentComponent
On the Client side, the amount parameter represents the maximum payment amount limit. If the Server requests an amount exceeding this limit, the Client will refuse to sign the payment.
Payment Flow Automation
Http402Client inherits from httpx.AsyncClient and is fully compatible with all its methods. When receiving a 402 response, the Client automatically:
- Parses payment requirements
- Matches registered payment methods
- Generates payment signature
- Exchanges for access token at
/tokenendpoint - Retries original request with new token
The entire process is transparent to developers; you just need to use the HTTP client normally.
Complete Examples
Complete Server-side Example
from x402_mock.servers import Http402Server, create_private_key
from x402_mock.adapters.evm.schemas import EVMPaymentComponent
from x402_mock.engine.events import SettleSuccessEvent
# Create Server
token_key = create_private_key()
app = Http402Server(token_key=token_key, token_expires_in=300)
# Add payment method
app.add_payment_method(
EVMPaymentComponent(
amount=0.5,
currency="USDC",
caip2="eip155:11155111",
token="0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"
)
)
# Payment success event handling
@app.hook(SettleSuccessEvent)
async def log_payment_success(event, deps):
print(f"💰 Payment received: {event.settlement_result.authorized_amount} USDC")
# Protected API endpoint
@app.get("/api/premium-content")
@app.payment_required
async def get_premium_content(payload):
return {
"content": "This is premium content",
"paid_by": payload["address"],
"timestamp": payload.get("timestamp")
}
# Run Server (using uvicorn)
# uvicorn server:app --host 0.0.0.0 --port 8000
Complete Client-side Example
import asyncio
from x402_mock.clients.http_client import Http402Client
from x402_mock.adapters.evm.schemas import EVMPaymentComponent
async def main():
async with Http402Client() as client:
# Register payment method (supports multiple)
client.add_payment_method(
EVMPaymentComponent(
caip2="eip155:11155111",
amount=1.0, # Pay up to 1.0 USDC
currency="USDC",
token="0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"
)
)
# Request protected content (automatically handles payment)
response = await client.get("http://localhost:8000/api/premium-content")
if response.status_code == 200:
data = response.json()
print(f"✅ Content obtained: {data['content']}")
print(f" Payer: {data['paid_by']}")
else:
print(f"❌ Request failed: {response.status_code}")
if __name__ == "__main__":
asyncio.run(main())
Advanced Configuration
Custom RPC Nodes
# Server-side specify RPC node
app.add_payment_method(
EVMPaymentComponent(
amount=0.5,
currency="USDC",
caip2="eip155:1", # Ethereum mainnet
token="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", # USDC
rpc_url="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
)
)
Multi-Chain Support
# Support payment methods for multiple chains
app.add_payment_method(
EVMPaymentComponent(
amount=0.1,
currency="ETH",
caip2="eip155:1", # Ethereum mainnet
token=None # Use native token
)
)
app.add_payment_method(
EVMPaymentComponent(
amount=1.0,
currency="USDC",
caip2="eip155:42161", # Arbitrum
token="0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"
)
)
On-chain Information Retrieval Tools
When configuring payment methods, you may need to obtain various on-chain information such as RPC node addresses, token contract addresses, token decimals, and versions. x402-mock provides a series of utility methods to simplify retrieving this information.
Import Utility Methods
from x402_mock.adapters.evm.constants import (
EvmChainInfoFromEthereumLists,
EvmPublicRpcFromChainList,
EvmTokenListFromUniswap,
fetch_erc20_name_version_decimals,
get_rpc_key_from_env
)
Method Description
1. EvmChainInfoFromEthereumLists
Function: Retrieve detailed EVM chain configuration information from the ethereum-lists repository.
Main uses:
- Get chain's Infura/Alchemy RPC URLs (with API key placeholders)
- Get public RPC node lists
- Get basic chain information (name, explorer addresses, etc.)
Example usage:
chain_info = EvmChainInfoFromEthereumLists()
# Get Infura RPC URL (needs API key filled)
infura_url = chain_info.get_infura_rpc_url("eip155:1")
# Returns something like: https://mainnet.infura.io/v3/{RPC_KEYS}
# Get Alchemy RPC URL (needs API key filled)
alchemy_url = chain_info.get_alchemy_rpc_url("eip155:1")
# Returns something like: https://eth-mainnet.g.alchemy.com/v2/{RPC_KEYS}
# Get public RPC node list
public_rpcs = chain_info.get_public_rpc_urls("eip155:1")
# Returns: ["https://api.mycryptoapi.com/eth", ...]
2. EvmPublicRpcFromChainList
Function: Retrieve public RPC node information from Chainlist.org.
Main uses:
- Get public RPC nodes with no tracking or limited tracking
- Select RPC nodes based on privacy preferences
- Support HTTPS and WebSocket protocols
Example usage:
rpc_finder = EvmPublicRpcFromChainList()
# Get public RPC node with no tracking
public_rpc = rpc_finder.pick_public_rpc(
caip2="eip155:1",
start_with="https://",
tracking_type="none"
)
# Returns something like: https://rpc.ankr.com/eth
# Get all public RPC information for a specific chain
chain_rpcs = rpc_finder.get_specific_chain_public_rpcs("eip155:1")
3. EvmTokenListFromUniswap
Function: Retrieve token information from Uniswap's official token list.
Main uses:
- Get token contract addresses and decimals
- Support multi-chain token queries
- Automatic data caching to reduce network requests
Example usage:
token_finder = EvmTokenListFromUniswap()
# Get token address and decimals
address, decimals = token_finder.get_token_address_and_decimals(
caip2="eip155:1",
symbol="USDC"
)
# Returns: ("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", 6)
4. fetch_erc20_name_version_decimals
Function: Query token details directly from chain RPC.
Main uses:
- Query token's
name(),version()anddecimals()functions - Verify complete token contract information
- Get latest on-chain data
Example usage:
# Query token information from chain
name, version, decimals = fetch_erc20_name_version_decimals(
rpc_url="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
token_address="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
)
# Returns: ("USD Coin", "2", 6)
5. get_rpc_key_from_env
Function: Get RPC service provider API keys from environment variables.
Main uses:
- Securely get Infura or Alchemy API keys
- Support custom environment variable names
- Returns
Noneto indicate using public nodes
Example usage:
# Get Infura key (default)
infura_key = get_rpc_key_from_env("EVM_INFURA_KEY")
# Get Alchemy key
alchemy_key = get_rpc_key_from_env("EVM_ALCHEMY_KEY")
# Build RPC URL using key
if infura_key:
rpc_url = f"https://mainnet.infura.io/v3/{infura_key}"
else:
# Use public node
rpc_finder = EvmPublicRpcFromChainList()
rpc_url = rpc_finder.pick_public_rpc("eip155:1")
Auto-filling Payment Components
These utility methods are typically used internally by EVMPaymentComponent to automatically fill in missing information:
# Only provide basic information, system automatically queries missing data
payment = EVMPaymentComponent(
amount=0.5,
currency="USDC",
caip2="eip155:1"
# token, token_name, token_decimals, token_version are automatically queried
)
# System internally will:
# 1. Use EvmTokenListFromUniswap to get USDC contract address and decimals
# 2. Use fetch_erc20_name_version_decimals to get token name and version
# 3. Use EvmPublicRpcFromChainList to get public RPC node
# 4. Use get_rpc_key_from_env to check if there are private RPC keys
Best Practices
- Production environment: Recommend providing complete
token,rpc_url, etc. information to reduce network queries - Development environment: Can rely on automatic queries to simplify configuration
- Performance considerations: Initial queries make network requests, subsequent uses cache
- Error handling: When network is unavailable, automatically falls back to built-in default configurations
Troubleshooting
Common Issues
-
Payment fails after 402 response
- Check if private key configuration is correct
- Confirm sufficient token balance
- Verify chain ID and token address match
-
Token verification fails
- Check if
token_keyis consistent - Confirm token hasn't expired
- Verify signature algorithm
- Check if
-
RPC connection issues
- Check network connection
- Confirm RPC URL is valid
- Consider using backup nodes
Debug Mode
Enable detailed logging:
import logging
logging.basicConfig(level=logging.DEBUG)
Next Steps
- View API Reference Documentation for detailed interface specifications
- Explore Example Code
- Understand Event System to implement custom business logic
Tips: In production environments, please ensure:
- Use secure key management solutions
- Configure appropriate timeout and retry strategies
- Monitor payment success rate and failure reasons
- Regularly update dependencies to get security fixes
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file x402_mock-0.2.2.tar.gz.
File metadata
- Download URL: x402_mock-0.2.2.tar.gz
- Upload date:
- Size: 88.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
abf52226b59555a71cd1c22fc343ad868313c06a52fe5a28529bf99fbea8ddc0
|
|
| MD5 |
d2df455f5c1737f737834d25e80ddb21
|
|
| BLAKE2b-256 |
22cb6e7fa2f560708fbac553dc816e38a60e1b0bcb5f2c046f7daac230e89c0d
|
Provenance
The following attestation bundles were made for x402_mock-0.2.2.tar.gz:
Publisher:
publish.yml on OpenPayhub/x402-mock
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
x402_mock-0.2.2.tar.gz -
Subject digest:
abf52226b59555a71cd1c22fc343ad868313c06a52fe5a28529bf99fbea8ddc0 - Sigstore transparency entry: 1017702020
- Sigstore integration time:
-
Permalink:
OpenPayhub/x402-mock@ba98e5a93f9c791745b273a46fe2003a1084f363 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/OpenPayhub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ba98e5a93f9c791745b273a46fe2003a1084f363 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file x402_mock-0.2.2-py3-none-any.whl.
File metadata
- Download URL: x402_mock-0.2.2-py3-none-any.whl
- Upload date:
- Size: 94.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80c00dd9e62c92c15056b9598d5649c23b2dda189c1e2b6bcf7d06e7ccec34f9
|
|
| MD5 |
17768049c72aa2fa7f42030d6cf9b986
|
|
| BLAKE2b-256 |
222e6d436fcb16624276cdef61bdac052cf25528bdd98ad8317d2ecb1f68bc86
|
Provenance
The following attestation bundles were made for x402_mock-0.2.2-py3-none-any.whl:
Publisher:
publish.yml on OpenPayhub/x402-mock
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
x402_mock-0.2.2-py3-none-any.whl -
Subject digest:
80c00dd9e62c92c15056b9598d5649c23b2dda189c1e2b6bcf7d06e7ccec34f9 - Sigstore transparency entry: 1017702030
- Sigstore integration time:
-
Permalink:
OpenPayhub/x402-mock@ba98e5a93f9c791745b273a46fe2003a1084f363 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/OpenPayhub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ba98e5a93f9c791745b273a46fe2003a1084f363 -
Trigger Event:
workflow_dispatch
-
Statement type: