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

> Payment middleware for Express

# @armory-sh/middleware-express

Accept crypto payments in your Express API with a single middleware function.

## Installation

```bash theme={null}
# npm
npm install @armory-sh/middleware-express

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

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

# bun
bun add @armory-sh/middleware-express
```

## Quick Start

This page covers Express v5+ with `@armory-sh/middleware-express`.

For Express v4, use the separate package page: [`@armory-sh/middleware-express-v4`](/packages/middleware-express-v4).

```typescript theme={null}
import express from 'express';
import { paymentMiddleware } from '@armory-sh/middleware-express';

const app = express();

app.use(paymentMiddleware({
  requirements: {
    to: '0xYourWalletAddress...',
    amount: 1000000n, // 1 USDC (6 decimals)
    expiry: Date.now() + 3600 * 1000,
    chainId: 'eip155:8453',
    assetId: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71B54bdA02913'
  }
}));

app.get('/api/protected', (req, res) => {
  // Payment info is attached to the request
  console.log(req.payment);
  res.json({ message: 'Payment verified!' });
});

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. Attaches payment info to `req.payment`
4. Calls `next()` if valid, or returns 402 if invalid

***

## Configuration

### Payment Requirements

```typescript theme={null}
{
  requirements: {
    to: string,           // Your wallet address
    amount: bigint,       // Amount in smallest unit (wei for native, token decimals for ERC20)
    expiry: number,       // Expiry timestamp (milliseconds)
    chainId: string,      // CAIP-2 chain ID (e.g., 'eip155:8453')
    assetId: string       // CAIP-19 asset ID
  }
}
```

### Common Token Addresses

| Network  | Token | Address                                      |
| -------- | ----- | -------------------------------------------- |
| Base     | USDC  | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
| Ethereum | USDC  | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` |
| Base     | USDT  | `0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb` |

### Verification Options

See runtime verification configuration in your deployment environment and middleware setup.

### Advanced Mix-and-Match Config (Current API)

```typescript theme={null}
import express from "express";
import { paymentMiddleware } from "@armory-sh/middleware-express";

const app = express();

app.use(
  "/api/premium/*",
  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" } },
    },
  }),
);
```

You can mix and match network, token, facilitator, and amount by providing explicit requirements:

```typescript theme={null}
app.use(
  "/api/checkout",
  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" },
    },
  }),
);
```

`extensions` are fail-open filtered per facilitator capability (`/supported`): unsupported keys are automatically omitted from the challenge header.

***

## Accessing Payment Info

The middleware augments the Express request with payment information:

```typescript theme={null}
app.get('/api/user', (req, res) => {
  const { payerAddress, payload, version, verified } = req.payment;

  res.json({
    welcome: `Hello ${payerAddress}`,
    youPaid: payload.amount,
    token: payload.token
  });
});
```

**Available Properties:**

| Property       | Type             | Description                 |
| -------------- | ---------------- | --------------------------- |
| `payload`      | `PaymentPayload` | Decoded payment payload     |
| `payerAddress` | `string`         | Wallet address of the payer |
| `version`      | `1 \| 2`         | Payment protocol version    |
| `verified`     | `boolean`        | Whether verification passed |

***

## Response Headers

On successful verification, the middleware adds:

```
X-Payment-Response: {"status":"verified","payerAddress":"0x...","version":2}
```

***

## Error Responses

### 402 Payment Required

No payment header was provided.

```json theme={null}
{
  "error": "Payment required",
  "requirements": { ... }
}
```

### 400 Invalid Payload

Payment header exists but couldn't be decoded.

```json theme={null}
{
  "error": "Invalid payment payload",
  "message": "..."
}
```

### 402 Verification Failed

Payment signature or amount was invalid.

```json theme={null}
{
  "error": "Verification failed: ..."
}
```

***

## Tips

<Note>
  Express middleware runs in order — place `paymentMiddleware` before any routes that require payment.
</Note>
