A Photo Gallery Agent built with Hono that sells photo access to AI agents via USDC payments.
A lightweight seller agent built with Hono that monetizes a photo gallery API. AI agents discover the seller via its A2A agent card, pay USDC on Base, and receive a JWT to access protected photo endpoints.
This example uses the legacy product/tier API (products, tierId, onIssueToken) rather than the current plans/planId/fetchResourceCredentials API. The payment flow is identical — only the configuration shape differs. See the Hono integration guide and the Express Seller example for the current API.
This example uses in-memory storage by default, so no Redis or Postgres is required. For production use, swap in RedisChallengeStore and RedisSeenTxStore.
import type { NetworkName } from "@key0ai/key0";import { AccessTokenIssuer, X402Adapter } from "@key0ai/key0";import { honoValidateAccessToken, key0App } from "@key0ai/key0/hono";import { Hono } from "hono";const PORT = Number(process.env["PORT"] ?? 3001);const NETWORK = (process.env["KEY0_NETWORK"] ?? "testnet") as NetworkName;const WALLET = (process.env["KEY0_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000000") as `0x${string}`;const SECRET = process.env["KEY0_ACCESS_TOKEN_SECRET"] ?? "dev-secret-change-me-in-production-32chars!";
All configuration comes from environment variables with safe defaults for local development. The @key0ai/key0/hono subpath export provides Hono-specific helpers.
key0App returns a Hono sub-app that mounts the A2A agent card at /.well-known/agent.json and the JSON-RPC endpoint at /a2a/jsonrpc. The products array defines two tiers — a single photo for $0.10 and a full album for $1.00.
onIssueToken runs after payment verification succeeds, minting a JWT with a tier-appropriate lifetime — 1 hour for single photos, 24 hours for full albums.
This example uses the legacy onIssueToken pattern. In the current API, it is replaced by fetchResourceCredentials. See the Express Seller example for the current approach.
The optional onPaymentReceived callback fires after a payment is verified and a credential is issued. Use it for logging, analytics, or notifying downstream systems:
Copy
Ask AI
onPaymentReceived: async (grant) => { console.log(`[Payment] Received payment for ${grant.resourceId} (${grant.tierId})`); console.log(` TX: ${grant.explorerUrl}`);},
The /api sub-router is gated by honoValidateAccessToken. Any request without a valid Bearer token receives a 401. The middleware is independent of the payment flow — it only verifies JWTs signed by the same secret.