DEV Community

moto
moto

Posted on

"I Built a Payment SDK for AI Agents — Here's Why Stripe Isn't Enough"

The Problem: AI Agents Can't Pay for Anything

AI agent frameworks — LangChain, AutoGen, OpenClaw — have gotten incredibly powerful. Agents can search the web, execute code, manage files, and call APIs.

But ask an agent to buy something, and it hits a wall.

Why? Every payment infrastructure in existence assumes a human is behind a browser. Stripe's checkout flow renders a card input form. 3D Secure redirects to an authentication page. Someone clicks "Pay."

An AI agent can't open a browser. Can't fill in a card form. Can't click buttons.

"Just call Stripe's server-to-server API directly," you might say. Technically, yes. But before you do, you need to answer three questions:

  1. Authentication: Is this API call actually from a legitimate agent, or a compromised one?
  2. Authorization: Should this agent be allowed to spend $500? What's its limit? Which categories?
  3. Audit: Can we prove, in a tamper-evident way, what this agent has spent and when?

AgentTrust solves all three. It's an open-source SDK that gives AI agents the ability to authenticate with DIDs, receive scoped authorization tokens, execute payments through Stripe, and record every transaction in a hash chain audit trail.

from agenttrust import AgentWallet, PaymentTool

wallet = AgentWallet()
await wallet.create(display_name="shopping-agent")

# Register as a LangChain tool
tools = [PaymentTool(wallet=wallet)]

# The agent can now autonomously execute payments
agent = create_tool_calling_agent(llm, tools, prompt)
agent.invoke({"input": "Buy a laptop case for $40"})
Enter fullscreen mode Exit fullscreen mode

Five lines. That's it.

GitHub: https://github.com/momo1235656/agenttrust-protocol


Architecture: Three Layers

Layer 1: DID-Based Identity

Every agent gets a Decentralized Identifier (DID) — a W3C standard for self-sovereign identity. The DID is derived from an Ed25519 public key.

from agenttrust.server.crypto.keys import generate_keypair, derive_did

private_key, public_key = generate_keypair()
did = derive_did(public_key)
# did = "did:key:z6MkhaXg..."
Enter fullscreen mode Exit fullscreen mode

Why DIDs instead of API keys?

API keys are secrets that travel over the network. If intercepted, the attacker has full access. With DIDs, the private key never leaves the agent. Instead, the agent signs each request, and the server verifies the signature using the public key from the DID document.

API keys are controlled by the issuer. If AgentTrust revokes your API key, your agent is dead. With DIDs, the identifier is mathematically derived from the agent's own key pair. AgentTrust can't revoke it — only the private key holder can deactivate the DID.

API keys aren't portable. A Stripe API key only works with Stripe. A DID works everywhere — it's a W3C standard. When we issue Verifiable Credentials (trust scores) in a future release, they'll be tied to the same DID.

Layer 2: Scoped JWT Authorization

After DID verification, the agent receives a scoped JWT (JSON Web Token) that defines exactly what the agent is allowed to do.

{
    "sub": "did:key:z6MkhaXg...",
    "scopes": ["payment:execute"],
    "max_amount": 50000,
    "currency": "jpy",
    "allowed_categories": ["electronics", "software"],
    "exp": 1710001800
}
Enter fullscreen mode Exit fullscreen mode

When the agent requests a payment, the Payment Service checks:

  • Is payment:execute in the scopes?
  • Is the amount ≤ max_amount?
  • Is the category in allowed_categories?
  • Has the token expired?

Only after all checks pass does AgentTrust call Stripe. This is the core value proposition: AgentTrust is the gatekeeper that decides whether an agent's payment request should reach the payment processor.

Layer 3: Hash Chain Audit Trail

Every transaction is recorded in a hash chain — each entry includes the hash of the previous entry, making the entire history tamper-evident.

def compute_hash(index, transaction_id, amount, status, timestamp, prev_hash):
    data = json.dumps({
        "index": index,
        "transaction_id": transaction_id,
        "amount": amount,
        "status": status,
        "timestamp": timestamp,
        "prev_hash": prev_hash
    }, sort_keys=True, separators=(',', ':'))
    return "sha256:" + hashlib.sha256(data.encode()).hexdigest()
Enter fullscreen mode Exit fullscreen mode

If anyone modifies a past transaction, every subsequent hash breaks. This isn't a blockchain (no distributed consensus), but it provides tamper evidence — you can verify the integrity of the entire transaction history at any time.


The Full Sequence

Here's what happens when a user says "Buy a laptop case for $40":

1. User → Agent: "Buy a laptop case for $40"
2. Agent → SDK: PaymentTool.execute(amount=4000, description="laptop case")
3. SDK: Signs the request with Ed25519 private key
4. SDK → API: POST /did/verify (DID + signature)
5. API: Retrieves DID document, verifies signature with public key
6. API → SDK: ✅ DID verified
7. SDK → API: POST /auth/token (DID + signature + requested scopes)
8. API: Checks agent's permission level, issues scoped JWT
9. API → SDK: JWT (max_50000jpy, expires in 30 min)
10. SDK → API: POST /payment/execute (JWT + amount=4000 + idempotency key)
11. API: Verifies JWT → scope check (4000 ≤ 50000 ✅) → calls Stripe
12. Stripe → API: ✅ PaymentIntent succeeded
13. API: Records transaction in hash chain audit log
14. API → SDK: ✅ Success (tx_id, audit_hash)
15. Agent → User: "Purchased laptop case for $40"
Enter fullscreen mode Exit fullscreen mode

Supported Frameworks

AgentTrust isn't tied to a single framework. It supports:

LangChain (Python)

from agenttrust import AgentWallet, PaymentTool

wallet = AgentWallet()
await wallet.create(display_name="langchain-agent")
tools = [PaymentTool(wallet=wallet)]
Enter fullscreen mode Exit fullscreen mode

AutoGen

from agenttrust.sdk.autogen_tools import create_payment_tools

wallet = AgentWallet()
await wallet.create(display_name="autogen-agent")
tools = create_payment_tools(wallet)

# AutoGen v0.4+
agent = AssistantAgent(name="pay_agent", model_client=client, tools=tools)
Enter fullscreen mode Exit fullscreen mode

OpenClaw

from agenttrust.sdk.openclaw_tools import AgentTrustPaymentAction

wallet = AgentWallet()
await wallet.create(display_name="openclaw-agent")
actions = [AgentTrustPaymentAction(wallet=wallet)]
Enter fullscreen mode Exit fullscreen mode

MCP (Claude Desktop, Cursor, etc.)

AgentTrust runs as an MCP server. Add this to your claude_desktop_config.json:

{
    "mcpServers": {
        "agenttrust": {
            "command": "python",
            "args": ["-m", "mcp_server.server"],
            "env": {
                "AGENTTRUST_SERVER_URL": "http://localhost:8000"
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Restart Claude Desktop. Now you can say "Buy X for $Y" and it executes a real payment.

TypeScript / Node.js

import { AgentWallet } from 'agenttrust';

const wallet = new AgentWallet({ serverUrl: 'http://localhost:8000' });
await wallet.create({ displayName: 'ts-agent' });

const result = await wallet.pay({
    amount: 5000,
    description: 'Test purchase',
});
Enter fullscreen mode Exit fullscreen mode

API Reference

Method Path Description
POST /did/create Create a new agent DID
GET /did/resolve/{did} Resolve a DID document
POST /did/verify Verify a DID signature
POST /auth/token Issue a scoped JWT
POST /auth/verify-token Verify a JWT
POST /payment/execute Execute a payment
GET /payment/{id} Check payment status
GET /audit/{agent_did} Get audit trail
POST /audit/verify Verify hash chain integrity

Full documentation: https://docs.agenttrust.io


Design Decisions

Why DIDs instead of simpler auth?

For an MVP, API keys would work fine. But AgentTrust aims to be payment infrastructure — something that lasts 10+ years.

In a world with billions of AI agents, having every agent's identity controlled by a single company is a single point of failure. DIDs are self-sovereign: the agent (or its owner) generates the key pair, and no one — not even AgentTrust — can revoke it without the private key.

We plan to migrate DID storage to an L2 blockchain (Polygon/Base) in a future release, achieving full decentralization.

Why a hash chain and not a blockchain?

Transactions happen at hundreds to thousands per second. Writing each one to a blockchain would cost tens of thousands of dollars per month in gas fees and add seconds of latency per transaction.

A hash chain in PostgreSQL gives us tamper evidence at database speeds. We plan to periodically anchor the chain head to a blockchain for additional guarantees.

Why Python (for now)?

Our users — AI agent developers — write Python. LangChain is Python. AutoGen is Python. The OpenAI SDK is Python.

The API server will be rewritten in Rust (Axum) in a future phase for performance and memory safety. The cryptographic core (server/crypto/) is already isolated for this purpose. The SDK will remain Python/TypeScript — users won't notice the server-side change.


What's Next

AgentTrust is in its early stages. The roadmap includes:

  • OAuth 2.0 authorization server (RFC 6749 compliant, with agent-to-agent delegation)
  • Trust Score Engine (credit score for agents, based on transaction history)
  • Verifiable Credentials (W3C standard tamper-proof certificates for trust scores)
  • Agent-to-Agent payments (with escrow and Saga-pattern distributed transactions)
  • Rust migration (Axum API server + native crypto core)

Getting Started

git clone https://github.com/agenttrust/agenttrust.git
cd agenttrust

cp .env.example .env
# Set STRIPE_SECRET_KEY=sk_test_... in .env

pip install -e ".[dev]"
uvicorn server.main:app --reload --port 8000

# In another terminal:
python demo.py
Enter fullscreen mode Exit fullscreen mode

Call for Feedback

I'd love to hear from you:

  • Do you have a use case for agent payments? I want to hear concrete scenarios.
  • Is DID overkill for your use case? I'm considering an API key mode for simpler setups.
  • Which frameworks should I prioritize? CrewAI, Semantic Kernel, etc.

Open an issue on GitHub, or reach out on X.

GitHub: https://github.com/momo1235656/agenttrust-protocol
X: https://x.com/liyn462Kzr95667


Context: Why This Matters Now

I analyzed the open issues across four major agent frameworks (LangChain: 241 open, AutoGen: 445 open, OpenClaw: 4,976 open) and found that agent authentication, safe tool execution, and audit trails are unsolved problems across the board.

Specific issues that informed AgentTrust's design:

  • AutoGen #7247 — Hash-chained audit trails for agent governance
  • AutoGen #7266 — Fail-closed defaults for untrusted MCP servers
  • LangChain #35393 — Agent identity verification for tool calls
  • AutoGen #7265 — Production reliability patterns for multi-agent systems (11 comments)

The community is asking for this. AgentTrust is one answer.


If you found this useful, consider starring the repo on GitHub. It helps more people discover the project.

Top comments (0)