Logo
Learn
  • Explore Course
Build
  • Explore Buildathon
  • Project Archive
Hack
Hack Coming Soon
  • Explore IRL Hackhouse
  • How to Qualify
  • Past Events

ClawPay

From telegram tell your OpenClaw to order from any website - it creates a secure credit card on demand from crypto and pays for the order

Videos

Description

ClawPay

The payment layer for your AI agent. Plug it in — ClawPay pays anywhere on the web, autonomously, without a card on file and without you touching anything.

Live Demo →

The Problem

Your agent can browse, decide, and act - but it can't pay. The only native option is x402, which requires the merchant to explicitly support it. That's a tiny fraction of the internet.

Giving your agent a real debit card is worse: it has persistent access to your funds, and one bad checkout later your card number is compromised.

What ClawPay Is

ClawPay sits between your agent and the web's payment infrastructure:

  • Your agent calls buy_virtual_card() with an amount

  • USDC is deposited into an on-chain escrow - nothing moves without your wallet signature

  • A single-use virtual card is issued with an exact spend limit

  • Your agent uses that card at any website checkout, just like a human would

  • Card is dead after one charge. Unused balance refunded as USDC.

No merchant opt-in. No card on file. No human in the loop.

x402 requires the website to support it. ClawPay works on every site that accepts Visa/Mastercard.

Why It's Secure

  • On-demand, per-transaction cards - a fresh card with an exact spend limit, every time. Your agent never holds a reusable number.

  • Spend-capped - even if the card leaks, it can only be charged once for that exact amount, then it's dead.

  • You hold the crypto - USDC stays in your wallet until you sign. ClawPay never custodies your funds.

  • No conversion - you don't sell crypto for fiat. Escrow holds USDC, card issuance is triggered on-chain confirmation.

How It Works

Your Agent (Claw / Claude / any MCP agent)
      │
      │  buy_virtual_card(amount_usd=25.00)
      ▼
ClawPay MCP Server
      │  signs + submits USDC escrow tx
      ▼
Arbitrum Sepolia Escrow Contract
      │  on-chain confirmation
      ▼
ClawPay Backend
      │  verifies tx, issues card
      ▼
Lithic Single-Use Virtual Card
  (exact spend limit, one-time use, dead after charge)
      │
      ▼
Agent checks out on any website - no human needed

Architecture

┌──────────────────────────────────────┐
│     Your Agent (Claw, Claude, etc.)  │
│   "buy X from website Y for $25"     │
└───────────────┬──────────────────────┘
                │ MCP: buy_virtual_card()
                ▼
┌──────────────────────────────────────┐
│         ClawPay MCP Server           │
│  Signs & submits USDC escrow tx      │
└───────────────┬──────────────────────┘
                │ POST /api/v1/payment/confirm
                ▼
┌──────────────────────────────────────┐
│         ClawPay Backend (FastAPI)    │
│  Verifies on-chain deposit           │
│  Issues Lithic single-use card       │
└───────────────┬──────────────────────┘
                │ {pan, cvv, expiry}
                ▼
┌──────────────────────────────────────┐
│   Agent fills card at any checkout   │
│   Works on any website, no opt-in    │
└──────────────────────────────────────┘

vs. x402

x402

ClawPay

Merchant opt-in required

Yes

No

Card exposure risk

N/A

None - single-use, spend-capped

Crypto stays as crypto

Yes

Yes - USDC, no conversion

Agent autonomy

Full

Full

Coverage

~handful of sites

Every site that accepts cards

Tech Stack

Layer

Technology

Blockchain

Arbitrum Sepolia · USDC (ERC-20)

Smart contract

Solidity escrow (ClawPayEscrow.sol)

Card issuance

Lithic virtual cards (SINGLE_USE)

Backend

Python / FastAPI

Agent interface

MCP (FastMCP)

Dashboard

React + Vite

Deployed Contracts (Arbitrum Sepolia)

Contract

Address

MockUSDC

0xFCABF780284B0d5997914C5b1ab7Ac34F0F01eaE

ClawPayEscrow

0x9ee0141d3FD09E4C15D183bD5017ef86e37b4254

Plug Into Your Agent

The MCP server gives your agent two tools:

  • buy_virtual_card(amount_usd, merchant_name?) - deposits USDC, returns a single-use card

  • check_wallet_balance() - returns the agent wallet's USDC and ETH balances

Add to your Claude Desktop config (~/.claude/claude_desktop_config.json):

{
  "mcpServers": {
    "clawpay": {
      "command": "python",
      "args": ["/path/to/clawpay/mcp/server.py"],
      "env": {
        "AGENT_PRIVATE_KEY": "0x...",
        "CLAWPAY_API_URL": "https://clawpay-production.up.railway.app",
        "CLAWPAY_API_KEY": "sk_clawpay_..."
      }
    }
  }
}

Your agent now handles payments autonomously:

"Buy me a Cadbury Dairy Milk from ChocoBazaar"

# Claw calls ClawPay - no human steps:
#  ◆  deposits USDC into escrow on-chain
#  ◆  receives a single-use virtual card
#  ◆  checks out on the merchant site
#  ✓  card is dead after one charge

OpenClaw Integration

Plug ClawPay into OpenClaw so your agent can pay autonomously without any additional setup.

Prerequisites

  • OpenClaw installed and running (openclaw plugins list works)

  • A wallet funded with ETH (gas) and USDC on Arbitrum Sepolia — get ETH from the faucet, then mint MockUSDC by calling mint(yourWallet, amount) on the MockUSDC contract

  • A ClawPay API key (sk_clawpay_...)


Step 1 — Create the plugin directory

mkdir -p ~/.openclaw/extensions/clawpay
cd ~/.openclaw/extensions/clawpay

Step 2 — Create package.json

{
  "name": "clawpay",
  "version": "1.0.0",
  "openclaw": {
    "extensions": ["./index.ts"]
  },
  "dependencies": {
    "ethers": "^6.0.0"
  }
}

Step 3 — Create openclaw.plugin.json

{
  "id": "clawpay",
  "name": "ClawPay",
  "description": "Buy anything online — deposits USDC into escrow on Arbitrum Sepolia, returns a single-use virtual card.",
  "version": "1.0.0",
  "openclaw": {
    "extensions": ["./index.ts"]
  },
  "configSchema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "privateKey":     { "type": "string" },
      "apiKey":         { "type": "string" },
      "usdcContract":   { "type": "string" }
    },
    "required": ["privateKey", "apiKey"]
  },
  "uiHints": {
    "privateKey":   { "label": "Wallet Private Key", "sensitive": true },
    "apiKey":       { "label": "ClawPay API Key", "sensitive": true },
    "usdcContract": { "label": "USDC Contract Address (optional override)" }
  }
}

Step 4 — Create index.ts

import { ethers } from "ethers";

const ARB_SEPOLIA_RPC  = "https://arbitrum-sepolia-testnet.api.pocket.network";
const CLAWPAY_API_URL  = "https://clawpay-production.up.railway.app";

const ESCROW_ABI = [
  {
    inputs: [
      { internalType: "string",  name: "sessionId", type: "string"  },
      { internalType: "uint256", name: "amount",    type: "uint256" },
    ],
    name: "deposit",
    outputs: [],
    stateMutability: "nonpayable",
    type: "function",
  },
];

const ERC20_ABI = [
  {
    inputs: [
      { internalType: "address", name: "spender", type: "address" },
      { internalType: "uint256", name: "amount",  type: "uint256" },
    ],
    name: "approve",
    outputs: [{ internalType: "bool", name: "", type: "bool" }],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [{ internalType: "address", name: "account", type: "address" }],
    name: "balanceOf",
    outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
    stateMutability: "view",
    type: "function",
  },
];

export default function (api: any) {
  const cfg        = api.config?.plugins?.entries?.clawpay?.config ?? {};
  const rawKey: string = cfg.privateKey ?? "";
  const apiKey: string = cfg.apiKey ?? "";
  const privateKey = rawKey.startsWith("0x") ? rawKey : `0x${rawKey}`;

  function wallet() {
    const provider = new ethers.JsonRpcProvider(ARB_SEPOLIA_RPC);
    return new ethers.Wallet(privateKey, provider);
  }

  // ── buy_virtual_card ───────────────────────────────────────────────
  api.registerTool(
    {
      name: "buy_virtual_card",
      description:
        "Use this whenever the user wants to buy something online. " +
        "Deposits USDC into escrow on Arbitrum Sepolia, then returns a single-use " +
        "virtual Visa/Mastercard to use at checkout. Card is dead after one charge.",
      parameters: {
        type: "object",
        properties: {
          amount_usd: {
            type: "number",
            description: "Exact amount in USD to put on the card (e.g. 25.00)",
          },
          merchant_name: {
            type: "string",
            description: "Name of the merchant or website (optional, for labelling)",
          },
        },
        required: ["amount_usd"],
      },
      async execute(_id: string, params: { amount_usd: number; merchant_name?: string }) {
        const w = wallet();

        // 1. Initiate session
        const initRes = await fetch(`${CLAWPAY_API_URL}/api/v1/payment/initiate`, {
          method: "POST",
          headers: { "Content-Type": "application/json", "X-API-Key": apiKey },
          body: JSON.stringify({
            amount_usd:           params.amount_usd,
            user_wallet_address:  w.address,
            merchant_name:        params.merchant_name,
          }),
        });
        if (!initRes.ok) {
          const body = await initRes.text();
          return { content: [{ type: "text", text: `ClawPay initiate error: ${body}` }] };
        }
        const session = await initRes.json();
        const { session_id, contract_address, usdc_contract, usdc_amount } = session;
        const amount = BigInt(usdc_amount);

        // 2. Approve USDC spend
        const usdc = new ethers.Contract(usdc_contract, ERC20_ABI, w);
        const approveTx = await usdc.approve(contract_address, amount);
        await approveTx.wait();

        // 3. Deposit into escrow
        const escrow = new ethers.Contract(contract_address, ESCROW_ABI, w);
        const depositTx = await escrow.deposit(session_id, amount);
        const receipt = await depositTx.wait();

        // 4. Confirm → get card
        const confirmRes = await fetch(`${CLAWPAY_API_URL}/api/v1/payment/confirm`, {
          method: "POST",
          headers: { "Content-Type": "application/json", "X-API-Key": apiKey },
          body: JSON.stringify({
            session_id,
            tx_hash:             receipt.hash,
            user_wallet_address: w.address,
          }),
        });
        if (!confirmRes.ok) {
          const body = await confirmRes.text();
          return { content: [{ type: "text", text: `ClawPay confirm error: ${body}` }] };
        }
        const result = await confirmRes.json();
        const card = result.card ?? result;

        return {
          content: [{
            type: "text",
            text: JSON.stringify({
              pan:             card.pan,
              cvv:             card.cvv,
              exp_month:       card.exp_month,
              exp_year:        card.exp_year,
              spend_limit_usd: params.amount_usd,
              merchant:        params.merchant_name ?? "(any)",
              note:            "Single-use card — dead after first charge.",
            }, null, 2),
          }],
        };
      },
    },
    { optional: true }
  );

  // ── check_wallet_balance ───────────────────────────────────────────
  api.registerTool(
    {
      name: "check_wallet_balance",
      description: "Check the agent wallet's USDC and ETH balances on Arbitrum Sepolia.",
      parameters: { type: "object", properties: {} },
      async execute(_id: string, _params: object) {
        const provider = new ethers.JsonRpcProvider(ARB_SEPOLIA_RPC);
        const w        = wallet();
        const usdcAddr = cfg.usdcContract ?? "0xFCABF780284B0d5997914C5b1ab7Ac34F0F01eaE";
        const usdc     = new ethers.Contract(usdcAddr, ERC20_ABI, provider);

        const [usdcBal, ethBal] = await Promise.all([
          usdc.balanceOf(w.address),
          provider.getBalance(w.address),
        ]);

        return {
          content: [{
            type: "text",
            text: JSON.stringify({
              wallet: w.address,
              usdc:   `${(Number(usdcBal) / 1e6).toFixed(2)} USDC`,
              eth:    `${ethers.formatEther(ethBal)} ETH`,
            }, null, 2),
          }],
        };
      },
    },
    { optional: true }
  );
}

Step 5 — Install dependencies

cd ~/.openclaw/extensions/clawpay
npm install

Step 6 — Add to openclaw.json

Edit ~/.openclaw/openclaw.json in two places:

a) Allow the tools (inside the existing "tools" block):

"tools": {
  "web": { "...": "..." },
  "allow": ["buy_virtual_card", "check_wallet_balance"]
}

b) Register the plugin (inside the existing "plugins"."entries" block):

"plugins": {
  "entries": {
    "clawpay": {
      "enabled": true,
      "config": {
        "privateKey": "0x<your-wallet-private-key>",
        "apiKey": "sk_clawpay_..."
      }
    }
  }
}

Step 7 — Restart the gateway

Step 8 — Verify

openclaw plugins list            # clawpay should show "loaded"
openclaw plugins info clawpay    # should show Tools: buy_virtual_card, check_wallet_balance

Your agent can now pay on any website the moment you say "buy X for $Y".


Running Locally

Backend

cd backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python3 -m uvicorn src.main:app --reload --port 8000

Set in backend/.env:

LITHIC_API_KEY=...
ARB_ESCROW_CONTRACT=0x9ee0141d3FD09E4C15D183bD5017ef86e37b4254
USDC_CONTRACT=0xFCABF780284B0d5997914C5b1ab7Ac34F0F01eaE
ARB_PLATFORM_PRIVATE_KEY=0x...

MCP Server

cd mcp
pip install -r requirements.txt
cp .env.example .env   # fill AGENT_PRIVATE_KEY + CLAWPAY_API_KEY
python server.py

Dashboard

cd dashboard && npm install && npm run dev

Runs at http://localhost:3001 - manual MetaMask flow for testing without an agent.

Project Structure

backend/      FastAPI server - payment sessions, card issuance
contracts/    Solidity escrow (ClawPayEscrow.sol) on Arbitrum Sepolia
dashboard/    React UI - manual payment + card display
extension/    Browser extension - "Pay with ClawPay" button
mcp/          MCP server - agent payment tools

Deployed

Backend API: https://clawpay-production.up.railway.app

Notes

  • Arbitrum Sepolia - get free ETH from the Arbitrum Sepolia faucet

  • Lithic sandbox - no real money involved

  • Cards are SINGLE_USE and closed after first charge

  • 5% buffer on every card spend limit to cover taxes/fees; unused amount refunded as USDC

Progress During Hackathon

<p>Made the utility, deployed the contracts to arbitrum sepolia and integrated into the Agent which now can visit and pay on any website with our on-demand card without even having to worry about crypto conversions.</p>

Tech Stack

ReactWeb3EthersNodeSolidity

Fundraising Status

<p>NA</p>

Team Leader
AAdil Husain
GitHub Link
github

GitHub

https://github.com/adilhusain01/clawpay
Product Category
InfraAISocialFi