<!--
Sitemap:
- [CLI Playground](/_cli)
- [Page Not Found](/404)
- [Brand](/brand): MPP brand assets and guidelines
- [Frequently asked questions](/faq): Common questions about the Machine Payments Protocol
- [Machine Payments Protocol](/overview): The open protocol for internet-native payments
- [Payment methods](/payment-methods/): Available methods and how to choose one
- [Protocol overview](/protocol/): Standardizing HTTP 402 for machine-to-machine payments
- [Quickstart](/quickstart/): Get started with MPP in minutes
- [SDKs & Tools](/sdk/): Official implementations in multiple languages
- [Building with AI](/guides/building-with-ai): Use llms-full.txt to give your coding agent complete MPP context.
- [Accept one-time payments](/guides/one-time-payments): Charge per request with a payment-gated API
- [Accept pay-as-you-go payments](/guides/pay-as-you-go): Session-based billing with payment channels
- [Accept streamed payments](/guides/streamed-payments): Per-token billing over Server-Sent Events
- [Charge](/intents/charge): Immediate one-time payments
- [Custom](/payment-methods/custom): Build your own payment method
- [Stripe](/payment-methods/stripe/): Cards, wallets, and other Stripe supported payment methods
- [Tempo](/payment-methods/tempo/): Stablecoin payments on the Tempo blockchain
- [Challenges](/protocol/challenges): Server-issued payment requirements
- [Credentials](/protocol/credentials): Client-submitted payment proofs
- [HTTP 402 payment required](/protocol/http-402): The status code that signals payment is required
- [Receipts](/protocol/receipts): Server acknowledgment of successful payment
- [Transports](/protocol/transports/): HTTP and MCP bindings for payment flows
- [Client quickstart](/quickstart/client): Handle payment-gated resources automatically
- [presto](/quickstart/presto): Make paid HTTP requests from the command line
- [Server quickstart](/quickstart/server): Charge for resources and verify payment credentials
- [Python SDK](/sdk/python/): The pympp Python library
- [Rust SDK](/sdk/rust/): The mpp Rust library
- [Getting started](/sdk/typescript/): The mppx TypeScript library
- [presto](/tools/presto): Command-line HTTP client for MPP
- [Stripe charge](/payment-methods/stripe/charge): One-time payments using Stripe Payment Tokens
- [Tempo charge](/payment-methods/tempo/charge): One-time TIP-20 token transfers
- [Session](/payment-methods/tempo/session): Low-cost high-throughput payments
- [HTTP transport](/protocol/transports/http): Payment flows using standard HTTP headers
- [MCP transport](/protocol/transports/mcp): Payment flows for AI tool calls
- [Client](/sdk/python/client): Handle 402 responses automatically
- [Core Types](/sdk/python/core): Challenge, Credential, and Receipt primitives
- [Server](/sdk/python/server): Protect endpoints with payment requirements
- [Client](/sdk/rust/client): Handle 402 responses automatically
- [Server](/sdk/rust/server): Protect endpoints with payment requirements
- [CLI Reference](/sdk/typescript/cli): Built-in command-line tool for paid HTTP requests
- [Method.from](/sdk/typescript/Method.from)
- [presto examples](/tools/presto/examples): Real-world usage patterns
- [tempo](/sdk/typescript/client/Method.tempo): Register all Tempo intents
- [Method.tempo.charge](/sdk/typescript/client/Method.tempo.charge): One-time payments
- [Method.tempo.session](/sdk/typescript/client/Method.tempo.session): Low-cost high-throughput payments
- [Mppx.create](/sdk/typescript/client/Mppx.create)
- [Mppx.restore](/sdk/typescript/client/Mppx.restore)
- [Transport.from](/sdk/typescript/client/Transport.from)
- [Transport.http](/sdk/typescript/client/Transport.http)
- [Transport.mcp](/sdk/typescript/client/Transport.mcp)
- [BodyDigest.compute](/sdk/typescript/core/BodyDigest.compute)
- [BodyDigest.verify](/sdk/typescript/core/BodyDigest.verify)
- [Challenge.deserialize](/sdk/typescript/core/Challenge.deserialize)
- [Challenge.from](/sdk/typescript/core/Challenge.from)
- [Challenge.fromHeaders](/sdk/typescript/core/Challenge.fromHeaders)
- [Challenge.fromMethod](/sdk/typescript/core/Challenge.fromMethod)
- [Challenge.fromResponse](/sdk/typescript/core/Challenge.fromResponse)
- [Challenge.meta](/sdk/typescript/core/Challenge.meta)
- [Challenge.serialize](/sdk/typescript/core/Challenge.serialize)
- [Challenge.verify](/sdk/typescript/core/Challenge.verify)
- [Credential.deserialize](/sdk/typescript/core/Credential.deserialize)
- [Credential.from](/sdk/typescript/core/Credential.from)
- [Credential.fromRequest](/sdk/typescript/core/Credential.fromRequest)
- [Credential.serialize](/sdk/typescript/core/Credential.serialize)
- [Expires](/sdk/typescript/core/Expires)
- [Method.from](/sdk/typescript/core/Method.from)
- [Method.toClient](/sdk/typescript/core/Method.toClient)
- [Method.toServer](/sdk/typescript/core/Method.toServer)
- [PaymentRequest.deserialize](/sdk/typescript/core/PaymentRequest.deserialize)
- [PaymentRequest.from](/sdk/typescript/core/PaymentRequest.from)
- [PaymentRequest.serialize](/sdk/typescript/core/PaymentRequest.serialize)
- [Receipt.deserialize](/sdk/typescript/core/Receipt.deserialize)
- [Receipt.from](/sdk/typescript/core/Receipt.from)
- [Receipt.fromResponse](/sdk/typescript/core/Receipt.fromResponse)
- [Receipt.serialize](/sdk/typescript/core/Receipt.serialize)
- [Elysia](/sdk/typescript/middlewares/elysia): Payment middleware for Elysia
- [Express](/sdk/typescript/middlewares/express): Payment middleware for Express
- [Hono](/sdk/typescript/middlewares/hono): Payment middleware for Hono
- [Next.js](/sdk/typescript/middlewares/nextjs): Payment middleware for Next.js
- [Method.tempo.charge](/sdk/typescript/server/Method.tempo.charge)
- [Method.tempo.session](/sdk/typescript/server/Method.tempo.session): Low-cost high-throughput payments
- [Mppx.create](/sdk/typescript/server/Mppx.create)
- [Mppx.toNodeListener](/sdk/typescript/server/Mppx.toNodeListener)
- [Transport.from](/sdk/typescript/server/Transport.from)
- [Transport.http](/sdk/typescript/server/Transport.http)
- [Transport.mcp](/sdk/typescript/server/Transport.mcp)
- [Transport.mcpSdk](/sdk/typescript/server/Transport.mcpSdk)
-->

# Server \[Protect endpoints with payment requirements]

Protect endpoints with payment requirements.

## Quick start

Create an `Mpp` instance with `Mpp.create()` and call `charge()` with a human-readable amount. The `tempo()` factory creates a Tempo payment method—configure `currency` and `recipient` once, then every `charge()` call uses those defaults.

```python [server.py]
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from mpp import Challenge
from mpp.server import Mpp
from mpp.methods.tempo import tempo, ChargeIntent

app = FastAPI()

mpp = Mpp.create(
    method=tempo(
        currency="0x20c0000000000000000000000000000000000000",
        recipient="0xa726a1CD723409074DF9108A2187cfA19899aCF8",
        intents={"charge": ChargeIntent()},
    ),
)

@app.get("/resource")
async def get_resource(request: Request):
    result = await mpp.charge(
        authorization=request.headers.get("Authorization"),
        amount="0.50",
    )

    if isinstance(result, Challenge):
        return JSONResponse(
            status_code=402,
            content={"error": "Payment required"},
            headers={"WWW-Authenticate": result.to_www_authenticate(mpp.realm)},
        )

    credential, receipt = result
    return {"data": "paid content", "payer": credential.source}
```

`Mpp.create()` auto-detects `realm` from environment variables (`VERCEL_URL`, `FLY_APP_NAME`, `HOSTNAME`, and others) and auto-generates a `secret_key` persisted to `.env`. Pass explicit values to override:

```python [server.py]
mpp = Mpp.create(
    method=tempo(
        currency="0x20c0000000000000000000000000000000000000",
        recipient="0xa726a1CD723409074DF9108A2187cfA19899aCF8",
        intents={"charge": ChargeIntent()},
    ),
    realm="api.example.com",
    secret_key="my-server-secret",
)
```

## `tempo()` factory

`tempo()` creates a `TempoMethod` that bundles a payment network, its intents, and default parameters together. Each intent must be explicitly registered via the `intents` parameter.

```python [server.py]
from mpp.methods.tempo import tempo, ChargeIntent

method = tempo(
    currency="0x20c0000000000000000000000000000000000000",
    recipient="0xa726a1CD723409074DF9108A2187cfA19899aCF8",
    intents={"charge": ChargeIntent()},
)
```

### With charge and session

Register both intents on a single method:

```python [server.py]
from mpp.methods.tempo import tempo, ChargeIntent, StreamIntent
from mpp.methods.tempo.stream.storage import MemoryStorage

mpp = Mpp.create(
    method=tempo(
        currency="0x20c0000000000000000000000000000000000000",
        recipient="0xa726a1CD723409074DF9108A2187cfA19899aCF8",
        intents={
            "charge": ChargeIntent(),
            "stream": StreamIntent(
                storage=MemoryStorage(),
                rpc_url="https://rpc.tempo.xyz",
            ),
        },
    ),
)
```

### `tempo()` parameters

### currency (optional)

* **Type:** `str`

Default TIP-20 token address for charges.

### decimals (optional)

* **Type:** `int`
* **Default:** `6`

Token decimal places for amount conversion (6 for pathUSD).

### intents

* **Type:** `dict[str, Intent]`

Intents to register (for example, `charge`, `session`). Each intent must be explicitly provided.

### recipient (optional)

* **Type:** `str`

Default recipient address for charges.

### rpc\_url (optional)

* **Type:** `str`
* **Default:** `"https://rpc.tempo.xyz"`

Tempo RPC endpoint URL.

## `Mpp.create()` parameters

### method

* **Type:** `Method`

Payment method instance returned by `tempo()`.

### realm (optional)

* **Type:** `str`

Server realm for `WWW-Authenticate` headers. Auto-detected from environment if omitted.

### secret\_key (optional)

* **Type:** `str`

HMAC secret for stateless Challenge ID verification. Auto-generated and persisted to `.env` if omitted.

## `charge()` parameters

### amount

* **Type:** `str`

Payment amount in human-readable units (for example, `"0.50"` for $0.50). Automatically converted to base units using the method's decimal precision (6 decimals for pathUSD).

### authorization

* **Type:** `str | None`

The `Authorization` header value from the incoming request.

### currency (optional)

* **Type:** `str`

Override the method's default currency address.

### description (optional)

* **Type:** `str`

Human-readable description attached to the Challenge.

### expires (optional)

* **Type:** `str`

Challenge expiration as ISO 8601 timestamp. Defaults to 5 minutes from now.

### recipient (optional)

* **Type:** `str`

Override the method's default recipient address.

## `session()` parameters

### amount

* **Type:** `str`

Price per unit in human-readable units (for example, `"0.000075"` for $0.000075 per token). Automatically converted to base units.

### authorization

* **Type:** `str | None`

The `Authorization` header value from the incoming request.

### currency (optional)

* **Type:** `str`

Override the method's default currency address.

### description (optional)

* **Type:** `str`

Human-readable description attached to the Challenge.

### recipient (optional)

* **Type:** `str`

Override the method's default recipient address.

### unit\_type (optional)

* **Type:** `str`
* **Default:** `"token"`

Service unit type label (for example, `"token"`, `"byte"`, `"request"`).

## Session example

`StreamIntent` requires a `storage` backend that implements the `ChannelStorage` protocol to persist channel and session state. The SDK ships `MemoryStorage` for development and testing—state lives in a Python dict and is lost on restart. For production, implement `ChannelStorage` backed by your database (Postgres, Redis, and others). The protocol has four methods:

| Method | Description |
|--------|-------------|
| `get_channel(channel_id)` | Look up a channel by ID |
| `get_session(challenge_id)` | Look up a session by Challenge ID |
| `update_channel(channel_id, fn)` | Atomic read-modify-write for channel state |
| `update_session(challenge_id, fn)` | Atomic read-modify-write for session state |

A full SSE endpoint with per-token payment sessions:

```python [server.py]
import asyncio
import json

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, StreamingResponse
from mpp import Challenge
from mpp.methods.tempo import StreamIntent, tempo
from mpp.methods.tempo.stream.storage import MemoryStorage
from mpp.server import Mpp

app = FastAPI()

mpp = Mpp.create(
    method=tempo(
        currency="0x20c0000000000000000000000000000000000000",
        recipient="0xa726a1CD723409074DF9108A2187cfA19899aCF8",
        intents={
            "stream": StreamIntent(
                storage=MemoryStorage(),
                rpc_url="https://rpc.tempo.xyz",
            ),
        },
    ),
)

@app.get("/api/chat")
async def chat(request: Request, prompt: str = "Hello!"):
    result = await mpp.stream(
        authorization=request.headers.get("Authorization"),
        amount="0.000075",
        unit_type="token",
    )

    if isinstance(result, Challenge):
        return JSONResponse(
            status_code=402,
            content={"error": "Payment required"},
            headers={"WWW-Authenticate": result.to_www_authenticate(mpp.realm)},
        )

    credential, receipt = result

    async def event_stream():
        for token in ["The", " answer", " is", " 42."]:
            yield f"data: {json.dumps({'token': token})}\n\n"
            await asyncio.sleep(0.05)
        yield "data: [DONE]\n\n"

    return StreamingResponse(
        event_stream(),
        media_type="text/event-stream",
        headers={"Payment-Receipt": receipt.to_payment_receipt()},
    )
```

## `@requires_payment` decorator

For the lower-level decorator API, use `@requires_payment` with an explicit intent, request, realm, and secret key:

```python [server.py]
from fastapi import FastAPI, Request
from mpp import Credential, Receipt
from mpp.server import requires_payment
from mpp.methods.tempo import ChargeIntent

app = FastAPI()
intent = ChargeIntent(rpc_url="https://rpc.tempo.xyz")

@app.get("/resource")
@requires_payment(
    intent=intent,
    request={"amount": "1000000", "currency": "0x...", "recipient": "0x..."},
    realm="api.example.com",
    secret_key="my-server-secret",
)
async def get_resource(request: Request, credential: Credential, receipt: Receipt):
    return {"data": "paid content", "payer": credential.source}
```

### Parameters

| Parameter | Description |
|-----------|-------------|
| `intent` | Payment intent for verification |
| `realm` | Server realm for `WWW-Authenticate` |
| `request` | Payment request data (dict or callable) |
| `secret_key` | Secret for Challenge IDs |
