Skip to content
Integra Protocol

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 id is the base64url (no padding) of H's 32 bytes, then . and the challenge's position in the 402 response. A Tempo subscription challenge's id is the bare base64url of H. challengeId(h, index) writes it and challengeH(id) reads H back.
  • The challenge's opaque map carries legalContext, H's LCP string, and legalContextUrl, the link. Where the pairing's payment carries H in nothing public, it also carries legalContextAgreementUrl.

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 1

parseChallenges(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

MethodPairingsWhere H rides in what the payer signs or lands
evmmpp/charge/evm/authorization, mpp/charge/evm/permit2The signed nonce, or the witness's challengeHash: keccak256 of the challenge id and realm (challengeHash).
evmmpp/charge/evm/transaction, mpp/charge/evm/hashNothing signed or landed; H is in the challenge.
tempompp/charge/tempo/memo, mpp/charge/tempo/pushMPP's attribution memo on the transferWithMemo, whose last 7 bytes are from keccak256 of the challenge id (attributionMemo).
solanampp/charge/solanaThe one Memo instruction, holding H's LCP string (the request's externalId).
stellarmpp/charge/stellarThe seller's muxed recipient address, whose 8-byte id is H's first 8 bytes.
xrplmpp/charge/xrplThe Payment's InvoiceID: H itself (the request's methodDetails.invoiceId).
hederampp/charge/hederaMPP's attribution memo as the signed body memo.
lightningmpp/charge/lightningThe BOLT11 invoice's description hash h, which the seller's node signs.
usdcmpp/charge/usdc/evm, mpp/charge/usdc/gatewayThe EIP-3009 nonce, or the Gateway burn intent's salt: usdc's derivation over the challenge id.
usdcmpp/charge/usdc/solana, mpp/charge/usdc/stacksThe signed Memo instruction (H's LCP string), or the SIP-010 transfer memo (H's 32 bytes).
nearintentsmpp/charge/nearintentsThe request's externalId; the deposit carries nothing.
card, stripempp/charge/card, mpp/charge/stripe, mpp/subscription/stripeThe 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.
sessionsmpp/session/evm, mpp/session/tempo, mpp/session/hedera, mpp/session/solana, mpp/session/xrpl, mpp/session/lightningWhere the channel opens. See Channels, sessions and subscriptions.
subscriptionmpp/subscription/tempoThe 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.

Edit on GitHub

Last updated on

On this page