Skip to content

Repository files navigation

PayGraph

Tests

Open-source spend governance for AI agents. Issue single-use virtual cards or pay x402-enabled APIs with USDC — with policy enforcement, audit logging, and human-in-the-loop approval.

Architecture

 Agent (LangGraph / CrewAI / any framework)
│
▼
┌────────────────────────────────────────┐
│ AgentWallet │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Policy Engine│ │ Audit Logger │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ┌──────▼─────────────────▼─────────┐ │
│ │ Payment Rails │ │
│ │ Card: Mock │ Stripe │ StripeMPP │ │
│ │ │ │ │ │ x402: X402Gateway │ MockX402 │ │
│ └──────────────────────────────────┘ │
└────────────────────────────────────────┘

Install

pip install paygraph

With LangGraph support:

pip install paygraph[langgraph]

With CrewAI support:

pip install paygraph[crewai]

With x402 support (EVM + Solana USDC payments):

pip install paygraph[x402]

With live demo (includes LLM providers):

pip install paygraph[live]

Quickstart

frompaygraphimportAgentWallet, SpendPolicy, MockGatewaywallet=AgentWallet(
gateway=MockGateway(auto_approve=True),
policy=SpendPolicy(
max_transaction=25.0,
daily_budget=100.0,
blocked_vendors=["doordash"],
),
)
result=wallet.request_spend(
amount=4.20,
vendor="Anthropic API",
justification="Need Claude credits for document summarization.",
)
print(result) # Card approved. PAN: 4111..., CVV: 123, Expiry: 12/28

StripeCardGateway (Real Cards)

frompaygraphimportAgentWallet, SpendPolicy, StripeCardGatewaywallet=AgentWallet(
gateway=StripeCardGateway(api_key="sk_test_..."),
policy=SpendPolicy(max_transaction=50.0),
)
result=wallet.request_spend(
amount=4.20,
vendor="Anthropic API",
justification="API credits for task completion.",
)

Configuration

StripeCardGateway accepts the following parameters:

ParameterTypeDefaultDescription
api_keystrrequiredStripe secret key (sk_test_... or sk_live_...)
cardholder_idstr | NoneNoneExisting cardholder ID to use (skips auto-creation)
currencystr"usd"Card currency (e.g. "eur", "gbp")
billing_addressdict | NoneUS addressCardholder billing address (line1, city, postal_code, country)
single_useboolTrueMint a new card per transaction; set False to reuse one card

When single_use=False, a single card is created on the first spend and reused for subsequent calls (spending limit is updated each time).

StripeMPPGateway (Shared Payment Tokens)

For sellers that accept Stripe machine payments (MPP / agentic commerce), use StripeMPPGateway to issue scoped Shared Payment Tokens instead of virtual cards.

Requires Stripe machine payments access (preview API). See Stripe docs.

frompaygraphimportAgentWallet, SpendPolicy, StripeMPPGatewaywallet=AgentWallet(
gateway=StripeMPPGateway(
api_key="sk_test_...",
payment_method="pm_...",
grantee="profile_...",
),
policy=SpendPolicy(max_transaction=50.0),
)
result=wallet.request_spend(
amount=4.20,
vendor="Anthropic API",
justification="API credits for task completion.",
)
print(result) # SPT approved. Token: spt_... (spend limit: $4.20)

Configuration

ParameterTypeDefaultDescription
api_keystrrequiredStripe secret key (sk_test_... or sk_live_...)
payment_methodstrrequiredSaved PaymentMethod id (pm_...)
granteestrrequiredSeller identifier (typically profile_...)
currencystr"usd"ISO currency code
expires_in_secondsint3600Token lifetime from issuance in seconds

x402 Gateway (Pay APIs with USDC)

x402 is an HTTP 402-based protocol for machine-to-machine payments. Instead of minting a card, the gateway makes an HTTP request, handles the 402→sign→retry cycle on-chain, and returns the API response.

frompaygraphimportAgentWallet, X402Gateway, SpendPolicywallet=AgentWallet(
x402_gateway=X402Gateway(evm_private_key="0x..."), # Base/Polygon USDCpolicy=SpendPolicy(max_transaction=5.0),
)
response=wallet.request_x402(
url="https://api.example.com/paid-endpoint",
amount=0.01,
vendor="ExampleAPI",
justification="Need data for analysis.",
)
print(response) # The API response body

Supports both EVM (Base, Polygon, etc.) and Solana:

# Solana onlygateway=X402Gateway(svm_private_key="BASE58_KEY")
# Both networksgateway=X402Gateway(evm_private_key="0x...", svm_private_key="BASE58_KEY")

For testing without blockchain:

frompaygraphimportAgentWallet, MockX402Gatewaywallet=AgentWallet(
x402_gateway=MockX402Gateway(auto_approve=True, response_body='{"data": 42}'),
)

LangGraph Integration

frompaygraphimportAgentWallet, SpendPolicy, MockX402Gatewaywallet=AgentWallet(
x402_gateway=MockX402Gateway(auto_approve=True),
policy=SpendPolicy(max_transaction=25.0, blocked_vendors=["doordash"]),
)
# wallet.spend_tool → card payments, wallet.x402_tool → x402 API paymentstools= [wallet.spend_tool, wallet.x402_tool]
# Use with LangGraphfromlanggraph.prebuiltimportcreate_react_agentfromlangchain_anthropicimportChatAnthropicllm=ChatAnthropic(model="claude-sonnet-4-6")
agent=create_react_agent(llm, tools=tools)
result=agent.invoke({"messages": [("user", "Buy $4.20 in API credits from Anthropic")]})

CrewAI Integration

pip install paygraph[crewai]
fromcrewaiimportAgent, Task, CrewfrompaygraphimportAgentWallet, SpendPolicy, MockGatewaywallet=AgentWallet(
gateway=MockGateway(auto_approve=True),
policy=SpendPolicy(max_transaction=25.0),
)
agent=Agent(
role="Purchasing Agent",
goal="Buy API credits when needed",
tools=[wallet.crewai_tool],
)

MCP Server

pip install paygraph[mcp]

Run the MCP server over stdio:

PAYGRAPH_GATEWAY=mock \
PAYGRAPH_DAILY_BUDGET=100 \
PAYGRAPH_MAX_TRANSACTION=25 \
paygraph-mcp

Claude Desktop config:

{
"mcpServers": {
"paygraph": {
"command": "paygraph-mcp",
"env": {
"PAYGRAPH_GATEWAY": "mock",
"PAYGRAPH_DAILY_BUDGET": "100",
"PAYGRAPH_MAX_TRANSACTION": "25"
}
}
}
}

Policy Configuration

SpendPolicy accepts the following parameters:

ParameterTypeDefaultDescription
max_transactionfloat50.0Maximum amount per transaction (dollars)
daily_budgetfloat200.0Maximum total spend per day
allowed_vendorslist[str] | NoneNoneIf set, only these vendors are allowed (case-insensitive substring match)
blocked_vendorslist[str] | NoneNoneVendors that are always denied (case-insensitive substring match)
allowed_mccslist[int] | NoneNoneMerchant category code allowlist
require_justificationboolTrueRequire non-empty justification string

Environment Variables

VariableRequired forDescription
ANTHROPIC_API_KEY--live (default)Anthropic API key for Claude LLM
OPENAI_API_KEY--live --model openaiOpenAI API key for GPT LLM
STRIPE_API_KEY--stripe or --stripe-mppStripe secret key (sk_test_ or sk_live_)
STRIPE_CURRENCYStripe gateways (optional)Currency for card/SPT limits (default: usd)
STRIPE_BILLING_COUNTRY--stripe (optional)Billing address country code (e.g. FR)
STRIPE_CARDHOLDER_ID--stripe (optional)Reuse an existing cardholder ID
STRIPE_MPP_PAYMENT_METHOD--stripe-mppPaymentMethod id (pm_...) for SPT issuance
STRIPE_MPP_GRANTEE--stripe-mppSeller grantee id (typically profile_...)
STRIPE_MPP_EXPIRES_IN_SECONDS--stripe-mpp (optional)SPT lifetime in seconds (default: 3600)
EVM_PRIVATE_KEYx402 (EVM)EVM private key for Base/Polygon USDC payments
SVM_PRIVATE_KEYx402 (Solana)Solana private key (base58) for USDC payments

Copy .env.example to .env and fill in your keys:

cp .env.example .env

CLI

# Run simulated demo (no API keys needed)
paygraph demo
# Run live demo with a real LLMexport ANTHROPIC_API_KEY=sk-ant-...
paygraph demo --live
# Use OpenAI insteadexport OPENAI_API_KEY=sk-...
paygraph demo --live --model openai
# Use Stripe Issuing for real card issuanceexport STRIPE_API_KEY=sk_test_...
paygraph demo --live --stripe
# Use Stripe MPP (Shared Payment Tokens)export STRIPE_API_KEY=sk_test_...
export STRIPE_MPP_PAYMENT_METHOD=pm_...
export STRIPE_MPP_GRANTEE=profile_...
paygraph demo --live --stripe-mpp

License

MIT

Releases

Packages

Contributors

Languages