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
@@ -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
@@ -73,6 +73,7 @@ export function create(config) {
73
73
  input: ctx.input,
74
74
  receipt: ctx.receipt,
75
75
  request: ctx.request,
76
+ ...(ctx.requestInput !== undefined && { requestInput: ctx.requestInput }),
76
77
  });
77
78
  }
78
79
  }));
@@ -83,6 +84,7 @@ export function create(config) {
83
84
  const fn = createMethodFn({
84
85
  ...(verifyAttestation && { attest: verifyAttestation }),
85
86
  authorize: mi.authorize,
87
+ credentialHeader,
86
88
  defaults: mi.defaults,
87
89
  method: mi,
88
90
  realm,
@@ -143,6 +145,7 @@ export function create(config) {
143
145
  if (!challengeHandlers[mi.name])
144
146
  challengeHandlers[mi.name] = {};
145
147
  challengeHandlers[mi.name][mi.alias ?? mi.intent] = createChallengeFn({
148
+ credentialHeader,
146
149
  defaults: mi.defaults,
147
150
  method: mi,
148
151
  realm,
@@ -235,6 +238,7 @@ export function create(config) {
235
238
  credential: parsedCredential,
236
239
  defaults: mi.defaults,
237
240
  expires: credential.challenge.expires,
241
+ header: credential.challenge.header,
238
242
  meta: expectedMeta,
239
243
  method: mi,
240
244
  realm: expectedRealm,
@@ -277,6 +281,7 @@ export function create(config) {
277
281
  parsedCredential,
278
282
  parsedRequest,
279
283
  request,
284
+ requestInput: shouldValidateRoute ? request : undefined,
280
285
  };
281
286
  }
282
287
  async function validateCredentialFn(input, options) {
@@ -290,7 +295,7 @@ export function create(config) {
290
295
  // broadcastCredential: single-call end-to-end validation and broadcast
291
296
  async function broadcastCredentialFn(input, options) {
292
297
  const prepared = await prepareStandaloneCredential(input, options, { emitFailures: true });
293
- const { method: mi, parsedCredential, parsedRequest, request, envelope } = prepared;
298
+ const { method: mi, parsedCredential, parsedRequest, request, requestInput, envelope, } = prepared;
294
299
  const emitStandalonePaymentFailed = async (parameters) => {
295
300
  await serverEvents.emit('payment.failed', createPaymentFailedContext({
296
301
  capturedRequest: options?.capturedRequest,
@@ -310,7 +315,7 @@ export function create(config) {
310
315
  receipt = await broadcast({ credential: parsedCredential, envelope, request });
311
316
  }
312
317
  catch (e) {
313
- const error = e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError();
318
+ const error = e instanceof Errors.PaymentError ? e : new Errors.InternalPaymentError();
314
319
  await emitStandalonePaymentFailed({
315
320
  challenge: prepared.credential.challenge,
316
321
  credential: parsedCredential,
@@ -328,6 +333,7 @@ export function create(config) {
328
333
  method: mi,
329
334
  receipt,
330
335
  request: parsedRequest,
336
+ ...(requestInput !== undefined && { requestInput }),
331
337
  }));
332
338
  return receipt;
333
339
  }
@@ -375,15 +381,9 @@ export function create(config) {
375
381
  ...handlers,
376
382
  };
377
383
  }
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
384
  // biome-ignore lint/correctness/noUnusedVariables: _
385
385
  function createMethodFn(parameters) {
386
- const { attest, authorize, defaults, events, method, preflight, realm, respond, secretKey, broadcast, stableBinding, transport, validate, verify, } = parameters;
386
+ const { attest, authorize, credentialHeader, defaults, events, method, preflight, realm, respond, secretKey, broadcast, stableBinding, transport, validate, verify, } = parameters;
387
387
  return (options) => {
388
388
  const { description, meta, scope, ...rest } = options;
389
389
  const staticMeta = Scope.merge({ meta, scope });
@@ -491,6 +491,7 @@ function createMethodFn(parameters) {
491
491
  defaults,
492
492
  description,
493
493
  expires,
494
+ header: credentialHeader,
494
495
  meta: effectiveMeta,
495
496
  method,
496
497
  realm,
@@ -505,6 +506,7 @@ function createMethodFn(parameters) {
505
506
  defaults: defaults ?? {},
506
507
  description,
507
508
  expires,
509
+ header: credentialHeader,
508
510
  meta: effectiveMeta,
509
511
  method,
510
512
  realm,
@@ -549,7 +551,11 @@ function createMethodFn(parameters) {
549
551
  // Credential was provided but malformed
550
552
  if (credentialError) {
551
553
  const reason = getSafeCredentialReason(credentialError);
552
- const error = new Errors.MalformedCredentialError(reason ? { reason } : {});
554
+ const error = credentialError instanceof Errors.PaymentError
555
+ ? credentialError
556
+ : reason
557
+ ? new Errors.MalformedCredentialError({ reason })
558
+ : new Errors.InternalPaymentError();
553
559
  await emitPaymentFailed({
554
560
  challenge,
555
561
  credential: null,
@@ -611,6 +617,7 @@ function createMethodFn(parameters) {
611
617
  method,
612
618
  receipt: authorized.receipt,
613
619
  request: parsedRequest,
620
+ requestInput: request,
614
621
  }));
615
622
  return success(authorized.receipt, {
616
623
  managementResponse: authorized.response,
@@ -620,7 +627,7 @@ function createMethodFn(parameters) {
620
627
  catch (e) {
621
628
  if (!(e instanceof Errors.PaymentError))
622
629
  console.error('mppx: internal authorization error', e);
623
- const error = e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError();
630
+ const error = e instanceof Errors.PaymentError ? e : new Errors.InternalPaymentError();
624
631
  await emitPaymentFailed({
625
632
  challenge,
626
633
  credential: null,
@@ -793,7 +800,7 @@ function createMethodFn(parameters) {
793
800
  catch (e) {
794
801
  if (!(e instanceof Errors.PaymentError))
795
802
  console.error('mppx: internal verification error', e);
796
- const error = e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError();
803
+ const error = e instanceof Errors.PaymentError ? e : new Errors.InternalPaymentError();
797
804
  await emitPaymentFailed({
798
805
  challenge,
799
806
  credential: parsedCredential,
@@ -833,6 +840,7 @@ function createMethodFn(parameters) {
833
840
  method,
834
841
  receipt: receiptData,
835
842
  request: parsedRequest,
843
+ requestInput: request,
836
844
  }));
837
845
  return success(receiptData, {
838
846
  challengeId: credential.challenge.id,
@@ -850,7 +858,7 @@ function createMethodFn(parameters) {
850
858
  * but returns a Challenge object directly instead of a request handler.
851
859
  */
852
860
  function createChallengeFn(parameters) {
853
- const { defaults, method, realm, secretKey } = parameters;
861
+ const { credentialHeader, defaults, method, realm, secretKey } = parameters;
854
862
  return async (options) => {
855
863
  const { description, meta, scope, ...rest } = options;
856
864
  const effectiveMeta = Scope.merge({ meta, scope });
@@ -861,6 +869,7 @@ function createChallengeFn(parameters) {
861
869
  defaults,
862
870
  description,
863
871
  expires,
872
+ header: credentialHeader,
864
873
  meta: effectiveMeta,
865
874
  method,
866
875
  realm,
@@ -991,6 +1000,9 @@ function createPaymentSuccessContext(parameters) {
991
1000
  method: snapshotMethod(parameters.method),
992
1001
  receipt: snapshotValue(parameters.receipt),
993
1002
  request: snapshotValue(parameters.request),
1003
+ ...(parameters.requestInput !== undefined && {
1004
+ requestInput: snapshotValue(parameters.requestInput),
1005
+ }),
994
1006
  });
995
1007
  }
996
1008
  function snapshotMethod(method) {
@@ -1035,8 +1047,65 @@ function snapshotValue(value) {
1035
1047
  return freezeSnapshot(structuredClone(value));
1036
1048
  }
1037
1049
  catch {
1038
- return freezeSnapshot(value);
1050
+ return freezeSnapshot(cloneSnapshotFallback(value));
1051
+ }
1052
+ }
1053
+ /**
1054
+ * Copies object containers when `structuredClone` rejects callable values.
1055
+ * Functions remain callable references, while their containing request data is
1056
+ * detached so snapshot freezing and nested writes cannot affect hook-owned
1057
+ * objects.
1058
+ */
1059
+ function cloneSnapshotFallback(value, seen = new WeakMap()) {
1060
+ if (!value || typeof value !== 'object')
1061
+ return value;
1062
+ const existing = seen.get(value);
1063
+ if (existing)
1064
+ return existing;
1065
+ if (value instanceof Date)
1066
+ return new Date(value);
1067
+ if (value instanceof RegExp)
1068
+ return new RegExp(value.source, value.flags);
1069
+ if (value instanceof URL)
1070
+ return new URL(value);
1071
+ if (value instanceof Headers)
1072
+ return new Headers(value);
1073
+ if (value instanceof ArrayBuffer)
1074
+ return value.slice(0);
1075
+ if (ArrayBuffer.isView(value)) {
1076
+ const buffer = new Uint8Array(value.buffer, value.byteOffset, value.byteLength).slice().buffer;
1077
+ if (value instanceof DataView)
1078
+ return new DataView(buffer);
1079
+ const Constructor = value.constructor;
1080
+ return new Constructor(buffer);
1081
+ }
1082
+ if (value instanceof Map) {
1083
+ const snapshot = new Map();
1084
+ seen.set(value, snapshot);
1085
+ for (const [key, entry] of value)
1086
+ snapshot.set(cloneSnapshotFallback(key, seen), cloneSnapshotFallback(entry, seen));
1087
+ return snapshot;
1088
+ }
1089
+ if (value instanceof Set) {
1090
+ const snapshot = new Set();
1091
+ seen.set(value, snapshot);
1092
+ for (const entry of value)
1093
+ snapshot.add(cloneSnapshotFallback(entry, seen));
1094
+ return snapshot;
1095
+ }
1096
+ const snapshot = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value));
1097
+ seen.set(value, snapshot);
1098
+ for (const key of Reflect.ownKeys(value)) {
1099
+ if (Array.isArray(value) && key === 'length')
1100
+ continue;
1101
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
1102
+ if (!descriptor)
1103
+ continue;
1104
+ if ('value' in descriptor)
1105
+ descriptor.value = cloneSnapshotFallback(descriptor.value, seen);
1106
+ Object.defineProperty(snapshot, key, descriptor);
1039
1107
  }
1108
+ return snapshot;
1040
1109
  }
1041
1110
  function snapshotInputProperty(input) {
1042
1111
  if (input === undefined)
@@ -1163,6 +1232,7 @@ async function resolveRouteChallenge(parameters) {
1163
1232
  const challenge = Challenge.fromMethod(parameters.method, {
1164
1233
  description: parameters.description,
1165
1234
  expires: parameters.expires,
1235
+ header: parameters.header,
1166
1236
  meta: parameters.meta,
1167
1237
  realm: effectiveRealm,
1168
1238
  request: request,
@@ -1178,6 +1248,7 @@ function createFallbackChallenge(parameters) {
1178
1248
  return Challenge.fromMethod(parameters.method, {
1179
1249
  description: parameters.description,
1180
1250
  expires: parameters.expires,
1251
+ header: parameters.header,
1181
1252
  meta: parameters.meta,
1182
1253
  realm: parameters.realm ??
1183
1254
  (parameters.capturedRequest
@@ -1405,8 +1476,9 @@ function composeHandlers(handlers, composeOptions, selectOffers) {
1405
1476
  // Try to extract a Payment credential to decide whether to dispatch or challenge.
1406
1477
  // Only gate on the Payment scheme — other auth schemes (Bearer, Basic, etc.)
1407
1478
  // 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;
1479
+ const paymentHeader = Array.from(input.headers.values())
1480
+ .map((value) => Credential.extractPaymentScheme(value))
1481
+ .find((value) => value !== null);
1410
1482
  if (paymentHeader) {
1411
1483
  // Parse the credential to find method+intent for dispatch.
1412
1484
  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
  *
@@ -1,8 +1,8 @@
1
1
  import * as Challenge from '../Challenge.js';
2
2
  import * as Constants from '../Constants.js';
3
3
  import * as Credential from '../Credential.js';
4
- import * as Errors from '../Errors.js';
5
4
  import * as core_Mcp from '../Mcp.js';
5
+ import * as McpCredential from '../mcp/internal/Credential.js';
6
6
  import * as Receipt from '../Receipt.js';
7
7
  import * as Html from './internal/html/config.js';
8
8
  import { serviceWorker } from './internal/html/serviceWorker.gen.js';
@@ -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);
@@ -144,17 +147,20 @@ export function mcp() {
144
147
  },
145
148
  getCredential(request) {
146
149
  const meta = request.params?._meta;
147
- const credential = meta?.[core_Mcp.credentialMetaKey];
148
- if (!credential)
150
+ const value = meta?.[core_Mcp.credentialMetaKey];
151
+ if (value === undefined)
149
152
  return null;
150
- return credential;
153
+ const parsed = McpCredential.parse(value);
154
+ if (!parsed)
155
+ throw new Credential.InvalidCredentialEncodingError();
156
+ return parsed.value;
151
157
  },
152
158
  respondChallenge({ challenge, input, error }) {
153
159
  return {
154
160
  jsonrpc: '2.0',
155
161
  id: input.id,
156
162
  error: {
157
- code: mcpErrorCode(error),
163
+ code: core_Mcp.errorCode(error),
158
164
  message: error?.message ?? 'Payment Required',
159
165
  data: {
160
166
  httpStatus: error?.status ?? 402,
@@ -184,16 +190,6 @@ export function mcp() {
184
190
  },
185
191
  });
186
192
  }
187
- /** @internal */
188
- function mcpErrorCode(error) {
189
- if (!error)
190
- return core_Mcp.paymentRequiredCode;
191
- if (error instanceof Errors.MalformedCredentialError)
192
- return -32602;
193
- if (error instanceof Errors.PaymentRequiredError)
194
- return core_Mcp.paymentRequiredCode;
195
- return core_Mcp.paymentVerificationFailedCode;
196
- }
197
193
  export function safeUrl(url) {
198
194
  try {
199
195
  if (url instanceof URL)
@@ -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
@@ -22,6 +22,9 @@ export declare const charge: {
22
22
  externalId: z.ZodMiniOptional<z.ZodMiniString<string>>;
23
23
  metadata: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
24
24
  networkId: z.ZodMiniString<string>;
25
+ paymentIntentOptions: z.ZodMiniOptional<z.ZodMiniObject<{
26
+ metadata: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
27
+ }, z.core.$strip>>;
25
28
  paymentMethodTypes: z.ZodMiniArray<z.ZodMiniString<string>>;
26
29
  recipient: z.ZodMiniOptional<z.ZodMiniString<string>>;
27
30
  }, z.core.$strip>, z.ZodMiniTransform<{
@@ -43,6 +46,9 @@ export declare const charge: {
43
46
  externalId?: string | undefined;
44
47
  metadata?: Record<string, string> | undefined;
45
48
  networkId: string;
49
+ paymentIntentOptions?: {
50
+ metadata: Record<string, string>;
51
+ } | undefined;
46
52
  paymentMethodTypes: string[];
47
53
  recipient?: string | undefined;
48
54
  }>>;
@@ -1,6 +1,7 @@
1
1
  import { parseUnits } from 'viem';
2
2
  import * as Method from '../Method.js';
3
3
  import * as z from '../zod.js';
4
+ import * as PaymentIntent from './internal/payment-intent.js';
4
5
  /**
5
6
  * Stripe charge intent for one-time payments via Shared Payment Tokens (SPTs).
6
7
  *
@@ -24,9 +25,10 @@ export const charge = Method.from({
24
25
  externalId: z.optional(z.string()),
25
26
  metadata: z.optional(z.record(z.string(), z.string())),
26
27
  networkId: z.string(),
28
+ paymentIntentOptions: z.optional(PaymentIntent.Schema),
27
29
  paymentMethodTypes: z.array(z.string()).check(z.minLength(1)),
28
30
  recipient: z.optional(z.string()),
29
- }), z.transform(({ amount, decimals, metadata, networkId, paymentMethodTypes, ...rest }) => ({
31
+ }), z.transform(({ amount, decimals, metadata, networkId, paymentIntentOptions: _, paymentMethodTypes, ...rest }) => ({
30
32
  ...rest,
31
33
  amount: parseUnits(amount, decimals).toString(),
32
34
  methodDetails: {
@@ -55,6 +55,9 @@ export declare function charge(parameters: charge.Parameters): Method.Client<{
55
55
  externalId: z.ZodMiniOptional<z.ZodMiniString<string>>;
56
56
  metadata: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
57
57
  networkId: z.ZodMiniString<string>;
58
+ paymentIntentOptions: z.ZodMiniOptional<z.ZodMiniObject<{
59
+ metadata: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
60
+ }, z.core.$strip>>;
58
61
  paymentMethodTypes: z.ZodMiniArray<z.ZodMiniString<string>>;
59
62
  recipient: z.ZodMiniOptional<z.ZodMiniString<string>>;
60
63
  }, z.core.$strip>, z.ZodMiniTransform<{
@@ -76,6 +79,9 @@ export declare function charge(parameters: charge.Parameters): Method.Client<{
76
79
  externalId?: string | undefined;
77
80
  metadata?: Record<string, string> | undefined;
78
81
  networkId: string;
82
+ paymentIntentOptions?: {
83
+ metadata: Record<string, string>;
84
+ } | undefined;
79
85
  paymentMethodTypes: string[];
80
86
  recipient?: string | undefined;
81
87
  }>>;
@@ -42,6 +42,9 @@ export declare function stripe(parameters: stripe.Parameters): readonly [import(
42
42
  externalId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
43
43
  metadata: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniString<string>>>;
44
44
  networkId: import("zod/mini").ZodMiniString<string>;
45
+ paymentIntentOptions: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
46
+ metadata: import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniString<string>>;
47
+ }, import("zod/v4/core").$strip>>;
45
48
  paymentMethodTypes: import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniString<string>>;
46
49
  recipient: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
47
50
  }, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniTransform<{
@@ -63,6 +66,9 @@ export declare function stripe(parameters: stripe.Parameters): readonly [import(
63
66
  externalId?: string | undefined;
64
67
  metadata?: Record<string, string> | undefined;
65
68
  networkId: string;
69
+ paymentIntentOptions?: {
70
+ metadata: Record<string, string>;
71
+ } | undefined;
66
72
  paymentMethodTypes: string[];
67
73
  recipient?: string | undefined;
68
74
  }>>;
@@ -0,0 +1,7 @@
1
+ import * as z from '../../zod.js';
2
+ /** Stripe PaymentIntent options accepted only by server-side method input. */
3
+ export declare const Schema: z.ZodMiniObject<{
4
+ metadata: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
5
+ }, z.core.$strip>;
6
+ export type Options = z.infer<typeof Schema>;
7
+ //# sourceMappingURL=payment-intent.d.ts.map
@@ -0,0 +1,6 @@
1
+ import * as z from '../../zod.js';
2
+ /** Stripe PaymentIntent options accepted only by server-side method input. */
3
+ export const Schema = z.object({
4
+ metadata: z.record(z.string(), z.string()),
5
+ });
6
+ //# sourceMappingURL=payment-intent.js.map
@@ -49,6 +49,9 @@ export declare function charge<const parameters extends charge.Parameters>(param
49
49
  externalId: z.ZodMiniOptional<z.ZodMiniString<string>>;
50
50
  metadata: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
51
51
  networkId: z.ZodMiniString<string>;
52
+ paymentIntentOptions: z.ZodMiniOptional<z.ZodMiniObject<{
53
+ metadata: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
54
+ }, z.core.$strip>>;
52
55
  paymentMethodTypes: z.ZodMiniArray<z.ZodMiniString<string>>;
53
56
  recipient: z.ZodMiniOptional<z.ZodMiniString<string>>;
54
57
  }, z.core.$strip>, z.ZodMiniTransform<{
@@ -70,6 +73,9 @@ export declare function charge<const parameters extends charge.Parameters>(param
70
73
  externalId?: string | undefined;
71
74
  metadata?: Record<string, string> | undefined;
72
75
  networkId: string;
76
+ paymentIntentOptions?: {
77
+ metadata: Record<string, string>;
78
+ } | undefined;
73
79
  paymentMethodTypes: string[];
74
80
  recipient?: string | undefined;
75
81
  }>>;
@@ -117,13 +117,14 @@ export function charge(parameters) {
117
117
  onPaymentSuccess,
118
118
  async verify({ credential, envelope, request }) {
119
119
  const { challenge } = credential;
120
+ const { paymentIntentOptions, ...methodRequest } = request;
120
121
  const resolvedRequest = (() => {
121
- const parsed = Methods.charge.schema.request.safeParse(request);
122
+ const parsed = Methods.charge.schema.request.safeParse(methodRequest);
122
123
  if (parsed.success)
123
124
  return parsed.data;
124
125
  // verifyCredential() passes the HMAC-bound challenge request, which is
125
126
  // already in canonical output form and should not be transformed again.
126
- return request;
127
+ return methodRequest;
127
128
  })();
128
129
  Expires.assert(challenge.expires, challenge.id);
129
130
  const parsed = Methods.charge.schema.credential.payload.safeParse(credential.payload);
@@ -138,7 +139,11 @@ export function charge(parameters) {
138
139
  });
139
140
  }
140
141
  const userMetadata = resolvedRequest.methodDetails?.metadata;
141
- const resolvedMetadata = { ...buildAnalytics({ credential }), ...userMetadata };
142
+ const resolvedMetadata = {
143
+ ...buildAnalytics({ credential }),
144
+ ...userMetadata,
145
+ ...paymentIntentOptions?.metadata,
146
+ };
142
147
  const settlement = validateConnectSettlement({
143
148
  amount: resolvedRequest.amount,
144
149
  settlement: typeof connect === 'function'