Skip to main content

Overview

The Cloudflare Agents x402 demo showcases wallet-to-wallet autonomous payments between two agents running on Cloudflare Workers. This example demonstrates how agents can independently manage payments without requiring private keys from users. Key Features:
  • Two-agent system: Server Agent (receives payments) and Client Agent (makes payments)
  • Crossmint smart wallets managed via API (no private keys needed)
  • x402 protocol for HTTP-native payments
  • Fully autonomous payment flow

What This Demo Does

User β†’ PayAgent (Wallet A) β†’ Protected API (Wallet B)
                           ↓ (402 Payment Required)
                           ↓ Agent pays $0.10 automatically
                           ↓ Content returned βœ…
  1. Server Agent - Has a Crossmint wallet that receives $0.10 payments
  2. Client Agent - Has a Crossmint wallet that automatically pays for protected content
No private keys needed - both wallets are Crossmint smart wallets managed via API.

Prerequisites

Crossmint API Key

Get a server API key from Crossmint Console:
  1. Create a new Smart Wallet project
  2. Navigate to API Keys section
  3. Create an API key with scopes: wallets.create, wallets.read, wallets:messages.sign
  4. Copy the key (starts with sk_)

Test Tokens (Optional)

Get test USDC on Base Sepolia from Circle Faucet

Setup Instructions

Step 1: Install Dependencies

cd cloudflare-agents
npm install

Step 2: Configure Environment Variables

Create .dev.vars with your Crossmint API key:
CROSSMINT_API_KEY=sk_your_api_key_here

Step 3: Run Locally

npm run dev
You should see:
πŸ’Ό Server wallet created: 0xABC...
πŸ’° Will receive payments at Crossmint smart wallet
βŽ” Ready on http://localhost:8787

Testing the Payment Flow

1. Check Wallets Are Created

curl http://localhost:8787/
Response shows both wallet addresses and their roles.

2. Try Accessing Protected Route Directly (Will Fail)

curl http://localhost:8787/protected-route
Returns 402 Payment Required with payment instructions.

3. Trigger the Agent to Pay and Access

curl http://localhost:8787/agent
The PayAgent will:
  1. Create its own Crossmint wallet (on first request)
  2. Attempt to fetch /protected-route
  3. Receive 402 Payment Required
  4. Automatically pay $0.10 to the server wallet
  5. Receive the protected content
Response:
{
  "message": "πŸŽ‰ This content is behind a paywall. Thanks for paying!",
  "data": {
    "premium": true,
    "timestamp": "2025-10-02T...",
    "service": "Crossmint Premium API"
  }
}

4. Check Wallet Balances

curl http://localhost:8787/wallets/status
Response:
{
  "server": {
    "address": "0xABC...",
    "balance": { "usdc": "0.10", "eth": "0" }
  },
  "agent": {
    "address": "0xDEF...",
    "balance": { "usdc": "0", "eth": "0" }
  }
}

How It Works

Server Wallet (Receives Payments)

// Create Crossmint wallet for server
const crossmint = createCrossmint({ apiKey });
const crossmintWallets = CrossmintWallets.from(crossmint);
const serverWallet = await crossmintWallets.createWallet({
  chain: "base-sepolia",
  signer: { type: "api-key" }
});

// Protect routes with payment middleware
paymentMiddleware(serverWallet.address, {
  "/protected-route": { price: "$0.10", network: "base-sepolia" }
});

Client Agent (Makes Payments)

export class PayAgent extends Agent {
  async onStart() {
    // Create agent's Crossmint wallet
    this.wallet = await crossmintWallets.createWallet({
      chain: "base-sepolia",
      signer: { type: "api-key" }
    });

    // Create x402-compatible signer
    const x402Signer = createX402Signer(this.wallet);
    this.fetchWithPay = wrapFetchWithPayment(fetch, x402Signer);
  }

  async onRequest(req: Request) {
    // Automatically pays when encountering 402 status
    return this.fetchWithPay(protectedUrl, {});
  }
}

Use Cases

This pattern enables:
  • πŸ” Pay-per-use APIs: Agents pay per call instead of subscriptions
  • πŸ“Š Data marketplaces: Buy/sell data between agents
  • 🀝 Agent-to-agent commerce: Autonomous service transactions
  • ⚑ Micropayments: Pay fractions of a cent per API call
  • 🌐 Cross-platform payments: No platform lock-in

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Client    β”‚
β”‚   (Agent)   β”‚
β”‚  + Wallet   β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚ GET /protected-route
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Payment Middleware β”‚
β”‚   (x402-hono)       β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚ 402 Payment Required
       β”‚ + payment instructions
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Client    │────────▢│    Facilitator   β”‚
β”‚   Pays      β”‚         β”‚  (x402.org)      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚                         β”‚
       β”‚                         β”‚ Verify
       β”‚ GET + payment proof     β”‚
       β–Ό                         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Protected Content  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Client β”‚ β”‚ (Agent) β”‚ β”‚ + Wallet β”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ GET /protected-route β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Payment Middleware β”‚ β”‚ (x402-hono) β”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ 402 Payment Required β”‚ + payment instructions β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Client │────────▢│ Facilitator β”‚ β”‚ Pays β”‚ β”‚ (x402.org) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ Verify β”‚ GET + payment proof β”‚ β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Protected Content β”‚ β”‚ Returned β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

## Payment Flow Sequence

```mermaid
sequenceDiagram
    participant Client as PayAgent
    participant Server as Protected API
    participant Facilitator as x402 Facilitator
    participant Chain as Base Sepolia

    Client->>Server: GET /protected-route
    Server-->>Client: 402 Payment Required<br/>{amount, to, network}

    Client->>Client: Sign payment (EIP-712)
    Client->>Facilitator: Submit payment signature
    Facilitator->>Chain: Verify & settle USDC
    Chain-->>Facilitator: TX confirmation

    Client->>Server: GET /protected-route + proof
    Server->>Facilitator: Verify payment
    Facilitator-->>Server: Payment verified
    Server-->>Client: Protected content
```text

## Deploying to Production

### Step 1: Set Production Secret

```bash
npx wrangler secret put CROSSMINT_API_KEY
# Paste your API key when prompted
```text

### Step 2: Update to Mainnet

Edit `src/index.ts` to use Base mainnet:

```typescript
// Change from:
chain: "base-sepolia",
network: "base-sepolia",

// To:
chain: "base",
network: "base",
```text

### Step 3: Deploy

```bash
npm run deploy
```text

### Step 4: Test Live

```bash
curl https://crossmint-x402.your-subdomain.workers.dev/agent
```text

## Troubleshooting

### "Wallets need test tokens"

- Both wallets start with zero balance
- Get test USDC from [Circle Faucet](https://faucet.circle.com/)
- Fund the agent wallet address shown in logs

### "Cannot find module '@crossmint/wallets-sdk'"

- Run `npm install` to install dependencies
- Make sure package.json has the correct version

### "Payment verification failed"

- Check agent wallet has sufficient USDC balance
- Verify network is "base-sepolia" consistently
- Ensure facilitator URL is accessible

## Key Technical Details

### Smart Contract Wallets

Crossmint wallets are ERC-4337 smart contract accounts:
- Work before on-chain deployment (ERC-6492 signatures)
- Auto-deploy on first transaction
- Gas-sponsored transactions available
- No private key management required

### x402 Protocol

HTTP-native payment protocol:
- Uses `402 Payment Required` status code
- Payment details in response headers
- Signature-based payment proof
- Async settlement via facilitator

### x402 Adapter

The `x402Adapter.ts` file bridges Crossmint wallets to x402:
- Converts Crossmint wallet API to viem-compatible Account
- Handles EIP-712 typed data signing
- Supports both ERC-6492 and EIP-1271 signatures

```typescript
export function createX402Signer(wallet: CrossmintWallet): Account {
  return {
    address: wallet.address as `0x${string}`,
    type: "local",
    signTypedData: async (typedData) => {
      return wallet.signTypedData(typedData);
    }
  };
}
```text

## Learn More

- πŸ“– **Crossmint Wallets**: https://docs.crossmint.com/wallets
- πŸ“– **x402 Protocol**: https://developers.cloudflare.com/agents/x402/
- πŸ—οΈ **Cloudflare Agents**: https://developers.cloudflare.com/agents/
- πŸ’° **Base Network**: https://base.org/

## Key Features Summary

βœ… **No private key management** - Crossmint handles wallet security

βœ… **Smart contract wallets** - Account abstraction, gas sponsorship

βœ… **Agent-to-agent payments** - Autonomous commerce between services

βœ… **HTTP-native payments** - x402 protocol for seamless integration

---

Built with Cloudflare Agents + Crossmint Wallets + x402 Protocol

Build docs developers (and LLMs) love