OpenLibx402 is a library ecosystem that implements the X402 protocol for enabling autonomous, frictionless payments in AI agents and web APIs. The protocol leverages HTTP 402 "Payment Required" status code and Solana blockchain for instant, low-cost transactions.
# PythonfromdataclassesimportdataclassfromtypingimportOptionalfromdatetimeimportdatetime@dataclassclassPaymentRequest:"""Represents an X402 payment request (402 response)"""max_amount_required:str# Amount in token units (e.g., "0.10")asset_type:str# "SPL" for Solana tokensasset_address:str# Token mint addresspayment_address:str# Recipient's wallet addressnetwork:str# "solana-devnet" | "solana-mainnet"expires_at:datetime# Expiration timestampnonce:str# Unique identifier for replay protectionpayment_id:str# Unique payment request IDresource:str# API endpoint being accesseddescription:Optional[str]=None# Human-readable descriptiondefto_dict(self)->dict:"""Convert to JSON-serializable dict"""pass@classmethoddeffrom_dict(cls,data:dict)->'PaymentRequest':"""Parse from 402 response JSON"""passdefis_expired(self)->bool:"""Check if payment request has expired"""pass
@dataclassclassPaymentAuthorization:"""Signed payment authorization to be sent with retry request"""payment_id:str# From payment requestactual_amount:str# Amount being paid (≤ max_amount_required)payment_address:str# Recipient addressasset_address:str# Token mint addressnetwork:str# Blockchain networktimestamp:datetime# Authorization timestampsignature:str# Solana signaturepublic_key:str# Payer's public keytransaction_hash:Optional[str]# On-chain tx hash (after broadcast)defto_header_value(self)->str:"""Encode as X-Payment-Authorization header value"""pass@classmethoddeffrom_header(cls,header_value:str)->'PaymentAuthorization':"""Parse from request header"""pass
fromsolana.rpc.async_apiimportAsyncClientfromsolana.transactionimportTransactionfromsolders.keypairimportKeypairfromsolders.pubkeyimportPubkeyfromspl.token.instructionsimporttransfer_checked,TransferCheckedParamsclassSolanaPaymentProcessor:"""Handles Solana blockchain operations"""def__init__(self,rpc_url:str,keypair:Optional[Keypair]=None):self.client=AsyncClient(rpc_url)self.keypair=keypairasyncdefcreate_payment_transaction(self,request:PaymentRequest,amount:str,payer_keypair:Keypair)->Transaction:"""Create a Solana transaction for the payment"""passasyncdefsign_and_send_transaction(self,transaction:Transaction,keypair:Keypair)->str:"""Sign and broadcast transaction, return tx hash"""passasyncdefverify_transaction(self,transaction_hash:str,expected_recipient:str,expected_amount:str,expected_token_mint:str)->bool:"""Verify a transaction was successful and matches expectations"""passasyncdefget_token_balance(self,wallet_address:str,token_mint:str)->float:"""Get token balance for a wallet"""pass
classX402Error(Exception):"""Base exception for X402 protocol errors"""code:strmessage:strdetails:Optional[dict]classPaymentRequiredError(X402Error):"""Raised when 402 response received"""code="PAYMENT_REQUIRED"payment_request:PaymentRequestclassPaymentExpiredError(X402Error):"""Payment request has expired"""code="PAYMENT_EXPIRED"classInsufficientFundsError(X402Error):"""Wallet has insufficient funds"""code="INSUFFICIENT_FUNDS"required_amount:stravailable_amount:strclassPaymentVerificationError(X402Error):"""Payment verification failed"""code="PAYMENT_VERIFICATION_FAILED"classTransactionBroadcastError(X402Error):"""Failed to broadcast transaction"""code="TRANSACTION_BROADCAST_FAILED"classInvalidPaymentRequestError(X402Error):"""Payment request format is invalid"""code="INVALID_PAYMENT_REQUEST"
fromlangchain.toolsimportBaseToolfrompydanticimportFieldfromtypingimportOptionalclassX402PaymentTool(BaseTool):""" LangChain tool that allows agents to make payments for API access Usage: wallet_keypair = load_keypair() payment_tool = X402PaymentTool( wallet_keypair=wallet_keypair, name="pay_for_api", description="Make payment to access premium API data" ) agent = initialize_agent( tools=[payment_tool, ...], llm=llm, ) """name:str="x402_payment"description:str=("Make an X402 payment to access a paid API endpoint. ""Input should be a JSON string with 'url' and optional 'amount'. ""Returns the API response after successful payment.")wallet_keypair:Keypair=Field(exclude=True)rpc_url:Optional[str]=Field(default=None,exclude=True)max_payment:Optional[str]=Field(default="1.0",exclude=True)def_run(self,url:str,amount:Optional[str]=None,method:str="GET",**kwargs)->str:"""Synchronous run (calls async version)"""importasyncioreturnasyncio.run(self._arun(url,amount,method,**kwargs))asyncdef_arun(self,url:str,amount:Optional[str]=None,method:str="GET",**kwargs)->str:""" Make paid API request Args: url: API endpoint URL amount: Optional payment amount override method: HTTP method **kwargs: Additional request parameters Returns: API response as string """client=X402AutoClient(wallet_keypair=self.wallet_keypair,rpc_url=self.rpc_url,max_payment_amount=self.max_payment,)try:response=awaitclient.fetch(url,method=method,**kwargs)returnresponse.textexceptX402Errorase:returnf"Payment error: {e.code} - {e.message}"
fromlangchain.requestsimportRequestsWrapperfromtypingimportDict,Any,OptionalclassX402RequestsWrapper(RequestsWrapper):""" Drop-in replacement for LangChain's RequestsWrapper with X402 support Usage: from langchain.agents import load_tools wallet_keypair = load_keypair() requests_wrapper = X402RequestsWrapper( wallet_keypair=wallet_keypair ) # Use with LangChain tools that make HTTP requests tools = load_tools( ["requests_all"], llm=llm, requests_wrapper=requests_wrapper ) """def__init__(self,wallet_keypair:Keypair,rpc_url:Optional[str]=None,max_payment:Optional[str]="1.0",**kwargs):super().__init__(**kwargs)self.client=X402AutoClient(wallet_keypair=wallet_keypair,rpc_url=rpc_url,max_payment_amount=max_payment,)asyncdefaget(self,url:str,**kwargs)->str:"""Async GET with X402 support"""response=awaitself.client.get(url,**kwargs)returnresponse.textasyncdefapost(self,url:str,**kwargs)->str:"""Async POST with X402 support"""response=awaitself.client.post(url,**kwargs)returnresponse.textdefget(self,url:str,**kwargs)->str:"""Sync GET with X402 support"""importasyncioreturnasyncio.run(self.aget(url,**kwargs))defpost(self,url:str,**kwargs)->str:"""Sync POST with X402 support"""importasyncioreturnasyncio.run(self.apost(url,**kwargs))
fromlangchain.chat_modelsimportChatOpenAIfromlangchain.agentsimportinitialize_agent,AgentTypedefcreate_x402_agent(wallet_keypair:Keypair,llm:Optional[Any]=None,tools:Optional[list]=None,rpc_url:Optional[str]=None,max_payment:str="1.0",**agent_kwargs):""" Convenience function to create LangChain agent with X402 support Usage: agent = create_x402_agent( wallet_keypair=my_keypair, llm=ChatOpenAI(), tools=[custom_tool_1, custom_tool_2], max_payment="5.0" ) response = agent.run("Get me premium market data from api.example.com") """fromlangchain.agentsimportload_tools# Create X402-enabled requests wrapperrequests_wrapper=X402RequestsWrapper(wallet_keypair=wallet_keypair,rpc_url=rpc_url,max_payment=max_payment,)# Load standard tools with X402 wrapperx402_tools=load_tools(["requests_all"],llm=llm,requests_wrapper=requests_wrapper)# Add custom toolsiftools:x402_tools.extend(tools)# Create agentagent=initialize_agent(tools=x402_tools,llm=llmorChatOpenAI(),agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,**agent_kwargs)returnagent
asyncdeffetch_with_payment_node(state:AgentState)->AgentState:""" Combined node that fetches API and handles payment automatically This is simpler than separate nodes but gives less control """client=X402AutoClient(wallet_keypair=state["wallet_keypair"],max_payment_amount="1.0",)try:response=awaitclient.fetch(state["api_url"],method="GET")state["api_response"]=response.textstate["payment_completed"]=TrueexceptInsufficientFundsErrorase:state["payment_error"]=f"Insufficient funds: need {e.required_amount}"exceptX402Errorase:state["payment_error"]=f"{e.code}: {e.message}"returnstate
fromlanggraph.graphimportStateGraph,ENDdefcreate_x402_workflow(wallet_keypair:Keypair)->StateGraph:""" Create a LangGraph workflow with X402 payment support Workflow: 1. Determine what API to call 2. Check if payment required 3. Make payment if needed 4. Process response """workflow=StateGraph(AgentState)# Add nodesworkflow.add_node("plan",planning_node)workflow.add_node("fetch_api",fetch_api_node)workflow.add_node("make_payment",payment_node)workflow.add_node("process",process_response_node)# Set entry pointworkflow.set_entry_point("plan")# Add edgesworkflow.add_edge("plan","fetch_api")workflow.add_conditional_edges("fetch_api",check_payment_required,{"payment_required":"make_payment","success":"process","error":END})workflow.add_edge("make_payment","fetch_api")# Retry after paymentworkflow.add_edge("process",END)returnworkflow.compile()
defcreate_payment_capable_graph(state_schema:type,wallet_keypair:Keypair,rpc_url:Optional[str]=None,)->StateGraph:""" Create a StateGraph with X402 payment capabilities built-in Automatically adds wallet_keypair to state and provides helper methods """passdefadd_payment_node(graph:StateGraph,node_name:str="x402_payment",max_payment:str="1.0",)->StateGraph:"""Add a payment node to existing graph"""pass
fromdataclassesimportdataclass@dataclassclassRetryConfig:"""Configuration for automatic retry behavior"""enabled:bool=Truemax_retries:int=1# For payment, 1 retry is usually sufficientretry_on_402:bool=True# Retry when 402 receivedretry_on_network_error:bool=Trueexponential_backoff:bool=False# Usually not needed for 402classX402ClientWithRetry:"""Client with configurable retry logic"""def__init__(self,wallet_keypair:Keypair,retry_config:Optional[RetryConfig]=None,):self.wallet_keypair=wallet_keypairself.retry_config=retry_configorRetryConfig()self.client=X402AutoClient(wallet_keypair=wallet_keypair,auto_retry=retry_config.enabledifretry_configelseTrue,)
ERROR_CODES={"PAYMENT_REQUIRED":{"code":"PAYMENT_REQUIRED","message":"Payment is required to access this resource","retry":True,"user_action":"Ensure wallet has sufficient funds and retry"},"PAYMENT_EXPIRED":{"code":"PAYMENT_EXPIRED","message":"Payment request has expired","retry":True,"user_action":"Request a new payment authorization"},"INSUFFICIENT_FUNDS":{"code":"INSUFFICIENT_FUNDS","message":"Wallet has insufficient token balance","retry":False,"user_action":"Add funds to wallet"},"PAYMENT_VERIFICATION_FAILED":{"code":"PAYMENT_VERIFICATION_FAILED","message":"Server could not verify payment","retry":True,"user_action":"Contact API provider if issue persists"},"TRANSACTION_BROADCAST_FAILED":{"code":"TRANSACTION_BROADCAST_FAILED","message":"Failed to broadcast transaction to blockchain","retry":True,"user_action":"Check network connection and RPC endpoint"},"INVALID_PAYMENT_REQUEST":{"code":"INVALID_PAYMENT_REQUEST","message":"Payment request format is invalid","retry":False,"user_action":"Contact API provider"},}
# In openlibx402-coreclassMockSolanaPaymentProcessor(SolanaPaymentProcessor):"""Mock processor for testing without real blockchain"""def__init__(self,*args,**kwargs):super().__init__(*args,**kwargs)self.transactions:list=[]self.balance=100.0# Mock balanceasyncdefcreate_payment_transaction(self,*args,**kwargs):"""Return mock transaction"""returnMockTransaction()asyncdefsign_and_send_transaction(self,*args,**kwargs):"""Return mock tx hash"""tx_hash=f"mock_tx_{len(self.transactions)}"self.transactions.append(tx_hash)returntx_hashasyncdefverify_transaction(self,*args,**kwargs):"""Always verify successfully"""returnTrueasyncdefget_token_balance(self,*args,**kwargs):"""Return mock balance"""returnself.balanceclassTestServer:"""Mock X402 server for testing"""def__init__(self,payment_address:str,token_mint:str):self.payment_address=payment_addressself.token_mint=token_mintself.payments_received:list=[]defrequire_payment(self,amount:str,resource:str):"""Decorator that adds payment requirement"""passdefstart(self,port:int=8402):"""Start test server"""pass
importpytestfromopenlibx402_core.testingimportMockSolanaPaymentProcessor,TestServer@pytest.mark.asyncioasyncdeftest_payment_flow():"""Test complete payment flow"""# Setup test serverserver=TestServer(payment_address="mock_address",token_mint="mock_usdc")server.start(port=8402)# Create client with mock processorkeypair=Keypair()# Generate test keypairclient=X402AutoClient(wallet_keypair=keypair)client.client.processor=MockSolanaPaymentProcessor()# Make request to paywalled endpointresponse=awaitclient.fetch("http://localhost:8402/premium-data")# Verify payment was madeassertresponse.status_code==200assertlen(client.client.processor.transactions)==1# Cleanupserver.stop()
# examples/langchain-agent/main.pyfromlangchain.chat_modelsimportChatOpenAIfromlangchain.agentsimportinitialize_agent,AgentTypefromopenlibx402_langchainimportX402PaymentTool,X402RequestsWrapperfromsolders.keypairimportKeypairimportosimportjson# Load walletwithopen("wallet.json")asf:wallet_data=json.load(f)keypair=Keypair.from_bytes(bytes(wallet_data))# Create X402-enabled toolspayment_tool=X402PaymentTool(wallet_keypair=keypair,max_payment="5.0",rpc_url="https://api.devnet.solana.com")# Create requests wrapper for automatic payment handlingrequests_wrapper=X402RequestsWrapper(wallet_keypair=keypair,max_payment="1.0")# Load LangChain tools with X402 supportfromlangchain.agentsimportload_toolstools=load_tools(["requests_all"],llm=ChatOpenAI(),requests_wrapper=requests_wrapper)tools.append(payment_tool)# Create agentagent=initialize_agent(tools=tools,llm=ChatOpenAI(temperature=0),agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,verbose=True)# Run agentresponse=agent.run("Get me the premium market data from http://localhost:8000/premium-data ""and tell me the current price")print(response)
# examples/langgraph-workflow/main.pyfromtypingimportTypedDict,Annotated,Optionalfromlanggraph.graphimportStateGraph,ENDfromopenlibx402_langgraphimportpayment_node,check_payment_requiredfromopenlibx402_clientimportX402AutoClientfromsolders.keypairimportKeypairimportjsonimportasyncio# Define stateclassResearchState(TypedDict):query:strapi_url:strapi_response:Optional[str]payment_required:boolpayment_completed:boolpayment_error:Optional[str]wallet_keypair:Keypairfinal_answer:Optional[str]# Load walletwithopen("wallet.json")asf:wallet_data=json.load(f)keypair=Keypair.from_bytes(bytes(wallet_data))# Define nodesdefplan_node(state:ResearchState)->ResearchState:"""Determine which API to call"""state["api_url"]="http://localhost:8000/premium-data"state["payment_required"]=Falsereturnstateasyncdeffetch_api_node(state:ResearchState)->ResearchState:"""Fetch from API"""client=X402AutoClient(wallet_keypair=state["wallet_keypair"],auto_retry=False,# We'll handle retry via graph)try:response=awaitclient.fetch(state["api_url"])state["api_response"]=response.textstate["payment_required"]=FalseexceptExceptionase:if"402"instr(e):state["payment_required"]=Trueelse:state["payment_error"]=str(e)returnstatedefprocess_node(state:ResearchState)->ResearchState:"""Process API response"""ifstate["api_response"]:# Parse and format responsestate["final_answer"]=f"Retrieved data: {state['api_response']}"returnstate# Build workflowworkflow=StateGraph(ResearchState)workflow.add_node("plan",plan_node)workflow.add_node("fetch",fetch_api_node)workflow.add_node("payment",payment_node)# From openlibx402-langgraphworkflow.add_node("process",process_node)workflow.set_entry_point("plan")workflow.add_edge("plan","fetch")workflow.add_conditional_edges("fetch",check_payment_required,# From openlibx402-langgraph{"payment_required":"payment","success":"process","error":END})workflow.add_edge("payment","fetch")# Retry after paymentworkflow.add_edge("process",END)# Compile and runapp=workflow.compile()# Executeresult=app.invoke({"query":"Get premium market data","wallet_keypair":keypair,})print(result["final_answer"])
# pyproject.toml example for openlibx402-core[build-system]requires=["hatchling"]build-backend="hatchling.build"[project]name="openlibx402-core"version="0.1.1"description="Core implementation of X402 payment protocol"authors=[{name="OpenLibx402 Contributors",email="hello@openlibx402.org"},]readme="README.md"license={text="MIT"}requires-python=">=3.8"dependencies=["solana>=0.30.0","solders>=0.18.0","httpx>=0.24.0","pydantic>=2.0.0",][project.optional-dependencies]dev=["pytest>=7.0.0","pytest-asyncio>=0.21.0","black>=23.0.0","mypy>=1.0.0",][project.urls]Homepage="https://openlib.xyz"Documentation="https://openlibx402.github.io/docs"Repository="https://github.com/openlibx402/openlibx402"
// package.json example for @openlibx402/core{"name":"@openlibx402/core","version":"0.1.0","description":"Core implementation of X402 payment protocol","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","test":"jest","lint":"eslint src/**/*.ts"},"keywords":["x402","payments","solana","ai-agents","web3"],"author":"OpenLibx402 Contributors","license":"MIT","dependencies":{"@solana/web3.js":"^1.87.0","@solana/spl-token":"^0.3.9","axios":"^1.6.0"},"devDependencies":{"@types/node":"^20.0.0","typescript":"^5.0.0","jest":"^29.0.0","eslint":"^8.0.0"}}
This specification provides a comprehensive blueprint for implementing the OpenLibx402 library ecosystem. The architecture is modular, allowing for incremental development and easy addition of new framework integrations.