@agent-score/commerce 1.8.1 → 2.0.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 (90) hide show
  1. package/README.md +73 -9
  2. package/dist/{_response-9yp6Fit2.d.mts → _response-BFYN3b6i.d.mts} +17 -19
  3. package/dist/{_response-CC6jNb8q.d.ts → _response-_iPD5AIj.d.ts} +17 -19
  4. package/dist/challenge/index.d.mts +106 -198
  5. package/dist/challenge/index.d.ts +106 -198
  6. package/dist/challenge/index.js +238 -111
  7. package/dist/challenge/index.js.map +1 -1
  8. package/dist/challenge/index.mjs +238 -111
  9. package/dist/challenge/index.mjs.map +1 -1
  10. package/dist/checkout-BoFwnVsj.d.ts +931 -0
  11. package/dist/checkout-DRbQ0Fsh.d.mts +931 -0
  12. package/dist/core.d.mts +2 -2
  13. package/dist/core.d.ts +2 -2
  14. package/dist/core.js +1 -1
  15. package/dist/core.js.map +1 -1
  16. package/dist/core.mjs +1 -1
  17. package/dist/core.mjs.map +1 -1
  18. package/dist/discovery/index.d.mts +453 -51
  19. package/dist/discovery/index.d.ts +453 -51
  20. package/dist/discovery/index.js +1092 -58
  21. package/dist/discovery/index.js.map +1 -1
  22. package/dist/discovery/index.mjs +1060 -57
  23. package/dist/discovery/index.mjs.map +1 -1
  24. package/dist/identity/express.d.mts +3 -3
  25. package/dist/identity/express.d.ts +3 -3
  26. package/dist/identity/express.js +30 -19
  27. package/dist/identity/express.js.map +1 -1
  28. package/dist/identity/express.mjs +30 -19
  29. package/dist/identity/express.mjs.map +1 -1
  30. package/dist/identity/fastify.d.mts +4 -4
  31. package/dist/identity/fastify.d.ts +4 -4
  32. package/dist/identity/fastify.js +30 -19
  33. package/dist/identity/fastify.js.map +1 -1
  34. package/dist/identity/fastify.mjs +30 -19
  35. package/dist/identity/fastify.mjs.map +1 -1
  36. package/dist/identity/hono.d.mts +3 -3
  37. package/dist/identity/hono.d.ts +3 -3
  38. package/dist/identity/hono.js +30 -19
  39. package/dist/identity/hono.js.map +1 -1
  40. package/dist/identity/hono.mjs +30 -19
  41. package/dist/identity/hono.mjs.map +1 -1
  42. package/dist/identity/nextjs.d.mts +6 -7
  43. package/dist/identity/nextjs.d.ts +6 -7
  44. package/dist/identity/nextjs.js +30 -19
  45. package/dist/identity/nextjs.js.map +1 -1
  46. package/dist/identity/nextjs.mjs +30 -19
  47. package/dist/identity/nextjs.mjs.map +1 -1
  48. package/dist/identity/policy.d.mts +41 -4
  49. package/dist/identity/policy.d.ts +41 -4
  50. package/dist/identity/policy.js +3662 -18
  51. package/dist/identity/policy.js.map +1 -1
  52. package/dist/identity/policy.mjs +3648 -3
  53. package/dist/identity/policy.mjs.map +1 -1
  54. package/dist/identity/web.d.mts +3 -3
  55. package/dist/identity/web.d.ts +3 -3
  56. package/dist/identity/web.js +30 -19
  57. package/dist/identity/web.js.map +1 -1
  58. package/dist/identity/web.mjs +30 -19
  59. package/dist/identity/web.mjs.map +1 -1
  60. package/dist/index.d.mts +72 -329
  61. package/dist/index.d.ts +72 -329
  62. package/dist/index.js +3651 -373
  63. package/dist/index.js.map +1 -1
  64. package/dist/index.mjs +3628 -361
  65. package/dist/index.mjs.map +1 -1
  66. package/dist/payment/index.d.mts +256 -265
  67. package/dist/payment/index.d.ts +256 -265
  68. package/dist/payment/index.js +586 -149
  69. package/dist/payment/index.js.map +1 -1
  70. package/dist/payment/index.mjs +573 -148
  71. package/dist/payment/index.mjs.map +1 -1
  72. package/dist/{agent_instructions-DiMSGkdm.d.mts → pricing-CQ9DIFaw.d.ts} +109 -56
  73. package/dist/{agent_instructions-DiMSGkdm.d.ts → pricing-CxzwyiO6.d.mts} +109 -56
  74. package/dist/rail_spec-XP0wKgJV.d.mts +132 -0
  75. package/dist/rail_spec-XP0wKgJV.d.ts +132 -0
  76. package/dist/{signer-CFVQsWjL.d.mts → signer-3FAit11j.d.mts} +27 -1
  77. package/dist/{signer-CFVQsWjL.d.ts → signer-3FAit11j.d.ts} +27 -1
  78. package/dist/solana-Cds87OTu.d.mts +67 -0
  79. package/dist/solana-Cds87OTu.d.ts +67 -0
  80. package/dist/stripe-multichain/index.d.mts +55 -66
  81. package/dist/stripe-multichain/index.d.ts +55 -66
  82. package/dist/stripe-multichain/index.js +68 -42
  83. package/dist/stripe-multichain/index.js.map +1 -1
  84. package/dist/stripe-multichain/index.mjs +68 -41
  85. package/dist/stripe-multichain/index.mjs.map +1 -1
  86. package/dist/{wwwauthenticate-CU1eNvMQ.d.mts → wwwauthenticate-D_FMnPgU.d.mts} +9 -10
  87. package/dist/{wwwauthenticate-CU1eNvMQ.d.ts → wwwauthenticate-D_FMnPgU.d.ts} +9 -10
  88. package/dist/x402_server-hgQzWQwB.d.mts +81 -0
  89. package/dist/x402_server-hgQzWQwB.d.ts +81 -0
  90. package/package.json +9 -7
@@ -115,49 +115,116 @@ function lookupRail(name) {
115
115
  }
116
116
 
117
117
  // src/payment/directive.ts
118
- function buildPaymentRequestBlob(input) {
119
- const railDef = input.rail ? lookupRail(input.rail) : void 0;
120
- const decimals = input.decimals ?? railDef?.decimals ?? 6;
121
- const currency = input.currency ?? railDef?.currency ?? "usd";
122
- const chainId = input.chainId ?? railDef?.chainId;
123
- const amountNum = typeof input.amountUsd === "string" ? Number(input.amountUsd) : input.amountUsd;
124
- const amountRaw = BigInt(Math.round(amountNum * 10 ** decimals)).toString();
125
- const blob = { amount: amountRaw, currency, decimals };
126
- if (input.recipient) blob.recipient = input.recipient;
118
+ function buildPaymentRequestBlob({
119
+ rail,
120
+ amountUsd,
121
+ currency,
122
+ decimals,
123
+ recipient,
124
+ chainId,
125
+ networkId
126
+ }) {
127
+ const railDef = rail ? lookupRail(rail) : void 0;
128
+ const decimalsResolved = decimals ?? railDef?.decimals ?? 6;
129
+ const currencyResolved = currency ?? railDef?.currency ?? "usd";
130
+ const chainIdResolved = chainId ?? railDef?.chainId;
131
+ const amountNum = typeof amountUsd === "string" ? Number(amountUsd) : amountUsd;
132
+ const amountRaw = BigInt(Math.round(amountNum * 10 ** decimalsResolved)).toString();
133
+ const blob = { amount: amountRaw, currency: currencyResolved, decimals: decimalsResolved };
134
+ if (recipient) blob.recipient = recipient;
127
135
  const methodDetails = {};
128
- if (chainId !== void 0) methodDetails.chainId = chainId;
129
- if (input.networkId) methodDetails.networkId = input.networkId;
136
+ if (chainIdResolved !== void 0) methodDetails.chainId = chainIdResolved;
137
+ if (networkId) methodDetails.networkId = networkId;
130
138
  if (Object.keys(methodDetails).length > 0) blob.methodDetails = methodDetails;
131
139
  return Buffer.from(JSON.stringify(blob)).toString("base64url");
132
140
  }
133
- function paymentDirective(input) {
134
- const railDef = input.rail ? lookupRail(input.rail) : void 0;
135
- const method = input.method ?? railDef?.method ?? "unknown";
136
- const intent = input.intent ?? "charge";
137
- const expires = input.expires ?? new Date(Date.now() + 5 * 60 * 1e3).toISOString();
138
- return `Payment id="${input.id}", realm="${input.realm}", method="${method}", intent="${intent}", expires="${expires}", request="${input.request}"`;
141
+ function paymentDirective({
142
+ rail,
143
+ id,
144
+ realm,
145
+ method,
146
+ intent,
147
+ expires,
148
+ request
149
+ }) {
150
+ const railDef = rail ? lookupRail(rail) : void 0;
151
+ const methodResolved = method ?? railDef?.method ?? "unknown";
152
+ const intentResolved = intent ?? "charge";
153
+ const expiresResolved = expires ?? new Date(Date.now() + 5 * 60 * 1e3).toISOString();
154
+ return `Payment id="${id}", realm="${realm}", method="${methodResolved}", intent="${intentResolved}", expires="${expiresResolved}", request="${request}"`;
139
155
  }
140
- function buildPaymentDirective(input) {
156
+ function buildPaymentDirective({
157
+ rail,
158
+ id,
159
+ realm,
160
+ amountUsd,
161
+ currency,
162
+ decimals,
163
+ recipient,
164
+ chainId,
165
+ networkId,
166
+ method,
167
+ intent,
168
+ expires
169
+ }) {
141
170
  const request = buildPaymentRequestBlob({
142
- rail: input.rail,
143
- amountUsd: input.amountUsd,
144
- currency: input.currency,
145
- decimals: input.decimals,
146
- recipient: input.recipient,
147
- chainId: input.chainId,
148
- networkId: input.networkId
171
+ rail,
172
+ amountUsd,
173
+ currency,
174
+ decimals,
175
+ recipient,
176
+ chainId,
177
+ networkId
149
178
  });
150
179
  return paymentDirective({
151
- rail: input.rail,
152
- id: input.id,
153
- realm: input.realm,
154
- method: input.method,
155
- intent: input.intent,
156
- expires: input.expires,
180
+ rail,
181
+ id,
182
+ realm,
183
+ method,
184
+ intent,
185
+ expires,
157
186
  request
158
187
  });
159
188
  }
160
189
 
190
+ // src/payment/rail_spec.ts
191
+ async function resolveRecipient(r) {
192
+ if (typeof r === "string") return r;
193
+ return Promise.resolve(r());
194
+ }
195
+ var RAIL_SPEC_DEFAULTS = {
196
+ tempo: {
197
+ network: "tempo-mainnet",
198
+ chainId: 4217,
199
+ token: USDC.tempo.mainnet.address,
200
+ symbol: "USDC.e",
201
+ decimals: 6,
202
+ testnet: false,
203
+ recommend: "both"
204
+ },
205
+ x402Base: {
206
+ network: "eip155:8453",
207
+ chainId: 8453,
208
+ token: USDC.base.mainnet.address,
209
+ symbol: "USDC",
210
+ decimals: 6,
211
+ mode: "exact"
212
+ },
213
+ solanaMpp: {
214
+ network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
215
+ token: USDC.solana.mainnet.mint,
216
+ symbol: "USDC",
217
+ decimals: 6
218
+ },
219
+ stripe: {
220
+ rails: ["card", "link", "shared_payment_token"]
221
+ },
222
+ tempoSession: {
223
+ currency: USDC.tempo.mainnet.address,
224
+ testnet: false
225
+ }
226
+ };
227
+
161
228
  // src/payment/x402.ts
162
229
  function registerX402SchemesV1V2(server, network, scheme) {
163
230
  server.register(network, scheme);
@@ -175,7 +242,8 @@ async function createX402Server(opts = {}) {
175
242
  );
176
243
  }
177
244
  let facilitator;
178
- if (opts.facilitator === "coinbase") {
245
+ const facilitatorChoice = opts.facilitator ?? (process.env.CDP_API_KEY_ID && process.env.CDP_API_KEY_SECRET ? "coinbase" : "http");
246
+ if (facilitatorChoice === "coinbase") {
179
247
  const cb = await dynamicImport("@coinbase/x402");
180
248
  if (!cb?.facilitator) {
181
249
  throw new Error(
@@ -183,10 +251,10 @@ async function createX402Server(opts = {}) {
183
251
  );
184
252
  }
185
253
  facilitator = new x402Core.HTTPFacilitatorClient(cb.facilitator);
186
- } else if (opts.facilitator === void 0 || opts.facilitator === "http") {
254
+ } else if (facilitatorChoice === "http") {
187
255
  facilitator = new x402Core.HTTPFacilitatorClient();
188
256
  } else {
189
- facilitator = opts.facilitator;
257
+ facilitator = facilitatorChoice;
190
258
  }
191
259
  const server = new x402Core.x402ResourceServer(facilitator);
192
260
  let evmExactModule = null;
@@ -296,44 +364,95 @@ function classifyX402SettleResult(result) {
296
364
  };
297
365
  }
298
366
  }
299
- async function processX402Settle(input) {
300
- const server = input.x402Server;
367
+ function classifyOrchestrationError(err) {
368
+ let msg;
369
+ if (err instanceof Error) {
370
+ msg = err.message;
371
+ } else if (typeof err === "string") {
372
+ msg = err;
373
+ } else {
374
+ return null;
375
+ }
376
+ const msgLower = msg.toLowerCase();
377
+ if (msgLower.includes("x402version") || msgLower.includes("invalid payment") || msgLower.includes("unsupported x402")) {
378
+ return {
379
+ status: 400,
380
+ code: "payment_proof_invalid",
381
+ message: "Payment credential is malformed or uses an unsupported version",
382
+ nextSteps: {
383
+ action: "regenerate_payment_credential",
384
+ user_message: "The payment credential is malformed or uses an unsupported version. Regenerate from a fresh 402 challenge and re-sign."
385
+ }
386
+ };
387
+ }
388
+ if (msgLower.includes("stripe") || msgLower.includes("facilitator") || msgLower.includes("cdp")) {
389
+ return {
390
+ status: 503,
391
+ code: "payment_provider_unavailable",
392
+ message: "Payment provider returned an error",
393
+ nextSteps: {
394
+ action: "retry_or_swap_method",
395
+ retry_after_seconds: 10,
396
+ user_message: "Transient payment-provider error. Retry in a few seconds, or pick a different rail from the 402 challenge."
397
+ }
398
+ };
399
+ }
400
+ return null;
401
+ }
402
+ async function processX402Settle({
403
+ x402Server,
404
+ payload,
405
+ resourceConfig,
406
+ resourceMeta,
407
+ extension,
408
+ transportContext
409
+ }) {
410
+ const server = x402Server;
301
411
  let builtRequirements;
302
412
  try {
303
- builtRequirements = await server.buildPaymentRequirements(input.resourceConfig);
413
+ builtRequirements = await server.buildPaymentRequirements(resourceConfig);
304
414
  } catch (err) {
415
+ console.warn("[x402_settle] build_requirements failed:", err instanceof Error ? err.message : err);
305
416
  return { success: false, phase: "facilitator_error", step: "build_requirements", error: err };
306
417
  }
307
418
  const matchedRequirement = builtRequirements[0];
308
419
  if (!matchedRequirement) {
309
420
  return { success: false, phase: "no_requirements", reason: "x402Server.buildPaymentRequirements returned empty" };
310
421
  }
311
- const transportContext = input.transportContext ?? (() => {
312
- const path = new URL(input.resourceMeta.url).pathname;
422
+ const resolvedTransportContext = transportContext ?? (() => {
423
+ const path = new URL(resourceMeta.url).pathname;
313
424
  return { method: "POST", adapter: { getPath: () => path }, routePattern: path };
314
425
  })();
315
426
  let enrichedExt;
316
427
  try {
317
- enrichedExt = input.extension !== void 0 ? server.enrichExtensions(input.extension, transportContext) : void 0;
428
+ enrichedExt = extension !== void 0 ? server.enrichExtensions(extension, resolvedTransportContext) : void 0;
318
429
  } catch (err) {
430
+ console.warn("[x402_settle] enrich_extensions failed:", err instanceof Error ? err.message : err);
319
431
  return { success: false, phase: "facilitator_error", step: "enrich_extensions", error: err };
320
432
  }
321
433
  let verifyResult;
322
434
  try {
323
- verifyResult = await server.processPaymentRequest(
324
- input.payload,
325
- input.resourceConfig,
326
- input.resourceMeta,
327
- enrichedExt
435
+ verifyResult = await server.verifyPayment(
436
+ payload,
437
+ matchedRequirement,
438
+ enrichedExt,
439
+ resolvedTransportContext
328
440
  );
329
441
  } catch (err) {
330
- return { success: false, phase: "facilitator_error", step: "process_payment_request", error: err };
442
+ console.warn("[x402_settle] verify_payment failed:", err instanceof Error ? err.message : err);
443
+ return { success: false, phase: "facilitator_error", step: "verify_payment", error: err };
331
444
  }
332
- if (!verifyResult.success) {
445
+ const verifyOk = verifyResult.isValid === true || verifyResult.success === true;
446
+ if (!verifyOk) {
333
447
  return { success: false, phase: "verify_failed", verifyResult };
334
448
  }
335
449
  try {
336
- const settleResult = await server.settlePayment(input.payload, matchedRequirement);
450
+ const settleResult = await server.settlePayment(
451
+ payload,
452
+ matchedRequirement,
453
+ enrichedExt,
454
+ resolvedTransportContext
455
+ );
337
456
  const paymentResponseHeader = settleResult ? Buffer.from(JSON.stringify(settleResult)).toString("base64") : void 0;
338
457
  return {
339
458
  success: true,
@@ -352,10 +471,10 @@ var X402_SUPPORTED_BASE_NETWORKS = /* @__PURE__ */ new Set([
352
471
  networks.base.mainnet.caip2,
353
472
  networks.base.sepolia.caip2
354
473
  ]);
355
- function validateX402NetworkConfig(input) {
356
- if (!X402_SUPPORTED_BASE_NETWORKS.has(input.baseNetwork)) {
474
+ function validateX402NetworkConfig({ baseNetwork }) {
475
+ if (!X402_SUPPORTED_BASE_NETWORKS.has(baseNetwork)) {
357
476
  throw new Error(
358
- `X402_BASE_NETWORK=${input.baseNetwork} is not supported. Use one of: ${[...X402_SUPPORTED_BASE_NETWORKS].join(", ")}`
477
+ `X402_BASE_NETWORK=${baseNetwork} is not supported. Use one of: ${[...X402_SUPPORTED_BASE_NETWORKS].join(", ")}`
359
478
  );
360
479
  }
361
480
  }
@@ -371,8 +490,12 @@ function regenerateBody(message, userMessage) {
371
490
  }
372
491
  };
373
492
  }
374
- async function verifyX402Request(input) {
375
- const headerValue = input.request.headers.get("payment-signature") ?? input.request.headers.get("x-payment");
493
+ async function verifyX402Request({
494
+ request,
495
+ isCachedAddress,
496
+ acceptedNetwork
497
+ }) {
498
+ const headerValue = request.headers.get("payment-signature") ?? request.headers.get("x-payment");
376
499
  if (!headerValue) {
377
500
  return {
378
501
  ok: false,
@@ -398,13 +521,13 @@ async function verifyX402Request(input) {
398
521
  }
399
522
  const signedNetwork = payload.accepted?.network;
400
523
  const signedPayTo = payload.accepted?.payTo;
401
- if (!signedNetwork || signedNetwork !== input.acceptedNetwork) {
524
+ if (!signedNetwork || signedNetwork !== acceptedNetwork) {
402
525
  if (signedNetwork && signedNetwork.toLowerCase().startsWith("solana:")) {
403
526
  return {
404
527
  ok: false,
405
528
  status: 400,
406
529
  body: regenerateBody(
407
- `x402 on ${signedNetwork} is not accepted; Solana payments must use the \`solana/charge\` rail advertised in the 402 challenge. This server accepts x402 on ${input.acceptedNetwork} only.`,
530
+ `x402 on ${signedNetwork} is not accepted; Solana payments must use the \`solana/charge\` rail advertised in the 402 challenge. This server accepts x402 on ${acceptedNetwork} only.`,
408
531
  "Solana payments are not accepted over x402 at this merchant. Pick the `solana/charge` rail from the 402 challenge and re-sign."
409
532
  )
410
533
  };
@@ -413,7 +536,7 @@ async function verifyX402Request(input) {
413
536
  ok: false,
414
537
  status: 400,
415
538
  body: regenerateBody(
416
- `Unsupported x402 network ${signedNetwork ?? "<missing>"}; this server accepts ${input.acceptedNetwork}.`,
539
+ `Unsupported x402 network ${signedNetwork ?? "<missing>"}; this server accepts ${acceptedNetwork}.`,
417
540
  "The credential signed for an unsupported network. Pick the accepted network from the 402 challenge and re-sign."
418
541
  )
419
542
  };
@@ -429,7 +552,7 @@ async function verifyX402Request(input) {
429
552
  )
430
553
  };
431
554
  }
432
- if (!await input.isCachedAddress(signedPayTo)) {
555
+ if (!await isCachedAddress(signedPayTo)) {
433
556
  return {
434
557
  ok: false,
435
558
  status: 400,
@@ -443,7 +566,11 @@ async function verifyX402Request(input) {
443
566
  }
444
567
 
445
568
  // src/stripe-multichain/mppx_stripe.ts
446
- async function createMppxStripe(input) {
569
+ async function createMppxStripe({
570
+ profileId,
571
+ secretKey,
572
+ paymentMethodTypes
573
+ }) {
447
574
  const moduleName = "mppx/server";
448
575
  const mppx = await import(moduleName).catch(() => null);
449
576
  if (!mppx?.stripe?.charge) {
@@ -452,80 +579,130 @@ async function createMppxStripe(input) {
452
579
  );
453
580
  }
454
581
  return mppx.stripe.charge({
455
- networkId: input.profileId,
456
- paymentMethodTypes: input.paymentMethodTypes ?? ["card", "link"],
457
- secretKey: input.secretKey
582
+ networkId: profileId,
583
+ paymentMethodTypes: paymentMethodTypes ?? ["card", "link"],
584
+ secretKey
458
585
  });
459
586
  }
460
587
 
461
588
  // src/payment/mppx_server.ts
462
- async function createMppxServer(opts) {
589
+ function isStripeRailSpec(s) {
590
+ return !("recipient" in s);
591
+ }
592
+ function isTempoSessionRailSpec(s) {
593
+ return "escrowContract" in s && "store" in s;
594
+ }
595
+ function isSolanaMppRailSpec(s) {
596
+ if (!("recipient" in s)) return false;
597
+ if ("escrowContract" in s) return false;
598
+ if ("rpcUrl" in s || "tokenProgram" in s) return true;
599
+ return s.network?.startsWith("solana:") ?? false;
600
+ }
601
+ function solanaNetworkFromCAIP2(caip2) {
602
+ if (caip2 === networks.solana.devnet.caip2) return "devnet";
603
+ return "mainnet-beta";
604
+ }
605
+ function solanaDefaultRpcUrl(network) {
606
+ if (network === "mainnet-beta") return "https://api.mainnet-beta.solana.com";
607
+ if (network === "devnet") return "https://api.devnet.solana.com";
608
+ return "http://localhost:8899";
609
+ }
610
+ async function createMppxServer({
611
+ rails: rails2,
612
+ methods: extraMethods,
613
+ secretKey
614
+ }) {
463
615
  const mppx = await dynamicImport2("mppx/server");
464
616
  if (!mppx?.Mppx?.create) {
465
617
  throw new Error("mppx not installed \u2014 `npm install mppx` to use createMppxServer.");
466
618
  }
467
- const methods = [...opts.methods ?? []];
468
- if (opts.rails?.tempo) {
469
- if (!mppx.tempo?.charge) {
470
- throw new Error("mppx.tempo.charge not available \u2014 check installed mppx version.");
619
+ const methods = [...extraMethods ?? []];
620
+ for (const [name, spec] of Object.entries(rails2 ?? {})) {
621
+ if (isStripeRailSpec(spec)) {
622
+ methods.push(await registerStripe(spec));
623
+ continue;
624
+ }
625
+ if (isTempoSessionRailSpec(spec)) {
626
+ methods.push(await registerTempoSession(mppx, spec));
627
+ continue;
471
628
  }
472
- const t = opts.rails.tempo;
473
- const defaultCurrency = t.testnet ? USDC.tempo.testnet.address : USDC.tempo.mainnet.address;
474
- methods.push(
475
- mppx.tempo.charge({
476
- currency: t.currency ?? defaultCurrency,
477
- recipient: t.recipient,
478
- testnet: t.testnet ?? false
479
- })
629
+ if (isSolanaMppRailSpec(spec)) {
630
+ methods.push(await registerSolana(spec));
631
+ continue;
632
+ }
633
+ methods.push(registerTempo(mppx, spec, name));
634
+ }
635
+ return mppx.Mppx.create({ methods, secretKey });
636
+ }
637
+ function registerTempo(mppx, spec, _name) {
638
+ if (!mppx.tempo?.charge) {
639
+ throw new Error("mppx.tempo.charge not available \u2014 check installed mppx version.");
640
+ }
641
+ const defaultCurrency = spec.testnet ? USDC.tempo.testnet.address : USDC.tempo.mainnet.address;
642
+ if (typeof spec.recipient !== "string") {
643
+ throw new TypeError(
644
+ "createMppxServer: TempoRailSpec requires a string recipient (per-order factories not supported here)."
480
645
  );
481
646
  }
482
- if (opts.rails?.tempo_session) {
483
- if (!mppx.tempo?.session) {
484
- throw new Error(
485
- "mppx.tempo.session not available \u2014 your mppx version may not support sessions yet. Upgrade with `npm install mppx@latest`."
486
- );
487
- }
488
- const s = opts.rails.tempo_session;
489
- const defaultCurrency = s.testnet ? USDC.tempo.testnet.address : USDC.tempo.mainnet.address;
490
- methods.push(
491
- mppx.tempo.session({
492
- currency: s.currency ?? defaultCurrency,
493
- recipient: s.recipient,
494
- escrowContract: s.escrowContract,
495
- store: s.store,
496
- testnet: s.testnet ?? false,
497
- ...s.chains ? { chains: s.chains } : {}
498
- })
647
+ return mppx.tempo.charge({
648
+ currency: spec.token ?? defaultCurrency,
649
+ recipient: spec.recipient,
650
+ testnet: spec.testnet ?? false
651
+ });
652
+ }
653
+ async function registerTempoSession(mppx, spec) {
654
+ if (!mppx.tempo?.session) {
655
+ throw new Error(
656
+ "mppx.tempo.session not available \u2014 your mppx version may not support sessions yet. Upgrade with `npm install mppx@latest`."
499
657
  );
500
658
  }
501
- if (opts.rails?.solana) {
502
- const solanaMpp = await dynamicImport2("@solana/mpp/server");
503
- if (!solanaMpp?.charge) {
504
- throw new Error(
505
- "@solana/mpp not installed \u2014 `npm install @solana/mpp @solana/kit` to use the solana rail."
506
- );
507
- }
508
- const s = opts.rails.solana;
509
- const network = s.network ?? "mainnet-beta";
510
- const defaultMint = network === "mainnet-beta" ? USDC.solana.mainnet.mint : USDC.solana.devnet.mint;
511
- const defaultDecimals = network === "mainnet-beta" ? USDC.solana.mainnet.decimals : USDC.solana.devnet.decimals;
512
- const baseMethod = solanaMpp.charge({
513
- recipient: s.recipient,
514
- currency: s.currency ?? defaultMint,
515
- decimals: s.decimals ?? defaultDecimals,
516
- network,
517
- ...s.rpcUrl ? { rpcUrl: s.rpcUrl } : {},
518
- ...s.signer ? { signer: s.signer } : {},
519
- ...s.tokenProgram ? { tokenProgram: s.tokenProgram } : {}
520
- });
521
- const rpcUrl = s.rpcUrl ?? (network === "mainnet-beta" ? "https://api.mainnet-beta.solana.com" : network === "devnet" ? "https://api.devnet.solana.com" : "http://localhost:8899");
522
- methods.push(wrapSolanaChargeWithFinalizedBlockhash(baseMethod, rpcUrl));
659
+ const defaultCurrency = spec.testnet ? USDC.tempo.testnet.address : USDC.tempo.mainnet.address;
660
+ return mppx.tempo.session({
661
+ currency: spec.currency ?? defaultCurrency,
662
+ recipient: await resolveRecipient(spec.recipient),
663
+ escrowContract: spec.escrowContract,
664
+ store: spec.store,
665
+ testnet: spec.testnet ?? false,
666
+ ...spec.chains ? { chains: spec.chains } : {}
667
+ });
668
+ }
669
+ async function registerSolana(spec) {
670
+ const solanaMpp = await dynamicImport2("@solana/mpp/server");
671
+ if (!solanaMpp?.charge) {
672
+ throw new Error(
673
+ "@solana/mpp not installed \u2014 `npm install @solana/mpp @solana/kit` to use the solana rail."
674
+ );
523
675
  }
524
- if (opts.rails?.stripe) {
525
- const stripeMethod = await createMppxStripe(opts.rails.stripe);
526
- methods.push(stripeMethod);
676
+ const network = solanaNetworkFromCAIP2(spec.network);
677
+ const defaultMint = network === "mainnet-beta" ? USDC.solana.mainnet.mint : USDC.solana.devnet.mint;
678
+ const defaultDecimals = network === "mainnet-beta" ? USDC.solana.mainnet.decimals : USDC.solana.devnet.decimals;
679
+ if (typeof spec.recipient !== "string") {
680
+ throw new TypeError(
681
+ "createMppxServer: SolanaMppRailSpec requires a string recipient (per-order factories not supported here)."
682
+ );
527
683
  }
528
- return mppx.Mppx.create({ methods, secretKey: opts.secretKey });
684
+ const baseMethod = solanaMpp.charge({
685
+ recipient: spec.recipient,
686
+ currency: spec.token ?? defaultMint,
687
+ decimals: spec.decimals ?? defaultDecimals,
688
+ network,
689
+ ...spec.rpcUrl ? { rpcUrl: spec.rpcUrl } : {},
690
+ ...spec.signer ? { signer: spec.signer } : {},
691
+ ...spec.tokenProgram ? { tokenProgram: spec.tokenProgram } : {}
692
+ });
693
+ return wrapSolanaChargeWithFinalizedBlockhash(baseMethod, spec.rpcUrl ?? solanaDefaultRpcUrl(network));
694
+ }
695
+ async function registerStripe(spec) {
696
+ if (!spec.profileId || !spec.secretKey) {
697
+ throw new Error(
698
+ "createMppxServer: StripeRailSpec requires both profileId and secretKey."
699
+ );
700
+ }
701
+ return createMppxStripe({
702
+ profileId: spec.profileId,
703
+ secretKey: spec.secretKey,
704
+ paymentMethodTypes: spec.paymentMethodTypes
705
+ });
529
706
  }
530
707
  async function dynamicImport2(moduleName) {
531
708
  try {
@@ -567,6 +744,29 @@ function wrapSolanaChargeWithFinalizedBlockhash(baseMethod, rpcUrl) {
567
744
  }
568
745
 
569
746
  // src/payment/dispatch.ts
747
+ function detectRailFromHeaders(headers) {
748
+ const get = (name) => {
749
+ if (headers instanceof Headers) {
750
+ return headers.get(name) ?? "";
751
+ }
752
+ const lower = name.toLowerCase();
753
+ for (const [k, v] of Object.entries(headers)) {
754
+ if (k.toLowerCase() === lower) {
755
+ if (v === void 0 || v === null) return "";
756
+ return Array.isArray(v) ? v[0] ?? "" : v;
757
+ }
758
+ }
759
+ return "";
760
+ };
761
+ const auth = get("authorization");
762
+ if (auth.toLowerCase().startsWith("payment ")) {
763
+ return "mpp";
764
+ }
765
+ if (get("payment-signature") || get("x-payment")) {
766
+ return "x402";
767
+ }
768
+ return null;
769
+ }
570
770
  async function dispatchSettlementByNetwork(payload, handlers) {
571
771
  const network = payload.accepted.network;
572
772
  if (network.startsWith("eip155:")) {
@@ -599,37 +799,45 @@ function aliasAmountFields(accepts) {
599
799
  return e;
600
800
  });
601
801
  }
602
- function paymentRequiredHeader(input) {
603
- return Buffer.from(JSON.stringify(input)).toString("base64");
802
+ function paymentRequiredHeader({
803
+ x402Version,
804
+ accepts,
805
+ resource
806
+ }) {
807
+ return Buffer.from(JSON.stringify({ x402Version, accepts, ...resource ? { resource } : {} })).toString("base64");
604
808
  }
605
809
 
606
810
  // src/payment/headers.ts
607
- function buildPaymentHeaders(input) {
608
- const directives = input.rails.map((rail) => {
609
- const directiveInput = {
610
- id: `${input.orderId}-${rail.rail}`,
611
- realm: input.realm,
811
+ function buildPaymentHeaders({
812
+ rails: rails2,
813
+ orderId,
814
+ realm,
815
+ x402
816
+ }) {
817
+ const directives = rails2.map(
818
+ (rail) => buildPaymentDirective({
819
+ id: `${orderId}-${rail.rail}`,
820
+ realm,
612
821
  rail: rail.rail,
613
- amountUsd: rail.amountUsd
614
- };
615
- if (rail.recipient !== void 0) directiveInput.recipient = rail.recipient;
616
- if (rail.networkId !== void 0) directiveInput.networkId = rail.networkId;
617
- if (rail.chainId !== void 0) directiveInput.chainId = rail.chainId;
618
- if (rail.currency !== void 0) directiveInput.currency = rail.currency;
619
- if (rail.decimals !== void 0) directiveInput.decimals = rail.decimals;
620
- if (rail.method !== void 0) directiveInput.method = rail.method;
621
- if (rail.intent !== void 0) directiveInput.intent = rail.intent;
622
- if (rail.expires !== void 0) directiveInput.expires = rail.expires;
623
- return buildPaymentDirective(directiveInput);
624
- });
822
+ amountUsd: rail.amountUsd,
823
+ ...rail.recipient !== void 0 ? { recipient: rail.recipient } : {},
824
+ ...rail.networkId !== void 0 ? { networkId: rail.networkId } : {},
825
+ ...rail.chainId !== void 0 ? { chainId: rail.chainId } : {},
826
+ ...rail.currency !== void 0 ? { currency: rail.currency } : {},
827
+ ...rail.decimals !== void 0 ? { decimals: rail.decimals } : {},
828
+ ...rail.method !== void 0 ? { method: rail.method } : {},
829
+ ...rail.intent !== void 0 ? { intent: rail.intent } : {},
830
+ ...rail.expires !== void 0 ? { expires: rail.expires } : {}
831
+ })
832
+ );
625
833
  const result = {
626
834
  "www-authenticate": wwwAuthenticateHeader(directives)
627
835
  };
628
- if (input.x402) {
836
+ if (x402) {
629
837
  result["PAYMENT-REQUIRED"] = paymentRequiredHeader({
630
- x402Version: input.x402.version ?? 2,
631
- accepts: input.x402.accepts,
632
- ...input.x402.resource ? { resource: input.x402.resource } : {}
838
+ x402Version: x402.version ?? 2,
839
+ accepts: x402.accepts,
840
+ ...x402.resource ? { resource: x402.resource } : {}
633
841
  });
634
842
  }
635
843
  return result;
@@ -637,15 +845,20 @@ function buildPaymentHeaders(input) {
637
845
 
638
846
  // src/payment/idempotency.ts
639
847
  var SERVER_IDEMPOTENCY_KEY_MAX = 200;
640
- function buildIdempotencyKey(input) {
641
- const prefix = input.prefix ? `${input.prefix}-` : "";
642
- if (input.paymentIntentId) {
643
- const key = `${prefix}${input.paymentIntentId}`;
848
+ function buildIdempotencyKey({
849
+ paymentIntentId,
850
+ orderId,
851
+ amountCents,
852
+ prefix
853
+ }) {
854
+ const prefixPart = prefix ? `${prefix}-` : "";
855
+ if (paymentIntentId) {
856
+ const key = `${prefixPart}${paymentIntentId}`;
644
857
  return clampKey(key);
645
858
  }
646
- if (input.orderId) {
647
- const amountSuffix = input.amountCents !== void 0 ? `-${input.amountCents}` : "";
648
- const key = `${prefix}pi-${input.orderId}${amountSuffix}`;
859
+ if (orderId) {
860
+ const amountSuffix = amountCents !== void 0 ? `-${amountCents}` : "";
861
+ const key = `${prefixPart}pi-${orderId}${amountSuffix}`;
649
862
  return clampKey(key);
650
863
  }
651
864
  return void 0;
@@ -731,16 +944,217 @@ async function extractPaymentSigner(request, x402PaymentHeader) {
731
944
  }
732
945
  return null;
733
946
  }
947
+ async function extractPaymentSignerFromAuth(authHeader, x402PaymentHeader) {
948
+ const request = new Request("http://internal.gate/", {
949
+ headers: authHeader ? { authorization: authHeader } : {}
950
+ });
951
+ return extractPaymentSigner(request, x402PaymentHeader);
952
+ }
734
953
  function readX402PaymentHeader(request) {
735
954
  return request.headers.get("payment-signature") ?? request.headers.get("x-payment") ?? void 0;
736
955
  }
956
+ function lowerHeaders(headers) {
957
+ const out = {};
958
+ for (const [k, v] of Object.entries(headers)) out[k.toLowerCase()] = v;
959
+ return out;
960
+ }
961
+ async function extractSignerForPrecheck(headers) {
962
+ const lower = lowerHeaders(headers);
963
+ const x402 = lower["payment-signature"] ?? lower["x-payment"];
964
+ if (x402) {
965
+ const signer = await extractPaymentSignerFromAuth(void 0, x402);
966
+ if (signer !== null) return signer;
967
+ }
968
+ const authorization = lower["authorization"];
969
+ if (authorization && authorization.toLowerCase().startsWith("payment ")) {
970
+ return await extractPaymentSignerFromAuth(authorization);
971
+ }
972
+ return null;
973
+ }
737
974
 
738
975
  // src/payment/settlement_override.ts
739
976
  var SETTLEMENT_OVERRIDES_HEADER = "Settlement-Overrides";
740
977
  function settlementOverrideHeader(overrides) {
741
978
  return { name: SETTLEMENT_OVERRIDES_HEADER, value: JSON.stringify(overrides) };
742
979
  }
980
+
981
+ // src/payment/amounts.ts
982
+ function usdToAtomic(usd, opts) {
983
+ const { decimals } = opts;
984
+ if (!Number.isInteger(decimals) || decimals < 0) {
985
+ throw new RangeError(`decimals must be a non-negative integer, got ${decimals}`);
986
+ }
987
+ if (typeof usd === "number") {
988
+ if (!Number.isFinite(usd)) {
989
+ throw new RangeError(`usd must be finite, got ${usd}`);
990
+ }
991
+ if (usd < 0) {
992
+ throw new RangeError(`usd must be non-negative, got ${usd}`);
993
+ }
994
+ }
995
+ const s = (typeof usd === "number" ? usd.toString() : usd).trim();
996
+ if (s.startsWith("-")) {
997
+ throw new RangeError(`usd must be non-negative, got ${s}`);
998
+ }
999
+ if (s === "NaN" || s === "Infinity") {
1000
+ throw new RangeError(`usd must be finite, got ${s}`);
1001
+ }
1002
+ const match = /^(\d*)(?:\.(\d*))?$/.exec(s);
1003
+ if (!match || match[1] === "" && (match[2] === void 0 || match[2] === "")) {
1004
+ throw new SyntaxError(`invalid usd value: ${JSON.stringify(usd)}`);
1005
+ }
1006
+ const intPart = match[1] || "0";
1007
+ const fracPart = match[2] ?? "";
1008
+ if (fracPart.length <= decimals) {
1009
+ return BigInt(intPart + fracPart.padEnd(decimals, "0"));
1010
+ }
1011
+ const kept = fracPart.slice(0, decimals);
1012
+ const roundDigit = fracPart[decimals];
1013
+ let result = BigInt(intPart + kept);
1014
+ if (roundDigit >= "5") {
1015
+ result += 1n;
1016
+ }
1017
+ return result;
1018
+ }
1019
+ function formatUsdCents(cents) {
1020
+ return (cents / 100).toFixed(2);
1021
+ }
1022
+
1023
+ // src/payment/zero-settle.ts
1024
+ var EVM_RE = /^0x[0-9a-fA-F]{40}$/;
1025
+ var SOLANA_BASE58_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
1026
+ var NULL_RESULT = {
1027
+ signerAddress: null,
1028
+ signerNetwork: null,
1029
+ txHash: null
1030
+ };
1031
+ function zeroAmountCarveOut({
1032
+ rail,
1033
+ payload,
1034
+ authorizationHeader
1035
+ }) {
1036
+ if (rail === "x402-base") {
1037
+ return x402SignerFromPayload(payload);
1038
+ }
1039
+ if (rail === "tempo" || rail === "solana") {
1040
+ return mppSignerFromAuth(authorizationHeader);
1041
+ }
1042
+ return NULL_RESULT;
1043
+ }
1044
+ function x402SignerFromPayload(payload) {
1045
+ if (!payload || typeof payload !== "object") return NULL_RESULT;
1046
+ const inner = payload.payload;
1047
+ if (!inner || typeof inner !== "object") return NULL_RESULT;
1048
+ const authorization = inner.authorization;
1049
+ if (!authorization || typeof authorization !== "object") return NULL_RESULT;
1050
+ const fromAddr = authorization.from;
1051
+ if (typeof fromAddr !== "string" || !EVM_RE.test(fromAddr)) return NULL_RESULT;
1052
+ return {
1053
+ signerAddress: fromAddr.toLowerCase(),
1054
+ signerNetwork: "evm",
1055
+ txHash: null
1056
+ };
1057
+ }
1058
+ function mppSignerFromAuth(authorizationHeader) {
1059
+ if (typeof authorizationHeader !== "string") return NULL_RESULT;
1060
+ if (!authorizationHeader.toLowerCase().startsWith("payment ")) return NULL_RESULT;
1061
+ const token = authorizationHeader.slice("payment ".length).trim();
1062
+ if (!token) return NULL_RESULT;
1063
+ let credential;
1064
+ try {
1065
+ credential = JSON.parse(atob(token));
1066
+ } catch {
1067
+ return NULL_RESULT;
1068
+ }
1069
+ if (!credential || typeof credential !== "object") return NULL_RESULT;
1070
+ let source = credential.source;
1071
+ if (typeof source !== "string") {
1072
+ const challenge = credential.challenge;
1073
+ if (challenge && typeof challenge === "object") {
1074
+ source = challenge.source;
1075
+ }
1076
+ }
1077
+ if (typeof source !== "string") return NULL_RESULT;
1078
+ const parts = source.split(":");
1079
+ if (parts.length < 4 || parts[0] !== "did" || parts[1] !== "pkh") return NULL_RESULT;
1080
+ const family = parts[2];
1081
+ const addr = parts[parts.length - 1] ?? "";
1082
+ if (family === "eip155" && EVM_RE.test(addr)) {
1083
+ return { signerAddress: addr.toLowerCase(), signerNetwork: "evm", txHash: null };
1084
+ }
1085
+ if (family === "solana" && SOLANA_BASE58_RE.test(addr)) {
1086
+ return { signerAddress: addr, signerNetwork: "solana", txHash: null };
1087
+ }
1088
+ return NULL_RESULT;
1089
+ }
1090
+
1091
+ // src/payment/lazy.ts
1092
+ function x402RailName(spec) {
1093
+ const network = spec.network ?? "eip155:8453";
1094
+ if (network === "eip155:8453") return "x402-base-mainnet";
1095
+ if (network === "eip155:84532") return "x402-base-sepolia";
1096
+ throw new Error(
1097
+ `lazyX402Server: unsupported X402BaseRailSpec.network=${JSON.stringify(network)}`
1098
+ );
1099
+ }
1100
+ function lazyX402Server(opts) {
1101
+ const { spec, cdpApiKeyId, cdpApiKeySecret } = opts;
1102
+ const railName = x402RailName(spec);
1103
+ const useCdp = Boolean(cdpApiKeyId && cdpApiKeySecret);
1104
+ const facilitator = useCdp ? "coinbase" : "http";
1105
+ let cached;
1106
+ let pending;
1107
+ return async () => {
1108
+ if (cached !== void 0) return cached;
1109
+ if (pending !== void 0) return pending;
1110
+ pending = (async () => {
1111
+ const server = await createX402Server({ facilitator, rails: [railName] });
1112
+ cached = server;
1113
+ pending = void 0;
1114
+ return server;
1115
+ })();
1116
+ return pending;
1117
+ };
1118
+ }
1119
+ function lazyMppxServer(opts) {
1120
+ const { rails: rails2, secretKey } = opts;
1121
+ let cached;
1122
+ let pending;
1123
+ return async () => {
1124
+ if (cached !== void 0) return cached;
1125
+ if (pending !== void 0) return pending;
1126
+ pending = (async () => {
1127
+ const server = await createMppxServer({ secretKey, rails: rails2 });
1128
+ cached = server;
1129
+ pending = void 0;
1130
+ return server;
1131
+ })();
1132
+ return pending;
1133
+ };
1134
+ }
1135
+
1136
+ // src/payment/solana.ts
1137
+ async function loadSolanaFeePayer(opts) {
1138
+ const raw = opts.privateKey;
1139
+ if (!raw) return void 0;
1140
+ const moduleName = "@solana/kit";
1141
+ const kit = await import(moduleName).catch(() => null);
1142
+ if (!kit?.createKeyPairSignerFromPrivateKeyBytes || !kit.getBase58Codec) {
1143
+ throw new Error(
1144
+ "@solana/kit not installed \u2014 `npm install @solana/kit` for loadSolanaFeePayer."
1145
+ );
1146
+ }
1147
+ let bytes;
1148
+ if (/^[0-9a-fA-F]{128}$/.test(raw)) {
1149
+ bytes = new Uint8Array(raw.match(/.{2}/g).map((h) => parseInt(h, 16))).slice(0, 32);
1150
+ } else {
1151
+ const decoded = new Uint8Array(kit.getBase58Codec().encode(raw));
1152
+ bytes = decoded.length === 64 ? decoded.slice(0, 32) : decoded;
1153
+ }
1154
+ return kit.createKeyPairSignerFromPrivateKeyBytes(bytes);
1155
+ }
743
1156
  export {
1157
+ RAIL_SPEC_DEFAULTS,
744
1158
  SETTLEMENT_OVERRIDES_HEADER,
745
1159
  USDC,
746
1160
  X402_SUPPORTED_BASE_NETWORKS,
@@ -750,11 +1164,19 @@ export {
750
1164
  buildPaymentHeaders,
751
1165
  buildPaymentRequestBlob,
752
1166
  buildX402AcceptsFor402,
1167
+ classifyOrchestrationError,
753
1168
  classifyX402SettleResult,
754
1169
  createMppxServer,
755
1170
  createX402Server,
1171
+ detectRailFromHeaders,
756
1172
  dispatchSettlementByNetwork,
757
1173
  extractPaymentSigner,
1174
+ extractPaymentSignerFromAuth,
1175
+ extractSignerForPrecheck,
1176
+ formatUsdCents,
1177
+ lazyMppxServer,
1178
+ lazyX402Server,
1179
+ loadSolanaFeePayer,
758
1180
  lookupRail,
759
1181
  networkFamily,
760
1182
  networks,
@@ -764,10 +1186,13 @@ export {
764
1186
  rails,
765
1187
  readX402PaymentHeader,
766
1188
  registerX402SchemesV1V2,
1189
+ resolveRecipient,
767
1190
  settlementOverrideHeader,
1191
+ usdToAtomic,
768
1192
  validateX402NetworkConfig,
769
1193
  verifyX402Request,
770
1194
  wrapSolanaChargeWithFinalizedBlockhash,
771
- wwwAuthenticateHeader
1195
+ wwwAuthenticateHeader,
1196
+ zeroAmountCarveOut
772
1197
  };
773
1198
  //# sourceMappingURL=index.mjs.map