MPP — Machine Payments Protocol
Machine Payments Protocol (MPP) defines HTTP payment authentication using theWWW-Authenticate: payment challenge scheme (draft-ryan-httpauth-payment). PEAC maps MPP payment challenges and receipts to portable signed records — the same way it handles x402.
Package: @peac/mappings-paymentauth
MPP and x402 — complementary payment rails
MPP and x402 solve similar problems — machine-to-machine HTTP payments — but with different wire protocols. PEAC evidence adapters exist for both. Use whichever rail your infrastructure supports; PEAC produces equivalent portable signed records for each.
| Dimension | MPP / paymentauth | x402 |
|---|---|---|
| HTTP mechanism | WWW-Authenticate: payment (RFC 9110 auth framework) | HTTP 402 Payment Required status code |
| Standard body | IETF HTTP WG (draft-ryan-httpauth-payment) | Linux Foundation (x402.org, 22+ members) |
| PEAC package | @peac/mappings-paymentauth | @peac/adapter-x402 + @peac/rails-x402 |
| Challenge header | WWW-Authenticate: payment scheme=... | X-Payment-Required or 402 body |
| Receipt header | Authorization: payment token=... | X-Payment / custom header |
| PEAC evidence types | payment-attempt-observed, payment-settlement-observed | x402-offer-observed, x402-settlement-observed |
Install
pnpm add @peac/mappings-paymentauth @peac/protocolServer — parse and record the challenge
When your server issues an MPP payment challenge, record it as a PEAC evidence record. The challenge record proves what payment terms the server offered, independently of the upstream payment processor.
import { parsePaymentChallenge, mapChallengeToRecord } from '@peac/mappings-paymentauth';
import { issue } from '@peac/protocol';
import { loadKey } from '@peac/crypto';
const signingKey = await loadKey(process.env.PEAC_SIGNING_KEY_JSON!);
// Express middleware example
app.use(async (req, res, next) => {
if (!req.headers.authorization) {
// Parse your internal payment config into MPP challenge format
const challenge = await paymentConfig.buildChallenge(req);
// Map to PEAC record claims
const record = mapChallengeToRecord({
challenge,
issuer: 'https://api.example.com',
subject: req.headers['x-agent-id'] || 'unknown',
});
// Issue signed evidence of the challenge offered
const { jws } = await issue({ claims: record, issuer: 'https://api.example.com', signingKey });
// Return 401 with payment challenge + PEAC record ref in header
res.set('WWW-Authenticate', `payment scheme="${challenge.scheme}" ...`);
res.set('PEAC-Receipt', jws);
return res.status(401).json({
type: 'about:blank',
title: 'Payment Required',
status: 401,
});
}
next();
});Client — record payment attempt and settlement
When the agent pays and receives a response, record the payment attempt and settlement observation. The settlement record proves what the server attested — not more. PEAC does not synthesize finality from non-payment artifacts.
import {
parsePaymentResponse,
mapPaymentAttemptToRecord,
mapSettlementToRecord,
} from '@peac/mappings-paymentauth';
import { issue, verifyLocal } from '@peac/protocol';
import { loadKey } from '@peac/crypto';
const signingKey = await loadKey(process.env.PEAC_SIGNING_KEY_JSON!);
// 1. Record the payment attempt
const attemptRecord = mapPaymentAttemptToRecord({
scheme: 'mpp-v1',
payment_ref: 'urn:payment:attempt:xyz',
amount_minor: '100',
currency: 'USD',
target_ref: 'https://api.example.com/resource',
});
const { jws: attemptJws } = await issue({
claims: attemptRecord,
issuer: 'https://client.example.com',
signingKey,
});
// 2. Make the actual HTTP call with payment token in Authorization
const response = await fetch('https://api.example.com/resource', {
headers: {
Authorization: `payment token="${paymentToken}"`,
'PEAC-Receipt': attemptJws,
},
});
// 3. If payment accepted, record settlement
if (response.ok) {
const settlement = parsePaymentResponse(response.headers);
const settlementRecord = mapSettlementToRecord({
settlement,
attempt_ref: 'urn:payment:attempt:xyz',
upstream_artifact: { source: 'payment-server', ref: settlement.ref },
});
const { jws: settlementJws } = await issue({
claims: settlementRecord,
issuer: 'https://client.example.com',
signingKey,
});
// settlementJws is your portable payment proof
console.log('Payment settled:', settlementJws);
}Evidence type URIs
| Type URI | When emitted | Key fields |
|---|---|---|
| org.peacprotocol/payment-challenge-observed | Server issues WWW-Authenticate: payment challenge | scheme, challenge_ref, amount_minor, currency |
| org.peacprotocol/payment-attempt-observed | Client submits Authorization: payment token | scheme, payment_ref, amount_minor, currency, target_ref |
| org.peacprotocol/payment-settlement-observed | Server responds 200 with payment accepted | attempt_ref, outcome, upstream_artifact |
| org.peacprotocol/payment-error-observed | Server rejects payment with a structured error | error_code, error_ref, attempt_ref |
MCP and JSON-RPC error helpers
When a paymentauth error occurs inside an MCP tool call or JSON-RPC handler, the error needs to be structured in a way that MCP clients can handle.@peac/mappings-paymentauth includes helpers for this.
import { buildPaymentauthMcpError, buildPaymentauthJsonRpcError } from '@peac/mappings-paymentauth';
// Inside an MCP tool handler
if (paymentRequired) {
// Returns a structured MCP error object with payment challenge embedded
return buildPaymentauthMcpError({
challenge,
message: 'Payment required to call this tool',
});
}
// Inside a JSON-RPC handler
if (paymentRequired) {
return buildPaymentauthJsonRpcError({
challenge,
code: -32099, // custom error code for payment required
});
}Payment finality rules
PEAC records what the upstream payment server attested — nothing more. A payment-settlement-observed record proves the server said "payment accepted". It does not guarantee funds cleared, chargeback immunity, or settlement finality at the rails level.
Never derive settlement finality from a challenge or attempt record alone. The mapSettlementToRecord function requires explicit upstream_artifact from the payment server — it will throw MapperBoundaryErrorif called with incomplete data.
Resources
Add payment evidence to your MPP implementation
@peac/mappings-paymentauth works alongside your existing MPP server implementation. No protocol changes required — just map each event to a PEAC record.