#### 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({
currency: '0x20c0000000000000000000000000000000000000',
recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
})],
})
// [!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({
currency: '0x20c0000000000000000000000000000000000000',
recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
})],
})
// [!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({
currency: '0x20c0000000000000000000000000000000000000',
recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
})],
})
// [!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({
currency: '0x20c0000000000000000000000000000000000000',
recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
})],
})
// [!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.
:::
***
#### 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({
currency: '0x20c0000000000000000000000000000000000000',
recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
})],
})
// [!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)
```
:::
***
## 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({
currency: '0x20c0000000000000000000000000000000000000',
recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
})],
})
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: '...' }))
}
```