Skip to main content
Accept crypto payments in your Hono API with a single middleware function.

Installation

# npm
npm install @armory-sh/middleware-hono

# yarn
yarn add @armory-sh/middleware-hono

# pnpm
pnpm add @armory-sh/middleware-hono

# bun
bun add @armory-sh/middleware-hono

Quick Start

import { Hono } from 'hono';
import { acceptPaymentsViaArmory } from '@armory-sh/middleware-hono';

const app = new Hono();

app.use('/api/*', acceptPaymentsViaArmory({
  payTo: '0xYourWalletAddress...',
  amount: '1.0'
}));

app.get('/api/protected', (c) => {
  const payment = c.get('payment');
  return c.json({ message: `Hello ${payment.payerAddress}!` });
});

app.listen(3000);

How It Works

The middleware intercepts requests and:
  1. Checks for payment headers (X-Payment or X402-PAYMENT)
  2. Verifies the payment signature and amount
  3. Stores payment info in the Hono context
  4. Calls next() if valid, or returns 402 if invalid

Configuration

Minimal Setup

acceptPaymentsViaArmory({
  payTo: '0xYourWallet...',  // Your wallet address
  amount: '1.0'              // Amount in tokens (e.g., 1 USDC)
});

All Options

{
  payTo: string,                           // Your wallet address
  amount: string | bigint,                 // Amount to charge
  token?: string,                          // Token symbol (default: USDC)
  network?: string,                        // Network name (default: base)
  defaultVersion?: 1 | 2                   // Payment version (default: 2)
}

Accessing Payment Info

Payment information is stored in the Hono context:
app.get('/api/user', (c) => {
  const payment = c.get('payment');

  return c.json({
    payer: payment.payerAddress,
    paid: payment.payload.amount,
    token: payment.payload.token
  });
});
Available Properties:
PropertyTypeDescription
payloadPaymentPayloadDecoded payment payload
payerAddressstringWallet address of the payer
version1 | 2Payment protocol version
verifiedtrueAlways true when request reaches your handler

Response Headers

On successful verification:
X-Payment-Verified: true
X-Payer-Address: 0x...
X-Payment-Response: {"status":"verified","payerAddress":"0x...","version":2}

Error Responses

402 Payment Required

{
  "error": "Payment required",
  "requirements": { ... }
}

402 Verification Failed

{
  "error": "Payment verification failed: ..."
}

Multi-Network Support

Accept payments across multiple networks and tokens:
app.use('/api/*', acceptPaymentsViaArmory({
  payTo: '0x...',
  amount: '1.0',
  accept: {
    networks: ['base', 'ethereum', 'polygon'],
    tokens: ['usdc', 'usdt']
  }
}));

Advanced Mix-and-Match Config (Current API)

import { Hono } from "hono";
import { paymentMiddleware } from "@armory-sh/middleware-hono";

const app = new Hono();

app.use(
  "/api/*",
  paymentMiddleware({
    payTo: "0x1234567890123456789012345678901234567890",
    chains: ["base", "skale-base"],
    tokens: ["usdc", "usdt", "weth", "wbtc"],
    amount: "1.0",
    facilitatorUrl: "https://fallback-facilitator.example",
    facilitatorUrlByChain: {
      base: "https://payai.example",
    },
    facilitatorUrlByToken: {
      base: { usdc: "https://payai.example" },
      "skale-base": {
        usdc: "https://payai.example",
        usdt: "https://kobaru.example",
        weth: "https://kobaru.example",
        wbtc: "https://kobaru.example",
      },
    },
    extensions: {
      bazaar: { info: { input: { sku: "pro-plan" } }, schema: { type: "object" } },
      "sign-in-with-x": { info: { domain: "api.example.com" }, schema: { type: "object" } },
    },
  }),
);
If you need fully custom amount-per-requirement combinations, use explicit requirements:
paymentMiddleware({
  requirements: [
    {
      scheme: "exact",
      network: "eip155:8453",
      amount: "1000000",
      asset: "0x833589fCD6eDb6E08f4c7C32D4f71B54bdA02913",
      payTo: "0x1234567890123456789012345678901234567890",
      maxTimeoutSeconds: 300,
    },
    {
      scheme: "exact",
      network: "eip155:1187947933",
      amount: "2500000",
      asset: "0x5f9beea3f6f22be1f4f8efc120f5095f58dbb8a2",
      payTo: "0x1234567890123456789012345678901234567890",
      maxTimeoutSeconds: 300,
    },
  ],
  facilitatorUrlByToken: {
    base: { usdc: "https://payai.example" },
    "skale-base": { usdt: "https://kobaru.example" },
  },
});
Extension keys are fail-open filtered by facilitator capability: unsupported keys are automatically omitted from PAYMENT-REQUIRED.

Tips

The amount parameter accepts human-readable strings like "1.0" for 1 token, or use bigint for precise decimal control.
Hono middleware applies to routes that match the path pattern. Use /api/* to protect all API routes, or specific paths like /premium/* for paid content.
The middleware will return 402 before your handler runs if payment is missing or invalid. Only verified requests reach your route handlers.