> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://mpp.dev/api/mcp` to find what you need.

# Elysia \[Payment middleware for Elysia]

## Choose a signing account

### Direct

Create a server-only wallet.ts module, then import account wherever an example creates a local signing account.

```ts
import { privateKeyToAccount } from 'viem/accounts'

export const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
```

### Privy

Create an EVM wallet in Privy, fund it with the required currency on this page's network, and keep PRIVY\_APP\_SECRET server-side.

```bash
pnpm add @privy-io/node
```

Create a server-only privy.ts module, then import its account wherever an example configures account or feePayer.

```ts
import { PrivyClient } from '@privy-io/node'
import { createViemAccount } from '@privy-io/node/viem'

const privy = new PrivyClient({
  appId: process.env.PRIVY_APP_ID!,
  appSecret: process.env.PRIVY_APP_SECRET!,
})

export const account = createViemAccount(privy, {
  address: process.env.PRIVY_WALLET_ADDRESS as `0x${string}`,
  walletId: process.env.PRIVY_WALLET_ID!,
})
```

createViemAccount delegates signatures to the Privy wallet, so it replaces any local viem account in the examples on this page.

Native [Elysia](https://elysiajs.com) middleware that gates routes behind payment intents. Requires Elysia 1.2.0 or newer.

## Install

:::code-group
```bash [npm]
$ npm install mppx elysia
```

```bash [pnpm]
$ pnpm add mppx elysia
```

```bash [bun]
$ bun add mppx elysia
```
:::

## Usage

Import `Mppx` and `tempo` from `mppx/elysia` to create an Elysia-aware payment handler. Each intent returns paired `beforeHandle` and `afterHandle` hooks. Pass the complete result to `.guard()` so `mppx` verifies the Credential before the route and attaches the Receipt to the actual route response.

```ts [server.ts]
import { Elysia } from 'elysia'
import { Mppx, tempo } from 'mppx/elysia'

const mppx = Mppx.create({ methods: [tempo.charge()] })

const app = new Elysia()
  .guard(
    mppx.charge({ amount: '1' }),
    (app) => app.get('/premium', () => ({ data: 'paid content' })),
  )
```

Don't register only the `beforeHandle` function. Streaming payments require the paired `afterHandle` hook to meter the route response.

When route options omit `scope` and reserved scope metadata, the middleware binds the Challenge to the Elysia method and route path automatically.

### Protect multiple routes

Put multiple routes inside the same `.guard()` callback to apply one payment configuration to all of them.

```ts [server.ts]
import { Elysia } from 'elysia'
import { Mppx, tempo } from 'mppx/elysia'

const mppx = Mppx.create({ methods: [tempo.charge()] }) // [!code hl]

const app = new Elysia()
  .guard(
    mppx.charge({ amount: '1' }), // [!code hl]
    (app) => app
      .get('/premium', () => ({ data: 'paid content' }))
      .get('/another', () => ({ data: 'also paid' })),
  )
```

### Session payments

Use `mppx.session()` with `tempo.session()` to gate routes behind current v2 Sessions.

```ts [server.ts]
import { Store } from 'mppx'
import { Elysia } from 'elysia'
import { Mppx, tempo } from 'mppx/elysia'
import { privateKeyToAccount } from 'viem/accounts'

const account = privateKeyToAccount('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')

const mppx = Mppx.create({
  methods: [
    tempo.session({
      account,
      chainId: 4217,
      currencies: ['0x20c0000000000000000000006a37DA5C996874BE'], // OUSD on Tempo
      store: Store.memory(),
    }),
  ],
})

const app = new Elysia()
  .guard(
    mppx.session({ amount: '1', unitType: 'token' }),
    (app) => app.get('/content', () => ({ data: 'session content' })),
  )
```

## x402-compatible clients

Elysia apps can serve MPP and x402 clients from the same endpoint when you register [`evm.charge`](/payment-methods/evm/charge) with `x402.facilitator`.

```ts [server.ts]
import { Elysia } from 'elysia'
import { Mppx, evm } from 'mppx/elysia'

const mppx = Mppx.create({
  methods: [
    evm.charge({
      currency: evm.assets.baseSepolia.USDC,
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
      x402: {
        facilitator: 'https://x402.org/facilitator',
      },
    }),
  ],
  secretKey: process.env.MPP_SECRET_KEY ?? 'local-dev-secret',
})

const app = new Elysia().guard(
  mppx.evm.charge({
    amount: '0.01',
    description: 'Premium API access',
  }),
  (app) => app.get('/paid', () => ({ data: 'paid content' })),
)
```

See [build a client for MPP and x402](/guides/use-mpp-with-x402#build-a-client-for-mpp-and-x402) for the client setup.
