{"_id":"@atlasprotocol/mpp","_rev":"4-98f1a85c8331f866fc644fcc1f43e54d","name":"@atlasprotocol/mpp","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"@atlasprotocol/mpp","version":"0.1.0","keywords":["mpp","machine-payments","stripe","tempo","agent-commerce","atlas","http-402","jws"],"license":"MIT","_id":"@atlasprotocol/mpp@0.1.0","maintainers":[{"name":"nooblemon_eth","email":"kc@lemonade.social"}],"homepage":"https://github.com/lemonadesocial/atlas-protocol#readme","bugs":{"url":"https://github.com/lemonadesocial/atlas-protocol/issues"},"dist":{"shasum":"cf01e47801567f1f4ab0abdb339ae2b83be6e07d","tarball":"https://registry.npmjs.org/@atlasprotocol/mpp/-/mpp-0.1.0.tgz","fileCount":40,"integrity":"sha512-JI61DxP+CIl8SJ+rpsxpmm4u1/2g/IHEe9Aygiu+PC2Va7VIKOtrY1+NhddxZR9GxvOC9moLhvqo6wklqyQB5A==","signatures":[{"sig":"MEQCICQhBCATUY63IACgYZSO9/ZKjvXq8k18X1grYo53Z0goAiBh8S7/tptsbUkq46kzso6KFtZbmZtvSp8bnu937jVv3g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@atlasprotocol%2fmpp@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":68944},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"7af48bdc455a16b92154f888643d588c162e98d2","scripts":{"lint":"echo \"no lint configured\" && exit 0","test":"vitest run","build":"tsc -p tsconfig.json","clean":"rm -rf dist *.tsbuildinfo","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"nooblemon_eth","email":"kc@lemonade.social"},"repository":{"url":"git+https://github.com/lemonadesocial/atlas-protocol.git","type":"git","directory":"packages/mpp"},"_npmVersion":"10.9.7","description":"Standalone implementation of the Machine Payments Protocol (MPP) envelope: challenge / credential / receipt encode + sign + verify. Optional JWS wrapping layer for signed envelopes.","directories":{},"_nodeVersion":"22.22.2","dependencies":{"jose":"^5.9.0"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","typescript":"^5.6.3","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mpp_0.1.0_1777548806732_0.42581685898229593","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@atlasprotocol/mpp","version":"0.1.1","keywords":["mpp","machine-payments","stripe","tempo","agent-commerce","atlas","http-402","jws"],"license":"MIT","_id":"@atlasprotocol/mpp@0.1.1","maintainers":[{"name":"nooblemon_eth","email":"kc@lemonade.social"}],"homepage":"https://github.com/lemonadesocial/atlas-protocol#readme","bugs":{"url":"https://github.com/lemonadesocial/atlas-protocol/issues"},"dist":{"shasum":"852d327e765db37ae19123334af36682f9a5ff15","tarball":"https://registry.npmjs.org/@atlasprotocol/mpp/-/mpp-0.1.1.tgz","fileCount":40,"integrity":"sha512-gr6rdZBPwaqor7MKaE31DqvJxEWJahSWGJqeqkaBDThgwPL77TGjV5gTv6HzhjExDKQM6r94UmQ0DuGn1YhXRg==","signatures":[{"sig":"MEYCIQCaRLwbZDkyO/m/Z6EvD2qcTqgG/qpLoneR9qXDTNF6iAIhAO/TRy2sqqnohpkX2TlqhlF3Cxv86/DtgSXUMyQP0Alz","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@atlasprotocol%2fmpp@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":69456},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"117333a62b200f2d9df3a1b37e69c76ec446ca92","scripts":{"lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.json","clean":"rm -rf dist *.tsbuildinfo","typecheck":"tsc --noEmit -p tsconfig.json","format:check":"prettier --check ."},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05b0e9d1-61e9-4612-8fb4-b804f8a3691b"}},"repository":{"url":"git+https://github.com/lemonadesocial/atlas-protocol.git","type":"git","directory":"packages/mpp"},"_npmVersion":"11.11.0","description":"Standalone implementation of the Machine Payments Protocol (MPP) envelope: challenge / credential / receipt encode + sign + verify. Optional JWS wrapping layer for signed envelopes.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"jose":"^5.9.0"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","typescript":"^5.6.3","@types/node":"^24.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mpp_0.1.1_1777572913400_0.38452065316590267","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@atlasprotocol/mpp","version":"0.2.0","keywords":["mpp","machine-payments","stripe","tempo","agent-commerce","atlas","http-402","x402","jws"],"license":"MIT","_id":"@atlasprotocol/mpp@0.2.0","maintainers":[{"name":"nooblemon_eth","email":"kc@lemonade.social"}],"homepage":"https://github.com/lemonadesocial/atlas-protocol#readme","bugs":{"url":"https://github.com/lemonadesocial/atlas-protocol/issues"},"dist":{"shasum":"99fb4d27acf0b0f280bfdf68eb5312357bef28c1","tarball":"https://registry.npmjs.org/@atlasprotocol/mpp/-/mpp-0.2.0.tgz","fileCount":48,"integrity":"sha512-Mw4QmWF2gf1Lk4NNVBli44EpUul5e4h3Odm2Mp6/fC2+lBJij4X7r7NMVcjT4p4J0AMoN8SUAHED/pgDivEXgw==","signatures":[{"sig":"MEQCIEkEBVDYxHawQXhC0qVgkpVpTQiTMuE58Fs7c0ibRQ/NAiAfafb4Pf7fQy9qUua3KUI4j+icJ+SjZHPBtnr0RRdmCg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@atlasprotocol%2fmpp@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":124819},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./x402":{"types":"./dist/x402/index.d.ts","import":"./dist/x402/index.js"},"./stripe-mpp":{"types":"./dist/stripe-mpp/index.d.ts","import":"./dist/stripe-mpp/index.js"},"./package.json":"./package.json"},"gitHead":"835089646bad9e244176b024d767628fedbde3bb","scripts":{"lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.json","clean":"rm -rf dist *.tsbuildinfo","typecheck":"tsc --noEmit -p tsconfig.json","format:check":"prettier --check ."},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05b0e9d1-61e9-4612-8fb4-b804f8a3691b"}},"repository":{"url":"git+https://github.com/lemonadesocial/atlas-protocol.git","type":"git","directory":"packages/mpp"},"_npmVersion":"11.11.0","description":"Standalone implementation of the Machine Payments Protocol (MPP) envelope: challenge / credential / receipt encode + sign + verify. Optional JWS wrapping layer for signed envelopes. Optional x402 client (on-chain USDC) and stripe-mpp client (Stripe SPT) f","directories":{},"_nodeVersion":"24.14.1","dependencies":{"jose":"^5.9.0"},"_hasShrinkwrap":false,"devDependencies":{"viem":"^2.0.0","vitest":"^2.0.0","typescript":"^5.6.3","@types/node":"^24.0.0"},"peerDependencies":{"viem":"^2.0.0"},"peerDependenciesMeta":{"viem":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/mpp_0.2.0_1777708545901_0.8374014107705154","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@atlasprotocol/mpp","version":"0.3.0","description":"Standalone implementation of the Machine Payments Protocol (MPP) envelope: challenge / credential / receipt encode + sign + verify. Optional JWS wrapping layer for signed envelopes. Optional x402 client (on-chain USDC) and stripe-mpp client (Stripe SPT) f","license":"MIT","repository":{"type":"git","url":"git+https://github.com/lemonadesocial/atlas-protocol.git","directory":"packages/mpp"},"keywords":["mpp","machine-payments","stripe","tempo","agent-commerce","atlas","http-402","x402","jws"],"type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./x402":{"types":"./dist/x402/index.d.ts","import":"./dist/x402/index.js"},"./stripe-mpp":{"types":"./dist/stripe-mpp/index.d.ts","import":"./dist/stripe-mpp/index.js"},"./package.json":"./package.json"},"scripts":{"build":"tsc -p tsconfig.json","test":"vitest run","lint":"eslint .","format:check":"prettier --check .","typecheck":"tsc --noEmit -p tsconfig.json","clean":"rm -rf dist *.tsbuildinfo"},"dependencies":{"jose":"^5.9.0"},"peerDependencies":{"viem":"^2.0.0"},"peerDependenciesMeta":{"viem":{"optional":true}},"devDependencies":{"@types/node":"^24.0.0","typescript":"^5.6.3","viem":"^2.0.0","vitest":"^2.0.0"},"gitHead":"7916bb19b9ca5e086ed2ea2ae7672b1d647b4f1a","_id":"@atlasprotocol/mpp@0.3.0","bugs":{"url":"https://github.com/lemonadesocial/atlas-protocol/issues"},"homepage":"https://github.com/lemonadesocial/atlas-protocol#readme","_nodeVersion":"24.14.1","_npmVersion":"11.11.0","dist":{"integrity":"sha512-hsP8nU0wNIbKQ+nc8/IVvzrS1wOXsmwGw67Qob9Qc8BSSfO22H7h6ryql873b3qmfTLabAKIhj0mKr8ffISTHg==","shasum":"0d78c0aca85201ed5a79d438b778fd8ee72aafaf","tarball":"https://registry.npmjs.org/@atlasprotocol/mpp/-/mpp-0.3.0.tgz","fileCount":48,"unpackedSize":125593,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@atlasprotocol%2fmpp@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIAMsTDK9F9vbTlqExGCmWNHwnA4hrVq4o9CB5TZA72okAiAnL1lKvtV+oW02wpj3Udo3defVkmvSCX3m048dABydBQ=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05b0e9d1-61e9-4612-8fb4-b804f8a3691b"}},"directories":{},"maintainers":[{"name":"nooblemon_eth","email":"kc@lemonade.social"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mpp_0.3.0_1777766825220_0.21988514169214457"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-30T11:33:26.644Z","modified":"2026-05-03T00:07:05.787Z","0.1.0":"2026-04-30T11:33:26.926Z","0.1.1":"2026-04-30T18:15:13.525Z","0.2.0":"2026-05-02T07:55:46.023Z","0.3.0":"2026-05-03T00:07:05.462Z"},"bugs":{"url":"https://github.com/lemonadesocial/atlas-protocol/issues"},"license":"MIT","homepage":"https://github.com/lemonadesocial/atlas-protocol#readme","keywords":["mpp","machine-payments","stripe","tempo","agent-commerce","atlas","http-402","x402","jws"],"repository":{"type":"git","url":"git+https://github.com/lemonadesocial/atlas-protocol.git","directory":"packages/mpp"},"description":"Standalone implementation of the Machine Payments Protocol (MPP) envelope: challenge / credential / receipt encode + sign + verify. Optional JWS wrapping layer for signed envelopes. Optional x402 client (on-chain USDC) and stripe-mpp client (Stripe SPT) f","maintainers":[{"name":"nooblemon_eth","email":"kc@lemonade.social"}],"readme":"# @atlasprotocol/mpp\n\nStandalone implementation of the **Machine Payments Protocol (MPP)** envelope, plus an optional JWS signing layer and two optional client helpers for agent-side payment + retry: `x402` (on-chain USDC) and `stripe-mpp` (Stripe SPT).\n\n> The package has zero coupling to `@atlasprotocol/server-sdk`. It can be used by any HTTP-402 server or agent client.\n\n## Three surfaces\n\n`@atlasprotocol/mpp` exposes three independent surfaces. Pick the one you need — they do not depend on each other and you can use any subset alone.\n\n| Surface                       | Import                              | What it does                                                                                                            | Runtime deps          |\n| ----------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------- |\n| **Wire format** (always)      | `@atlasprotocol/mpp`                | `encode` / `decode` / `serialize` / `deserialize` for the canonical Challenge / Credential / Receipt envelope. Optional `signEnvelope` / `verifyEnvelope` JWS layer. | `jose` only           |\n| **x402 client** (opt-in)      | `@atlasprotocol/mpp/x402`           | `fetchWithPayment` — drop-in `fetch` that handles a 402 by paying on-chain (default: USDC ERC-20 via viem) and retrying. | `viem` (peer, optional) |\n| **stripe-mpp client** (opt-in) | `@atlasprotocol/mpp/stripe-mpp`     | `fetchWithPaymentSpt` — drop-in `fetch` that handles a 402 by completing a Stripe SPT (Stablecoin Payment Token) charge through a caller-supplied callback and retrying. | none                  |\n\nThe wire format is intentionally chain-agnostic: it does not verify on-chain payments and it does not move funds. **On-chain verification (server-side), x402 settlement (client-side), and Stripe authorization + SPT minting (client-side) are the consumer's job.** The `x402` and `stripe-mpp` subpaths are reference client implementations; the server-side counterpart lives in `@atlasprotocol/server-sdk` (`generateMppChallenge`, `verifyPayment`, `verifyStripePayment`).\n\n## Install\n\n```bash\npnpm add @atlasprotocol/mpp\n# Add viem only if you plan to use the x402 subpath:\npnpm add viem\n# stripe-mpp has no runtime deps — the agent surface owns the Stripe call.\n```\n\n## Wire format\n\n### Encode + decode an envelope\n\n```ts\nimport { encode, decode, serialize, deserialize } from \"@atlasprotocol/mpp\";\n\nconst envelope = encode({\n  rail: \"usdc-base\",\n  realm: \"api.example.com\",\n  paymentId: \"pay_abc123\",\n  amount: \"12.50\",\n  currency: \"usd\",\n  recipient: \"0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00\",\n  description: \"Ticket: Lemonade x ATLAS Launch\",\n  expires: \"2026-04-30T18:00:00.000Z\",\n  metadata: { event_id: \"evt_42\" },\n});\n\nconst wire = serialize(envelope);\nconst recovered = decode(deserialize(wire));\n```\n\n### Sign + verify with JWS (ES256)\n\n```ts\nimport { generateKeyPair } from \"jose\";\nimport { encode, signEnvelope, verifyEnvelope } from \"@atlasprotocol/mpp\";\n\nconst { privateKey, publicKey } = await generateKeyPair(\"ES256\");\n\nconst envelope = encode({\n  rail: \"usdc-tempo\",\n  realm: \"api.example.com\",\n  paymentId: \"pay_signed\",\n  amount: \"5.00\",\n  currency: \"usd\",\n  recipient: \"0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00\",\n});\n\nconst signed = await signEnvelope(envelope, {\n  alg: \"ES256\",\n  kid: \"organizer-key-1\",\n  key: privateKey,\n});\n\nconst result = await verifyEnvelope(signed, { alg: \"ES256\", key: publicKey });\nif (result.valid) {\n  console.log(\"payload\", result.payload);\n}\n```\n\n## Server-side: verify a paid credential\n\nThe package gives you envelope decode for free; on-chain proof is yours to add. Sketch:\n\n```ts\nimport { decode, deserialize } from \"@atlasprotocol/mpp\";\nimport { createPublicClient, http, parseAbiItem } from \"viem\";\n\nconst TRANSFER_EVENT = parseAbiItem(\n  \"event Transfer(address indexed from, address indexed to, uint256 value)\",\n);\n\nexport async function verifyCredential(wire: string) {\n  const payload = decode(deserialize(wire));\n  // 1. Sanity-check rail / recipient / amount against your config.\n  // 2. Pull tx hash from the credential metadata.\n  const txHash = payload.metadata?.tx_hash;\n  if (!txHash) return { valid: false, error: \"missing tx_hash\" };\n  // 3. Resolve the tx receipt and walk its logs for a USDC Transfer that\n  //    pays >= the expected amount to your receiver.\n  const client = createPublicClient({ transport: http(process.env.RPC_URL) });\n  const receipt = await client.getTransactionReceipt({ hash: txHash as `0x${string}` });\n  // ... walk receipt.logs against TRANSFER_EVENT ...\n  return { valid: true, txHash };\n}\n```\n\nThe full reference (~75 lines) lives at `lemonade-backend/src/app/services/atlas/mpp-onchain.ts` in the consuming repo.\n\n## Client-side: pay a 402 challenge\n\nThe `x402` subpath gives you a drop-in `fetch` that pays once and retries. Suitable for agents that want machine-to-machine commerce without owning the on-chain plumbing.\n\n```ts\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { baseSepolia } from \"viem/chains\";\nimport { fetchWithPayment } from \"@atlasprotocol/mpp/x402\";\n\nconst account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as `0x${string}`);\n\nconst response = await fetchWithPayment(\n  \"https://api.example.com/mpp/v1/ping-paid\",\n  { method: \"GET\" },\n  {\n    account,\n    chain: baseSepolia,\n    // Safety: refuse 402s asking us to pay anyone outside this list.\n    allowedReceivers: [\"0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00\"],\n    // Safety: refuse 402s asking for tokens we don't recognise.\n    allowedStablecoins: [\"0x036CbD53842c5426634e7929541eC2318f3dCF7e\"], // Base Sepolia USDC\n    // Safety: hard cap, in 6-decimal USDC micro-units. 1_000n = $0.001.\n    maxAmountUsdcMicro: 10_000n,\n    waitForConfirmations: 1,\n    onPayment: ({ txHash, amount }) => {\n      console.log(`paid ${amount} micro-USDC, tx=${txHash}`);\n    },\n  },\n);\n\nif (response.status === 200) {\n  console.log(await response.json());\n}\n```\n\nFailure modes:\n\n- **No 402** → response is returned unchanged.\n- **Safety check fails** (receiver/token not allowed, or amount > cap) → throws `MppPaymentRefusedError` with `err.reason` set to one of `receiver-not-allowed`, `stablecoin-not-allowed`, `amount-exceeds-cap`, `amount-malformed`, `challenge-malformed`, `challenge-missing`. **No on-chain payment is made.**\n- **viem call fails** (RPC error, revert) → the underlying viem error propagates.\n- **Server returns 402 again on retry** → that response is returned. The helper does not loop.\n\n## Safety\n\n`fetchWithPayment` is a wallet-drain footgun if you skip the allowlist. Treat the safety options as required — none have defaults that \"just work\":\n\n- **`allowedReceivers`** — every 402 names a recipient. If you do not pin this, a malicious or compromised endpoint can ask your agent to pay any address. List the recipients you actually expect.\n- **`allowedStablecoins`** — same logic for the token contract. USDC on Base Sepolia is a different contract than USDC on Base mainnet; chain-mismatched 402s should not pay.\n- **`maxAmountUsdcMicro`** — per-request cap in 6-decimal micro-units. 1 USDC = `1_000_000n`. Pick the smallest cap that covers the endpoints you call.\n\nFor a multi-endpoint agent, scope these to the specific call (e.g. wrap `fetchWithPayment` in a thin per-endpoint wrapper that pins the allowlists).\n\n## Client-side: pay a 402 challenge with Stripe SPT\n\nThe `stripe-mpp` subpath gives you a drop-in `fetch` that handles a 402 by completing a Stripe Stablecoin Payment Token charge. Stripe's SPT pipeline lets the buyer pay in fiat (cards / Apple Pay / Google Pay / Link) and converts to USDC server-side. **The Stripe SDK call lives in your code, not in this package** — `stripe-mpp` calls back into your `getSpt` so the agent surface (Claude / ChatGPT / Gemini) can show the user the amount, get authorization, and complete the PaymentIntent however it wants.\n\n```ts\nimport { fetchWithPaymentSpt } from \"@atlasprotocol/mpp/stripe-mpp\";\nimport Stripe from \"stripe\";\n\nconst stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);\n\nconst response = await fetchWithPaymentSpt(\n  \"https://api.example.com/atlas/v1/events/evt_42/purchase\",\n  { method: \"POST\", body: JSON.stringify({ ticket_type_id: \"ga\", quantity: 1 }) },\n  {\n    // Safety: refuse 402s asking for more than this. In CENTS (USD).\n    maxAmountUsdCents: 5000,\n    // Safety: only complete SPTs for known platform receivers.\n    allowedReceivers: [\"stripe:acct_atlas_demo\"],\n    // Caller-owned: prompt the user, complete the Stripe PaymentIntent,\n    // return the payment_intent_id once it has succeeded.\n    getSpt: async ({ amount, currency, challenge_id }) => {\n      // amount is in cents; currency is always \"usd\"\n      const intent = await stripe.paymentIntents.create({\n        amount,\n        currency,\n        confirm: true,\n        payment_method: process.env.PAYMENT_METHOD_ID,\n        metadata: { atlas_challenge: challenge_id },\n      });\n      if (intent.status !== \"succeeded\") {\n        throw new Error(`Stripe intent did not succeed: ${intent.status}`);\n      }\n      return intent.id;\n    },\n    onPayment: ({ paymentIntentId, amountCents }) => {\n      console.log(`paid ${amountCents}¢ via Stripe, intent=${paymentIntentId}`);\n    },\n  },\n);\n```\n\nFailure modes:\n\n- **No 402** → response is returned unchanged.\n- **Safety check fails** → throws `MppPaymentRefusedError` with `err.reason` set to one of `no_stripe_method_offered`, `receiver-not-allowed`, `amount-exceeds-cap`, `amount-malformed`, `currency-not-usd`, `challenge-malformed`, `challenge-missing`. **No call to `getSpt` is made.**\n- **`getSpt` rejects** → wrapped as `MppPaymentRefusedError` with `reason: \"spt-callback-failed\"`.\n- **Server returns 402 again on retry** → that response is returned. The helper does not loop.\n\nThe retry credential carries the Stripe `payment_intent_id` in `metadata.payment_intent_id` — the form the server-side `verifyStripePayment` (in `@atlasprotocol/server-sdk`) inspects when accepting a settlement.\n\n## Supported rails\n\n```ts\nimport { SUPPORTED_RAILS } from \"@atlasprotocol/mpp\";\n// 'usdc-base' | 'usdc-tempo' | 'usdc-arbitrum' | 'usdc-polygon' | 'usdc-optimism' | 'stripe-spt'\n```\n\n`@atlasprotocol/mpp` accepts any rail string that conforms to the canonical MPP method identifier grammar — `isValidMethodIdentifier(s)` — so non-supported rails still flow through `decode()`.\n\n## Conformance status\n\nThis package follows the canonical MPP wire shape published at <https://mpp.dev/protocol> (accessed 2026-04-30). It implements:\n\n- The `Challenge` / `Credential` / `Receipt` envelope shape.\n- Base64url-encoded JCS-canonicalized JSON for the request payload.\n- The canonical method identifier grammar (lowercase alpha + digits + `:_-`).\n- The reserved fields (`id`, `realm`, `method`, `intent`, `request`, `expires`, `description`, `digest`, `opaque`).\n\nThe MPP spec does **not** mandate JWS for envelope authenticity (it pins challenge ids via HMAC-SHA256 and lets each method define its own credential payload signature). The JWS layer in this package is an `@atlasprotocol/mpp` extension above the canonical spec — it is the natural choice when you want a single signed blob with cross-domain verifiability.\n\nFor every field where the spec leaves an ambiguity (organizer identity, line items, free-form metadata, MPP version literal), the implementation flags an `MPP-GAP-XXX` and documents the resolution in [`SPEC-NOTES.md`](./SPEC-NOTES.md).\n\n## License\n\nMIT\n","readmeFilename":"README.md"}