rmi-backend/sdks/python/x402_sdk/client.py
opencode c762564d40 style(rmi-backend): complete lint cleanup — 1175→0 ruff errors
- Fix 71 invalid-syntax files (class-body newline-broken assignments)
- Add from/None chain to 307 B904 raise-without-from sites
- Add B008 ignore to ruff.toml (already in pyproject.toml)
- Noqa F401 on __init__.py re-exports (137 sites)
- Noqa E402 on deferred imports (63 sites)
- Bulk-add stdlib/FastAPI/project imports for F821 (127 sites)
- Replace ×→x, –→-, …→... in docstrings (4093 chars)
- Manual refactor of 5 SIM103/SIM116 patterns

Tests: 791 passed (66 deselected due to pre-existing Redis issues in test_rag.py)
Co-authored-by: opencode <opencode@rugmunch.io>
2026-07-06 15:43:20 +02:00

207 lines
5.8 KiB
Python

"""x402 Python SDK - pay-per-call AI tool access.
Usage:
pip install x402-sdk
from x402_sdk import x402
# Initialize
client = x402(api_key="your-key")
# or without API key (free trials only)
client = x402()
# List available tools
tools = client.list_tools(category="intelligence")
for t in tools:
print(f"{t['id']}: ${t['price_usd']}")
# Create invoice and get payment challenge
invoice = client.create_invoice(tool="token_scan")
# Pay on-chain (user connects wallet, sends USDC)
# Then verify:
result = client.verify_payment(
tx_hash="0x...",
tool="token_scan",
)
print(f"Verified: {result['verified']}")
# Check balance and usage
balance = client.check_balance()
usage = client.get_usage(days=7)
"""
from __future__ import annotations
import json
from dataclasses import dataclass
import httpx
@dataclass
class Invoice:
id: str
tool: str
amount_usd: float
amount_wei: int
pay_to: str
chain: str
signature: str
created_at: str
expires_at: float
@dataclass
class VerificationResult:
verified: bool
tx_hash: str = ""
tool: str = ""
amount_usd: float = 0.0
chain: str = ""
payer: str = ""
@dataclass
class ToolInfo:
id: str
price_usd: float
category: str
trials: int
description: str = ""
class x402:
"""x402 client - pay-per-call AI tool access.
Args:
api_key: Optional API key for authenticated access.
base_url: x402 gateway URL (default: https://mcp.rugmunch.io)
"""
def __init__(self, api_key: str = "", base_url: str = "https://mcp.rugmunch.io"):
self.api_key = api_key
self.base_url = base_url.rstrip("/")
self._http = httpx.Client(
headers={"X-API-Key": api_key} if api_key else {},
timeout=30,
)
def list_tools(self, category: str = "") -> list[ToolInfo]:
"""List all available x402 tools with prices and trial info.
Args:
category: Optional filter (intelligence, premium, monitoring, etc.)
Returns:
List of ToolInfo with id, price_usd, category, trials, description
"""
params = {}
if category:
params["category"] = category
r = self._http.post(
f"{self.base_url}/mcp/x402",
json={"method": "tools/call", "params": {"name": "x402_list_tools", "arguments": params}},
)
data = r.json().get("result", {})
text = json.loads(data.get("content", [{}])[0].get("text", "{}"))
return [ToolInfo(**t) for t in text.get("tools", [])]
def create_invoice(self, tool: str, agent_id: str = "") -> Invoice:
"""Create a payment challenge for a tool.
Returns an invoice with amount, pay_to address, chain, and HMAC
signature. Pay on-chain, then call verify_payment().
Args:
tool: Tool ID (e.g., 'token_scan', 'rug_probability')
agent_id: Optional agent identifier
Returns:
Invoice with payment details
"""
r = self._http.post(
f"{self.base_url}/mcp/x402",
json={
"method": "tools/call",
"params": {
"name": "x402_create_invoice",
"arguments": {"tool": tool, "agent_id": agent_id or ""},
},
},
)
data = r.json().get("result", {})
text = json.loads(data.get("content", [{}])[0].get("text", "{}"))
return Invoice(**text)
def verify_payment(self, tx_hash: str, tool: str, amount_usd: float = 0) -> VerificationResult:
"""Verify an on-chain payment.
Once verified, the caller is authorized to use the tool.
Args:
tx_hash: Transaction hash from the on-chain payment
tool: Tool ID that was paid for
amount_usd: Expected payment amount
Returns:
VerificationResult with verification status
"""
r = self._http.post(
f"{self.base_url}/mcp/x402",
json={
"method": "tools/call",
"params": {
"name": "x402_verify_payment",
"arguments": {"tx_hash": tx_hash, "tool": tool, "amount_usd": amount_usd},
},
},
)
data = r.json().get("result", {})
text = json.loads(data.get("content", [{}])[0].get("text", "{}"))
return VerificationResult(**text)
def check_balance(self, agent_id: str = "") -> dict:
"""Check remaining free trials and usage.
Args:
agent_id: Agent identifier
Returns:
Dict with balances per tool
"""
r = self._http.post(
f"{self.base_url}/mcp/x402",
json={
"method": "tools/call",
"params": {
"name": "x402_check_balance",
"arguments": {"agent_id": agent_id or "sdk_user"},
},
},
)
data = r.json().get("result", {})
return json.loads(data.get("content", [{}])[0].get("text", "{}"))
def get_usage(self, agent_id: str = "", days: int = 7) -> dict:
"""View usage history and spending.
Args:
agent_id: Agent identifier
days: Days of history
Returns:
Dict with usage stats
"""
r = self._http.post(
f"{self.base_url}/mcp/x402",
json={
"method": "tools/call",
"params": {
"name": "x402_get_usage",
"arguments": {"agent_id": agent_id or "sdk_user", "days": days},
},
},
)
data = r.json().get("result", {})
return json.loads(data.get("content", [{}])[0].get("text", "{}"))