MPP
The ATR hash as the MPP challenge id, the opaque members, and the MPP pairings.
MPP, the Machine Payments Protocol, carries payment in HTTP authentication, as the
Payment HTTP authentication scheme defines it: the seller answers with one or more
WWW-Authenticate: Payment challenges, each naming a method (such as evm, tempo, solana or stripe) and an
intent (charge, session or subscription), and the buyer answers with a credential that echoes the challenge
it pays. Import the MPP pieces from @integraledger/lcp/mpp.
Where H rides
On every MPP pairing, H rides in the challenge itself, in two places:
- The challenge
idis the base64url (no padding) of H's 32 bytes, then.and the challenge's position in the 402 response. A Temposubscriptionchallenge's id is the bare base64url of H.challengeId(h, index)writes it andchallengeH(id)reads H back. - The challenge's
opaquemap carrieslegalContext, H's LCP string, andlegalContextUrl, the link. Where the pairing's payment carries H in nothing public, it also carrieslegalContextAgreementUrl.
MPP binds the credential to the challenge it echoes, so whatever the payer signs over the challenge id, or over a
value derived from it, commits to H. Some methods also carry H in a request member the payer signs; CARRIER names
that member for each intent and method (for example externalId for solana charges, and
methodDetails.invoiceId for xrpl).
Placing and reading
place(doc, h, link, option) is the seller's placement over a list of challenges: the challenge whose bound members
equal option gets the id derived from H and an opaque holding the seller's own map plus the LCP members.
read(doc) is the buyer's reading: the challenges whose opaque carries an LCP hash and an https link and whose id
derives from that hash. pairingsOf(challenge) names the pairings a challenge offers, and tie(challenges) is the
ATR's binding slot: every challenge's bound members, as issued.
import { } from "@integraledger/lcp";
import { , , , , type MppChallenge } from "@integraledger/lcp/mpp";
const = .(
.({
: "10000",
: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
: {
: 84532,
: ["permit2", "authorization"],
: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
},
: "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
}),
).("base64url");
const : MppChallenge = {
: "issued-by-the-seller",
: "api.seller.example",
: "evm",
: "charge",
,
: "2026-09-21T14:14:20Z",
};
.(());
const = await (new ().("abc"));
const = ([], , `https://atr.seller.example/${}`, );
if ("refused" in ) throw new (.);
const = [0]!.!;
.(, () === );
.(.([0]!.!, "base64url").());
const = ();
if ("refused" in ) throw new (.);
.(. === , ...);[ 'mpp/charge/evm/permit2', 'mpp/charge/evm/authorization' ]
ungWv48Bz-pBQUDeXa4iI7ADYaOWF3qctBD_YfIAFa0.0 true
{"legalContext":"lcp:sha256:0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad","legalContextUrl":"https://atr.seller.example/0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"}
true 1parseChallenges(fieldValues) parses WWW-Authenticate field values into challenges, and problem(code) gives the
Payment scheme's problem type and HTTP status for a refusal code.
The MPP pairings
| Method | Pairings | Where H rides in what the payer signs or lands |
|---|---|---|
evm | mpp/charge/evm/authorization, mpp/charge/evm/permit2 | The signed nonce, or the witness's challengeHash: keccak256 of the challenge id and realm (challengeHash). |
evm | mpp/charge/evm/transaction, mpp/charge/evm/hash | Nothing signed or landed; H is in the challenge. |
tempo | mpp/charge/tempo/memo, mpp/charge/tempo/push | MPP's attribution memo on the transferWithMemo, whose last 7 bytes are from keccak256 of the challenge id (attributionMemo). |
solana | mpp/charge/solana | The one Memo instruction, holding H's LCP string (the request's externalId). |
stellar | mpp/charge/stellar | The seller's muxed recipient address, whose 8-byte id is H's first 8 bytes. |
xrpl | mpp/charge/xrpl | The Payment's InvoiceID: H itself (the request's methodDetails.invoiceId). |
hedera | mpp/charge/hedera | MPP's attribution memo as the signed body memo. |
lightning | mpp/charge/lightning | The BOLT11 invoice's description hash h, which the seller's node signs. |
usdc | mpp/charge/usdc/evm, mpp/charge/usdc/gateway | The EIP-3009 nonce, or the Gateway burn intent's salt: usdc's derivation over the challenge id. |
usdc | mpp/charge/usdc/solana, mpp/charge/usdc/stacks | The signed Memo instruction (H's LCP string), or the SIP-010 transfer memo (H's 32 bytes). |
nearintents | mpp/charge/nearintents | The request's externalId; the deposit carries nothing. |
card, stripe | mpp/charge/card, mpp/charge/stripe, mpp/subscription/stripe | The request's externalId, or methodDetails.metadata.legal_context, which Stripe copies into the PaymentIntent. The buyer's card or token signs nothing that carries H. |
| sessions | mpp/session/evm, mpp/session/tempo, mpp/session/hedera, mpp/session/solana, mpp/session/xrpl, mpp/session/lightning | Where the channel opens. See Channels, sessions and subscriptions. |
| subscription | mpp/subscription/tempo | The key authorization the payer's root key signs, whose witness is H. |
Each pairing's build(choice, h) takes the chosen challenge (MppChoice) and returns what the buyer signs. Its
bound(credential) reads H from the echoed challenge and checks that what was signed carries it; reference and
status read the settlement where the method lands on a chain.
The LCP profile mpp/charge states these rules in full.
Last updated on