mppx 0.8.19 → 0.9.1

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 (135) hide show
  1. package/CHANGELOG.md +24 -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/Errors.d.ts +10 -0
  10. package/dist/Errors.js +12 -0
  11. package/dist/Html.js +1 -1
  12. package/dist/Mcp.d.ts +9 -1
  13. package/dist/Mcp.js +18 -0
  14. package/dist/Method.d.ts +7 -0
  15. package/dist/cli/THIRD-PARTY-LICENSES.txt +1 -1
  16. package/dist/cli/cli.d.ts +2 -2
  17. package/dist/cli/cli.js +134 -134
  18. package/dist/cli/plugins/index.js +13 -13
  19. package/dist/cli/sessions/Manager.js +4 -6
  20. package/dist/cli/sessions/commands.d.ts +1 -1
  21. package/dist/cli/sessions/store.js +13 -19
  22. package/dist/cli/validate/index.d.ts +2 -2
  23. package/dist/client/Mppx.d.ts +39 -2
  24. package/dist/client/Mppx.js +102 -46
  25. package/dist/client/Transport.js +1 -1
  26. package/dist/client/internal/protocols/Mcp.js +2 -1
  27. package/dist/client/internal/protocols/Mpp.d.ts +1 -1
  28. package/dist/client/internal/protocols/Mpp.js +5 -3
  29. package/dist/client/internal/protocols/Protocol.d.ts +3 -1
  30. package/dist/client/internal/protocols/Shared.js +5 -1
  31. package/dist/integrations/x402/Evm.d.ts +29 -0
  32. package/dist/integrations/x402/Evm.js +150 -0
  33. package/dist/integrations/x402/Negotiator.d.ts +36 -0
  34. package/dist/integrations/x402/Negotiator.js +165 -0
  35. package/dist/mcp/client/McpClient.js +22 -10
  36. package/dist/mcp/internal/Credential.d.ts +9 -0
  37. package/dist/mcp/internal/Credential.js +16 -0
  38. package/dist/mcp/server/Transport.d.ts +2 -2
  39. package/dist/mcp/server/Transport.js +11 -6
  40. package/dist/middlewares/express.js +5 -12
  41. package/dist/middlewares/internal/express.d.ts +8 -0
  42. package/dist/middlewares/internal/express.js +24 -0
  43. package/dist/middlewares/internal/node.d.ts +10 -0
  44. package/dist/middlewares/internal/node.js +34 -0
  45. package/dist/proxy/Proxy.js +15 -9
  46. package/dist/server/Mppx.d.ts +8 -0
  47. package/dist/server/Mppx.js +92 -20
  48. package/dist/server/Transport.d.ts +8 -2
  49. package/dist/server/Transport.js +14 -18
  50. package/dist/server/internal/SecretKey.d.ts +3 -0
  51. package/dist/server/internal/SecretKey.js +9 -0
  52. package/dist/server/internal/html/serviceWorker.client.d.ts +1 -1
  53. package/dist/server/internal/html/serviceWorker.client.js +2 -2
  54. package/dist/server/internal/html/serviceWorker.gen.d.ts +1 -1
  55. package/dist/server/internal/html/serviceWorker.gen.js +1 -1
  56. package/dist/stripe/Methods.d.ts +6 -0
  57. package/dist/stripe/Methods.js +3 -1
  58. package/dist/stripe/client/Charge.d.ts +6 -0
  59. package/dist/stripe/client/Methods.d.ts +6 -0
  60. package/dist/stripe/internal/payment-intent.d.ts +7 -0
  61. package/dist/stripe/internal/payment-intent.js +6 -0
  62. package/dist/stripe/server/Charge.d.ts +6 -0
  63. package/dist/stripe/server/Charge.js +8 -3
  64. package/dist/stripe/server/Methods.js +62 -7
  65. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  66. package/dist/stripe/server/internal/html.gen.js +1 -1
  67. package/dist/tempo/Methods.d.ts +0 -10
  68. package/dist/tempo/Methods.js +3 -12
  69. package/dist/tempo/Tokens.d.ts +13 -0
  70. package/dist/tempo/Tokens.js +13 -0
  71. package/dist/tempo/client/Charge.d.ts +1 -4
  72. package/dist/tempo/client/Charge.js +20 -13
  73. package/dist/tempo/client/Methods.d.ts +0 -10
  74. package/dist/tempo/index.d.ts +1 -0
  75. package/dist/tempo/index.js +1 -0
  76. package/dist/tempo/internal/account.d.ts +15 -6
  77. package/dist/tempo/internal/account.js +17 -26
  78. package/dist/tempo/internal/defaults.d.ts +0 -11
  79. package/dist/tempo/internal/defaults.js +0 -11
  80. package/dist/tempo/internal/fee-payer.d.ts +22 -4
  81. package/dist/tempo/internal/fee-payer.js +12 -31
  82. package/dist/tempo/internal/fee-token.d.ts +2 -0
  83. package/dist/tempo/internal/fee-token.js +7 -0
  84. package/dist/tempo/internal/remote-fee-payer.d.ts +12 -0
  85. package/dist/tempo/internal/remote-fee-payer.js +17 -0
  86. package/dist/tempo/legacy/client/Session.d.ts +0 -7
  87. package/dist/tempo/server/Charge.d.ts +0 -3
  88. package/dist/tempo/server/Charge.js +26 -68
  89. package/dist/tempo/server/Methods.d.ts +4 -18
  90. package/dist/tempo/server/Methods.js +4 -8
  91. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  92. package/dist/tempo/server/internal/html.gen.js +1 -1
  93. package/dist/tempo/session/client/ChannelOps.d.ts +0 -14
  94. package/dist/tempo/session/client/ChannelOps.js +0 -22
  95. package/dist/tempo/session/client/ChannelStore.d.ts +5 -0
  96. package/dist/tempo/session/client/ChannelStore.js +2 -2
  97. package/dist/tempo/session/client/CredentialState.d.ts +2 -12
  98. package/dist/tempo/session/client/CredentialState.js +75 -73
  99. package/dist/tempo/session/client/Session.d.ts +2 -9
  100. package/dist/tempo/session/client/Session.js +71 -176
  101. package/dist/tempo/session/client/SessionManager.js +7 -27
  102. package/dist/tempo/session/precompile/Chain.d.ts +0 -4
  103. package/dist/tempo/session/precompile/Chain.js +1 -29
  104. package/dist/tempo/session/precompile/Protocol.d.ts +0 -13
  105. package/dist/tempo/session/precompile/Voucher.d.ts +1 -3
  106. package/dist/tempo/session/precompile/Voucher.js +12 -6
  107. package/dist/tempo/session/server/ChannelStore.d.ts +0 -6
  108. package/dist/tempo/session/server/ChannelStore.js +4 -11
  109. package/dist/tempo/session/server/CredentialVerification.d.ts +7 -9
  110. package/dist/tempo/session/server/CredentialVerification.js +118 -184
  111. package/dist/tempo/session/server/RequestState.d.ts +0 -17
  112. package/dist/tempo/session/server/RequestState.js +15 -59
  113. package/dist/tempo/session/server/Session.d.ts +0 -9
  114. package/dist/tempo/session/server/Session.js +13 -39
  115. package/dist/tempo/session/server/Settlement.d.ts +0 -7
  116. package/dist/tempo/session/server/Settlement.js +16 -50
  117. package/dist/viem/Client.d.ts +7 -2
  118. package/dist/viem/Client.js +17 -5
  119. package/dist/x402/Types.d.ts +2 -0
  120. package/dist/x402/Types.js +2 -0
  121. package/dist/x402/mcp.d.ts +20 -0
  122. package/dist/x402/mcp.js +154 -0
  123. package/dist/x402/middlewares/express.d.ts +23 -0
  124. package/dist/x402/middlewares/express.js +49 -0
  125. package/dist/x402/middlewares/hono.d.ts +13 -0
  126. package/dist/x402/middlewares/hono.js +36 -0
  127. package/dist/x402/middlewares/next.d.ts +17 -0
  128. package/dist/x402/middlewares/next.js +58 -0
  129. package/package.json +41 -1
  130. package/dist/tempo/internal/machine-token-charge.d.ts +0 -51
  131. package/dist/tempo/internal/machine-token-charge.js +0 -113
  132. package/dist/tempo/internal/machine-token-session.d.ts +0 -88
  133. package/dist/tempo/internal/machine-token-session.js +0 -173
  134. package/dist/tempo/internal/signature-envelope.d.ts +0 -14
  135. 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
@@ -5,6 +5,7 @@ import * as AcceptPayment from '../../internal/AcceptPayment.js';
5
5
  import * as core_Mcp from '../../Mcp.js';
6
6
  import * as z from '../../zod.js';
7
7
  const MPPX_MCP_CLIENT_WRAPPER = Symbol.for('mppx.mcp.client.wrapper');
8
+ const maxPaymentAttempts = 3;
8
9
  /**
9
10
  * Adds automatic payment handling to an MCP SDK client.
10
11
  *
@@ -80,7 +81,8 @@ export function isPaymentRequiredError(error) {
80
81
  return false;
81
82
  if (!('code' in error) || !('message' in error))
82
83
  return false;
83
- if (error.code !== core_Mcp.paymentRequiredCode)
84
+ const code = error.code;
85
+ if (code !== core_Mcp.paymentRequiredCode && code !== core_Mcp.paymentVerificationFailedCode)
84
86
  return false;
85
87
  return isPaymentRequiredData(error.data);
86
88
  }
@@ -98,7 +100,7 @@ async function createCredential(challenge, config) {
98
100
  function createPaymentAwareCallTool(callTool, config) {
99
101
  const methods = config.methods.flat();
100
102
  const paymentPreferences = AcceptPayment.resolve(methods, config.paymentPreferences);
101
- const retryWithPayment = async (params, call, paymentRequired, cause) => {
103
+ const retryWithPayment = async (params, call, paymentRequired, cause, attemptsRemaining = maxPaymentAttempts) => {
102
104
  const challenges = paymentRequired.challenges;
103
105
  const candidates = AcceptPayment.selectChallengeCandidates(challenges, methods, paymentPreferences.entries);
104
106
  const orderedCandidates = config.orderChallenges
@@ -122,14 +124,24 @@ function createPaymentAwareCallTool(callTool, config) {
122
124
  methods,
123
125
  });
124
126
  const parsed = Credential.deserialize(credential);
125
- const retryResult = await callTool({
126
- ...params,
127
- _meta: {
128
- ...params._meta,
129
- [core_Mcp.credentialMetaKey]: parsed,
130
- },
131
- }, call.resultSchema, call.requestOptions);
132
- return withReceipt(retryResult);
127
+ try {
128
+ const retryResult = await callTool({
129
+ ...params,
130
+ _meta: {
131
+ ...params._meta,
132
+ [core_Mcp.credentialMetaKey]: parsed,
133
+ },
134
+ }, call.resultSchema, call.requestOptions);
135
+ const nextPaymentRequired = getPaymentRequiredMeta(retryResult);
136
+ if (nextPaymentRequired && attemptsRemaining > 1)
137
+ return retryWithPayment(params, call, nextPaymentRequired, retryResult, attemptsRemaining - 1);
138
+ return withReceipt(retryResult);
139
+ }
140
+ catch (error) {
141
+ if (!isPaymentRequiredError(error) || attemptsRemaining <= 1)
142
+ throw error;
143
+ return retryWithPayment(params, call, error.data, error, attemptsRemaining - 1);
144
+ }
133
145
  };
134
146
  return async (params, call) => {
135
147
  try {
@@ -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,5 @@
1
1
  import type { CallToolResult, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import type * as Credential from '../../Credential.js';
2
+ import * as Credential from '../../Credential.js';
3
3
  import * as core_Mcp from '../../Mcp.js';
4
4
  import * as Transport from '../../server/Transport.js';
5
5
  /**
@@ -18,7 +18,7 @@ export type McpSdk = Transport.Transport<Extra, McpError, CallToolResult>;
18
18
  * MCP SDK transport for server-side payment handling with `@modelcontextprotocol/sdk`.
19
19
  *
20
20
  * - Reads credentials from `_meta["org.paymentauth/credential"]`
21
- * - Issues challenges as `McpError` with code `-32042` and challenge in `error.data`
21
+ * - Maps payment errors to their specification-defined JSON-RPC codes
22
22
  * - Attaches receipts via `_meta["org.paymentauth/receipt"]` on tool results
23
23
  *
24
24
  * @example
@@ -1,10 +1,12 @@
1
+ import * as Credential from '../../Credential.js';
1
2
  import * as core_Mcp from '../../Mcp.js';
2
3
  import * as Transport from '../../server/Transport.js';
4
+ import * as McpCredential from '../internal/Credential.js';
3
5
  /**
4
6
  * MCP SDK transport for server-side payment handling with `@modelcontextprotocol/sdk`.
5
7
  *
6
8
  * - Reads credentials from `_meta["org.paymentauth/credential"]`
7
- * - Issues challenges as `McpError` with code `-32042` and challenge in `error.data`
9
+ * - Maps payment errors to their specification-defined JSON-RPC codes
8
10
  * - Attaches receipts via `_meta["org.paymentauth/receipt"]` on tool results
9
11
  *
10
12
  * @example
@@ -40,10 +42,13 @@ export function mcpSdk() {
40
42
  };
41
43
  },
42
44
  getCredential(extra) {
43
- const credential = extra._meta?.[core_Mcp.credentialMetaKey];
44
- if (!credential)
45
+ const value = extra._meta?.[core_Mcp.credentialMetaKey];
46
+ if (value === undefined)
45
47
  return null;
46
- return credential;
48
+ const parsed = McpCredential.parse(value);
49
+ if (!parsed)
50
+ throw new Credential.InvalidCredentialEncodingError();
51
+ return parsed.value;
47
52
  },
48
53
  async respondChallenge({ challenge, error }) {
49
54
  if (!McpErrorClass) {
@@ -57,8 +62,8 @@ export function mcpSdk() {
57
62
  throw err;
58
63
  }
59
64
  }
60
- return new McpErrorClass(core_Mcp.paymentRequiredCode, error?.message ?? 'Payment Required', {
61
- httpStatus: 402,
65
+ return new McpErrorClass(core_Mcp.errorCode(error), error?.message ?? 'Payment Required', {
66
+ httpStatus: error?.status ?? 402,
62
67
  challenges: [challenge],
63
68
  ...(error && { problem: error.toProblemDetails(challenge.id) }),
64
69
  });
@@ -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)
@@ -99,7 +99,13 @@ export type PaymentSuccessContext<method extends Method.Method = Method.Method,
99
99
  input?: Transport.InputOf<transport> | undefined;
100
100
  method: ServerMethodDescriptor<method>;
101
101
  receipt: Receipt.Receipt;
102
+ /** Canonical request represented by the challenge. */
102
103
  request: z.output<method['schema']['request']>;
104
+ /**
105
+ * Resolved method input before request-schema output transforms. Absent during
106
+ * standalone credential verification when no route options are supplied.
107
+ */
108
+ requestInput?: z.input<method['schema']['request']> | undefined;
103
109
  }>;
104
110
  /** Options for standalone credential verification. */
105
111
  export type VerifyCredentialOptions = {
@@ -315,6 +321,8 @@ export declare namespace create {
315
321
  attestation?: transport extends Transport.Http ? Attestation.VerifierMap | undefined : never;
316
322
  /** Array of configured methods. @example [tempo()] */
317
323
  methods: methods;
324
+ /** Uses `Payment-Authorization` for Payment credentials so `Authorization` remains available for application authentication. */
325
+ requiresAuth?: transport extends Transport.Http ? boolean | undefined : never;
318
326
  /** 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
327
  realm?: string | undefined;
320
328
  /** Secret key for HMAC-bound challenge IDs for stateless verification. Must be at least 32 bytes. Auto-detected from `MPP_SECRET_KEY` environment variable. */