mppx 0.8.18 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +0 -6
  3. package/dist/Challenge.d.ts +7 -0
  4. package/dist/Challenge.js +32 -6
  5. package/dist/Constants.d.ts +1 -0
  6. package/dist/Constants.js +1 -0
  7. package/dist/Credential.d.ts +11 -5
  8. package/dist/Credential.js +8 -8
  9. package/dist/Html.js +1 -1
  10. package/dist/cli/THIRD-PARTY-LICENSES.txt +1 -1
  11. package/dist/cli/cli.d.ts +2 -2
  12. package/dist/cli/cli.js +134 -134
  13. package/dist/cli/plugins/index.js +13 -13
  14. package/dist/cli/sessions/Manager.js +4 -6
  15. package/dist/cli/sessions/commands.d.ts +1 -1
  16. package/dist/cli/sessions/store.js +13 -19
  17. package/dist/cli/validate/index.d.ts +2 -2
  18. package/dist/client/Mppx.d.ts +39 -2
  19. package/dist/client/Mppx.js +102 -46
  20. package/dist/client/Transport.js +1 -1
  21. package/dist/client/internal/protocols/Mpp.d.ts +1 -1
  22. package/dist/client/internal/protocols/Mpp.js +5 -3
  23. package/dist/client/internal/protocols/Protocol.d.ts +3 -1
  24. package/dist/client/internal/protocols/Shared.js +5 -1
  25. package/dist/integrations/x402/Evm.d.ts +29 -0
  26. package/dist/integrations/x402/Evm.js +150 -0
  27. package/dist/integrations/x402/Negotiator.d.ts +36 -0
  28. package/dist/integrations/x402/Negotiator.js +165 -0
  29. package/dist/mcp/internal/Credential.d.ts +9 -0
  30. package/dist/mcp/internal/Credential.js +16 -0
  31. package/dist/mcp/server/Transport.js +2 -4
  32. package/dist/middlewares/express.js +5 -12
  33. package/dist/middlewares/internal/express.d.ts +8 -0
  34. package/dist/middlewares/internal/express.js +24 -0
  35. package/dist/middlewares/internal/node.d.ts +10 -0
  36. package/dist/middlewares/internal/node.js +34 -0
  37. package/dist/proxy/Proxy.js +15 -9
  38. package/dist/server/Mppx.d.ts +2 -0
  39. package/dist/server/Mppx.js +17 -14
  40. package/dist/server/Transport.d.ts +8 -2
  41. package/dist/server/Transport.js +6 -3
  42. package/dist/server/internal/SecretKey.d.ts +3 -0
  43. package/dist/server/internal/SecretKey.js +9 -0
  44. package/dist/server/internal/html/serviceWorker.client.d.ts +1 -1
  45. package/dist/server/internal/html/serviceWorker.client.js +2 -2
  46. package/dist/server/internal/html/serviceWorker.gen.d.ts +1 -1
  47. package/dist/server/internal/html/serviceWorker.gen.js +1 -1
  48. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  49. package/dist/stripe/server/internal/html.gen.js +1 -1
  50. package/dist/tempo/Methods.d.ts +0 -10
  51. package/dist/tempo/Methods.js +3 -12
  52. package/dist/tempo/client/Charge.d.ts +0 -3
  53. package/dist/tempo/client/Charge.js +3 -13
  54. package/dist/tempo/client/Methods.d.ts +0 -10
  55. package/dist/tempo/internal/account.d.ts +15 -6
  56. package/dist/tempo/internal/account.js +17 -26
  57. package/dist/tempo/internal/defaults.d.ts +0 -11
  58. package/dist/tempo/internal/defaults.js +0 -11
  59. package/dist/tempo/internal/fee-payer.d.ts +21 -3
  60. package/dist/tempo/internal/fee-payer.js +10 -19
  61. package/dist/tempo/internal/remote-fee-payer.d.ts +12 -0
  62. package/dist/tempo/internal/remote-fee-payer.js +17 -0
  63. package/dist/tempo/legacy/client/Session.d.ts +0 -7
  64. package/dist/tempo/server/Charge.d.ts +0 -3
  65. package/dist/tempo/server/Charge.js +26 -68
  66. package/dist/tempo/server/Methods.d.ts +4 -18
  67. package/dist/tempo/server/Methods.js +4 -8
  68. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  69. package/dist/tempo/server/internal/html.gen.js +1 -1
  70. package/dist/tempo/session/client/ChannelOps.d.ts +0 -14
  71. package/dist/tempo/session/client/ChannelOps.js +0 -22
  72. package/dist/tempo/session/client/ChannelStore.d.ts +5 -0
  73. package/dist/tempo/session/client/ChannelStore.js +2 -2
  74. package/dist/tempo/session/client/CredentialState.d.ts +2 -12
  75. package/dist/tempo/session/client/CredentialState.js +75 -73
  76. package/dist/tempo/session/client/Session.d.ts +2 -9
  77. package/dist/tempo/session/client/Session.js +71 -176
  78. package/dist/tempo/session/client/SessionManager.js +9 -28
  79. package/dist/tempo/session/precompile/Chain.d.ts +0 -4
  80. package/dist/tempo/session/precompile/Chain.js +2 -29
  81. package/dist/tempo/session/precompile/Protocol.d.ts +0 -13
  82. package/dist/tempo/session/precompile/Voucher.d.ts +1 -3
  83. package/dist/tempo/session/precompile/Voucher.js +12 -6
  84. package/dist/tempo/session/server/ChannelStore.d.ts +0 -6
  85. package/dist/tempo/session/server/ChannelStore.js +4 -11
  86. package/dist/tempo/session/server/CredentialVerification.d.ts +7 -9
  87. package/dist/tempo/session/server/CredentialVerification.js +118 -184
  88. package/dist/tempo/session/server/RequestState.d.ts +0 -17
  89. package/dist/tempo/session/server/RequestState.js +15 -59
  90. package/dist/tempo/session/server/Session.d.ts +0 -9
  91. package/dist/tempo/session/server/Session.js +8 -37
  92. package/dist/tempo/session/server/Settlement.d.ts +0 -7
  93. package/dist/tempo/session/server/Settlement.js +16 -50
  94. package/dist/viem/Client.d.ts +7 -2
  95. package/dist/viem/Client.js +17 -5
  96. package/dist/x402/Types.d.ts +2 -0
  97. package/dist/x402/Types.js +2 -0
  98. package/dist/x402/mcp.d.ts +20 -0
  99. package/dist/x402/mcp.js +153 -0
  100. package/dist/x402/middlewares/express.d.ts +23 -0
  101. package/dist/x402/middlewares/express.js +49 -0
  102. package/dist/x402/middlewares/hono.d.ts +13 -0
  103. package/dist/x402/middlewares/hono.js +36 -0
  104. package/dist/x402/middlewares/next.d.ts +17 -0
  105. package/dist/x402/middlewares/next.js +58 -0
  106. package/package.json +41 -1
  107. package/dist/tempo/internal/machine-token-charge.d.ts +0 -51
  108. package/dist/tempo/internal/machine-token-charge.js +0 -113
  109. package/dist/tempo/internal/machine-token-session.d.ts +0 -88
  110. package/dist/tempo/internal/machine-token-session.js +0 -173
  111. package/dist/tempo/internal/signature-envelope.d.ts +0 -14
  112. package/dist/tempo/internal/signature-envelope.js +0 -28
@@ -0,0 +1,165 @@
1
+ import { decodePaymentRequiredHeader } from '@x402/core/http';
2
+ import { getFacilitatorResponseError, x402HTTPResourceServer } from '@x402/core/server';
3
+ import * as Constants from '../../Constants.js';
4
+ import * as Credential from '../../Credential.js';
5
+ import * as SecretKey from '../../server/internal/SecretKey.js';
6
+ import * as Types from '../../x402/Types.js';
7
+ import * as Evm from './Evm.js';
8
+ /** Creates the native MPP side of an x402 compatibility integration. */
9
+ export function create(config) {
10
+ SecretKey.assert(config.secretKey);
11
+ const httpServer = new x402HTTPResourceServer(config.server, config.routes);
12
+ let initialization;
13
+ async function initialize() {
14
+ initialization ??= httpServer.initialize().catch((error) => {
15
+ initialization = undefined;
16
+ throw error;
17
+ });
18
+ await initialization;
19
+ }
20
+ async function negotiate(request, context) {
21
+ if (!httpServer.requiresPayment(context))
22
+ return { status: 'unprotected' };
23
+ if (hasMppCredential(request) && getX402Credential(request))
24
+ return {
25
+ response: Response.json({ error: 'Send either an MPP credential or an x402 payment signature, not both.' }, { status: 400 }),
26
+ status: 'handled',
27
+ };
28
+ try {
29
+ await initialize();
30
+ if (getX402Credential(request))
31
+ return { status: 'x402' };
32
+ const result = await httpServer.processHTTPRequest(withoutX402Credential(context), config.paywallConfig);
33
+ if (result.type === 'no-payment-required')
34
+ return { status: 'unprotected' };
35
+ if (result.type !== 'payment-error')
36
+ throw new Error(`Expected x402 payment response, received ${result.type}.`);
37
+ const x402Response = responseFromInstructions(result.response);
38
+ const paymentRequired = getPaymentRequired(result);
39
+ const handler = paymentRequired
40
+ ? Evm.createHandler({ config, context, paymentRequired })
41
+ : undefined;
42
+ if (!handler)
43
+ return { response: x402Response, status: 'handled' };
44
+ const { payment, skipHandlerResponse } = await handler(request);
45
+ if (payment.status !== 402)
46
+ return skipHandlerResponse
47
+ ? { response: payment.withReceipt(skipHandlerResponse), status: 'handled' }
48
+ : { status: 'paid', withReceipt: payment.withReceipt };
49
+ const response = mergeChallenge(payment.challenge, x402Response);
50
+ return { response, status: 'handled' };
51
+ }
52
+ catch (error) {
53
+ const facilitatorError = getFacilitatorResponseError(error);
54
+ if (!facilitatorError)
55
+ throw error;
56
+ return {
57
+ response: Response.json({ error: facilitatorError.message }, { status: 502 }),
58
+ status: 'handled',
59
+ };
60
+ }
61
+ }
62
+ return { httpServer, negotiate };
63
+ }
64
+ /** Reads a current or legacy x402 payment credential. */
65
+ export function getX402Credential(request) {
66
+ return (request.headers.get(Types.paymentSignatureHeader) ??
67
+ request.headers.get(Types.legacyPaymentSignatureHeader) ??
68
+ undefined);
69
+ }
70
+ /** Converts a Fetch request to the context consumed by x402 core. */
71
+ export function createContext(request, body) {
72
+ const paymentHeader = getX402Credential(request);
73
+ return {
74
+ adapter: new FetchAdapter(request, body),
75
+ method: request.method,
76
+ path: new URL(request.url).pathname,
77
+ ...(paymentHeader ? { paymentHeader } : {}),
78
+ };
79
+ }
80
+ function hasMppCredential(request) {
81
+ const authorization = request.headers.get(Constants.Headers.authorization);
82
+ return authorization ? Credential.extractPaymentScheme(authorization) !== null : false;
83
+ }
84
+ function withoutX402Credential(context) {
85
+ const { paymentHeader: _, ...withoutCredential } = context;
86
+ return withoutCredential;
87
+ }
88
+ function getPaymentRequired(result) {
89
+ if (result.type !== 'payment-error')
90
+ return undefined;
91
+ const target = Types.paymentRequiredHeader.toLowerCase();
92
+ const encoded = Object.entries(result.response.headers).find(([name]) => name.toLowerCase() === target)?.[1];
93
+ return encoded ? decodePaymentRequiredHeader(encoded) : undefined;
94
+ }
95
+ function responseFromInstructions(instructions) {
96
+ const headers = new Headers(instructions.headers);
97
+ let body = null;
98
+ if (instructions.body !== undefined) {
99
+ if (instructions.isHtml || typeof instructions.body === 'string')
100
+ body = String(instructions.body);
101
+ else {
102
+ body = JSON.stringify(instructions.body);
103
+ if (!headers.has('Content-Type'))
104
+ headers.set('Content-Type', 'application/json');
105
+ }
106
+ }
107
+ return new Response(body, { headers, status: instructions.status });
108
+ }
109
+ function mergeChallenge(mpp, x402) {
110
+ const headers = new Headers(x402.headers);
111
+ const challenge = mpp.headers.get('WWW-Authenticate');
112
+ if (challenge)
113
+ headers.append('WWW-Authenticate', challenge);
114
+ headers.set('Cache-Control', 'no-store');
115
+ return new Response(x402.body, {
116
+ headers,
117
+ status: x402.status,
118
+ statusText: x402.statusText,
119
+ });
120
+ }
121
+ /** Fetch implementation used only by the MCP compatibility wrapper. */
122
+ class FetchAdapter {
123
+ request;
124
+ body;
125
+ constructor(request, body) {
126
+ this.request = request;
127
+ this.body = body;
128
+ }
129
+ getAcceptHeader() {
130
+ return this.request.headers.get('Accept') ?? '';
131
+ }
132
+ getHeader(name) {
133
+ return this.request.headers.get(name) ?? undefined;
134
+ }
135
+ getBody() {
136
+ return this.body;
137
+ }
138
+ getMethod() {
139
+ return this.request.method;
140
+ }
141
+ getPath() {
142
+ return new URL(this.request.url).pathname;
143
+ }
144
+ getQueryParam(name) {
145
+ const values = new URL(this.request.url).searchParams.getAll(name);
146
+ if (values.length === 0)
147
+ return undefined;
148
+ return values.length === 1 ? values[0] : values;
149
+ }
150
+ getQueryParams() {
151
+ const parameters = {};
152
+ for (const key of new URL(this.request.url).searchParams.keys()) {
153
+ const values = new URL(this.request.url).searchParams.getAll(key);
154
+ parameters[key] = values.length === 1 ? values[0] : values;
155
+ }
156
+ return parameters;
157
+ }
158
+ getUrl() {
159
+ return this.request.url;
160
+ }
161
+ getUserAgent() {
162
+ return this.request.headers.get('User-Agent') ?? '';
163
+ }
164
+ }
165
+ //# sourceMappingURL=Negotiator.js.map
@@ -0,0 +1,9 @@
1
+ import * as Credential from '../../Credential.js';
2
+ /** Parsed MCP credential and its canonical HTTP Authorization value. */
3
+ export type Parsed = {
4
+ header: string;
5
+ value: Credential.Credential;
6
+ };
7
+ /** Validates an MCP metadata credential through the canonical wire serializer. */
8
+ export declare function parse(value: unknown): Parsed | undefined;
9
+ //# sourceMappingURL=Credential.d.ts.map
@@ -0,0 +1,16 @@
1
+ import * as Credential from '../../Credential.js';
2
+ /** Validates an MCP metadata credential through the canonical wire serializer. */
3
+ export function parse(value) {
4
+ if (!value || typeof value !== 'object')
5
+ return undefined;
6
+ if (!('challenge' in value) || !('payload' in value))
7
+ return undefined;
8
+ try {
9
+ const header = Credential.serialize(value);
10
+ return { header, value: Credential.deserialize(header) };
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ }
16
+ //# sourceMappingURL=Credential.js.map
@@ -1,5 +1,6 @@
1
1
  import * as core_Mcp from '../../Mcp.js';
2
2
  import * as Transport from '../../server/Transport.js';
3
+ import * as McpCredential from '../internal/Credential.js';
3
4
  /**
4
5
  * MCP SDK transport for server-side payment handling with `@modelcontextprotocol/sdk`.
5
6
  *
@@ -40,10 +41,7 @@ export function mcpSdk() {
40
41
  };
41
42
  },
42
43
  getCredential(extra) {
43
- const credential = extra._meta?.[core_Mcp.credentialMetaKey];
44
- if (!credential)
45
- return null;
46
- return credential;
44
+ return McpCredential.parse(extra._meta?.[core_Mcp.credentialMetaKey])?.value ?? null;
47
45
  },
48
46
  async respondChallenge({ challenge, error }) {
49
47
  if (!McpErrorClass) {
@@ -1,5 +1,6 @@
1
1
  import { generate } from '../discovery/OpenApi.js';
2
2
  import * as Mppx_core from '../server/Mppx.js';
3
+ import * as ExpressAdapter from './internal/express.js';
3
4
  import * as Mppx_internal from './internal/mppx.js';
4
5
  export * from '../server/Methods.js';
5
6
  export var Mppx;
@@ -48,17 +49,11 @@ export var Mppx;
48
49
  */
49
50
  export function payment(intent, options) {
50
51
  return async (req, res, next) => {
51
- const request = new Request(`${req.protocol}://${req.hostname}${req.originalUrl}`, {
52
- method: req.method,
53
- headers: req.headers,
54
- });
52
+ const request = ExpressAdapter.toRequest(req);
55
53
  const result = await intent(options)(request);
56
54
  if (result.status === 402) {
57
55
  const challenge = result.challenge;
58
- res.status(challenge.status);
59
- for (const [key, value] of challenge.headers)
60
- res.setHeader(key, value);
61
- res.send(await challenge.text());
56
+ await ExpressAdapter.sendResponse(res, challenge);
62
57
  return;
63
58
  }
64
59
  const managementResponse = (() => {
@@ -73,8 +68,7 @@ export function payment(intent, options) {
73
68
  })();
74
69
  if (managementResponse) {
75
70
  res.status(managementResponse.status);
76
- for (const [key, value] of managementResponse.headers)
77
- res.setHeader(key, value);
71
+ ExpressAdapter.copyHeaders(res, managementResponse.headers);
78
72
  if (managementResponse.body === null) {
79
73
  res.end();
80
74
  return;
@@ -85,8 +79,7 @@ export function payment(intent, options) {
85
79
  const originalJson = res.json.bind(res);
86
80
  res.json = (body) => {
87
81
  const wrapped = result.withReceipt(Response.json(body));
88
- for (const [key, value] of wrapped.headers)
89
- res.setHeader(key, value);
82
+ ExpressAdapter.copyHeaders(res, wrapped.headers);
90
83
  return originalJson(body);
91
84
  };
92
85
  next();
@@ -0,0 +1,8 @@
1
+ import type { Request as ExpressRequest, Response as ExpressResponse } from 'express';
2
+ /** Copies Fetch response headers to an Express response. */
3
+ export declare function copyHeaders(response: ExpressResponse, headers: Headers): void;
4
+ /** Sends a Fetch response through Express without changing its status, headers, or bytes. */
5
+ export declare function sendResponse(response: ExpressResponse, fetchResponse: Response): Promise<ExpressResponse<any, Record<string, any>>>;
6
+ /** Converts an Express parsed request into the Fetch request consumed by mppx core. */
7
+ export declare function toRequest(request: ExpressRequest): Request;
8
+ //# sourceMappingURL=express.d.ts.map
@@ -0,0 +1,24 @@
1
+ import * as NodeRequest from './node.js';
2
+ /** Copies Fetch response headers to an Express response. */
3
+ export function copyHeaders(response, headers) {
4
+ for (const [name, value] of headers)
5
+ response.setHeader(name, value);
6
+ }
7
+ /** Sends a Fetch response through Express without changing its status, headers, or bytes. */
8
+ export async function sendResponse(response, fetchResponse) {
9
+ copyHeaders(response, fetchResponse.headers);
10
+ response.status(fetchResponse.status);
11
+ if (fetchResponse.body === null)
12
+ return response.end();
13
+ return response.send(Buffer.from(await fetchResponse.arrayBuffer()));
14
+ }
15
+ /** Converts an Express parsed request into the Fetch request consumed by mppx core. */
16
+ export function toRequest(request) {
17
+ return NodeRequest.toRequest({
18
+ body: request.body,
19
+ headers: request.headers,
20
+ method: request.method,
21
+ url: `${request.protocol}://${request.get('host')}${request.originalUrl}`,
22
+ });
23
+ }
24
+ //# sourceMappingURL=express.js.map
@@ -0,0 +1,10 @@
1
+ /** Parsed Node framework request fields needed to construct a Fetch request. */
2
+ export type ParsedRequest = {
3
+ body?: unknown;
4
+ headers: Record<string, string | string[] | undefined>;
5
+ method: string;
6
+ url: string;
7
+ };
8
+ /** Converts a parsed Node framework request into the Fetch request consumed by mppx core. */
9
+ export declare function toRequest(request: ParsedRequest): Request;
10
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1,34 @@
1
+ /** Converts a parsed Node framework request into the Fetch request consumed by mppx core. */
2
+ export function toRequest(request) {
3
+ const headers = new Headers();
4
+ for (const [name, value] of Object.entries(request.headers)) {
5
+ if (value === undefined)
6
+ continue;
7
+ if (Array.isArray(value))
8
+ for (const entry of value)
9
+ headers.append(name, entry);
10
+ else
11
+ headers.set(name, value);
12
+ }
13
+ const body = request.method === 'GET' || request.method === 'HEAD' || request.body === undefined
14
+ ? undefined
15
+ : toBody(request.body);
16
+ return new Request(request.url, {
17
+ ...(body === undefined ? {} : { body }),
18
+ headers,
19
+ method: request.method,
20
+ });
21
+ }
22
+ /** Preserves Fetch-compatible bodies and serializes parsed JSON values. */
23
+ function toBody(body) {
24
+ if (typeof body === 'string' ||
25
+ body instanceof ArrayBuffer ||
26
+ ArrayBuffer.isView(body) ||
27
+ body instanceof Blob ||
28
+ body instanceof FormData ||
29
+ body instanceof URLSearchParams ||
30
+ body instanceof ReadableStream)
31
+ return body;
32
+ return JSON.stringify(body);
33
+ }
34
+ //# sourceMappingURL=node.js.map
@@ -310,16 +310,22 @@ function withBasePath(basePath, path) {
310
310
  return `${trimmed}${path}`;
311
311
  }
312
312
  function getPaymentBinding(request) {
313
- try {
314
- const credential = Credential.fromRequest(request);
315
- return {
316
- intent: credential.challenge.intent,
317
- method: credential.challenge.method,
318
- };
319
- }
320
- catch {
321
- return null;
313
+ for (const [, value] of request.headers) {
314
+ const payment = Credential.extractPaymentScheme(value);
315
+ if (!payment)
316
+ continue;
317
+ try {
318
+ const credential = Credential.deserialize(payment);
319
+ return {
320
+ intent: credential.challenge.intent,
321
+ method: credential.challenge.method,
322
+ };
323
+ }
324
+ catch {
325
+ // A malformed payment header is handled by the selected route.
326
+ }
322
327
  }
328
+ return null;
323
329
  }
324
330
  function matchesPaymentBinding(endpoint, binding) {
325
331
  if (endpoint === true)
@@ -315,6 +315,8 @@ export declare namespace create {
315
315
  attestation?: transport extends Transport.Http ? Attestation.VerifierMap | undefined : never;
316
316
  /** Array of configured methods. @example [tempo()] */
317
317
  methods: methods;
318
+ /** Uses `Payment-Authorization` for Payment credentials so `Authorization` remains available for application authentication. */
319
+ requiresAuth?: transport extends Transport.Http ? boolean | undefined : never;
318
320
  /** Server realm (e.g., hostname). Resolution order: explicit value > env vars (`MPP_REALM`, `FLY_APP_NAME`, `VERCEL_URL`, etc.) > request URL hostname > `"MPP Payment"`. */
319
321
  realm?: string | undefined;
320
322
  /** Secret key for HMAC-bound challenge IDs for stateless verification. Must be at least 32 bytes. Auto-detected from `MPP_SECRET_KEY` environment variable. */
@@ -14,11 +14,10 @@ import * as z from '../zod.js';
14
14
  import * as Html from './internal/html/config.js';
15
15
  import { serviceWorker } from './internal/html/serviceWorker.gen.js';
16
16
  import * as Scope from './internal/scope.js';
17
+ import * as SecretKey from './internal/SecretKey.js';
17
18
  import * as NodeListener from './NodeListener.js';
18
19
  import * as Request from './Request.js';
19
20
  import * as Transport from './Transport.js';
20
- const minimumSecretKeyBytes = 32;
21
- const secretKeyGenerationCommand = 'openssl rand -base64 32';
22
21
  const reservedMppxKeyValues = [
23
22
  'challenge',
24
23
  'compose',
@@ -52,11 +51,12 @@ export const reservedMppxKeys = new Set(reservedMppxKeyValues);
52
51
  * ```
53
52
  */
54
53
  export function create(config) {
55
- const { attestation, realm = Env.get('realm'), selectOffers, secretKey = Env.get('secretKey'), transport = Transport.http(), } = config;
54
+ const { attestation, requiresAuth, realm = Env.get('realm'), selectOffers, secretKey = Env.get('secretKey'), transport = Transport.http({ requiresAuth }), } = config;
55
+ const credentialHeader = requiresAuth ? Constants.Headers.paymentAuthorization : undefined;
56
56
  if (!secretKey) {
57
57
  throw new Error('Missing secret key. Set the MPP_SECRET_KEY environment variable or pass `secretKey` to Mppx.create().');
58
58
  }
59
- assertSecretKey(secretKey);
59
+ SecretKey.assert(secretKey);
60
60
  const methods = config.methods.flat();
61
61
  const serverEvents = createServerEventDispatcher();
62
62
  const verifyAttestation = attestation
@@ -83,6 +83,7 @@ export function create(config) {
83
83
  const fn = createMethodFn({
84
84
  ...(verifyAttestation && { attest: verifyAttestation }),
85
85
  authorize: mi.authorize,
86
+ credentialHeader,
86
87
  defaults: mi.defaults,
87
88
  method: mi,
88
89
  realm,
@@ -143,6 +144,7 @@ export function create(config) {
143
144
  if (!challengeHandlers[mi.name])
144
145
  challengeHandlers[mi.name] = {};
145
146
  challengeHandlers[mi.name][mi.alias ?? mi.intent] = createChallengeFn({
147
+ credentialHeader,
146
148
  defaults: mi.defaults,
147
149
  method: mi,
148
150
  realm,
@@ -235,6 +237,7 @@ export function create(config) {
235
237
  credential: parsedCredential,
236
238
  defaults: mi.defaults,
237
239
  expires: credential.challenge.expires,
240
+ header: credential.challenge.header,
238
241
  meta: expectedMeta,
239
242
  method: mi,
240
243
  realm: expectedRealm,
@@ -375,15 +378,9 @@ export function create(config) {
375
378
  ...handlers,
376
379
  };
377
380
  }
378
- function assertSecretKey(secretKey) {
379
- const byteLength = new TextEncoder().encode(secretKey).byteLength;
380
- if (byteLength >= minimumSecretKeyBytes)
381
- return;
382
- throw new Error(`Secret key must be at least ${minimumSecretKeyBytes} bytes. Generate one with \`${secretKeyGenerationCommand}\` and set MPP_SECRET_KEY or pass it to Mppx.create().`);
383
- }
384
381
  // biome-ignore lint/correctness/noUnusedVariables: _
385
382
  function createMethodFn(parameters) {
386
- const { attest, authorize, defaults, events, method, preflight, realm, respond, secretKey, broadcast, stableBinding, transport, validate, verify, } = parameters;
383
+ const { attest, authorize, credentialHeader, defaults, events, method, preflight, realm, respond, secretKey, broadcast, stableBinding, transport, validate, verify, } = parameters;
387
384
  return (options) => {
388
385
  const { description, meta, scope, ...rest } = options;
389
386
  const staticMeta = Scope.merge({ meta, scope });
@@ -491,6 +488,7 @@ function createMethodFn(parameters) {
491
488
  defaults,
492
489
  description,
493
490
  expires,
491
+ header: credentialHeader,
494
492
  meta: effectiveMeta,
495
493
  method,
496
494
  realm,
@@ -505,6 +503,7 @@ function createMethodFn(parameters) {
505
503
  defaults: defaults ?? {},
506
504
  description,
507
505
  expires,
506
+ header: credentialHeader,
508
507
  meta: effectiveMeta,
509
508
  method,
510
509
  realm,
@@ -850,7 +849,7 @@ function createMethodFn(parameters) {
850
849
  * but returns a Challenge object directly instead of a request handler.
851
850
  */
852
851
  function createChallengeFn(parameters) {
853
- const { defaults, method, realm, secretKey } = parameters;
852
+ const { credentialHeader, defaults, method, realm, secretKey } = parameters;
854
853
  return async (options) => {
855
854
  const { description, meta, scope, ...rest } = options;
856
855
  const effectiveMeta = Scope.merge({ meta, scope });
@@ -861,6 +860,7 @@ function createChallengeFn(parameters) {
861
860
  defaults,
862
861
  description,
863
862
  expires,
863
+ header: credentialHeader,
864
864
  meta: effectiveMeta,
865
865
  method,
866
866
  realm,
@@ -1163,6 +1163,7 @@ async function resolveRouteChallenge(parameters) {
1163
1163
  const challenge = Challenge.fromMethod(parameters.method, {
1164
1164
  description: parameters.description,
1165
1165
  expires: parameters.expires,
1166
+ header: parameters.header,
1166
1167
  meta: parameters.meta,
1167
1168
  realm: effectiveRealm,
1168
1169
  request: request,
@@ -1178,6 +1179,7 @@ function createFallbackChallenge(parameters) {
1178
1179
  return Challenge.fromMethod(parameters.method, {
1179
1180
  description: parameters.description,
1180
1181
  expires: parameters.expires,
1182
+ header: parameters.header,
1181
1183
  meta: parameters.meta,
1182
1184
  realm: parameters.realm ??
1183
1185
  (parameters.capturedRequest
@@ -1405,8 +1407,9 @@ function composeHandlers(handlers, composeOptions, selectOffers) {
1405
1407
  // Try to extract a Payment credential to decide whether to dispatch or challenge.
1406
1408
  // Only gate on the Payment scheme — other auth schemes (Bearer, Basic, etc.)
1407
1409
  // should fall through to the merged-402 path so all offers are presented.
1408
- const header = input.headers.get(Constants.Headers.authorization);
1409
- const paymentHeader = header ? Credential.extractPaymentScheme(header) : null;
1410
+ const paymentHeader = Array.from(input.headers.values())
1411
+ .map((value) => Credential.extractPaymentScheme(value))
1412
+ .find((value) => value !== null);
1410
1413
  if (paymentHeader) {
1411
1414
  // Parse the credential to find method+intent for dispatch.
1412
1415
  let credential;
@@ -89,11 +89,17 @@ export declare function from<input = unknown, challengeOutput = unknown, receipt
89
89
  /**
90
90
  * HTTP transport for server-side payment handling.
91
91
  *
92
- * - Reads credentials from the `Authorization` header
92
+ * - Reads credentials from the configured payment credential header
93
93
  * - Issues challenges via `WWW-Authenticate` header with 402 status
94
94
  * - Attaches receipts via `Payment-Receipt` header
95
95
  */
96
- export declare function http(): Http;
96
+ export declare function http(options?: http.Options): Http;
97
+ export declare namespace http {
98
+ type Options = {
99
+ /** Uses `Payment-Authorization` for Payment credentials so `Authorization` remains available for application authentication. */
100
+ requiresAuth?: boolean | undefined;
101
+ };
102
+ }
97
103
  /**
98
104
  * MCP transport for server-side payment handling with raw JSON-RPC.
99
105
  *
@@ -29,11 +29,14 @@ export function from(transport) {
29
29
  /**
30
30
  * HTTP transport for server-side payment handling.
31
31
  *
32
- * - Reads credentials from the `Authorization` header
32
+ * - Reads credentials from the configured payment credential header
33
33
  * - Issues challenges via `WWW-Authenticate` header with 402 status
34
34
  * - Attaches receipts via `Payment-Receipt` header
35
35
  */
36
- export function http() {
36
+ export function http(options = {}) {
37
+ const credentialHeader = options.requiresAuth
38
+ ? Constants.Headers.paymentAuthorization
39
+ : Constants.Headers.authorization;
37
40
  return from({
38
41
  name: 'http',
39
42
  captureRequest(request) {
@@ -45,7 +48,7 @@ export function http() {
45
48
  };
46
49
  },
47
50
  getCredential(request) {
48
- const header = request.headers.get(Constants.Headers.authorization);
51
+ const header = request.headers.get(credentialHeader);
49
52
  if (!header)
50
53
  return null;
51
54
  const payment = Credential.extractPaymentScheme(header);
@@ -0,0 +1,3 @@
1
+ /** Validates the minimum entropy required for HMAC-bound payment challenges. */
2
+ export declare function assert(secretKey: string): void;
3
+ //# sourceMappingURL=SecretKey.d.ts.map
@@ -0,0 +1,9 @@
1
+ const minimumBytes = 32;
2
+ const generationCommand = 'openssl rand -base64 32';
3
+ /** Validates the minimum entropy required for HMAC-bound payment challenges. */
4
+ export function assert(secretKey) {
5
+ if (new TextEncoder().encode(secretKey).byteLength >= minimumBytes)
6
+ return;
7
+ throw new Error(`Secret key must be at least ${minimumBytes} bytes. Generate one with \`${generationCommand}\` and set MPP_SECRET_KEY or pass it to Mppx.create().`);
8
+ }
9
+ //# sourceMappingURL=SecretKey.js.map
@@ -1,2 +1,2 @@
1
- export declare function submitCredential(credential: string): Promise<void>;
1
+ export declare function submitCredential(credential: string, header?: string): Promise<void>;
2
2
  //# sourceMappingURL=serviceWorker.client.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { params } from './constants.js';
2
- export async function submitCredential(credential) {
2
+ export async function submitCredential(credential, header = 'Authorization') {
3
3
  const url = new URL(location.href);
4
4
  url.searchParams.set(params.serviceWorker, '');
5
5
  const registration = await navigator.serviceWorker.register(url.pathname + url.search);
@@ -19,7 +19,7 @@ export async function submitCredential(credential) {
19
19
  await new Promise((resolve) => {
20
20
  const channel = new MessageChannel();
21
21
  channel.port1.onmessage = () => resolve();
22
- serviceWorker.postMessage({ credential }, [channel.port2]);
22
+ serviceWorker.postMessage({ credential, header }, [channel.port2]);
23
23
  });
24
24
  location.reload();
25
25
  }
@@ -1,2 +1,2 @@
1
- export declare const serviceWorker = "(function(){let e=self,t;e.addEventListener(`activate`,t=>{t.waitUntil(e.clients.claim())}),e.addEventListener(`message`,e=>{if(!e.source)return;let n=e.data?.credential;typeof n!=`string`||!n.startsWith(`Payment `)||(t=n,e.ports[0]?.postMessage(`ack`))}),e.addEventListener(`fetch`,n=>{if(!t||n.request.mode!==`navigate`||new URL(n.request.url).origin!==e.location.origin)return;let r=new Headers(n.request.headers);r.set(`Authorization`,t),t=void 0,n.respondWith(fetch(n.request,{headers:r})),e.registration.unregister()})})();";
1
+ export declare const serviceWorker = "(function(){let e=self,t,n=`Authorization`;e.addEventListener(`activate`,t=>{t.waitUntil(e.clients.claim())}),e.addEventListener(`message`,e=>{if(!e.source)return;let r=e.data?.credential;typeof r!=`string`||!r.startsWith(`Payment `)||(t=r,typeof e.data?.header==`string`&&(n=e.data.header),e.ports[0]?.postMessage(`ack`))}),e.addEventListener(`fetch`,r=>{if(!t||r.request.mode!==`navigate`||new URL(r.request.url).origin!==e.location.origin)return;let i=new Headers(r.request.headers);i.set(n,t),t=void 0,r.respondWith(fetch(r.request,{headers:i})),e.registration.unregister()})})();";
2
2
  //# sourceMappingURL=serviceWorker.gen.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Generated — do not edit.
2
- export const serviceWorker = "(function(){let e=self,t;e.addEventListener(`activate`,t=>{t.waitUntil(e.clients.claim())}),e.addEventListener(`message`,e=>{if(!e.source)return;let n=e.data?.credential;typeof n!=`string`||!n.startsWith(`Payment `)||(t=n,e.ports[0]?.postMessage(`ack`))}),e.addEventListener(`fetch`,n=>{if(!t||n.request.mode!==`navigate`||new URL(n.request.url).origin!==e.location.origin)return;let r=new Headers(n.request.headers);r.set(`Authorization`,t),t=void 0,n.respondWith(fetch(n.request,{headers:r})),e.registration.unregister()})})();";
2
+ export const serviceWorker = "(function(){let e=self,t,n=`Authorization`;e.addEventListener(`activate`,t=>{t.waitUntil(e.clients.claim())}),e.addEventListener(`message`,e=>{if(!e.source)return;let r=e.data?.credential;typeof r!=`string`||!r.startsWith(`Payment `)||(t=r,typeof e.data?.header==`string`&&(n=e.data.header),e.ports[0]?.postMessage(`ack`))}),e.addEventListener(`fetch`,r=>{if(!t||r.request.mode!==`navigate`||new URL(r.request.url).origin!==e.location.origin)return;let i=new Headers(r.request.headers);i.set(n,t),t=void 0,r.respondWith(fetch(r.request,{headers:i})),e.registration.unregister()})})();";
3
3
  //# sourceMappingURL=serviceWorker.gen.js.map