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

# Create a payment link \[Share a link. Get paid.]

Create a full payment page for any API endpoint—no frontend required. Share the link anywhere and users pay directly from the page.

## Supported methods

| Method | `html` type | What renders |
| --- | --- | --- |
| [`tempo`](/sdk/typescript/server/Method.tempo) | `boolean` or config object | "Continue with Tempo" wallet UI |
| [`stripe`](/sdk/typescript/server/Method.stripe) | Config object | Full Stripe Elements card form |
| [`solana.charge`](/payment-methods/solana/charge#payment-links) | `boolean` | "Continue with Solana" wallet UI |

For Tempo and Stripe, `html` also accepts an object so you can customize page text and theme without building a frontend.

## Demo

This is a live payment link. Click "Continue with Tempo" to pay $0.01 and receive a random photo from [Picsum](https://picsum.photos).

[Open the live payment link](https://mpp.dev/api/payment-link/photo) to pay $0.01 with Tempo and receive a photo.

## Prompt mode

Paste this into your coding agent to create a payment link in one prompt:

```text
Use https://mpp.dev/guides/payment-links.md as reference.
Add mppx to my app with a payment-gated photo endpoint
that charges $0.01 per request using the Tempo payment method with
PathUSD. Enable payment links so browsers can pay directly
from the page by setting html: true on the tempo.charge() method config.
When payment is verified, fetch a random photo from
https://picsum.photos/1024/1024 and return the URL as JSON.
```

## Manual mode

Select your framework to follow a step-by-step guide. If your framework isn't listed, choose **Other** for a generic [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) approach compatible with most TypeScript server frameworks.

### Next.js

::::steps
### Install `mppx`

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

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

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

### Set up `Mppx` instance with `html: true`

```ts [app/api/photo/route.ts]
import crypto from 'crypto'
import { Mppx, tempo } from 'mppx/nextjs'

export const mppx = Mppx.create({
  methods: [tempo.charge({
    currency: '0x20c0000000000000000000000000000000000000',
    html: true, // [!code hl]
    recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    testnet: true,
  })],
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
})
```

### Create the `/api/photo` route

Create the photo route with `mppx.charge`. Browsers see a payment page; programmatic clients get the standard `402` flow.

```ts [app/api/photo/route.ts]
import crypto from 'crypto'
import { Mppx, tempo } from 'mppx/nextjs'

export const mppx = Mppx.create({
  methods: [tempo.charge({
    currency: '0x20c0000000000000000000000000000000000000',
    html: true,
    recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    testnet: true,
  })],
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
})

// [!code focus:start]
export const GET =
  mppx.charge({ amount: '0.01', description: 'Random stock photo' })
  (async () => {
    const res = await fetch('https://picsum.photos/1024/1024')
    return Response.json({ url: res.url })
  })
// [!code focus:end]
```

### Test in the browser

Open `http://localhost:3000/api/photo` in your browser. The server returns a payment page with a "Continue with Tempo" button. After payment, the page reloads with the photo URL.

Programmatic clients work the same way:

```bash [terminal]
$ npx mppx http://localhost:3000/api/photo
```
::::

### Hono

::::steps
### Install `mppx` and `hono`

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

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

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

### Set up `Mppx` instance with `html: true`

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

const app = new Hono()

const mppx = Mppx.create({
  methods: [tempo.charge({
    currency: '0x20c0000000000000000000000000000000000000',
    html: true, // [!code hl]
    recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    testnet: true,
  })],
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
})
```

### Create the `/api/photo` route

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

const app = new Hono()

const mppx = Mppx.create({
  methods: [tempo.charge({
    currency: '0x20c0000000000000000000000000000000000000',
    html: true,
    recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    testnet: true,
  })],
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
})

// [!code focus:start]
app.get(
  '/api/photo',
  mppx.charge({ amount: '0.01', description: 'Random stock photo' }),
  async (c) => {
    const res = await fetch('https://picsum.photos/1024/1024')
    return c.json({ url: res.url })
  },
)
// [!code focus:end]
```

### Test in the browser

Open `http://localhost:3000/api/photo` in your browser.

```bash [terminal]
$ npx mppx http://localhost:3000/api/photo
```
::::

### Express

::::steps
### Install `mppx` and `express`

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

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

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

### Set up `Mppx` instance with `html: true`

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

const app = express()

const mppx = Mppx.create({
  methods: [tempo.charge({
    currency: '0x20c0000000000000000000000000000000000000',
    html: true, // [!code hl]
    recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    testnet: true,
  })],
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
})
```

### Create the `/api/photo` route

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

const app = express()

const mppx = Mppx.create({
  methods: [tempo.charge({
    currency: '0x20c0000000000000000000000000000000000000',
    html: true,
    recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    testnet: true,
  })],
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
})

// [!code focus:start]
app.get(
  '/api/photo',
  mppx.charge({ amount: '0.01', description: 'Random stock photo' }),
  async (req, res) => {
    const response = await fetch('https://picsum.photos/1024/1024')
    res.json({ url: response.url })
  },
)
// [!code focus:end]
```

### Test in the browser

Open `http://localhost:3000/api/photo` in your browser.

```bash [terminal]
$ npx mppx http://localhost:3000/api/photo
```
::::

### Other

This guide walks through using `mppx/server` directly with any [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)-compatible framework: [Bun](https://bun.sh), [Deno](https://deno.com), [Cloudflare Workers](https://workers.dev), and others.

<div className="h-6" />

::::steps
### Install `mppx`

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

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

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

### Set up `Mppx` instance with `html: true`

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

const mppx = Mppx.create({
  methods: [tempo.charge({
    currency: '0x20c0000000000000000000000000000000000000',
    html: true, // [!code hl]
    recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    testnet: true,
  })],
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
})
```

### Create the handler

The server detects the `Accept` header and returns a payment page for browsers. Programmatic clients get the standard `402` flow.

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

const mppx = Mppx.create({
  methods: [tempo.charge({
    currency: '0x20c0000000000000000000000000000000000000',
    html: true,
    recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    testnet: true,
  })],
  secretKey: process.env.MPP_SECRET_KEY || crypto.randomBytes(32).toString('base64'),
})

// [!code focus:start]
Bun.serve({
  async fetch(request) {
    const result = await mppx.charge({
      amount: '0.01',
      description: 'Random stock photo',
    })(request)

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

    const res = await fetch('https://picsum.photos/1024/1024')
    return result.withReceipt(Response.json({ url: res.url }))
  },
})
// [!code focus:end]
```

### Test in the browser

Open `http://localhost:3000` in your browser.

```bash [terminal]
$ npx mppx http://localhost:3000
```
::::

## Next steps
