MPP - Protocole de Paiement Machine
MPP est un protocole ouvert (co-écrit par Tempo et Stripe) qui standardise le code HTTP 402 Payment Required pour les paiements machine-à-machine. Les clients paient dans la même requête HTTP - pas besoin de comptes, de clés API ou de processus de paiement.
La spécification de base du protocole est soumise à l'IETF en tant que Schéma d'Authentification HTTP de Paiement.
Le code de ce skill utilise des noms de jetons fictifs (<USDC_TEMPO_MAINNET>, <PATHUSD_TESTNET>) ; les vraies adresses se trouvent dans la documentation Tempo et dans references/tempo-method.md.
Architecture de Base
Trois primitives alimentent chaque paiement MPP :
- Challenge - exigence de paiement émise par le serveur (dans l'en-tête
WWW-Authenticate: Payment) - Credential - preuve de paiement soumise par le client (dans l'en-tête
Authorization: Payment) - Receipt - confirmation par le serveur d'un paiement réussi (dans l'en-tête
Payment-Receipt)
Méthodes de Paiement et Intentions
MPP est indépendant de la méthode de paiement. Chaque méthode définit son propre rail de règlement :
| Méthode | Rail | Paquet SDK | Statut |
|---|---|---|---|
| Tempo | Stablecoins TIP-20 sur la chaîne Tempo | mppx (intégré) | Production |
| Stripe | Cartes/portefeuilles (SPT) + dépôt crypto on-chain | mppx (intégré) | Production |
| EVM | Autorisations de stablecoins EIP-3009 (compatible x402-exact) | mppx (intégré) | Production |
| Lightning | Bitcoin via le réseau Lightning | @buildonspark/lightning-mpp-sdk | Production |
| Stellar | Jetons SEP-41 sur Stellar, charge + channel | @stellar/mpp | Production (spécification du câble channel encore en cours d'élaboration - sujette à modification) |
| Solana | Charge + session natives Solana (SOL, SPL, Token-2022) | @solana/mpp | Production |
| Monad | Charge Monad (ERC-3009, modes de règlement) | @monad-crypto/mpp | Production |
| NEAR Intents | Charge inter-chaînes via des adresses de dépôt 1Click | @defuse-protocol/nearintents-mpp-sdk | Production (non sans confiance - transite par un backend de règlement, annoncé comme methodDetails.settlementBackend: "near-intents" pour une politique de risque par méthode) |
| RedotPay | Solde RedotPay (rdt) ou preuve de stablecoin, charge uniquement | @redotpay/mpp | Production |
| Carte | Jetons réseau cryptés (Visa) | mpp-card | Production |
| Personnalisée | N'importe quel rail | Method.from() + Method.toClient/toServer | Extensible |
Analyses approfondies par méthode : references/tempo-method.md, references/stripe-method.md, references/lightning-method.md, references/custom-methods.md.
| Intention | Modèle | Idéal Pour |
|---|---|---|
| charge | Paiement unique par requête | Appels API, accès à du contenu, endpoints à prix fixe |
| session | Paiement à l'usage via des canaux de paiement | Streaming LLM, facturation à l'usage, API à haute fréquence |
| subscription | Accès récurrent via une clé autorisée (Tempo) - voir references/subscriptions.md | Plans/niveaux où l'accès est séparé de la facturation par requête |
Démarrage Rapide : Serveur (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: '...' }))
}
Installation : npm install mppx viem (mppx 0.8.15 nécessite viem >= 2.54.0).
Validez que le serveur finalisé fonctionne de bout en bout avec npx mppx validate http://localhost:3000.
Démarrage Rapide : Client (TypeScript)
import { privateKeyToAccount } from 'viem/accounts'
import { Mppx, tempo } from 'mppx/client'
// Polyfill globalThis.fetch pour gérer automatiquement les codes 402
Mppx.create({
methods: [tempo({ account: privateKeyToAccount('0x...') })],
})
const res = await fetch('https://api.example.com/paid')
// Le paiement s'effectue de manière transparente lorsque le serveur renvoie un code 402
Dans les navigateurs, mppx 0.6.0 a changé le comportement par défaut : le fetch polyfillé n'envoie Accept-Payment qu'aux endpoints de même origine, donc les API payantes cross-origin nécessitent acceptPaymentPolicy ('always' / { origins: [...] }). Le fetch client réessaie les challenges incrémentaux jusqu'à maxPaymentRetries (par défaut 3). Pour les alternatives non globales (Fetch.from/polyfill/restore, Mppx.restore()), voir references/typescript-sdk.md.
Démarrage Rapide : Serveur (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}
Installation : pip install "pympp[tempo]". Voir references/python-sdk.md pour les schémas complets.
Démarrage Rapide : Serveur (Rust)
Installation : cargo add mpp --features tempo,server. Voir references/rust-sdk.md pour les schémas complets.
Middleware de Framework (TypeScript)
Chaque framework a sa propre importation (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: '...' }))
Voir references/typescript-sdk.md pour les exemples avec Express et Elysia.
Sessions : Streaming à l'Utilisation (Pay-as-You-Go)
Les sessions ouvrent un canal de paiement une seule fois, puis utilisent des justificatifs hors chaîne pour chaque requête - pas de transaction blockchain par requête. Latence inférieure à 100 ms, frais par requête quasi nuls.
Sessions v2 (par défaut depuis mppx 0.7.0) : tempo.session() est le flux de canal précompilé TIP-1034 ; l'implémentation précédente par contrat de dépôt est la Sessions v1, toujours disponible via tempo.sessionLegacy (obsolète). Un client qui attend la v2 rejette une session v1 et retombe sur le chemin de charge, alors gardez le client et le serveur sur des flux correspondants. Deux API client : tempo.session({ account, maxDeposit }) enregistre la méthode avec Mppx.create() (gestion transparente du 402 via fetch), tandis que tempo.session.manager({ account, maxDeposit }) retourne un client géré pour un contrôle direct du cycle de vie (.sse(), .close()).
// Serveur - endpoint de session avec règlement automatique
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, // permet aux clients de retour de récupérer leur canal sur cette route
})],
})
const result = await mppx.session({ amount: '0.001', unitType: 'token' })(request)
if (result.status === 402) return result.challenge
return result.withReceipt(Response.json({ data: '...' }))
// Serveur - streaming SSE avec facturation par mot
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
}
}
)
// Client - session avec canal auto-géré
Mppx.create({ methods: [tempo({ account, maxDeposit: '1' })] })
const res = await fetch('http://localhost:3000/api/resource')
// 1ère requête : ouvre le canal on-chain ; 2ème et suivantes : justificatifs hors chaîne
Les sessions diffusent également en continu via WebSocket via Ws.serve(). Voir references/sessions.md pour le cycle de vie complet, le règlement, les magasins, les schémas SSE et WebSocket, et la récupération de canal.
Prise en Charge Multi-Méthodes
Acceptez les stablecoins Tempo, les cartes Stripe et le Bitcoin Lightning sur un seul 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! }),
],
})
Utilisez Mppx.compose() pour présenter plusieurs méthodes dans une seule réponse 402 avec une tarification par route. Appliquez la même branche sur le site du challenge et le site de vérification, ou le 402 annonce moins d'options que le serveur n'en accepte. Voir references/typescript-sdk.md.
Liens de Paiement (HTML)
En définissant html: true sur une configuration de méthode de paiement, une page de paiement compatible navigateur s'affiche lorsqu'un endpoint 402 est visité dans un navigateur, avec des thèmes, des onglets de composition multi-méthodes et la prise en charge du portefeuille Solana. Les service workers gèrent la soumission des credentials, puis la page se recharge avec la réponse payée.
Personnalisez via les exports mppx/html (Config, Text, Theme), et construisez le lien de paiement d'une méthode personnalisée avec Html.init(methodName).
Authentification Zéro-Dollar (Preuve de Credential)
Authentifiez l'identité d'un agent sans paiement. Les clients signent une preuve EIP-712 sur l'ID du challenge au lieu de créer une transaction - pas de gaz brûlé, pas de transfert de fonds.
// Serveur - charge zéro-dollar, avec un magasin pour la protection contre les rejeux
const mppx = Mppx.create({
methods: [tempo.charge({ currency: '<PATHUSD_TESTNET>', recipient: '0x...', store })],
})
const result = await mppx.charge({ amount: '0' })(request)
Depuis mppx 0.8.0, ces preuves sont liées au portefeuille du payeur : les données typées EIP-712 Proof (exposées sous tempo.Proof) portent un champ account à la version de domaine 3, donc une preuve signée pour un compte ne se vérifie plus contre un autre.
Cas d'usage : vérification d'identité, interrogation de tâches de longue durée, déverrouillage payé avec accès gratuit ultérieur, pipelines d'agents multi-étapes. Voir mpp.dev/advanced/identity.
Proxy de Paiement
Protégez des API existantes derrière des paiements MPP :
// import { openai, Proxy } from 'mppx/proxy' - un service dans Proxy.create({ services: [...] })
openai({
apiKey: process.env.OPENAI_API_KEY,
routes: {
'POST /v1/chat/completions': mppx.charge({ amount: '0.05' }),
'GET /v1/models': true, // `true` littéral marque une route gratuite
},
})
Préréglages intégrés openai(), anthropic(), stripe(), plus custom() pour n'importe quel service en amont. Voir references/discovery-and-proxy.md pour Proxy.create(), les endpoints de découverte qu'il sert, et l'assistant discovery() pour les serveurs non proxy.
Transport MCP
Les appels d'outils MCP peuvent exiger un paiement en utilisant le code d'erreur JSON-RPC -32042 (les serveurs peuvent également émettre -32043) :
// Serveur - importer tempo depuis mppx/server, PAS mppx/tempo
import { McpServer } from 'mppx/mcp/server'
import { tempo } from 'mppx/server'
const server = McpServer.wrap(baseServer, { methods: [tempo.charge({ /* ... */ })], secretKey })
// Client - client MCP compatible paiement (importer tempo depuis 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: {} })
Les challenges MCP-over-HTTP sont réglés dans le même fetch compatible paiement, et les transports sont enfichables via Transport.from/http/mcp/mcpSdk des deux côtés. Voir references/transports.md.
Portefeuilles Serveur Privy
createViemAccount depuis @privy-io/node/viem (nécessite @privy-io/node >= 0.20.0) retourne un Account viem soutenu par un portefeuille serveur Privy, donc il s'intègre dans tempo({ account }) partout où un compte local irait.
La signature côté serveur fonctionne avec des portefeuilles serveur appartenant à l'application ; les portefeuilles intégrés appartenant à l'utilisateur nécessitent des clés d'autorisation ou des quorums de clés. Voir references/typescript-sdk.md pour la configuration complète et la construction manuelle de toAccount().
Tests & CLI
# Créer un compte (stocké dans le trousseau de clés), puis l'alimenter sur le testnet
npx mppx account create
npx mppx account fund --network testnet
# Effectuer une requête payante
npx mppx http://localhost:3000/resource
# Analyser un challenge sans le signer
npx mppx sign --dry-run --challenge '<valeur www-authenticate>'
# Valider une implémentation de serveur de bout en bout
npx mppx validate http://localhost:3000
La CLI couvre également init, sessions (lister/voir/fermer), discover, services, mcp add et skills add. La configuration provient de MPPX_CONFIG ou d'un --config explicite - il n'y a pas de découverte automatique depuis le répertoire de travail. Référence complète : references/cli.md.
Paquets SDK
| Langage | Paquet | Installation |
|---|---|---|
| TypeScript | mppx | npm install mppx |
| Python | pympp | pip install "pympp[tempo]" |
| Rust | mpp | cargo add mpp --features tempo,client,server |
| Ruby | mpp-rb (officiel, par Stripe) | voir le dépôt pour le nom de la gem |
| Go | mpp-go (officiel, par Tempo) | go get github.com/tempoxyz/mpp-go |
| Elixir | mpp (communauté) | hex.pm/packages/mpp |
| Swift | mpp-swift (communauté) | voir le dépôt |
Notes sur les capacités, vérifiées par rapport à la source du SDK plutôt qu'aux matrices de documentation (l'éditeur en publie deux qui se contredisent) :
- Intention Session : TypeScript et Rust uniquement.
- Preuves de Credential (authentification zéro-dollar) : TypeScript