MPP - Protocolo de Pagos entre Máquinas
MPP es un protocolo abierto (coautorado por Tempo y Stripe) que estandariza el código HTTP 402 Payment Required para pagos de máquina a máquina. Los clientes pagan en la misma solicitud HTTP, sin necesidad de cuentas, claves API ni flujos de pago.
La especificación central del protocolo se ha enviado al IETF como el Esquema de Autenticación HTTP de Pago.
El código en esta habilidad utiliza nombres de token placeholder (<USDC_TEMPO_MAINNET>, <PATHUSD_TESTNET>); las direcciones reales se encuentran en la documentación de Tempo y en references/tempo-method.md.
Arquitectura Central
Tres primitivas impulsan cada pago MPP:
- Challenge - requisito de pago emitido por el servidor (en el encabezado
WWW-Authenticate: Payment) - Credential - prueba de pago enviada por el cliente (en el encabezado
Authorization: Payment) - Receipt - confirmación del servidor de un pago exitoso (en el encabezado
Payment-Receipt)
Métodos de Pago e Intenciones
MPP es agnóstico en cuanto al método de pago. Cada método define su propio rail de liquidación:
| Método | Rail | Paquete SDK | Estado |
|---|---|---|---|
| Tempo | Stablecoins TIP-20 en la cadena Tempo | mppx (integrado) | Producción |
| Stripe | Tarjetas/monedero (SPT) + depósito cripto on-chain | mppx (integrado) | Producción |
| EVM | Autorizaciones de stablecoins EIP-3009 (compatible x402-exact) | mppx (integrado) | Producción |
| Lightning | Bitcoin sobre la Red Lightning | @buildonspark/lightning-mpp-sdk | Producción |
| Stellar | Tokens SEP-41 en Stellar, charge + channel | @stellar/mpp | Producción (especificación wire de channel aún en borrador - sujeta a cambios) |
| Solana | Charge + sesión nativos de Solana (SOL, SPL, Token-2022) | @solana/mpp | Producción |
| Monad | Charge de Monad (ERC-3009, modos de liquidación) | @monad-crypto/mpp | Producción |
| NEAR Intents | Charge cross-chain a través de direcciones de depósito 1Click | @defuse-protocol/nearintents-mpp-sdk | Producción (no es trustless - se enruta a través de un backend de liquidación, anunciado como methodDetails.settlementBackend: "near-intents" para políticas de riesgo por método) |
| RedotPay | Saldo de RedotPay (rdt) o prueba de stablecoin, solo charge | @redotpay/mpp | Producción |
| Tarjeta | Tokens de red encriptados (Visa) | mpp-card | Producción |
| Personalizado | Cualquier rail | Method.from() + Method.toClient/toServer | Extensible |
Análisis en profundidad por método: references/tempo-method.md, references/stripe-method.md, references/lightning-method.md, references/custom-methods.md.
| Intención | Patrón | Mejor para |
|---|---|---|
| charge | Pago único por solicitud | Llamadas a API, acceso a contenido, endpoints de precio fijo |
| session | Pago por uso a través de canales de pago | Streaming LLM, facturación por uso, APIs de alta frecuencia |
| subscription | Acceso recurrente mediante una clave autorizada (Tempo) - ver references/subscriptions.md | Planes/niveles donde el acceso está separado de la facturación por solicitud |
Inicio Rápido: Servidor (TypeScript)
import { Mppx, tempo } from 'mppx/server'
const mppx = Mppx.create({
methods: [tempo({
currency: '<PATHUSD_TESTNET>', // pathUSD testnet
recipient: '0xYourAddress',
})],
})
export async function handler(request: Request) {
const result = await mppx.charge({ amount: '0.01' })(request)
if (result.status === 402) return result.challenge
return result.withReceipt(Response.json({ data: '...' }))
}
Instalar: npm install mppx viem (mppx 0.8.15 requiere viem >= 2.54.0).
Valida el servidor completo de extremo a extremo con npx mppx validate http://localhost:3000.
Inicio Rápido: Cliente (TypeScript)
import { privateKeyToAccount } from 'viem/accounts'
import { Mppx, tempo } from 'mppx/client'
// Parchea globalThis.fetch para manejar 402 automáticamente
Mppx.create({
methods: [tempo({ account: privateKeyToAccount('0x...') })],
})
const res = await fetch('https://api.example.com/paid')
// El pago ocurre de forma transparente cuando el servidor devuelve 402
En navegadores, mppx 0.6.0 cambió el comportamiento predeterminado: el fetch parcheado solo envía Accept-Payment a endpoints del mismo origen, por lo que las APIs de pago de origen cruzado necesitan acceptPaymentPolicy ('always' / { origins: [...] }). El fetch del cliente reintenta desafíos incrementales hasta maxPaymentRetries (predeterminado 3). Para alternativas no globales (Fetch.from/polyfill/restore, Mppx.restore()), consulta references/typescript-sdk.md.
Inicio Rápido: Servidor (Python)
from fastapi import FastAPI
from mpp import Credential, Receipt
from mpp.server import Mpp
from mpp.methods.tempo import tempo, ChargeIntent
app = FastAPI()
server = Mpp.create(method=tempo(
currency="<PATHUSD_TESTNET>",
recipient="0xYourAddress", intents={"charge": ChargeIntent()},
))
@app.get("/resource")
@server.pay(amount="0.50")
async def get_resource(request, credential: Credential, receipt: Receipt):
return {"data": "paid content", "payer": credential.source}
Instalar: pip install "pympp[tempo]". Consulta references/python-sdk.md para patrones completos.
Inicio Rápido: Servidor (Rust)
Instalar: cargo add mpp --features tempo,server. Consulta references/rust-sdk.md para patrones completos.
Middleware de Frameworks (TypeScript)
Cada framework tiene su propia importación (mppx/nextjs, mppx/hono, mppx/express, mppx/elysia):
// Next.js
import { Mppx, tempo } from 'mppx/nextjs'
const mppx = Mppx.create({ methods: [tempo({ currency: '<PATHUSD_TESTNET>', recipient: '0x...' })] })
export const GET = mppx.charge({ amount: '0.1' })(() => Response.json({ data: '...' }))
// Hono
import { Mppx, tempo } from 'mppx/hono'
app.get('/resource', mppx.charge({ amount: '0.1' }), (c) => c.json({ data: '...' }))
Consulta references/typescript-sdk.md para ejemplos con Express y Elysia.
Sesiones: Streaming de Pago por Uso
Las sesiones abren un canal de pago una vez, luego usan vales fuera de la cadena para cada solicitud, sin transacción en la cadena por solicitud. Latencia inferior a 100 ms, tarifas por solicitud casi nulas.
Sesiones v2 (predeterminado desde mppx 0.7.0): tempo.session() es el flujo de canal precompilado TIP-1034; la implementación anterior de contrato de depósito en garantía es Sesiones v1, aún disponible como tempo.sessionLegacy (obsoleto). Un cliente que espera v2 rechaza una sesión v1 y vuelve a la ruta de charge, así que mantén el cliente y el servidor con flujos coincidentes. Dos APIs de cliente: tempo.session({ account, maxDeposit }) registra el método con Mppx.create() (manejo transparente de 402 mediante fetch), mientras que tempo.session.manager({ account, maxDeposit }) devuelve un cliente gestionado para control directo del ciclo de vida (.sse(), .close()).
// Servidor - endpoint de sesión con liquidación automática
const mppx = Mppx.create({
methods: [tempo.session({
currency: '<PATHUSD_TESTNET>', recipient: '0x...',
store: Store.redis(redis),
settlementSchedule: { amount: '1.00', intervalMs: 300_000 },
bootstrap: true, // permite que los clientes que regresan recuperen su canal en esta ruta
})],
})
const result = await mppx.session({ amount: '0.001', unitType: 'token' })(request)
if (result.status === 402) return result.challenge
return result.withReceipt(Response.json({ data: '...' }))
// Servidor - Streaming SSE con facturación por palabra
export const GET = mppx.session({ amount: '0.001', unitType: 'word' })(
async () => async function* (stream) {
for (const word of ['hello', 'world']) {
await stream.charge()
yield word
}
}
)
// Cliente - sesión con canal gestionado automáticamente
Mppx.create({ methods: [tempo({ account, maxDeposit: '1' })] })
const res = await fetch('http://localhost:3000/api/resource')
// 1.ª solicitud: abre el canal en la cadena; 2.ª en adelante: vales fuera de la cadena
Las sesiones también transmiten a través de WebSocket mediante Ws.serve(). Consulta references/sessions.md para conocer el ciclo de vida completo, la liquidación, los almacenes, los patrones SSE y WebSocket, y la recuperación de canales.
Soporte Multimétodo
Acepta stablecoins de Tempo, tarjetas de Stripe y Bitcoin Lightning en un solo endpoint:
const mppx = Mppx.create({
methods: [
tempo({ currency: '<PATHUSD_TESTNET>', recipient: '0x...' }),
stripe.charge({ client: new Stripe(key), networkId: 'profile_...', paymentMethodTypes: ['card'] }),
spark.charge({ mnemonic: process.env.MNEMONIC! }),
],
})
Usa Mppx.compose() para presentar múltiples métodos en una sola respuesta 402 con precios por ruta. Aplica la misma rama en el sitio del desafío y en el sitio de verificación, o el 402 anuncia menos opciones de las que acepta el servidor. Consulta references/typescript-sdk.md.
Enlaces de Pago (HTML)
Configurar html: true en un método de pago renderiza una página de pago compatible con el navegador cuando se visita un endpoint 402 en un navegador, con temas, pestañas de composición multimétodo y soporte para monedero Solana. Los service workers manejan el envío de credenciales, luego la página se recarga con la respuesta pagada.
Personaliza mediante las exportaciones de mppx/html (Config, Text, Theme), y crea el enlace de pago de un método personalizado con Html.init(methodName).
Autenticación de Costo Cero (Credenciales de Prueba)
Autentica la identidad del agente sin pago. Los clientes firman una prueba EIP-712 sobre el ID del desafío en lugar de crear una transacción: no se quema gas, no se transfieren fondos.
// Servidor - charge de costo cero, con un almacén para protección contra repetición
const mppx = Mppx.create({
methods: [tempo.charge({ currency: '<PATHUSD_TESTNET>', recipient: '0x...', store })],
})
const result = await mppx.charge({ amount: '0' })(request)
Desde mppx 0.8.0, estas pruebas están vinculadas al monedero del pagador: los datos tipados EIP-712 Proof (expuestos como tempo.Proof) llevan un campo account en la versión de dominio 3, por lo que una prueba firmada para una cuenta ya no se verifica contra otra.
Casos de uso: verificación de identidad, sondeo de trabajos de larga duración, desbloqueo de pago con acceso gratuito posterior, pipelines de agentes de varios pasos. Consulta mpp.dev/advanced/identity.
Proxy de Pagos
Protege APIs existentes detrás de pagos MPP:
// import { openai, Proxy } from 'mppx/proxy' - un servicio dentro de Proxy.create({ services: [...] })
openai({
apiKey: process.env.OPENAI_API_KEY,
routes: {
'POST /v1/chat/completions': mppx.charge({ amount: '0.05' }),
'GET /v1/models': true, // literal `true` marca una ruta gratuita
},
})
Presets integrados openai(), anthropic(), stripe(), más custom() para cualquier upstream. Consulta references/discovery-and-proxy.md para Proxy.create(), los endpoints de descubrimiento que sirve y el helper discovery() para servidores que no son proxy.
Transporte MCP
Las llamadas a herramientas MCP pueden requerir pago usando el código de error JSON-RPC -32042 (los servidores también pueden emitir -32043):
// Servidor - importar tempo desde mppx/server, NO desde mppx/tempo
import { McpServer } from 'mppx/mcp/server'
import { tempo } from 'mppx/server'
const server = McpServer.wrap(baseServer, { methods: [tempo.charge({ /* ... */ })], secretKey })
// Cliente - cliente MCP compatible con pagos (importar tempo desde mppx/client)
import { McpClient } from 'mppx/mcp/client'
import { tempo } from 'mppx/client'
const mcp = McpClient.wrap(client, { methods: [tempo({ account })] })
const result = await mcp.callTool({ name: 'premium_tool', arguments: {} })
Los desafíos MCP sobre HTTP se liquidan en el mismo fetch compatible con pagos, y los transportes son conectables mediante Transport.from/http/mcp/mcpSdk en ambos lados. Consulta references/transports.md.
Monederos de Servidor Privy
createViemAccount de @privy-io/node/viem (necesita @privy-io/node >= 0.20.0) devuelve una Account de viem respaldada por un monedero de servidor Privy, por lo que se puede usar en tempo({ account }) donde iría una cuenta local.
La firma del lado del servidor funciona con monederos de servidor propiedad de la aplicación; los monederos integrados propiedad del usuario requieren claves de autorización o quórums de claves. Consulta references/typescript-sdk.md para la configuración completa y la construcción manual toAccount().
Pruebas y CLI
# Crear una cuenta (almacenada en el llavero), luego financiarla en testnet
npx mppx account create
npx mppx account fund --network testnet
# Hacer una solicitud de pago
npx mppx http://localhost:3000/resource
# Analizar un desafío sin firmarlo
npx mppx sign --dry-run --challenge '<www-authenticate value>'
# Validar una implementación de servidor de extremo a extremo
npx mppx validate http://localhost:3000
La CLI también cubre init, sessions (listar/ver/cerrar), discover, services, mcp add y skills add. La configuración proviene de MPPX_CONFIG o de un --config explícito; no hay autodescubrimiento desde el directorio de trabajo. Referencia completa: references/cli.md.
Paquetes SDK
| Lenguaje | Paquete | Instalación |
|---|---|---|
| TypeScript | mppx | npm install mppx |
| Python | pympp | pip install "pympp[tempo]" |
| Rust | mpp | cargo add mpp --features tempo,client,server |
| Ruby | mpp-rb (oficial, por Stripe) | consulta el repositorio para el nombre de la gema |
| Go | mpp-go (oficial, por Tempo) | go get github.com/tempoxyz/mpp-go |
| Elixir | mpp (comunitario) | hex.pm/packages/mpp |
| Swift | mpp-swift (comunitario) | consulta el repositorio |
Notas de capacidad, verificadas contra el código fuente del SDK en lugar de las matrices de documentación (el upstream publica dos que no coinciden):
- Intención Session: solo TypeScript y Rust.
- Credenciales de Prueba (autenticación de costo cero): TypeScript
… [truncado para traducción]