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

# Accept multiple payment methods \[Stablecoins, cards, and Bitcoin on a single endpoint]

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

Build a payment-gated API that accepts [Tempo](/payment-methods/tempo) stablecoins, [Stripe](/payment-methods/stripe) cards, and [Lightning](/payment-methods/lightning) Bitcoin—all on the same endpoint. The server returns a `402` Challenge advertising every available method, and the client pays with whichever rail it supports.

:::info
MPP's multi-method support is additive. Each payment method is independent—you can start with one and add more at any time without changing your route handlers.
:::

## Prompt mode

Paste this into your coding agent to build the entire guide in one prompt:

```text
Use https://mpp.dev/guides/multiple-payment-methods.md as reference.
Add mppx to my app with a payment-gated endpoint that accepts
three payment methods: Tempo, Stripe, and Lightning. Charge $0.01 per request.
When payment is verified via any method, return a JSON response.
```

## How it works

When multiple methods are registered, the `402` response includes a `WWW-Authenticate` header for each one. The client picks the method it supports and sends the appropriate Credential.

```http
HTTP/1.1 402 Payment Required
WWW-Authenticate: Payment method="tempo", intent="charge", ...
WWW-Authenticate: Payment method="stripe", intent="charge", ...
WWW-Authenticate: Payment method="lightning", intent="charge", ...
```

The server verifies whichever Credential it receives. Your route handler stays the same regardless of which method the client chose.

## Server setup

::::steps
### Install dependencies

:::code-group
```bash [npm]
$ npm install mppx stripe @buildonspark/lightning-mpp-sdk viem
```

```bash [pnpm]
$ pnpm add mppx stripe @buildonspark/lightning-mpp-sdk viem
```

```bash [bun]
$ bun add mppx stripe @buildonspark/lightning-mpp-sdk viem
```
:::

### Configure payment methods

Register all three methods in a single `Mppx.create` call. Each method has its own configuration—Tempo needs a recipient address and currency, Stripe needs API credentials, and Lightning needs a wallet mnemonic.

```ts [server.ts]
import Stripe from 'stripe'
import { Mppx, tempo, stripe } from 'mppx/server'
import { spark } from '@buildonspark/lightning-mpp-sdk/server'

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

const mppx = Mppx.create({
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
  methods: [
    tempo.charge({
      testnet: true,
      currency: '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    }),
    stripe.charge({
      client: stripeClient,
      networkId: 'internal',
      paymentMethodTypes: ['card'],
    }),
    spark.charge({
      mnemonic: process.env.MNEMONIC!,
    }),
  ],
})
```

### Create a payment-gated route

The route handler is identical to a single-method setup. `mppx.charge` advertises all registered methods in the Challenge and verifies whichever Credential the client presents.

```ts [server.ts]
import crypto from 'crypto'
import Stripe from 'stripe'
import { Mppx, tempo, stripe } from 'mppx/server'
import { spark } from '@buildonspark/lightning-mpp-sdk/server'

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

const mppx = Mppx.create({
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
  methods: [
    tempo.charge({
      testnet: true,
      currency: '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    }),
    stripe.charge({
      client: stripeClient,
      networkId: 'internal',
      paymentMethodTypes: ['card'],
    }),
    spark.charge({
      mnemonic: process.env.MNEMONIC!,
    }),
  ],
})

// [!code focus:start]
Bun.serve({
  async fetch(request) {
    const result = await mppx.charge({
      amount: '0.01',
      currency: 'usd',
      decimals: 2,
      description: 'Premium API access',
    })(request)

    if (result.status === 402) return result.challenge

    return result.withReceipt(Response.json({ message: 'Paid content' }))
  },
})
// [!code focus:end]
```

### Test via the `mppx` CLI

The `mppx` CLI uses Tempo by default. Each payment method has its own client SDK—see the individual method docs for client setup.

```bash [terminal]
# Validate the paid route
$ npx mppx validate http://localhost:3000

# Create account funded with testnet tokens
$ npx mppx account create

# Make a paid request (pays with Tempo)
$ npx mppx http://localhost:3000
```
::::

## Framework examples

The `Mppx.create` configuration is the same across frameworks—only the route handler syntax changes.

### Hono

```ts [server.ts]
import crypto from 'crypto'
import { Hono } from 'hono'
import Stripe from 'stripe'
import { Mppx, tempo, stripe } from 'mppx/hono'
import { spark } from '@buildonspark/lightning-mpp-sdk/server'

const app = new Hono()
const stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY!)

const mppx = Mppx.create({
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
  methods: [
    tempo.charge({
      testnet: true,
      currency: '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    }),
    stripe.charge({
      client: stripeClient,
      networkId: 'internal',
      paymentMethodTypes: ['card'],
    }),
    spark.charge({
      mnemonic: process.env.MNEMONIC!,
    }),
  ],
})

app.get(
  '/api/resource',
  mppx.charge({ amount: '0.01', currency: 'usd', decimals: 2, description: 'Premium API access' }),
  async (c) => c.json({ message: 'Paid content' }),
)
```

### Express

```ts [server.ts]
import crypto from 'crypto'
import express from 'express'
import Stripe from 'stripe'
import { Mppx, tempo, stripe } from 'mppx/express'
import { spark } from '@buildonspark/lightning-mpp-sdk/server'

const app = express()
const stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY!)

const mppx = Mppx.create({
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
  methods: [
    tempo.charge({
      testnet: true,
      currency: '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    }),
    stripe.charge({
      client: stripeClient,
      networkId: 'internal',
      paymentMethodTypes: ['card'],
    }),
    spark.charge({
      mnemonic: process.env.MNEMONIC!,
    }),
  ],
})

app.get(
  '/api/resource',
  mppx.charge({ amount: '0.01', currency: 'usd', decimals: 2, description: 'Premium API access' }),
  async (req, res) => res.json({ message: 'Paid content' }),
)
```

### Next.js

```ts [app/api/resource/route.ts]
import crypto from 'crypto'
import Stripe from 'stripe'
import { Mppx, tempo, stripe } from 'mppx/nextjs'
import { spark } from '@buildonspark/lightning-mpp-sdk/server'

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

const mppx = Mppx.create({
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
  methods: [
    tempo.charge({
      testnet: true,
      currency: '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    }),
    stripe.charge({
      client: stripeClient,
      networkId: 'internal',
      paymentMethodTypes: ['card'],
    }),
    spark.charge({
      mnemonic: process.env.MNEMONIC!,
    }),
  ],
})

export const GET =
  mppx.charge({ amount: '0.01', currency: 'usd', decimals: 2, description: 'Premium API access' })
  (async () => Response.json({ message: 'Paid content' }))
```

## Method-specific configuration

Each payment method has its own parameters. Refer to the individual method docs for the full configuration reference:

## Client preferences

Clients can declare which payment methods they prefer by passing `paymentPreferences` to `Mppx.create`. This sends an `Accept-Payment` header on every request, and the server uses it to filter Challenges down to the methods the client supports.

### Accounts SDK

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

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

Mppx.create({
  methods: [
    tempo({
      account: provider.getAccount({ signable: true }),
      getClient: provider.getClient,
    }),
    stripe.charge({
      createToken: async (opts) => {
        const res = await fetch('/api/create-spt', {
          body: JSON.stringify(opts),
          headers: { 'Content-Type': 'application/json' },
          method: 'POST',
        })
        const { spt } = await res.json() as { spt: string }
        return spt
      },
    }),
  ],
  // [!code hl:start]
  paymentPreferences: ({ tempo, stripe }) => ({
    [tempo.charge]: 1,
    [stripe.charge]: 0.5,
    [tempo.session]: 0.2,
  }),
  // [!code hl:end]
})
```

### viem

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

const account = privateKeyToAccount('0x...')

Mppx.create({
  methods: [
    tempo({ account }),
    stripe.charge({
      createToken: async (opts) => {
        const res = await fetch('/api/create-spt', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(opts),
        })
        const { spt } = await res.json() as { spt: string }
        return spt
      },
    }),
  ],
  // [!code hl:start]
  paymentPreferences: ({ tempo, stripe }) => ({
    [tempo.charge]: 1,
    [stripe.charge]: 0.5,
    [tempo.session]: 0.2,
  }),
  // [!code hl:end]
})
```

See the [`paymentPreferences` parameter reference](/sdk/typescript/client/Mppx.create#paymentpreferences-optional) for the full configuration options.

## Next steps
