Skip to main content
A production-ready seller built with Express that monetizes a photo gallery API. AI agents discover pricing via the A2A agent card, pay USDC on Base, and receive a JWT to access protected endpoints — all without human intervention.

What it demonstrates

  • Mounting key0Router on an Express app to handle the full A2A payment flow
  • Defining multiple pricing plans with variable access durations
  • Issuing JWTs via AccessTokenIssuer after on-chain payment verification
  • Protecting routes with validateAccessToken middleware
  • Using Redis for challenge storage and double-spend prevention

Architecture

Prerequisites

  • Node.js 18+ or Bun runtime
  • Redis running locally or accessible via URL
  • A wallet address on Base to receive USDC payments

Directory structure

Environment variables

Never use the default KEY0_ACCESS_TOKEN_SECRET in production. Generate a random string of at least 32 characters.

Code walkthrough

1. Imports and configuration

server.ts
All configuration is driven by environment variables with sensible defaults for local development. The @key0ai/key0/express subpath export provides the Express-specific router and middleware.

2. Payment adapter and storage

server.ts
Three components wire together the payment infrastructure:
  • X402Adapter — verifies ERC-20 Transfer events on Base by reading on-chain transaction receipts via viem.
  • RedisChallengeStore — manages the challenge state machine (PENDING, PAID, DELIVERED, EXPIRED) with atomic Lua-script transitions.
  • RedisSeenTxStore — prevents double-spend by tracking used transaction hashes with atomic SET NX.
Both stores share a single Redis connection.

3. Seller config and pricing plans

server.ts
The plans array defines your pricing. Each plan has a unique planId, a unitAmount in USD, and a human-readable description. These are exposed in the auto-generated agent card so AI agents can discover what you sell and how much it costs. challengeTTLSeconds controls how long an agent has to complete payment before the challenge expires (15 minutes here).

4. Credential issuance callback

server.ts
This is the core monetization hook. After Key0 verifies the on-chain payment, it calls fetchResourceCredentials and returns whatever credential you issue. Here, the callback mints a JWT with plan-dependent expiry:
  • single-photo: 1-hour token
  • full-album: 24-hour token
The JWT payload includes the transaction hash and plan ID for downstream auditing.
fetchResourceCredentials can return any string — a JWT, an API key, an OAuth token, or a signed URL. Key0 passes it through to the agent as-is.

5. Payment lifecycle hook

server.ts
onPaymentReceived fires after payment verification and credential issuance succeed. Use it for logging, analytics, webhooks, or notifying downstream systems. resourceEndpointTemplate tells agents where to use their token. The {resourceId} placeholder is replaced with the actual resource identifier in the AccessGrant response.

6. Protecting routes

server.ts
A single middleware call gates all /api/* routes behind JWT validation. This is decoupled from the payment flow — it only verifies the token signature and expiry. Your route handlers stay clean and focused on business logic.

Running the example

1

Clone and install

2

Configure environment

Edit .env and set your wallet address and a strong token secret:
.env
3

Start Redis

4

Start the server

Expected output

Verify the agent card

The agent card is auto-generated from your SellerConfig. It describes the seller’s capabilities, pricing plans, and A2A endpoint URL — everything an agent needs to initiate a purchase.

Complete the payment flow

To run a full end-to-end payment, start this seller and point an agent at it. The agent will discover the seller, select a plan, pay USDC on Base Sepolia, and retrieve the protected photo data.

Source code

View examples/express-seller on GitHub