> ## Documentation Index
> Fetch the complete documentation index at: https://docs.key0.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Hono Seller

> A Photo Gallery Agent built with Hono that sells photo access to AI agents via USDC payments.

A lightweight seller agent built with [Hono](https://hono.dev) 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.

## What It Demonstrates

* Mounting the Key0 payment gateway on a Hono app with `key0App`
* Defining multiple pricing plans with different prices and access durations
* Issuing JWTs with plan-specific TTLs via `AccessTokenIssuer`
* Protecting downstream routes with `honoValidateAccessToken` middleware

## Architecture

```
Agent                          Hono Seller
  |                                |
  |-- GET /.well-known/agent.json -|  (discover capabilities)
  |-- GET /discover ------------->|  (browse plans and pricing)
  |<-------- plan catalog ---------|  (planIds, amounts, wallet)
  |-- POST /x402/access ---------->|  (request access, planId)
  |<-------- X402Challenge --------|  (amount, destination, chainId)
  |                                |
  |  ... pays USDC on-chain ...    |
  |                                |
  |-- POST /x402/access ---------->|  (submit payment proof)
  |<-------- AccessGrant ----------|  (JWT + resource endpoint)
  |                                |
  |-- GET /api/photos/:id -------->|  (Bearer token)
  |<-------- photo data -----------|
```

## Prerequisites

* [Bun](https://bun.sh) v1.0+
* A wallet address to receive USDC payments

<Warning>
  This example omits `store` and `seenTxStore` from the `key0App` options for brevity. In practice, `createKey0()` (used internally) throws if these are missing — `RedisChallengeStore` and `RedisSeenTxStore` (or their Postgres equivalents) are required for the server to start. See the [Storage guide](/sdk-reference/storage) for setup instructions.
</Warning>

## Key Files

The entire example is a single file:

```
examples/hono-seller/
├── server.ts          # Hono app with Key0 payment gateway
├── package.json
└── .env.example
```

## Code Walkthrough

### 1. Imports and configuration

```typescript theme={null}
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.

### 2. Payment adapter and token issuer

```typescript theme={null}
const adapter = new X402Adapter({
  network: NETWORK,
  rpcUrl: process.env["KEY0_RPC_URL"],
});

const tokenIssuer = new AccessTokenIssuer(SECRET);
```

`X402Adapter` handles on-chain verification of USDC transfers on Base. `AccessTokenIssuer` signs and verifies JWTs using the shared secret.

### 3. Payment gateway

```typescript theme={null}
const gate = key0App({
  config: {
    agentName: "Photo Gallery Agent",
    agentDescription: "Purchase access to premium photos via USDC payments on Base",
    agentUrl: `http://localhost:${PORT}`,
    providerName: "Example Corp",
    providerUrl: "https://example.com",
    walletAddress: WALLET,
    network: NETWORK,
    challengeTTLSeconds: 900,
    plans: [
      {
        planId: "single-photo",
        unitAmount: "$0.10",
        description: "Single photo — 1 hour access",
      },
      {
        planId: "full-album",
        unitAmount: "$1.00",
        description: "Full album — 24 hour access",
      },
    ],
    // ...fetchResourceCredentials shown below
  },
  adapter,
});
```

`key0App` returns a Hono sub-app that mounts the A2A agent card at `/.well-known/agent.json` and the x402 payment endpoint at `POST /x402/access`. The `plans` array defines two tiers -- a single photo for \$0.10 and a full album for \$1.00.

### 4. Token issuance

```typescript theme={null}
fetchResourceCredentials: async (params) => {
  const ttl = params.planId === "single-photo" ? 3600 : 86400;
  return tokenIssuer.sign(
    {
      sub: params.requestId,
      jti: params.challengeId,
      resourceId: params.resourceId,
      planId: params.planId,
      txHash: params.txHash,
    },
    ttl,
  );
},
```

`fetchResourceCredentials` runs after payment verification succeeds, minting a JWT with a plan-appropriate lifetime -- 1 hour for single photos, 24 hours for full albums.

### 5. Payment lifecycle hook

The optional `onPaymentReceived` callback fires after a payment is verified and a credential is issued. Use it for logging, analytics, or notifying downstream systems:

```typescript theme={null}
onPaymentReceived: async (grant) => {
  console.log(`[Payment] Received payment for ${grant.resourceId} (${grant.planId})`);
  console.log(`  TX: ${grant.explorerUrl}`);
},
```

### 6. Protecting routes with JWT middleware

```typescript theme={null}
const app = new Hono();
app.route("/", gate);

const api = new Hono();
api.use("/*", honoValidateAccessToken({ secret: SECRET }));

api.get("/photos/:id", (c) => {
  const photoId = c.req.param("id");
  return c.json({
    id: photoId,
    url: `https://cdn.example.com/photos/${photoId}.jpg`,
    title: `Premium Photo ${photoId}`,
    resolution: "4K",
  });
});

app.route("/api", api);
```

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.

### 7. Server export

```typescript theme={null}
export default {
  port: PORT,
  fetch: app.fetch,
};
```

Bun picks up the default export automatically. No explicit `serve()` call needed.

## Running the Example

<Steps>
  <Step title="Install dependencies">
    ```bash theme={null}
    cd examples/hono-seller
    bun install
    ```
  </Step>

  <Step title="Configure environment">
    ```bash theme={null}
    cp .env.example .env
    ```

    Edit `.env` and set your wallet address:

    ```bash .env theme={null}
    KEY0_WALLET_ADDRESS=0xYourWalletAddress
    KEY0_ACCESS_TOKEN_SECRET=your-secret-at-least-32-characters-long
    KEY0_NETWORK=testnet
    ```
  </Step>

  <Step title="Start the server">
    ```bash theme={null}
    bun run start
    ```
  </Step>
</Steps>

## Expected Output

```
Photo Gallery Agent (Hono) running on http://localhost:3001
  Agent card: http://localhost:3001/.well-known/agent.json
  x402 endpoint: http://localhost:3001/x402/access
  Network: testnet
  Wallet: 0xYour...Address
```

### Verify the agent card

```bash theme={null}
curl http://localhost:3001/.well-known/agent.json | jq .
```

This returns the auto-generated A2A agent card describing the seller's capabilities, pricing plans, and payment endpoint URL.

### Complete a payment flow

Run an agent against this seller to execute a full discovery, payment, and resource access cycle. When a payment completes, you will see:

```
[Payment] Received payment for photo-1 (single-photo)
  TX: https://sepolia.basescan.org/tx/0x...
```

## Hono vs Express

The Key0 integration surface is nearly identical across frameworks. The main differences:

|                  | Hono                               | Express                                  |
| ---------------- | ---------------------------------- | ---------------------------------------- |
| Gateway mount    | `key0App()` returns a Hono sub-app | `key0Router()` returns an Express router |
| Token middleware | `honoValidateAccessToken()`        | `validateAccessToken()`                  |
| Import path      | `@key0ai/key0/hono`                | `@key0ai/key0/express`                   |
| Server           | `export default { fetch }`         | `app.listen(port)`                       |

For full integration documentation, see the [Hono integration guide](/integrations/hono).

<Card title="Source code" icon="github" href="https://github.com/key0ai/key0/tree/main/examples/hono-seller">
  `examples/hono-seller/server.ts`
</Card>
