> **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.

# Getting started \[The mppx TypeScript library]

## 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.

## Overview

The `mppx` TypeScript library provides a typed interface over the Machine Payments Protocol, from high-level abstractions to low-level primitives and building blocks.

<div className="flex gap-2">
  [GitHub: wevm/mppx](https://github.com/wevm/mppx)

  Maintained by [Wevm](https://github.com/wevm)
</div>

## Install

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

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

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

## Server entrypoints

Use `mppx/server` for most servers. It exports the server runtime and every built-in payment method.

### x402 compatibility

Use a framework-specific x402 entrypoint when you already have an official x402 route table and resource server. These wrappers preserve x402 handling and add native MPP Challenges and Credentials to the same route.

| Runtime | Reference |
|---|---|
| Express | [`mppx/x402/express`](/sdk/typescript/x402/express) |
| Hono | [`mppx/x402/hono`](/sdk/typescript/x402/hono) |
| MCP | [`mppx/x402/mcp`](/sdk/typescript/x402/mcp) |
| Next.js | [`mppx/x402/next`](/sdk/typescript/x402/next) |

### Advanced options

Use lightweight entrypoints when a deployment only needs core server primitives or Stripe Shared Payment Tokens. These imports omit unrelated payment rails from the module graph.

```ts twoslash [server.ts]
import Stripe from 'stripe'
import { Mppx } from 'mppx/server/core'
import { stripe } from 'mppx/stripe/server/spt'

const client = new Stripe(process.env.STRIPE_SECRET_KEY!)

const mppx = Mppx.create({
  methods: [
    stripe.spt({
      client,
      networkId: process.env.STRIPE_NETWORK_ID!,
      paymentMethodTypes: ['card'],
    }),
  ],
  secretKey: process.env.MPP_SECRET_KEY!,
})
```

## Quick start

This quick start guide shows you how to use `mppx` with the [`tempo` payment method](/payment-methods/tempo).

You can apply the same patterns to [other payment methods](/payment-methods).

### Client

<div className="space-y-4">
  ::::steps
  ### Install peer dependencies

  In this example, you use the Tempo Accounts SDK to connect an account. You can also use a viem account directly.

  #### Accounts SDK

  :::code-group
  ```bash [npm]
  $ npm install accounts
  ```

  ```bash [pnpm]
  $ pnpm add accounts
  ```

  ```bash [bun]
  $ bun add accounts
  ```
  :::

  #### viem

  :::code-group
  ```bash [npm]
  $ npm install viem
  ```

  ```bash [pnpm]
  $ pnpm add viem
  ```

  ```bash [bun]
  $ bun add viem
  ```
  :::

  ### Connect an account

  Next, connect a Tempo account to sign payments.

  #### Accounts SDK

  ```ts twoslash [define-account.ts]
  import { Provider } from 'accounts'

  const provider = Provider.create({ mpp: false }) // Avoid double 402 handling; mppx is configured below.
  await provider.request({ method: 'wallet_connect' })
  ```

  #### viem

  ```ts twoslash [define-account.ts]
  import { privateKeyToAccount } from 'viem/accounts'

  const account = privateKeyToAccount('0xabc…123')
  ```

  ### Create payment handler

  Call `Mppx.create` at startup. This polyfills the global `fetch` to automatically handle `402` payment Challenges.

  #### Accounts SDK

  ```ts twoslash [create-paid-fetch.ts]
  import { Provider } from 'accounts'
  import { Mppx, tempo } from 'mppx/client' // [!code hl]

  const provider = Provider.create({ mpp: false }) // Avoid double 402 handling; mppx is configured below.
  await provider.request({ method: 'wallet_connect' })

  // [!code hl:start]
  Mppx.create({
    methods: [tempo({
      account: provider.getAccount({ signable: true }),
      getClient: provider.getClient,
    })],
  })
  // [!code hl:end]
  ```

  #### viem

  ```ts twoslash [create-paid-fetch.ts]
  import { privateKeyToAccount } from 'viem/accounts'
  import { Mppx, tempo } from 'mppx/client' // [!code hl]

  const account = privateKeyToAccount('0xabc…123')

  // [!code hl:start]
  Mppx.create({
    methods: [tempo({ account })],
  })
  // [!code hl:end]
  ```

  :::tip
  If you want to avoid polyfilling, use the returned `fetch` instead.

  ##### Accounts SDK

  ```ts twoslash
  import { Provider } from 'accounts'
  import { Mppx, tempo } from 'mppx/client'

  const provider = Provider.create({ mpp: false }) // Avoid double 402 handling; mppx is configured below.
  await provider.request({ method: 'wallet_connect' })

  const mppx = Mppx.create({
    methods: [tempo({
      account: provider.getAccount({ signable: true }),
      getClient: provider.getClient,
    })],
    polyfill: false, // [!code hl]
  })

  const response = await mppx.fetch('https://mpp.dev/api/ping/paid') // [!code hl]
  ```

  ##### viem

  ```ts twoslash
  import { privateKeyToAccount } from 'viem/accounts'
  import { Mppx, tempo } from 'mppx/client'

  const account = privateKeyToAccount('0xabc…123')

  const mppx = Mppx.create({
    methods: [tempo({ account })],
    polyfill: false, // [!code hl]
  })

  const response = await mppx.fetch('https://mpp.dev/api/ping/paid') // [!code hl]
  ```
  :::

  ### Request protected resources

  Use `fetch`. Payment happens when a server returns `402`.

  ```ts twoslash [fetch-resource.ts]
  const response = await fetch('https://mpp.dev/api/ping/paid')
  ```
  ::::
</div>

### Server

<div className="space-y-4">
  ### Framework mode

  Use the framework-specific middleware from `mppx` to integrate payment into your server. Each middleware handles the `402` Challenge/Credential flow and attaches Receipts automatically.

  :::code-group
  ```ts [Next.js]
  import { Mppx, tempo } from 'mppx/nextjs'

  // [!code hl:start]
  const mppx = Mppx.create({
    methods: [tempo.charge({
      currency: '0x20c0000000000000000000000000000000000000',
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    })],
    secretKey: process.env.MPP_SECRET_KEY!,
  })
  // [!code hl:end]

  export const GET = 
    mppx.charge({ amount: '0.1' }) // [!code hl]
    (() => Response.json({ data: '...' }))
  ```

  ```ts [Hono]
  import { Hono } from 'hono'
  import { Mppx, tempo } from 'mppx/hono'

  const app = new Hono()

  // [!code hl:start]
  const mppx = Mppx.create({
    methods: [tempo.charge({
      currency: '0x20c0000000000000000000000000000000000000',
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    })],
    secretKey: process.env.MPP_SECRET_KEY!,
  })
  // [!code hl:end]

  app.get(
    '/resource', 
    mppx.charge({ amount: '0.1' }), // [!code hl]
    (c) => c.json({ data: '...' }),
  )
  ```

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

  // [!code hl:start]
  const mppx = Mppx.create({
    methods: [tempo.charge({
      currency: '0x20c0000000000000000000000000000000000000',
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    })],
    secretKey: process.env.MPP_SECRET_KEY!,
  })
  // [!code hl:end]

  const app = new Elysia()
    .guard(
      { beforeHandle: mppx.charge({ amount: '0.1' }) }, // [!code hl]
      (app) => app.get('/resource', () => ({ data: '...' })),
    )
  ```

  ```ts [Express]
  import express from 'express'
  import { Mppx, tempo } from 'mppx/express'

  const app = express()

  // [!code hl:start]
  const mppx = Mppx.create({
    methods: [tempo.charge({
      currency: '0x20c0000000000000000000000000000000000000',
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    })],
    secretKey: process.env.MPP_SECRET_KEY!,
  })
  // [!code hl:end]

  app.get(
    '/resource', 
    mppx.charge({ amount: '0.1' }), // [!code hl]
    (req, res) => res.json({ data: '...' }))
  ```
  :::

  :::tip
  You can also override `currency` and `recipient` per call if different routes need different payment configurations.

  ```ts
  mppx.charge({ 
    amount: '0.1', 
    currency: '0x…', // [!code ++]
    recipient: '0x…', // [!code ++]
  })
  ```
  :::

  :::note
  Don't see your framework? `mppx` is designed to be framework-agnostic. See [Manual mode](#manual-mode) below.
  :::

  <div className="h-2" />

  ***

  <div className="h-px" />

  ### Manual mode

  If you prefer full control over the payment flow, use `mppx/server` directly with the Fetch API.

  ```ts twoslash
  import { Mppx, tempo } from 'mppx/server'

  const mppx = Mppx.create({
    methods: [tempo.charge({
      currency: '0x20c0000000000000000000000000000000000000',
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    })],
    secretKey: process.env.MPP_SECRET_KEY!,
  })

  // [!code focus:start]
  export async function handler(request: Request) { 
    const response = await mppx.charge({ amount: '0.1' })(request) 
    // [!code focus:end]

    // Payment required: send 402 response with challenge 
    if (response.status === 402) return response.challenge 

    // Payment verified: attach receipt and return resource 
    return response.withReceipt(Response.json({ data: '...' })) 
  } 
  ```

  :::info[Currency and recipient values]
  `currency` is the TIP-20 token contract address—`0x20c0…` is PathUSD on Tempo. `recipient` is the address that receives payment. See [Tempo payment method](/payment-methods/tempo) for supported tokens.
  :::

  The intent handler accepts a [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)-compatible request object, and returns a `Response` object.

  The Fetch API is compatible with most server frameworks, including: [Hono](https://hono.dev), [Deno](https://deno.com), [Cloudflare Workers](https://workers.dev), [Next.js](https://nextjs.org),
  [Bun](https://bun.sh), and other Fetch API-compatible frameworks.

  :::tip
  You can also override `currency` and `recipient` per call if different routes need different payment configurations.

  ```ts
  const response = await mppx.charge({ 
    amount: '0.1', 
    currency: '0x…', // [!code ++]
    recipient: '0x…', // [!code ++]
  })(request) 
  ```
  :::

  <div className="h-2" />

  ***

  <div className="h-px" />

  ## Node.js & Express compatibility

  If your framework doesn't support the **Fetch API** (for example, Express or Node.js), you're likely interfacing with the [Node.js Request Listener API](https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener).

  Use the `Mppx.toNodeListener` helper to transform the handler into a Node.js-compatible listener.

  ```ts twoslash
  import { Mppx, tempo } from 'mppx/server'

  const mppx = Mppx.create({
    methods: [tempo.charge({
      currency: '0x20c0000000000000000000000000000000000000',
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    })],
    secretKey: process.env.MPP_SECRET_KEY!,
  })

  type IncomingMessage = import('node:http').IncomingMessage
  type ServerResponse = import('node:http').ServerResponse
  // ---cut---
  export async function handler(req: IncomingMessage, res: ServerResponse) { 
    const response = await Mppx.toNodeListener( // [!code ++]
      mppx.charge({ amount: '0.1' })
    )(req, res) // [!code ++]

    // Payment required: send 402 response with challenge 
    if (response.status === 402) return response.challenge 

    // Payment verified: attach receipt and return resource 
    return response.withReceipt(Response.json({ data: '...' })) 
  } 
  ```
</div>

### CLI

<div className="space-y-4">
  The `mppx` package install automatically includes a [CLI tool](/sdk/typescript/cli) you can use to make the same request from the command line.

  ::::steps
  ### Create an account

  Create a Tempo mainnet account to sign payments. The key is stored in your system keychain. Fund the account with pathUSD before making a paid request.

  :::code-group
  ```bash [npm]
  $ npx mppx account create --network mainnet
  ```

  ```bash [pnpm]
  $ pnpm mppx account create --network mainnet
  ```

  ```bash [bun]
  $ bunx mppx account create --network mainnet
  ```
  :::

  ### Make a paid request

  Run the CLI with a URL to make a paid request. Payment is handled automatically when the server returns `402`.

  :::code-group
  ```bash [npm]
  $ npx mppx https://mpp.dev/api/ping/paid
  ```

  ```bash [pnpm]
  $ pnpm mppx https://mpp.dev/api/ping/paid
  ```

  ```bash [bun]
  $ bunx mppx https://mpp.dev/api/ping/paid
  ```
  :::
  ::::
</div>
