@astrasyncai/verification-gateway 5.17.2 → 5.18.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 (105) hide show
  1. package/CHANGELOG.md +1878 -0
  2. package/README.md +141 -7
  3. package/dist/adapters/express.d.mts +1 -1
  4. package/dist/adapters/express.d.ts +1 -1
  5. package/dist/adapters/express.js +30 -12
  6. package/dist/adapters/express.js.map +1 -1
  7. package/dist/adapters/express.mjs +30 -12
  8. package/dist/adapters/express.mjs.map +1 -1
  9. package/dist/adapters/kit-express.d.mts +63 -0
  10. package/dist/adapters/kit-express.d.ts +63 -0
  11. package/dist/adapters/kit-express.js +88 -0
  12. package/dist/adapters/kit-express.js.map +1 -0
  13. package/dist/adapters/kit-express.mjs +61 -0
  14. package/dist/adapters/kit-express.mjs.map +1 -0
  15. package/dist/adapters/mcp.d.mts +1 -1
  16. package/dist/adapters/mcp.d.ts +1 -1
  17. package/dist/adapters/mcp.js +1 -1
  18. package/dist/adapters/mcp.js.map +1 -1
  19. package/dist/adapters/mcp.mjs +1 -1
  20. package/dist/adapters/mcp.mjs.map +1 -1
  21. package/dist/adapters/nextjs.d.mts +1 -1
  22. package/dist/adapters/nextjs.d.ts +1 -1
  23. package/dist/adapters/nextjs.js +18 -8
  24. package/dist/adapters/nextjs.js.map +1 -1
  25. package/dist/adapters/nextjs.mjs +18 -8
  26. package/dist/adapters/nextjs.mjs.map +1 -1
  27. package/dist/adapters/sdk.d.mts +1 -1
  28. package/dist/adapters/sdk.d.ts +1 -1
  29. package/dist/adapters/sdk.js +1 -1
  30. package/dist/adapters/sdk.js.map +1 -1
  31. package/dist/adapters/sdk.mjs +1 -1
  32. package/dist/adapters/sdk.mjs.map +1 -1
  33. package/dist/agent/index.js +1 -1
  34. package/dist/agent/index.js.map +1 -1
  35. package/dist/agent/index.mjs +1 -1
  36. package/dist/agent/index.mjs.map +1 -1
  37. package/dist/bin/astrasync-claude-hook.js +1 -1
  38. package/dist/bin/astrasync-codex-hook.js +1 -1
  39. package/dist/bin/astrasync-guard.js +330 -112
  40. package/dist/bin/astrasync.js +359 -113
  41. package/dist/browser/background.js +1 -1
  42. package/dist/browser/background.js.map +1 -1
  43. package/dist/browser/background.mjs +1 -1
  44. package/dist/browser/background.mjs.map +1 -1
  45. package/dist/cli/index.js +1 -1
  46. package/dist/cli/index.js.map +1 -1
  47. package/dist/cli/index.mjs +1 -1
  48. package/dist/cli/index.mjs.map +1 -1
  49. package/dist/codex/index.js +1 -1
  50. package/dist/codex/index.js.map +1 -1
  51. package/dist/codex/index.mjs +1 -1
  52. package/dist/codex/index.mjs.map +1 -1
  53. package/dist/cursor/extension.js +1 -1
  54. package/dist/cursor/extension.js.map +1 -1
  55. package/dist/cursor/extension.mjs +1 -1
  56. package/dist/cursor/extension.mjs.map +1 -1
  57. package/dist/edge-config.d.mts +1 -1
  58. package/dist/edge-config.d.ts +1 -1
  59. package/dist/edge-config.js +1 -1
  60. package/dist/edge-config.js.map +1 -1
  61. package/dist/edge-config.mjs +1 -1
  62. package/dist/edge-config.mjs.map +1 -1
  63. package/dist/edge-core/index.d.mts +5 -4
  64. package/dist/edge-core/index.d.ts +5 -4
  65. package/dist/edge-core/index.js +18 -8
  66. package/dist/edge-core/index.js.map +1 -1
  67. package/dist/edge-core/index.mjs +18 -8
  68. package/dist/edge-core/index.mjs.map +1 -1
  69. package/dist/gateway/gateway.js +1 -1
  70. package/dist/gateway/gateway.js.map +1 -1
  71. package/dist/gateway/gateway.mjs +1 -1
  72. package/dist/gateway/gateway.mjs.map +1 -1
  73. package/dist/git-trigger/git-hooks.d.mts +1 -1
  74. package/dist/git-trigger/git-hooks.d.ts +1 -1
  75. package/dist/index.d.mts +391 -13
  76. package/dist/index.d.ts +391 -13
  77. package/dist/index.js +766 -230
  78. package/dist/index.js.map +1 -1
  79. package/dist/index.mjs +749 -222
  80. package/dist/index.mjs.map +1 -1
  81. package/dist/registration/index.d.mts +1 -1
  82. package/dist/registration/index.d.ts +1 -1
  83. package/dist/registration/index.js +1 -1
  84. package/dist/registration/index.js.map +1 -1
  85. package/dist/registration/index.mjs +1 -1
  86. package/dist/registration/index.mjs.map +1 -1
  87. package/dist/transport/index.d.mts +4 -3
  88. package/dist/transport/index.d.ts +4 -3
  89. package/dist/transport/index.js +1 -1
  90. package/dist/transport/index.js.map +1 -1
  91. package/dist/transport/index.mjs +1 -1
  92. package/dist/transport/index.mjs.map +1 -1
  93. package/dist/{types-vNBvYD7Z.d.mts → types-3ObBpsnf.d.mts} +7 -0
  94. package/dist/{types-DA8LzAOB.d.ts → types-BoxZEYPS.d.ts} +7 -0
  95. package/dist/{types-DRb_plOh.d.mts → types-Cns4l8gX.d.mts} +17 -0
  96. package/dist/{types-DRb_plOh.d.ts → types-Cns4l8gX.d.ts} +17 -0
  97. package/dist/ui/index.d.mts +1 -1
  98. package/dist/ui/index.d.ts +1 -1
  99. package/dist/verify.d.mts +1 -1
  100. package/dist/verify.d.ts +1 -1
  101. package/dist/verify.js +1 -1
  102. package/dist/verify.js.map +1 -1
  103. package/dist/verify.mjs +1 -1
  104. package/dist/verify.mjs.map +1 -1
  105. package/package.json +8 -2
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  ACP_API_VERSION: () => ACP_API_VERSION,
34
+ ACP_SUPPORTED_API_VERSIONS: () => ACP_SUPPORTED_API_VERSIONS,
34
35
  AEP_SPEC_VERSION: () => AEP_SPEC_VERSION,
35
36
  AP2_VCT: () => AP2_VCT,
36
37
  AepChain: () => AepChain,
@@ -51,6 +52,7 @@ __export(src_exports, {
51
52
  MCP_VERIFIED_HOP_MAX_AGE_MS: () => MCP_VERIFIED_HOP_MAX_AGE_MS,
52
53
  MemoryCheckoutStore: () => MemoryCheckoutStore,
53
54
  MemoryIdempotencyStore: () => MemoryIdempotencyStore,
55
+ MemoryMppChallengeStore: () => MemoryMppChallengeStore,
54
56
  PLATFORM_AGENT_SIGNATURES: () => PLATFORM_AGENT_SIGNATURES,
55
57
  RUNTIME_SIGNATURES: () => RUNTIME_SIGNATURES,
56
58
  RedisIdempotencyStore: () => RedisIdempotencyStore,
@@ -60,10 +62,12 @@ __export(src_exports, {
60
62
  SDK_USER_AGENT: () => SDK_USER_AGENT,
61
63
  SettlementReportError: () => SettlementReportError,
62
64
  TRUST_LEVEL_RANGES: () => TRUST_LEVEL_RANGES,
65
+ UCP_ORDER_CAPABILITY: () => UCP_ORDER_CAPABILITY,
63
66
  UCP_VERSION: () => UCP_VERSION,
64
67
  VERIFICATION_STATUS_HEADER: () => VERIFICATION_STATUS_HEADER,
65
68
  VERSION: () => SDK_VERSION,
66
69
  _resetEdgeConfigCache: () => _resetEdgeConfigCache,
70
+ acpPaymentHandlers: () => acpPaymentHandlers,
67
71
  aepCanonicalJson: () => aepCanonicalJson,
68
72
  aepHash: () => aepHash,
69
73
  aepPrivateKeyFromSeed: () => aepPrivateKeyFromSeed,
@@ -78,9 +82,12 @@ __export(src_exports, {
78
82
  authorizeSettlement: () => authorizeSettlement,
79
83
  awaitStepUpApproval: () => awaitStepUpApproval,
80
84
  beginIdempotent: () => beginIdempotent,
85
+ buildAcpWellKnown: () => buildAcpWellKnown,
81
86
  buildGuidance: () => buildGuidance,
87
+ buildMpp402Response: () => buildMpp402Response,
82
88
  buildMppChallenge: () => buildMppChallenge,
83
89
  buildSdkObservedMetadata: () => buildSdkObservedMetadata,
90
+ buildUcpOrder: () => buildUcpOrder,
84
91
  buildUcpProfile: () => buildUcpProfile,
85
92
  buildVerificationUnavailableBody: () => buildVerificationUnavailableBody,
86
93
  buildX402Challenge: () => buildX402Challenge,
@@ -104,6 +111,7 @@ __export(src_exports, {
104
111
  encodeMppReceipt: () => encodeMppReceipt,
105
112
  encodeX402SettlementResponse: () => encodeX402SettlementResponse,
106
113
  express: () => express_exports,
114
+ expressHandler: () => expressHandler,
107
115
  extractApiKeyFormat: () => extractApiKeyFormat,
108
116
  extractCredentials: () => extractCredentials,
109
117
  extractMcpCredentials: () => extractMcpCredentials,
@@ -128,6 +136,7 @@ __export(src_exports, {
128
136
  matchEdgePathRule: () => matchEdgePathRule,
129
137
  matchPlatformSignature: () => matchPlatformSignature,
130
138
  matchRoutePattern: () => matchRoutePattern,
139
+ mppBodyDigest: () => mppBodyDigest,
131
140
  negotiateUcp: () => negotiateUcp,
132
141
  nextjs: () => nextjs_exports,
133
142
  normalizeDeclaredSource: () => normalizeDeclaredSource,
@@ -205,7 +214,7 @@ function getTrustLevel(score) {
205
214
  }
206
215
 
207
216
  // src/version.ts
208
- var SDK_VERSION = "5.17.2";
217
+ var SDK_VERSION = "5.18.1";
209
218
 
210
219
  // src/http.ts
211
220
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -1347,17 +1356,17 @@ function normalizeDeclaredSource(value) {
1347
1356
  return trimmed && DECLARED_SOURCE_PATTERN.test(trimmed) ? trimmed : void 0;
1348
1357
  }
1349
1358
  function byteLength(s) {
1350
- let bytes = 0;
1359
+ let bytes2 = 0;
1351
1360
  for (let i = 0; i < s.length; i++) {
1352
1361
  const code = s.charCodeAt(i);
1353
- if (code < 128) bytes += 1;
1354
- else if (code < 2048) bytes += 2;
1362
+ if (code < 128) bytes2 += 1;
1363
+ else if (code < 2048) bytes2 += 2;
1355
1364
  else if (code >= 55296 && code <= 56319) {
1356
- bytes += 4;
1365
+ bytes2 += 4;
1357
1366
  i++;
1358
- } else bytes += 3;
1367
+ } else bytes2 += 3;
1359
1368
  }
1360
- return bytes;
1369
+ return bytes2;
1361
1370
  }
1362
1371
  function extractApiKeyFormat(rawValue) {
1363
1372
  if (!rawValue) return void 0;
@@ -1534,9 +1543,9 @@ function decodeBase64Url(seg) {
1534
1543
  const b642 = seg.replace(/-/g, "+").replace(/_/g, "/");
1535
1544
  const padded = b642 + "=".repeat((4 - b642.length % 4) % 4);
1536
1545
  const binary2 = atob(padded);
1537
- const bytes = new Uint8Array(binary2.length);
1538
- for (let i = 0; i < binary2.length; i++) bytes[i] = binary2.charCodeAt(i);
1539
- return new TextDecoder().decode(bytes);
1546
+ const bytes2 = new Uint8Array(binary2.length);
1547
+ for (let i = 0; i < binary2.length; i++) bytes2[i] = binary2.charCodeAt(i);
1548
+ return new TextDecoder().decode(bytes2);
1540
1549
  }
1541
1550
  function sniffJwtHeader(compact) {
1542
1551
  try {
@@ -1575,6 +1584,23 @@ function detectCommerce(req, host) {
1575
1584
  }
1576
1585
  const signature = headerValue(req, "signature");
1577
1586
  const signatureInput = headerValue(req, "signature-input");
1587
+ if (/\/checkout[-_]sessions(\/|$)/.test(req.uri) && headerValue(req, "ucp-agent")) {
1588
+ const signed = Boolean(signature && signatureInput);
1589
+ return {
1590
+ protocol: "ucp",
1591
+ artifacts: {
1592
+ ucpRequest: { method, url, ...jsonBody && { body: jsonBody } },
1593
+ ...signed && {
1594
+ rfc9421: { headers, method, url, ...req.bodyText && { body: req.bodyText } }
1595
+ }
1596
+ },
1597
+ evidence: {
1598
+ path: req.uri,
1599
+ ...signed && { keyid: signatureInput.match(/keyid="([^"]+)"/)?.[1] },
1600
+ ...truncated
1601
+ }
1602
+ };
1603
+ }
1578
1604
  if (signature && signatureInput) {
1579
1605
  const signatureAgent = headerValue(req, "signature-agent");
1580
1606
  const registryHint = headerValue(req, "x-agent-registry");
@@ -1609,13 +1635,6 @@ function detectCommerce(req, host) {
1609
1635
  evidence: { hasTimestamp: Boolean(acpTimestamp), ...truncated }
1610
1636
  };
1611
1637
  }
1612
- if (/\/checkout[-_]sessions(\/|$)/.test(req.uri) && headerValue(req, "ucp-agent")) {
1613
- return {
1614
- protocol: "ucp",
1615
- artifacts: { ucpRequest: { method, url, ...jsonBody && { body: jsonBody } } },
1616
- evidence: { path: req.uri, ...truncated }
1617
- };
1618
- }
1619
1638
  if (jsonBody) {
1620
1639
  const intent = jsonBody.intent_mandate ?? jsonBody.intentMandate;
1621
1640
  const cart = jsonBody.cart_mandate ?? jsonBody.cartMandate;
@@ -2714,6 +2733,7 @@ function createMiddleware(options) {
2714
2733
  anonymousBeacon = true,
2715
2734
  trustVerifiedHop = false,
2716
2735
  verifiedHopMaxAgeMs,
2736
+ defaultRoutePolicy = "pass",
2717
2737
  ...config
2718
2738
  } = options;
2719
2739
  if (config.apiBaseUrl) {
@@ -2779,12 +2799,15 @@ function createMiddleware(options) {
2779
2799
  });
2780
2800
  }
2781
2801
  const correlationId = req.headers["x-request-id"] || req.headers["x-correlation-id"];
2782
- const routeConfig = findRouteConfig(cachedRoutes, req.path, req.method, {
2802
+ const matchedRoute = findRouteConfig(cachedRoutes, req.path, req.method, {
2783
2803
  caseInsensitive: caseInsensitiveRouteMatch,
2784
2804
  logShadowDivergence: true,
2785
2805
  correlationId
2786
2806
  });
2787
- if (!routeConfig) {
2807
+ const credentials = customExtractCredentials ? customExtractCredentials(req) : defaultExtractCredentials(req);
2808
+ const credentialed = Boolean(credentials.astraId || credentials.apiKey || credentials.jwt);
2809
+ const observeUnmatched = !matchedRoute && (config.evaluateAlwaysIfCredentialed === true && Boolean(credentials.astraId) || defaultRoutePolicy === "observe" && credentialed);
2810
+ if (!matchedRoute && !observeUnmatched) {
2788
2811
  if (config.setPassThroughHeader) {
2789
2812
  res.setHeader("X-Astra-Gateway-Mode", "unenforced");
2790
2813
  res.setHeader(
@@ -2795,10 +2818,14 @@ function createMiddleware(options) {
2795
2818
  beaconPassThrough?.(req);
2796
2819
  return next();
2797
2820
  }
2821
+ const routeConfig = matchedRoute ?? {
2822
+ pattern: req.path,
2823
+ method: req.method,
2824
+ observe: true
2825
+ };
2798
2826
  const wellKnownUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
2799
- const credentials = customExtractCredentials ? customExtractCredentials(req) : defaultExtractCredentials(req);
2800
2827
  const shouldEnforce = !routeConfig.observe;
2801
- if (routeConfig.observe && (!config.evaluateAlwaysIfCredentialed || !credentials.astraId)) {
2828
+ if (routeConfig.observe && !observeUnmatched && (!config.evaluateAlwaysIfCredentialed || !credentials.astraId)) {
2802
2829
  if (config.setPassThroughHeader) {
2803
2830
  res.setHeader("X-Astra-Gateway-Mode", "unenforced");
2804
2831
  res.setHeader("X-Astra-Gateway-Reason", "route-observe");
@@ -4280,8 +4307,8 @@ function extractBase64(value) {
4280
4307
  }
4281
4308
  return null;
4282
4309
  }
4283
- function bufferToBase64(bytes) {
4284
- return Buffer.from(bytes).toString("base64");
4310
+ function bufferToBase64(bytes2) {
4311
+ return Buffer.from(bytes2).toString("base64");
4285
4312
  }
4286
4313
 
4287
4314
  // src/transport/rfc9421-verify.ts
@@ -5448,9 +5475,9 @@ async function tryVerify(subtle, jwk, signature, body, alg) {
5448
5475
  }
5449
5476
  return false;
5450
5477
  }
5451
- function toBuf(bytes) {
5452
- const out = new ArrayBuffer(bytes.byteLength);
5453
- new Uint8Array(out).set(bytes);
5478
+ function toBuf(bytes2) {
5479
+ const out = new ArrayBuffer(bytes2.byteLength);
5480
+ new Uint8Array(out).set(bytes2);
5454
5481
  return out;
5455
5482
  }
5456
5483
  function checkTimestamp(headerValue3, toleranceSec, nowFn) {
@@ -5973,11 +6000,11 @@ async function thumbprintsMatch(a, b) {
5973
6000
  }
5974
6001
  async function jwkThumbprint(jwk) {
5975
6002
  const canonical2 = canonicalJwk(jwk);
5976
- const bytes = new TextEncoder().encode(JSON.stringify(canonical2));
6003
+ const bytes2 = new TextEncoder().encode(JSON.stringify(canonical2));
5977
6004
  const subtle = import_node_crypto4.webcrypto.subtle;
5978
6005
  const buffer = await new Promise((resolve, reject) => {
5979
- const source = new ArrayBuffer(bytes.byteLength);
5980
- new Uint8Array(source).set(bytes);
6006
+ const source = new ArrayBuffer(bytes2.byteLength);
6007
+ new Uint8Array(source).set(bytes2);
5981
6008
  subtle.digest("SHA-256", source).then(resolve).catch(reject);
5982
6009
  });
5983
6010
  return Buffer.from(new Uint8Array(buffer)).toString("base64url").replace(/=+$/, "");
@@ -6370,14 +6397,14 @@ function createMastercardRegistry(options = {}) {
6370
6397
  const cache4 = /* @__PURE__ */ new Map();
6371
6398
  const ttlSec = options.cacheTtlSec ?? 3600;
6372
6399
  const fetchFn = options.fetch ?? globalThis.fetch;
6373
- let warned = false;
6400
+ let warned2 = false;
6374
6401
  return {
6375
6402
  name: "mastercard",
6376
6403
  async resolve(kid) {
6377
6404
  if (!kid) return null;
6378
6405
  if (!options.registryUrl) {
6379
- if (!warned && !options.silent) {
6380
- warned = true;
6406
+ if (!warned2 && !options.silent) {
6407
+ warned2 = true;
6381
6408
  console.warn(
6382
6409
  "[mastercard-registry] registryUrl not configured \u2014 key resolution disabled. Kid lookups will return null until a partnership registry is supplied."
6383
6410
  );
@@ -10930,6 +10957,7 @@ function astrasyncPaymentHandlers(handlers, specBase = "https://astrasync.ai/ucp
10930
10957
  }
10931
10958
  return out;
10932
10959
  }
10960
+ var UCP_ORDER_CAPABILITY = "dev.ucp.shopping.order";
10933
10961
  function buildUcpProfile(opts) {
10934
10962
  const capabilities = {
10935
10963
  "dev.ucp.shopping.checkout": [
@@ -10950,6 +10978,15 @@ function buildUcpProfile(opts) {
10950
10978
  }
10951
10979
  ]
10952
10980
  },
10981
+ ...opts.orders && {
10982
+ [UCP_ORDER_CAPABILITY]: [
10983
+ {
10984
+ version: UCP_VERSION,
10985
+ spec: `${SPEC}/shopping/order`,
10986
+ schema: `${SCHEMAS}/shopping/order.json`
10987
+ }
10988
+ ]
10989
+ },
10953
10990
  ...opts.capabilities ?? {}
10954
10991
  };
10955
10992
  return {
@@ -11326,7 +11363,7 @@ async function sendUcpWebhook(opts) {
11326
11363
  }
11327
11364
 
11328
11365
  // src/ucp/checkout-kit.ts
11329
- var import_crypto7 = require("crypto");
11366
+ var import_crypto8 = require("crypto");
11330
11367
  var import_jose3 = require("jose");
11331
11368
 
11332
11369
  // src/mandates/sd-jwt.ts
@@ -11726,8 +11763,8 @@ function requestFingerprint(body, profile = "ucp") {
11726
11763
  }
11727
11764
  return (0, import_crypto6.createHash)("sha256").update(text).digest("hex");
11728
11765
  }
11729
- function scopeOf(authorization) {
11730
- return authorization ? (0, import_crypto6.createHash)("sha256").update(authorization).digest("hex").slice(0, 16) : "anon";
11766
+ function scopeOf(caller) {
11767
+ return caller ? (0, import_crypto6.createHash)("sha256").update(caller).digest("hex").slice(0, 16) : null;
11731
11768
  }
11732
11769
  async function beginIdempotent(opts, req) {
11733
11770
  const methods = opts.methods ?? ["POST", "PUT", "PATCH", "DELETE"];
@@ -11748,7 +11785,16 @@ async function beginIdempotent(opts, req) {
11748
11785
  message: `Idempotency-Key must be at most ${MAX_KEY_LENGTH} characters.`
11749
11786
  };
11750
11787
  }
11751
- const storeKey = `${scopeOf(req.auth)}:${req.method.toUpperCase()} ${req.path}:${req.key}`;
11788
+ const scope = scopeOf(req.auth);
11789
+ if (!scope) {
11790
+ return opts.required ? {
11791
+ kind: "reject",
11792
+ status: 401,
11793
+ code: "idempotency_caller_required",
11794
+ message: "Idempotency keys are scoped to the caller: send Authorization."
11795
+ } : { kind: "skip" };
11796
+ }
11797
+ const storeKey = `${scope}:${req.method.toUpperCase()} ${req.path}:${req.key}`;
11752
11798
  const requestHash = requestFingerprint(req.body, opts.profile ?? "ucp");
11753
11799
  const ttl = opts.ttlMs ?? DEFAULT_IDEMPOTENCY_TTL_MS;
11754
11800
  try {
@@ -11824,7 +11870,7 @@ function idempotencyMiddleware(opts) {
11824
11870
  path: (req.originalUrl ?? req.url).split("?")[0],
11825
11871
  body,
11826
11872
  key: headerOf(req.headers[header]),
11827
- auth: headerOf(req.headers.authorization) ?? headerOf(req.headers["x-api-key"])
11873
+ auth: headerOf(req.headers.authorization) ?? headerOf(req.headers["x-api-key"]) ?? headerOf(req.headers["x-astra-id"])
11828
11874
  });
11829
11875
  if (decision.kind === "skip") return next();
11830
11876
  const key = headerOf(req.headers[header]);
@@ -11863,7 +11909,7 @@ function withIdempotency(opts, handler) {
11863
11909
  path: new URL(req.url).pathname,
11864
11910
  body,
11865
11911
  key: req.headers.get(header) ?? void 0,
11866
- auth: req.headers.get("authorization") ?? req.headers.get("x-api-key") ?? void 0
11912
+ auth: req.headers.get("authorization") ?? req.headers.get("x-api-key") ?? req.headers.get("x-astra-id") ?? void 0
11867
11913
  });
11868
11914
  if (decision.kind === "skip") return handler(req);
11869
11915
  const key = req.headers.get(header) ?? void 0;
@@ -11920,18 +11966,95 @@ function orderReportAmount(result, fallback) {
11920
11966
  return fallback;
11921
11967
  }
11922
11968
 
11969
+ // src/ucp/kit-shared.ts
11970
+ var import_crypto7 = require("crypto");
11971
+ function isHeld(result) {
11972
+ const r = result;
11973
+ return Boolean(
11974
+ r.requiresStepUp || r.requiresApproval || r.recommendation === "step_up_required" || r.settlementOutcome?.status === "requires_approval" || r.stepUpApproval && r.stepUpApproval.status !== "consumed"
11975
+ );
11976
+ }
11977
+ var scopeHash = (kind, value) => `${kind}:${(0, import_crypto7.createHash)("sha256").update(`${kind}
11978
+ ${value}`).digest("hex").slice(0, 32)}`;
11979
+ async function resolveCallerScope(req, ctx, resolve) {
11980
+ const verified = ctx?.callerId ?? (resolve ? await resolve(req) : void 0);
11981
+ if (verified) return scopeHash("astra", verified);
11982
+ const auth = req.headers.get("authorization");
11983
+ if (auth) return scopeHash("auth", auth);
11984
+ const astraId = req.headers.get("x-astra-id");
11985
+ if (astraId) return scopeHash("astra-header", astraId);
11986
+ return void 0;
11987
+ }
11988
+ var fallbackScope = (kind, value) => scopeHash(kind, value);
11989
+ function pendingResult(result) {
11990
+ const {
11991
+ settlementToken: _t,
11992
+ settlement: _s,
11993
+ mandates: _m,
11994
+ kyaToken: _k,
11995
+ aep: _a,
11996
+ ...rest
11997
+ } = result;
11998
+ return rest;
11999
+ }
12000
+ var warned = /* @__PURE__ */ new Set();
12001
+ function warnOnce(key, message) {
12002
+ if (warned.has(key) || process.env.NODE_ENV === "test") return;
12003
+ warned.add(key);
12004
+ console.warn(message);
12005
+ }
12006
+ function warnInMemoryStores(kit, missing) {
12007
+ if (!missing.length || process.env.NODE_ENV === "test") return;
12008
+ console.warn(
12009
+ `[astrasync] ${kit} checkout kit is using the in-memory ${missing.join(" and ")} (sessions are lost on restart and not shared across instances). Pass ${missing.map((m) => m.startsWith("idempotency") ? "idempotencyStore" : "store").join(" and ")} in production.`
12010
+ );
12011
+ }
12012
+
11923
12013
  // src/ucp/checkout-kit.ts
11924
12014
  var MemoryCheckoutStore = class {
11925
12015
  constructor() {
11926
12016
  this.m = /* @__PURE__ */ new Map();
12017
+ this.orders = /* @__PURE__ */ new Map();
11927
12018
  }
11928
12019
  async get(id) {
11929
12020
  return this.m.get(id) ?? null;
11930
12021
  }
11931
12022
  async put(s) {
11932
12023
  this.m.set(s.id, s);
12024
+ if (s.order) this.orders.set(s.order.id, s.id);
12025
+ }
12026
+ async claim(id, from, to) {
12027
+ const s = this.m.get(id);
12028
+ if (!s || s.status !== from) return false;
12029
+ s.status = to;
12030
+ return true;
12031
+ }
12032
+ async findByOrderId(orderId) {
12033
+ const id = this.orders.get(orderId);
12034
+ return id ? this.m.get(id) ?? null : null;
11933
12035
  }
11934
12036
  };
12037
+ function buildUcpOrder(session, opts = {}) {
12038
+ if (!session.order) return null;
12039
+ const version = opts.version ?? UCP_VERSION;
12040
+ return {
12041
+ ucp: { version, capabilities: { [UCP_ORDER_CAPABILITY]: [{ version }] } },
12042
+ id: session.order.id,
12043
+ checkout_id: session.id,
12044
+ ...session.order.permalink_url && { permalink_url: session.order.permalink_url },
12045
+ currency: session.currency,
12046
+ line_items: session.line_items.map((l) => ({
12047
+ id: l.id,
12048
+ item: l.item,
12049
+ quantity: { total: l.quantity, fulfilled: 0 },
12050
+ totals: l.totals,
12051
+ status: "processing"
12052
+ })),
12053
+ fulfillment: { expectations: [], events: [] },
12054
+ adjustments: [],
12055
+ totals: session.totals
12056
+ };
12057
+ }
11935
12058
  var json2 = (status, body, headers = {}) => new Response(JSON.stringify(body), {
11936
12059
  status,
11937
12060
  headers: { "content-type": "application/json", ...headers }
@@ -11944,6 +12067,24 @@ function createUcpCheckoutKit(opts) {
11944
12067
  store: opts.idempotencyStore ?? new MemoryIdempotencyStore(),
11945
12068
  profile: "ucp"
11946
12069
  };
12070
+ warnInMemoryStores("UCP", [
12071
+ ...opts.store ? [] : ["checkout store"],
12072
+ ...opts.idempotencyStore ? [] : ["idempotency store"]
12073
+ ]);
12074
+ if (!store.claim) {
12075
+ warnOnce(
12076
+ "ucp-claim",
12077
+ "[astrasync] UCP checkout kit: your CheckoutStore has no claim() \u2014 concurrent completes of one checkout aren't locked. Implement claim(id, from, to) as an atomic compare-and-set."
12078
+ );
12079
+ }
12080
+ const orderIndex = /* @__PURE__ */ new Map();
12081
+ const ordersOn = Boolean(profile.ucp.capabilities?.[UCP_ORDER_CAPABILITY]);
12082
+ const handlerIds = new Set(
12083
+ Object.entries(profile.ucp.payment_handlers).flatMap(([name, entries]) => [
12084
+ name,
12085
+ ...entries.map((e) => e.id)
12086
+ ])
12087
+ );
11947
12088
  const fetchProfile = opts.fetchProfile ?? (async (url, fresh) => {
11948
12089
  const r = await fetchUcpProfile(url, { fresh });
11949
12090
  return r.ok ? r.profile : r;
@@ -11955,12 +12096,12 @@ function createUcpCheckoutKit(opts) {
11955
12096
  const [h, , sig] = attached.split(".");
11956
12097
  return { detached: `${h}..${sig}`, attached };
11957
12098
  }
11958
- async function render(s, status = 200) {
12099
+ async function render(s, status = 200, mode = "persist") {
11959
12100
  const view = {
11960
12101
  ucp: {
11961
12102
  version: profile.ucp.version,
11962
12103
  capabilities: Object.fromEntries(
11963
- Object.keys(profile.ucp.capabilities ?? {}).filter((k) => k !== "dev.ucp.common.payment.ap2_mandate" || s._ap2).map((k) => [k, [{ version: profile.ucp.version }]])
12104
+ Object.keys(profile.ucp.capabilities ?? {}).filter((k) => k !== "dev.ucp.common.payment.ap2_mandate" || s._ap2).filter((k) => k !== UCP_ORDER_CAPABILITY).map((k) => [k, [{ version: profile.ucp.version }]])
11964
12105
  ),
11965
12106
  payment_handlers: profile.ucp.payment_handlers
11966
12107
  },
@@ -11969,21 +12110,132 @@ function createUcpCheckoutKit(opts) {
11969
12110
  currency: s.currency,
11970
12111
  line_items: s.line_items,
11971
12112
  totals: s.totals,
12113
+ ...opts.links?.length && { links: opts.links },
11972
12114
  ...s.buyer && { buyer: s.buyer },
11973
12115
  ...s.messages?.length && { messages: s.messages },
11974
12116
  ...s.continue_url && s.status === "requires_escalation" && { continue_url: s.continue_url },
11975
12117
  ...s.order && { order: s.order }
11976
12118
  };
11977
- if (s._ap2) {
11978
- const auth = await merchantAuthorization(view);
11979
- if (auth) {
11980
- view.ap2 = { merchant_authorization: auth.detached };
11981
- s._checkoutJwt = auth.attached;
11982
- await store.put(s);
12119
+ if (s._ap2 && opts.signingKey) {
12120
+ const viewHash = (0, import_crypto8.createHash)("sha256").update(canonicalJson2(view)).digest("hex");
12121
+ if (mode === "reuse" && s._merchantAuth && s._merchantAuthFor === viewHash) {
12122
+ view.ap2 = { merchant_authorization: s._merchantAuth };
12123
+ } else {
12124
+ const auth = await merchantAuthorization(view);
12125
+ if (auth) {
12126
+ view.ap2 = { merchant_authorization: auth.detached };
12127
+ if (mode !== "ephemeral") {
12128
+ s._checkoutJwt = auth.attached;
12129
+ s._merchantAuth = auth.detached;
12130
+ s._merchantAuthFor = viewHash;
12131
+ await store.put(s);
12132
+ }
12133
+ }
11983
12134
  }
11984
12135
  }
11985
12136
  return json2(status, view);
11986
12137
  }
12138
+ function busy(s) {
12139
+ return render(
12140
+ {
12141
+ ...s,
12142
+ messages: [
12143
+ ...s.messages ?? [],
12144
+ ucpError(
12145
+ "checkout_in_progress",
12146
+ "This checkout is already being completed. Retry shortly.",
12147
+ "recoverable"
12148
+ )
12149
+ ]
12150
+ },
12151
+ 200,
12152
+ "ephemeral"
12153
+ );
12154
+ }
12155
+ async function save(s) {
12156
+ if (s.order) orderIndex.set(s.order.id, s.id);
12157
+ await store.put(s);
12158
+ }
12159
+ function webhookTarget(s) {
12160
+ const w = opts.webhook;
12161
+ if (!w) return null;
12162
+ const url = w.url ?? s._webhookUrl;
12163
+ const privateKey = w.privateKey ?? opts.signingKey?.privateKey ?? void 0;
12164
+ const keyId = w.keyId ?? opts.signingKey?.kid;
12165
+ if (!url || !privateKey || !keyId) return null;
12166
+ return { w, url, privateKey, keyId };
12167
+ }
12168
+ async function deliverOrder(s) {
12169
+ const target = webhookTarget(s);
12170
+ const order = buildUcpOrder(s, { version: profile.ucp.version });
12171
+ if (!target || !order) return null;
12172
+ const r = await sendUcpWebhook({
12173
+ url: target.url,
12174
+ body: order,
12175
+ privateKey: target.privateKey,
12176
+ keyId: target.keyId,
12177
+ businessProfileUrl: target.w.businessProfileUrl ?? new URL("/.well-known/ucp", opts.profile.endpoint).toString(),
12178
+ ...target.w.attempts && { attempts: target.w.attempts },
12179
+ ...target.w.fetch && { fetch: target.w.fetch }
12180
+ });
12181
+ target.w.onResult?.(r, order);
12182
+ return r;
12183
+ }
12184
+ function emitOrder(s) {
12185
+ void deliverOrder(s).catch(() => void 0);
12186
+ }
12187
+ async function reportOutcome(p, settled) {
12188
+ if (!opts.reportSettlement || !p.orderId) return;
12189
+ await opts.reportSettlement({
12190
+ orderId: p.orderId,
12191
+ status: settled.status,
12192
+ ...p.report,
12193
+ ...settled.status === "settled" && settled.processorRef && { processorRef: settled.processorRef },
12194
+ ...settled.status === "settled" && settled.txHash && { txHash: settled.txHash, chainId: settled.chainId },
12195
+ ...settled.status === "failed" && settled.failureCode && { failureCode: settled.failureCode }
12196
+ }).catch(() => void 0);
12197
+ }
12198
+ async function finish(s, result, p, settled) {
12199
+ await reportOutcome(p, settled);
12200
+ delete s._pending;
12201
+ if (settled.status === "failed") {
12202
+ s.status = "ready_for_complete";
12203
+ s.messages = [
12204
+ ucpError("payment_failed", "The payment did not go through.", "requires_buyer_input")
12205
+ ];
12206
+ await save(s);
12207
+ return;
12208
+ }
12209
+ const order = await opts.fulfil({ session: s, result });
12210
+ s.order = {
12211
+ ...order,
12212
+ ...!order.permalink_url && opts.permalinkBase && {
12213
+ permalink_url: `${opts.permalinkBase.replace(/\/+$/, "")}/${encodeURIComponent(order.id)}`
12214
+ }
12215
+ };
12216
+ s.status = "completed";
12217
+ s.messages = [];
12218
+ await save(s);
12219
+ emitOrder(s);
12220
+ }
12221
+ async function claim2(s, from) {
12222
+ if (store.claim) {
12223
+ if (!await store.claim(s.id, from, "complete_in_progress")) return false;
12224
+ s.status = "complete_in_progress";
12225
+ return true;
12226
+ }
12227
+ s.status = "complete_in_progress";
12228
+ await store.put(s);
12229
+ return true;
12230
+ }
12231
+ async function bindAgent(req, result) {
12232
+ const mode = opts.ucpAgentBinding ?? "registered";
12233
+ if (mode === "off") return null;
12234
+ const identities = opts.resolveIdentities ? await opts.resolveIdentities(req, result) : result.agent?.protocolIdentities;
12235
+ if (mode === "registered" && !identities?.ucpProfile) return null;
12236
+ const b = checkUcpAgentBinding(req.headers.get("ucp-agent"), identities);
12237
+ return b.ok ? null : b;
12238
+ }
11987
12239
  async function priceLines(lines) {
11988
12240
  const out = [];
11989
12241
  const messages = [];
@@ -12076,13 +12328,13 @@ function createUcpCheckoutKit(opts) {
12076
12328
  ap2: Boolean(neg.capabilities["dev.ucp.common.payment.ap2_mandate"])
12077
12329
  };
12078
12330
  }
12079
- async function withIdempotency2(req, body, path, run) {
12331
+ async function withIdempotency2(req, body, path, caller, run) {
12080
12332
  const d = await beginIdempotent(idem, {
12081
12333
  method: req.method,
12082
12334
  path,
12083
12335
  body,
12084
12336
  key: req.headers.get("idempotency-key") ?? void 0,
12085
- auth: req.headers.get("ucp-agent") ?? void 0
12337
+ auth: caller
12086
12338
  });
12087
12339
  if (d.kind === "reject")
12088
12340
  return json2(d.status, { type: "invalid_request", code: d.code, message: d.message });
@@ -12103,6 +12355,7 @@ function createUcpCheckoutKit(opts) {
12103
12355
  }
12104
12356
  async function complete(req, s, body) {
12105
12357
  if (s.status === "completed" || s.status === "canceled") return render(s);
12358
+ if (s.status === "complete_in_progress") return busy(s);
12106
12359
  const ap2 = body.ap2;
12107
12360
  const checkoutJwt = s._ap2 ? s._checkoutJwt : void 0;
12108
12361
  if (s._ap2) {
@@ -12114,6 +12367,7 @@ function createUcpCheckoutKit(opts) {
12114
12367
  "unrecoverable"
12115
12368
  )
12116
12369
  ];
12370
+ await store.put(s);
12117
12371
  return render(s);
12118
12372
  }
12119
12373
  const kidResolver = async (kid) => opts.mandateKey ? opts.mandateKey(kid, s) : (s._platformKeys ?? []).find((k) => k.kid === kid) ?? null;
@@ -12124,71 +12378,106 @@ function createUcpCheckoutKit(opts) {
12124
12378
  });
12125
12379
  if (!v.ok) {
12126
12380
  s.messages = [ucpError(v.code, v.error, "unrecoverable")];
12381
+ await store.put(s);
12127
12382
  return render(s);
12128
12383
  }
12129
12384
  }
12130
- const result = await opts.verifyPurchase({
12131
- session: s,
12132
- checkoutSessionId: s._astraCheckoutSessionId ?? s.id,
12133
- astraId: req.headers.get("x-astra-id") ?? void 0,
12134
- attemptId: req.headers.get("x-astra-attempt-id") ?? void 0,
12135
- ...checkoutJwt && { checkoutJwt },
12136
- valueMajor: total(s) / 100
12137
- });
12138
- const held = result.settlementOutcome?.status === "requires_approval" || result.stepUpApproval && result.stepUpApproval.status === "pending";
12139
- if (held) {
12140
- s.status = "requires_escalation";
12141
- s.continue_url = result.stepUpApproval?.approvalUrl ?? opts.fallbackContinueUrl;
12142
- s.messages = [
12143
- ucpError(
12144
- "buyer_approval_required",
12145
- "The buyer needs to approve this purchase.",
12146
- "requires_buyer_review"
12147
- )
12148
- ];
12149
- await store.put(s);
12150
- return render(s);
12151
- }
12152
- if (!result.identityVerified || !result.policyAllowed) {
12385
+ const instruments = body.payment?.instruments;
12386
+ const instrument = Array.isArray(instruments) && instruments.length === 1 ? instruments[0] : null;
12387
+ if (!instrument || typeof instrument.handler_id !== "string" || !handlerIds.has(instrument.handler_id)) {
12153
12388
  s.messages = [
12154
12389
  ucpError(
12155
- "agent_not_authorized",
12156
- "The agent is not authorized for this purchase.",
12157
- "unrecoverable"
12390
+ "payment_failed",
12391
+ "Send exactly one payment instrument whose handler_id is one of this business\u2019s payment_handlers.",
12392
+ "recoverable",
12393
+ "$.payment.instruments"
12158
12394
  )
12159
12395
  ];
12160
12396
  await store.put(s);
12161
12397
  return render(s);
12162
12398
  }
12163
- s.status = "complete_in_progress";
12164
- const instruments = body.payment?.instruments;
12165
- const settled = await opts.settle({ session: s, result, instrument: instruments?.[0] });
12166
- const orderId = result.settlementOutcome?.orderId;
12167
- if (opts.reportSettlement && orderId && settled.status !== "pending") {
12168
- await opts.reportSettlement({
12169
- orderId,
12170
- status: settled.status === "settled" ? "settled" : "failed",
12171
- ...orderReportAmount(result, { amountMinor: total(s), currency: s.currency }),
12172
- ...settled.status === "settled" && settled.processorRef && { processorRef: settled.processorRef },
12173
- ...settled.status === "settled" && settled.txHash && { txHash: settled.txHash, chainId: settled.chainId },
12174
- ...settled.status === "failed" && settled.failureCode && { failureCode: settled.failureCode }
12175
- }).catch(() => void 0);
12399
+ const from = s.status;
12400
+ if (!await claim2(s, from)) {
12401
+ const current = await store.get(s.id);
12402
+ return busy(current ?? s);
12176
12403
  }
12177
- if (settled.status === "failed") {
12178
- s.status = "ready_for_complete";
12179
- s.messages = [
12180
- ucpError("payment_failed", "The payment did not go through.", "requires_buyer_input")
12181
- ];
12182
- await store.put(s);
12404
+ try {
12405
+ const result = await opts.verifyPurchase({
12406
+ session: s,
12407
+ checkoutSessionId: s._astraCheckoutSessionId ?? s.id,
12408
+ astraId: req.headers.get("x-astra-id") ?? void 0,
12409
+ attemptId: req.headers.get("x-astra-attempt-id") ?? void 0,
12410
+ ...checkoutJwt && { checkoutJwt },
12411
+ valueMajor: total(s) / 100
12412
+ });
12413
+ const binding2 = await bindAgent(req, result);
12414
+ if (binding2) {
12415
+ s.status = from;
12416
+ s.messages = [ucpError(binding2.code, binding2.message, "unrecoverable")];
12417
+ await store.put(s);
12418
+ return render(s);
12419
+ }
12420
+ if (isHeld(result)) {
12421
+ s.status = "requires_escalation";
12422
+ s.continue_url = result.stepUpApproval?.approvalUrl ?? opts.fallbackContinueUrl;
12423
+ s.messages = [
12424
+ ucpError(
12425
+ "buyer_approval_required",
12426
+ "The buyer needs to approve this purchase.",
12427
+ "requires_buyer_review"
12428
+ )
12429
+ ];
12430
+ await store.put(s);
12431
+ return render(s);
12432
+ }
12433
+ if (!result.identityVerified || !result.policyAllowed) {
12434
+ s.status = from;
12435
+ s.messages = [
12436
+ ucpError(
12437
+ "agent_not_authorized",
12438
+ "The agent is not authorized for this purchase.",
12439
+ "unrecoverable"
12440
+ )
12441
+ ];
12442
+ await store.put(s);
12443
+ return render(s);
12444
+ }
12445
+ const settled = await opts.settle({ session: s, result, instrument });
12446
+ const pending = {
12447
+ ...result.settlementOutcome?.orderId && {
12448
+ orderId: result.settlementOutcome.orderId
12449
+ },
12450
+ report: orderReportAmount(result, { amountMinor: total(s), currency: s.currency }),
12451
+ result: pendingResult(result)
12452
+ };
12453
+ if (settled.status === "pending") {
12454
+ s._pending = pending;
12455
+ s.messages = [];
12456
+ await store.put(s);
12457
+ return render(s);
12458
+ }
12459
+ await finish(s, result, pending, settled);
12183
12460
  return render(s);
12461
+ } catch (e) {
12462
+ if (s.status === "complete_in_progress" && !s._pending) {
12463
+ s.status = from;
12464
+ await store.put(s).catch(() => void 0);
12465
+ }
12466
+ throw e;
12184
12467
  }
12185
- s.order = await opts.fulfil({ session: s, result });
12186
- s.status = "completed";
12187
- s.messages = [];
12188
- await store.put(s);
12189
- return render(s);
12190
12468
  }
12191
- async function handle(req) {
12469
+ async function resolvePending(sessionId, outcome) {
12470
+ const s = await store.get(sessionId);
12471
+ if (!s) return null;
12472
+ if (s.status !== "complete_in_progress" || !s._pending) return s;
12473
+ await finish(s, s._pending.result, s._pending, outcome);
12474
+ return s;
12475
+ }
12476
+ async function sendOrderWebhook(sessionId) {
12477
+ const s = await store.get(sessionId);
12478
+ return s ? deliverOrder(s) : null;
12479
+ }
12480
+ async function handle(req, reqCtx) {
12192
12481
  const base = new URL(opts.profile.endpoint).pathname.replace(/\/+$/, "");
12193
12482
  const url = new URL(req.url);
12194
12483
  const path = url.pathname.startsWith(base) ? url.pathname.slice(base.length) : url.pathname;
@@ -12202,20 +12491,24 @@ function createUcpCheckoutKit(opts) {
12202
12491
  const p = await platform(req, body);
12203
12492
  if ("error" in p && p.error) return p.error;
12204
12493
  const ctx = p;
12494
+ const caller = await resolveCallerScope(req, reqCtx, opts.resolveCallerId) ?? fallbackScope("ucp-profile", ctx.profileUrl);
12205
12495
  if (req.method === "POST" && path === "/checkout-sessions") {
12206
- return withIdempotency2(req, body, path, async () => {
12496
+ return withIdempotency2(req, body, path, caller, async () => {
12207
12497
  const priced = await priceLines(parsed.line_items ?? []);
12498
+ const webhookUrl = ctx.plat.ucp.capabilities?.[UCP_ORDER_CAPABILITY]?.[0]?.config?.webhook_url;
12208
12499
  const s2 = {
12209
- id: `chk_${(0, import_crypto7.randomUUID)().replace(/-/g, "")}`,
12500
+ id: `chk_${(0, import_crypto8.randomUUID)().replace(/-/g, "")}`,
12210
12501
  status: priced.lines.length && !priced.messages.length ? "ready_for_complete" : "incomplete",
12211
12502
  currency: opts.currency,
12212
12503
  line_items: priced.lines,
12213
12504
  totals: priced.totals,
12214
12505
  messages: priced.messages,
12215
12506
  ...parsed.buyer ? { buyer: parsed.buyer } : {},
12507
+ _caller: caller,
12216
12508
  _platformProfile: ctx.profileUrl,
12217
12509
  _ap2: ctx.ap2,
12218
12510
  _platformKeys: ctx.plat.keys ?? [],
12511
+ ...typeof webhookUrl === "string" && /^https:\/\//.test(webhookUrl) && { _webhookUrl: webhookUrl },
12219
12512
  ...req.headers.get("x-astra-checkout-session") && {
12220
12513
  _astraCheckoutSessionId: req.headers.get("x-astra-checkout-session").slice(0, 64)
12221
12514
  }
@@ -12234,16 +12527,28 @@ function createUcpCheckoutKit(opts) {
12234
12527
  return render(s2, 201);
12235
12528
  });
12236
12529
  }
12530
+ const notFound = (what = "checkout session") => json2(404, { code: "not_found", content: `Unknown ${what}.` });
12531
+ const om = /^\/orders\/([^/]+)$/.exec(path);
12532
+ if (om && ordersOn) {
12533
+ if (req.method !== "GET")
12534
+ return json2(405, { code: "method_not_allowed", content: "Method not allowed." });
12535
+ const orderId = decodeURIComponent(om[1]);
12536
+ const viaIndex = orderIndex.get(orderId);
12537
+ const s2 = store.findByOrderId ? await store.findByOrderId(orderId) : viaIndex ? await store.get(viaIndex) : null;
12538
+ if (!s2 || s2._caller !== caller || s2._platformProfile !== ctx.profileUrl)
12539
+ return notFound("order");
12540
+ const order = buildUcpOrder(s2, { version: profile.ucp.version });
12541
+ return order ? json2(200, order) : notFound("order");
12542
+ }
12237
12543
  const m = /^\/checkout-sessions\/([^/]+)(\/complete|\/cancel)?$/.exec(path);
12238
12544
  if (!m) return json2(404, { code: "not_found", content: "Unknown operation." });
12239
12545
  const s = await store.get(m[1]);
12240
- if (!s) return json2(404, { code: "not_found", content: "Unknown checkout session." });
12241
- if (s._platformProfile !== ctx.profileUrl)
12242
- return json2(404, { code: "not_found", content: "Unknown checkout session." });
12243
- if (req.method === "GET" && !m[2]) return render(s);
12546
+ if (!s || s._caller !== caller || s._platformProfile !== ctx.profileUrl) return notFound();
12547
+ if (req.method === "GET" && !m[2]) return render(s, 200, "reuse");
12244
12548
  if (req.method === "PUT" && !m[2]) {
12245
- return withIdempotency2(req, body, path, async () => {
12549
+ return withIdempotency2(req, body, path, caller, async () => {
12246
12550
  if (s.status === "completed" || s.status === "canceled") return render(s);
12551
+ if (s.status === "complete_in_progress") return busy(s);
12247
12552
  if (parsed.line_items) {
12248
12553
  const priced = await priceLines(parsed.line_items);
12249
12554
  s.line_items = priced.lines;
@@ -12257,11 +12562,17 @@ function createUcpCheckoutKit(opts) {
12257
12562
  });
12258
12563
  }
12259
12564
  if (req.method === "POST" && m[2] === "/complete") {
12260
- return withIdempotency2(req, body, path, () => complete(req, s, parsed));
12565
+ return withIdempotency2(req, body, path, caller, () => complete(req, s, parsed));
12261
12566
  }
12262
12567
  if (req.method === "POST" && m[2] === "/cancel") {
12263
- return withIdempotency2(req, body, path, async () => {
12264
- if (s.status !== "completed") s.status = "canceled";
12568
+ return withIdempotency2(req, body, path, caller, async () => {
12569
+ if (s.status === "completed" || s.status === "canceled")
12570
+ return json2(405, {
12571
+ code: "method_not_allowed",
12572
+ content: `A ${s.status} checkout can't be canceled.`
12573
+ });
12574
+ if (s.status === "complete_in_progress") return busy(s);
12575
+ s.status = "canceled";
12265
12576
  await store.put(s);
12266
12577
  return render(s);
12267
12578
  });
@@ -12271,7 +12582,9 @@ function createUcpCheckoutKit(opts) {
12271
12582
  return {
12272
12583
  handle,
12273
12584
  profile,
12274
- profileHandler: () => json2(200, profile, { "cache-control": "public, max-age=300" })
12585
+ profileHandler: () => json2(200, profile, { "cache-control": "public, max-age=300" }),
12586
+ resolvePending,
12587
+ sendOrderWebhook
12275
12588
  };
12276
12589
  }
12277
12590
 
@@ -12324,7 +12637,7 @@ function x402PaymentMatchesOffer(payment, offered) {
12324
12637
  }
12325
12638
 
12326
12639
  // src/payments/mpp.ts
12327
- var import_crypto8 = require("crypto");
12640
+ var import_crypto9 = require("crypto");
12328
12641
  var b64url = (s) => Buffer.from(s, "utf8").toString("base64url");
12329
12642
  function canonical(value) {
12330
12643
  if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
@@ -12333,27 +12646,29 @@ function canonical(value) {
12333
12646
  }
12334
12647
  return JSON.stringify(value);
12335
12648
  }
12336
- var BOUND = [
12337
- "realm",
12338
- "method",
12339
- "intent",
12340
- "request",
12341
- "expires",
12342
- "digest",
12343
- "opaque",
12344
- "header"
12345
- ];
12649
+ var requestSlot = (request) => typeof request === "string" ? request : b64url(canonical(request ?? {}));
12346
12650
  function binding(c, secret) {
12347
- const material = BOUND.map(
12348
- (k) => `${k}=${c[k] ?? ""}`
12349
- ).join("\n");
12350
- return (0, import_crypto8.createHmac)("sha256", secret).update(material).digest("base64url");
12651
+ const slots = [
12652
+ c.realm,
12653
+ c.method,
12654
+ c.intent,
12655
+ requestSlot(c.request),
12656
+ c.expires ?? "",
12657
+ c.digest ?? ""
12658
+ ];
12659
+ if (c.header && c.header.toLowerCase() !== "authorization") slots.push(c.header);
12660
+ slots.push(c.opaque ?? "");
12661
+ return (0, import_crypto9.createHmac)("sha256", secret).update(slots.join("|")).digest("base64url");
12351
12662
  }
12352
12663
  var quote = (v) => `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
12353
12664
  function buildMppChallenge(input, hmacSecret) {
12354
12665
  if (!/^[a-z]+$/.test(input.method)) throw new Error("mpp: method must be lowercase ASCII");
12355
- const { request, ...rest } = input;
12356
- const c = { ...rest, request: b64url(canonical(request)) };
12666
+ const { request, meta, ...rest } = input;
12667
+ const c = {
12668
+ ...rest,
12669
+ request: b64url(canonical(request)),
12670
+ ...rest.opaque === void 0 && meta !== void 0 && { opaque: b64url(canonical(meta)) }
12671
+ };
12357
12672
  const challenge = { id: binding(c, hmacSecret), ...c };
12358
12673
  const parts = Object.keys(challenge).filter((k) => challenge[k] !== void 0).map((k) => `${k}=${quote(String(challenge[k]))}`);
12359
12674
  return { challenge, header: `Payment ${parts.join(", ")}` };
@@ -12370,13 +12685,71 @@ function parseMppCredential(header) {
12370
12685
  return null;
12371
12686
  }
12372
12687
  }
12373
- function verifyMppChallenge(challenge, hmacSecret, now = /* @__PURE__ */ new Date()) {
12688
+ var MemoryMppChallengeStore = class {
12689
+ constructor(defaultTtlMs = 60 * 60 * 1e3, now = Date.now) {
12690
+ this.defaultTtlMs = defaultTtlMs;
12691
+ this.now = now;
12692
+ this.seen = /* @__PURE__ */ new Map();
12693
+ }
12694
+ async consume(id, expiresAt) {
12695
+ const t = this.now();
12696
+ for (const [k, exp] of this.seen) if (exp <= t) this.seen.delete(k);
12697
+ if (this.seen.has(id)) return false;
12698
+ this.seen.set(id, Number.isFinite(expiresAt) ? expiresAt : t + this.defaultTtlMs);
12699
+ return true;
12700
+ }
12701
+ };
12702
+ function mppBodyDigest(body) {
12703
+ return `sha-256=${(0, import_crypto9.createHash)("sha256").update(body).digest("base64")}`;
12704
+ }
12705
+ function checkChallenge(challenge, hmacSecret, now) {
12374
12706
  const { id, ...rest } = challenge;
12375
12707
  const expected = Buffer.from(binding(rest, hmacSecret));
12376
12708
  const got = Buffer.from(String(id ?? ""));
12377
- if (expected.length !== got.length || !(0, import_crypto8.timingSafeEqual)(expected, got)) return false;
12378
- if (challenge.expires && Date.parse(challenge.expires) < now.getTime()) return false;
12379
- return true;
12709
+ if (expected.length !== got.length || !(0, import_crypto9.timingSafeEqual)(expected, got))
12710
+ return { ok: false, reason: "invalid_id" };
12711
+ if (challenge.expires && Date.parse(challenge.expires) < now.getTime())
12712
+ return { ok: false, reason: "expired" };
12713
+ return { ok: true };
12714
+ }
12715
+ function verifyMppChallenge(challenge, hmacSecret, nowOrOptions = /* @__PURE__ */ new Date()) {
12716
+ if (nowOrOptions instanceof Date) return checkChallenge(challenge, hmacSecret, nowOrOptions).ok;
12717
+ const opts = nowOrOptions;
12718
+ return (async () => {
12719
+ const base = checkChallenge(challenge, hmacSecret, opts.now ?? /* @__PURE__ */ new Date());
12720
+ if (!base.ok) return base;
12721
+ if (challenge.digest) {
12722
+ if (opts.body === void 0) return { ok: false, reason: "body_required" };
12723
+ const want = Buffer.from(challenge.digest);
12724
+ const got = Buffer.from(mppBodyDigest(opts.body));
12725
+ if (want.length !== got.length || !(0, import_crypto9.timingSafeEqual)(want, got))
12726
+ return { ok: false, reason: "digest_mismatch" };
12727
+ }
12728
+ if (opts.store) {
12729
+ const expiresAt = challenge.expires ? Date.parse(challenge.expires) : NaN;
12730
+ if (!await opts.store.consume(challenge.id, expiresAt))
12731
+ return { ok: false, reason: "already_used" };
12732
+ }
12733
+ return { ok: true };
12734
+ })();
12735
+ }
12736
+ function buildMpp402Response(challenges, problem = {}) {
12737
+ const list = Array.isArray(challenges) ? challenges : [challenges];
12738
+ const headers = new Headers({
12739
+ "content-type": "application/problem+json",
12740
+ "cache-control": "no-store"
12741
+ });
12742
+ for (const c of list) headers.append("www-authenticate", typeof c === "string" ? c : c.header);
12743
+ return new Response(
12744
+ JSON.stringify({
12745
+ type: problem.type ?? "about:blank",
12746
+ title: problem.title ?? "Payment Required",
12747
+ status: 402,
12748
+ ...problem.detail && { detail: problem.detail },
12749
+ ...problem.instance && { instance: problem.instance }
12750
+ }),
12751
+ { status: 402, headers }
12752
+ );
12380
12753
  }
12381
12754
  function decodeMppRequest(challenge) {
12382
12755
  try {
@@ -12453,7 +12826,7 @@ async function verifyKyaPayTokens(header, opts) {
12453
12826
  }
12454
12827
 
12455
12828
  // src/aep/core.ts
12456
- var import_crypto9 = require("crypto");
12829
+ var import_crypto10 = require("crypto");
12457
12830
  var AEP_SPEC_VERSION = "aep-1.0.3-rc.2";
12458
12831
  function aepCanonicalJson(value) {
12459
12832
  if (value === null) return "null";
@@ -12474,7 +12847,7 @@ function aepCanonicalJson(value) {
12474
12847
  throw new Error(`AEP canonical JSON: unsupported value of type ${typeof value}`);
12475
12848
  }
12476
12849
  function digest(input) {
12477
- return (0, import_crypto9.createHash)("sha256").update(aepCanonicalJson(input), "utf8").digest();
12850
+ return (0, import_crypto10.createHash)("sha256").update(aepCanonicalJson(input), "utf8").digest();
12478
12851
  }
12479
12852
  function aepHash(input) {
12480
12853
  return `sha256:${digest(input).toString("hex")}`;
@@ -12487,23 +12860,23 @@ var ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
12487
12860
  function aepPrivateKeyFromSeed(seedBase64) {
12488
12861
  const seed = Buffer.from(seedBase64, "base64");
12489
12862
  if (seed.length !== 32) throw new Error("Ed25519 seed must be 32 bytes");
12490
- return (0, import_crypto9.createPrivateKey)({
12863
+ return (0, import_crypto10.createPrivateKey)({
12491
12864
  key: Buffer.concat([ED25519_PKCS8_PREFIX, seed]),
12492
12865
  format: "der",
12493
12866
  type: "pkcs8"
12494
12867
  });
12495
12868
  }
12496
12869
  function aepPublicKeyFromBase64(raw) {
12497
- const bytes = Buffer.from(raw, "base64");
12498
- if (bytes.length !== 32) throw new Error("Ed25519 public key must be 32 bytes");
12499
- return (0, import_crypto9.createPublicKey)({
12500
- key: Buffer.concat([ED25519_SPKI_PREFIX, bytes]),
12870
+ const bytes2 = Buffer.from(raw, "base64");
12871
+ if (bytes2.length !== 32) throw new Error("Ed25519 public key must be 32 bytes");
12872
+ return (0, import_crypto10.createPublicKey)({
12873
+ key: Buffer.concat([ED25519_SPKI_PREFIX, bytes2]),
12501
12874
  format: "der",
12502
12875
  type: "spki"
12503
12876
  });
12504
12877
  }
12505
12878
  function aepRawPublicKey(key) {
12506
- const pub = key.type === "private" ? (0, import_crypto9.createPublicKey)(key) : key;
12879
+ const pub = key.type === "private" ? (0, import_crypto10.createPublicKey)(key) : key;
12507
12880
  return pub.export({ format: "der", type: "spki" }).subarray(12).toString("base64");
12508
12881
  }
12509
12882
  function hashBytes(currentHash) {
@@ -12512,11 +12885,11 @@ function hashBytes(currentHash) {
12512
12885
  return Buffer.from(m[1], "hex");
12513
12886
  }
12514
12887
  function aepSign(currentHash, privateKey) {
12515
- return (0, import_crypto9.sign)(null, hashBytes(currentHash), privateKey).toString("base64");
12888
+ return (0, import_crypto10.sign)(null, hashBytes(currentHash), privateKey).toString("base64");
12516
12889
  }
12517
12890
  function aepVerifySignature(currentHash, signature, publicKey) {
12518
12891
  try {
12519
- return (0, import_crypto9.verify)(null, hashBytes(currentHash), publicKey, Buffer.from(signature, "base64"));
12892
+ return (0, import_crypto10.verify)(null, hashBytes(currentHash), publicKey, Buffer.from(signature, "base64"));
12520
12893
  } catch {
12521
12894
  return false;
12522
12895
  }
@@ -12614,8 +12987,33 @@ function aepPublicKeyDocument(keys, revocations = []) {
12614
12987
  }
12615
12988
 
12616
12989
  // src/acp/checkout-kit.ts
12617
- var import_crypto10 = require("crypto");
12990
+ var import_crypto11 = require("crypto");
12618
12991
  var ACP_API_VERSION = "2026-04-17";
12992
+ var ACP_SUPPORTED_API_VERSIONS = [ACP_API_VERSION];
12993
+ function acpPaymentHandlers(names) {
12994
+ return names.map((name, i) => ({
12995
+ id: name.split(".").pop(),
12996
+ name,
12997
+ version: ACP_API_VERSION,
12998
+ spec: `https://astrasync.ai/ucp/handlers/${name}`,
12999
+ requires_delegate_payment: false,
13000
+ requires_pci_compliance: false,
13001
+ psp: "astrasync",
13002
+ config_schema: `https://astrasync.ai/ucp/handlers/${name}.json`,
13003
+ instrument_schemas: [],
13004
+ config: {},
13005
+ display_order: i
13006
+ }));
13007
+ }
13008
+ function buildAcpWellKnown(opts) {
13009
+ return {
13010
+ protocol: "acp",
13011
+ api_versions: [...opts.apiVersions ?? ACP_SUPPORTED_API_VERSIONS],
13012
+ endpoint: opts.endpoint,
13013
+ capabilities: { payment: { handlers: acpPaymentHandlers(opts.paymentHandlers) } },
13014
+ ...opts.links?.length && { links: opts.links }
13015
+ };
13016
+ }
12619
13017
  var json3 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
12620
13018
  var apiError = (status, code, message, param) => json3(status, { type: "invalid_request", code, message, ...param && { param } });
12621
13019
  var total2 = (s) => s.totals.find((t) => t.type === "total")?.amount ?? 0;
@@ -12623,26 +13021,30 @@ function createAcpCheckoutKit(opts) {
12623
13021
  const mem = /* @__PURE__ */ new Map();
12624
13022
  const store = opts.store ?? {
12625
13023
  get: async (id) => mem.get(id) ?? null,
12626
- put: async (s) => void mem.set(s.id, s)
13024
+ put: async (s) => void mem.set(s.id, s),
13025
+ claim: async (id, from, to) => {
13026
+ const s = mem.get(id);
13027
+ if (!s || s.status !== from) return false;
13028
+ s.status = to;
13029
+ return true;
13030
+ }
12627
13031
  };
12628
13032
  const idem = {
12629
13033
  store: opts.idempotencyStore ?? new MemoryIdempotencyStore(),
12630
13034
  profile: "acp",
12631
13035
  required: true
12632
13036
  };
12633
- const handlers = opts.paymentHandlers.map((name, i) => ({
12634
- id: name.split(".").pop(),
12635
- name,
12636
- version: ACP_API_VERSION,
12637
- spec: `https://astrasync.ai/ucp/handlers/${name}`,
12638
- requires_delegate_payment: false,
12639
- requires_pci_compliance: false,
12640
- psp: "astrasync",
12641
- config_schema: `https://astrasync.ai/ucp/handlers/${name}.json`,
12642
- instrument_schemas: [],
12643
- config: {},
12644
- display_order: i
12645
- }));
13037
+ warnInMemoryStores("ACP", [
13038
+ ...opts.store ? [] : ["checkout store"],
13039
+ ...opts.idempotencyStore ? [] : ["idempotency store"]
13040
+ ]);
13041
+ if (!store.claim) {
13042
+ warnOnce(
13043
+ "acp-claim",
13044
+ "[astrasync] ACP checkout kit: your store has no claim() \u2014 concurrent completes of one session aren't locked. Implement claim(id, from, to) as an atomic compare-and-set."
13045
+ );
13046
+ }
13047
+ const handlers = acpPaymentHandlers(opts.paymentHandlers);
12646
13048
  function view(s) {
12647
13049
  return {
12648
13050
  id: s.id,
@@ -12697,13 +13099,13 @@ function createAcpCheckoutKit(opts) {
12697
13099
  ]
12698
13100
  };
12699
13101
  }
12700
- async function idempotent(req, body, path, run) {
13102
+ async function idempotent(req, body, path, caller, run) {
12701
13103
  const d = await beginIdempotent(idem, {
12702
13104
  method: req.method,
12703
13105
  path,
12704
13106
  body,
12705
13107
  key: req.headers.get("idempotency-key") ?? void 0,
12706
- auth: req.headers.get("authorization") ?? void 0
13108
+ auth: caller
12707
13109
  });
12708
13110
  if (d.kind === "reject") return apiError(d.status, d.code, d.message);
12709
13111
  if (d.kind === "replay")
@@ -12720,63 +13122,21 @@ function createAcpCheckoutKit(opts) {
12720
13122
  });
12721
13123
  return res;
12722
13124
  }
12723
- async function complete(req, s, body) {
12724
- if (s.status === "completed" || s.status === "canceled") return json3(200, view(s));
12725
- if (!body.payment_data)
12726
- return apiError(400, "missing", "payment_data is required.", "$.payment_data");
12727
- const result = await opts.verifyPurchase({
12728
- session: s,
12729
- checkoutSessionId: s._astraCheckoutSessionId ?? s.id,
12730
- astraId: req.headers.get("x-astra-id") ?? void 0,
12731
- attemptId: req.headers.get("x-astra-attempt-id") ?? void 0,
12732
- valueMajor: total2(s) / 100
12733
- });
12734
- const held = result.settlementOutcome?.status === "requires_approval" || result.stepUpApproval?.status === "pending";
12735
- if (held) {
12736
- s.status = "pending_approval";
12737
- s.continue_url = result.stepUpApproval?.approvalUrl ?? opts.fallbackContinueUrl;
12738
- s.messages = [
12739
- {
12740
- type: "error",
12741
- code: "approval_required",
12742
- content: "The buyer needs to approve this purchase.",
12743
- severity: "medium",
12744
- resolution: "requires_buyer_review"
12745
- }
12746
- ];
12747
- await store.put(s);
12748
- return json3(200, view(s));
12749
- }
12750
- if (!result.identityVerified || !result.policyAllowed) {
12751
- s.messages = [
12752
- {
12753
- type: "error",
12754
- code: "intervention_required",
12755
- content: "The agent is not authorized for this purchase.",
12756
- severity: "high",
12757
- resolution: "requires_buyer_review"
12758
- }
12759
- ];
12760
- await store.put(s);
12761
- return json3(200, view(s));
12762
- }
12763
- s.status = "complete_in_progress";
12764
- const settled = await opts.settle({
12765
- session: s,
12766
- result,
12767
- paymentData: body.payment_data
12768
- });
12769
- const orderId = result.settlementOutcome?.orderId;
12770
- if (opts.reportSettlement && orderId && settled.status !== "pending") {
12771
- await opts.reportSettlement({
12772
- orderId,
12773
- status: settled.status === "settled" ? "settled" : "failed",
12774
- ...orderReportAmount(result, { amountMinor: total2(s), currency: s.currency }),
12775
- ...settled.status === "settled" && settled.processorRef && { processorRef: settled.processorRef },
12776
- ...settled.status === "settled" && settled.txHash && { txHash: settled.txHash, chainId: settled.chainId },
12777
- ...settled.status === "failed" && settled.failureCode && { failureCode: settled.failureCode }
12778
- }).catch(() => void 0);
12779
- }
13125
+ const inProgress = () => apiError(409, "checkout_in_progress", "This checkout session is already being completed.");
13126
+ async function reportOutcome(p, settled) {
13127
+ if (!opts.reportSettlement || !p.orderId) return;
13128
+ await opts.reportSettlement({
13129
+ orderId: p.orderId,
13130
+ status: settled.status,
13131
+ ...p.report,
13132
+ ...settled.status === "settled" && settled.processorRef && { processorRef: settled.processorRef },
13133
+ ...settled.status === "settled" && settled.txHash && { txHash: settled.txHash, chainId: settled.chainId },
13134
+ ...settled.status === "failed" && settled.failureCode && { failureCode: settled.failureCode }
13135
+ }).catch(() => void 0);
13136
+ }
13137
+ async function finish(s, result, p, settled) {
13138
+ await reportOutcome(p, settled);
13139
+ delete s._pending;
12780
13140
  if (settled.status === "failed") {
12781
13141
  s.status = "ready_for_payment";
12782
13142
  s.messages = [
@@ -12789,26 +13149,126 @@ function createAcpCheckoutKit(opts) {
12789
13149
  }
12790
13150
  ];
12791
13151
  await store.put(s);
12792
- return json3(200, view(s));
13152
+ return;
12793
13153
  }
12794
13154
  const order = await opts.fulfil({ session: s, result });
13155
+ const permalink = order.permalink_url ?? (opts.permalinkBase && `${opts.permalinkBase.replace(/\/+$/, "")}/${encodeURIComponent(order.id)}`);
12795
13156
  s.order = {
12796
13157
  id: order.id,
13158
+ checkout_session_id: s.id,
12797
13159
  status: "confirmed",
12798
13160
  currency: s.currency.toLowerCase(),
12799
13161
  total: total2(s),
12800
- line_items: [],
13162
+ line_items: s.line_items.map((l) => ({
13163
+ id: l.id,
13164
+ item: l.item,
13165
+ name: l.name,
13166
+ quantity: l.quantity,
13167
+ unit_amount: l.unit_amount,
13168
+ totals: l.totals
13169
+ })),
12801
13170
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
12802
- ...order.permalink_url && { permalink_url: order.permalink_url }
13171
+ ...permalink && { permalink_url: permalink }
12803
13172
  };
12804
13173
  s.status = "completed";
12805
13174
  s.messages = [];
12806
13175
  await store.put(s);
12807
- return json3(200, view(s));
12808
13176
  }
12809
- async function handle(req) {
12810
- if (!req.headers.get("api-version"))
12811
- return apiError(400, "missing", "The API-Version header is required.");
13177
+ async function complete(req, s, body) {
13178
+ if (s.status === "completed" || s.status === "canceled") return json3(200, view(s));
13179
+ if (s.status === "complete_in_progress") return inProgress();
13180
+ if (!body.payment_data)
13181
+ return apiError(400, "missing", "payment_data is required.", "$.payment_data");
13182
+ const from = s.status;
13183
+ if (store.claim) {
13184
+ if (!await store.claim(s.id, from, "complete_in_progress")) return inProgress();
13185
+ s.status = "complete_in_progress";
13186
+ } else {
13187
+ s.status = "complete_in_progress";
13188
+ await store.put(s);
13189
+ }
13190
+ try {
13191
+ const result = await opts.verifyPurchase({
13192
+ session: s,
13193
+ checkoutSessionId: s._astraCheckoutSessionId ?? s.id,
13194
+ astraId: req.headers.get("x-astra-id") ?? void 0,
13195
+ attemptId: req.headers.get("x-astra-attempt-id") ?? void 0,
13196
+ valueMajor: total2(s) / 100
13197
+ });
13198
+ if (isHeld(result)) {
13199
+ s.status = "pending_approval";
13200
+ s.continue_url = result.stepUpApproval?.approvalUrl ?? opts.fallbackContinueUrl;
13201
+ s.messages = [
13202
+ {
13203
+ type: "error",
13204
+ code: "approval_required",
13205
+ content: "The buyer needs to approve this purchase.",
13206
+ severity: "medium",
13207
+ resolution: "requires_buyer_review"
13208
+ }
13209
+ ];
13210
+ await store.put(s);
13211
+ return json3(200, view(s));
13212
+ }
13213
+ if (!result.identityVerified || !result.policyAllowed) {
13214
+ s.status = from;
13215
+ s.messages = [
13216
+ {
13217
+ type: "error",
13218
+ code: "intervention_required",
13219
+ content: "The agent is not authorized for this purchase.",
13220
+ severity: "high",
13221
+ resolution: "requires_buyer_review"
13222
+ }
13223
+ ];
13224
+ await store.put(s);
13225
+ return json3(200, view(s));
13226
+ }
13227
+ const settled = await opts.settle({
13228
+ session: s,
13229
+ result,
13230
+ paymentData: body.payment_data
13231
+ });
13232
+ const orderId = result.settlementOutcome?.orderId;
13233
+ const pending = {
13234
+ ...orderId && { orderId },
13235
+ report: orderReportAmount(result, { amountMinor: total2(s), currency: s.currency }),
13236
+ result: pendingResult(result)
13237
+ };
13238
+ if (settled.status === "pending") {
13239
+ s._pending = pending;
13240
+ s.messages = [];
13241
+ await store.put(s);
13242
+ return json3(200, view(s));
13243
+ }
13244
+ await finish(s, result, pending, settled);
13245
+ return json3(200, view(s));
13246
+ } catch (e) {
13247
+ if (s.status === "complete_in_progress" && !s._pending) {
13248
+ s.status = from;
13249
+ await store.put(s).catch(() => void 0);
13250
+ }
13251
+ throw e;
13252
+ }
13253
+ }
13254
+ async function resolvePending(sessionId, outcome) {
13255
+ const s = await store.get(sessionId);
13256
+ if (!s) return null;
13257
+ if (s.status !== "complete_in_progress" || !s._pending) return s;
13258
+ await finish(s, s._pending.result, s._pending, outcome);
13259
+ return s;
13260
+ }
13261
+ async function route(req, reqCtx) {
13262
+ const version = req.headers.get("api-version");
13263
+ if (!version) return apiError(400, "missing", "The API-Version header is required.");
13264
+ if (!ACP_SUPPORTED_API_VERSIONS.includes(version)) {
13265
+ return json3(400, {
13266
+ type: "invalid_request",
13267
+ code: "unsupported_api_version",
13268
+ message: `API-Version ${version} is not supported.`,
13269
+ supported_versions: [...ACP_SUPPORTED_API_VERSIONS]
13270
+ });
13271
+ }
12812
13272
  const url = new URL(req.url);
12813
13273
  const base = (opts.basePath ?? "").replace(/\/+$/, "");
12814
13274
  const path = url.pathname.startsWith(base) ? url.pathname.slice(base.length) : url.pathname;
@@ -12819,15 +13279,17 @@ function createAcpCheckoutKit(opts) {
12819
13279
  } catch {
12820
13280
  return apiError(400, "invalid", "Body is not JSON.");
12821
13281
  }
12822
- const caller = req.headers.get("authorization") ?? "";
13282
+ const caller = await resolveCallerScope(req, reqCtx, opts.resolveCallerId);
13283
+ if (!caller)
13284
+ return apiError(401, "unauthorized", "Send an Authorization header (or X-Astra-Id).");
12823
13285
  if (req.method === "POST" && path === "/checkout_sessions") {
12824
- return idempotent(req, body, path, async () => {
13286
+ return idempotent(req, body, path, caller, async () => {
12825
13287
  const items = parsed.line_items ?? parsed.items;
12826
13288
  if (!items?.length)
12827
13289
  return apiError(400, "missing", "line_items is required.", "$.line_items");
12828
13290
  const p = await price(items);
12829
13291
  const s2 = {
12830
- id: `cs_${(0, import_crypto10.randomUUID)().replace(/-/g, "")}`,
13292
+ id: `cs_${(0, import_crypto11.randomUUID)().replace(/-/g, "")}`,
12831
13293
  status: p.lines.length && !p.messages.length ? "ready_for_payment" : "not_ready_for_payment",
12832
13294
  currency: opts.currency,
12833
13295
  line_items: p.lines,
@@ -12849,8 +13311,9 @@ function createAcpCheckoutKit(opts) {
12849
13311
  if (req.method === "GET" && !m[2]) return json3(200, view(s));
12850
13312
  if (req.method !== "POST") return apiError(405, "unsupported", "Method not allowed.");
12851
13313
  if (!m[2]) {
12852
- return idempotent(req, body, path, async () => {
13314
+ return idempotent(req, body, path, caller, async () => {
12853
13315
  if (s.status === "completed" || s.status === "canceled") return json3(200, view(s));
13316
+ if (s.status === "complete_in_progress") return inProgress();
12854
13317
  const items = parsed.line_items ?? parsed.items;
12855
13318
  if (items) {
12856
13319
  const p = await price(items);
@@ -12865,18 +13328,83 @@ function createAcpCheckoutKit(opts) {
12865
13328
  return json3(200, view(s));
12866
13329
  });
12867
13330
  }
12868
- if (m[2] === "/complete") return idempotent(req, body, path, () => complete(req, s, parsed));
12869
- return idempotent(req, body, path, async () => {
12870
- if (s.status !== "completed") s.status = "canceled";
13331
+ if (m[2] === "/complete")
13332
+ return idempotent(req, body, path, caller, () => complete(req, s, parsed));
13333
+ return idempotent(req, body, path, caller, async () => {
13334
+ if (s.status === "completed" || s.status === "canceled")
13335
+ return apiError(405, "method_not_allowed", `A ${s.status} session can't be canceled.`);
13336
+ if (s.status === "complete_in_progress") return inProgress();
13337
+ s.status = "canceled";
12871
13338
  await store.put(s);
12872
13339
  return json3(200, view(s));
12873
13340
  });
12874
13341
  }
12875
- return { handle };
13342
+ async function handle(req, reqCtx) {
13343
+ const res = await route(req, reqCtx);
13344
+ const key = req.headers.get("idempotency-key");
13345
+ const requestId = req.headers.get("request-id");
13346
+ if (!key && !requestId) return res;
13347
+ const headers = new Headers(res.headers);
13348
+ if (key) headers.set("idempotency-key", key);
13349
+ if (requestId) headers.set("request-id", requestId);
13350
+ return new Response(res.body, { status: res.status, headers });
13351
+ }
13352
+ return { handle, resolvePending };
13353
+ }
13354
+
13355
+ // src/adapters/kit-express.ts
13356
+ var bytes = (b) => b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
13357
+ function rawBodyOf(req) {
13358
+ if (req.method === "GET" || req.method === "HEAD") return void 0;
13359
+ const raw = req.rawBody;
13360
+ if (Buffer.isBuffer(raw)) return bytes(raw);
13361
+ if (typeof raw === "string") return raw;
13362
+ if (Buffer.isBuffer(req.body)) return bytes(req.body);
13363
+ if (typeof req.body === "string") return req.body;
13364
+ if (req.body !== void 0 && req.body !== null && typeof req.body === "object") {
13365
+ if (Object.keys(req.body).length === 0) return void 0;
13366
+ warnOnce(
13367
+ "kit-express-parsed-body",
13368
+ "[astrasync] expressHandler: the request body was already parsed \u2014 re-serialising it. Mount express.raw() on the checkout routes (or keep req.rawBody) so signatures and idempotency fingerprints see the exact bytes."
13369
+ );
13370
+ return JSON.stringify(req.body);
13371
+ }
13372
+ return void 0;
13373
+ }
13374
+ function expressHandler(kit, options = {}) {
13375
+ return async (req, res, next) => {
13376
+ try {
13377
+ const host = req.get?.("host") ?? req.headers.host ?? "localhost";
13378
+ const url = `${req.protocol ?? "http"}://${host}${req.originalUrl ?? req.url}`;
13379
+ const headers = new Headers();
13380
+ for (const [k, v] of Object.entries(req.headers)) {
13381
+ if (v === void 0) continue;
13382
+ if (Array.isArray(v)) v.forEach((x) => headers.append(k, x));
13383
+ else headers.set(k, v);
13384
+ }
13385
+ const body = rawBodyOf(req);
13386
+ const request = new Request(url, {
13387
+ method: req.method,
13388
+ headers,
13389
+ ...body !== void 0 && { body }
13390
+ });
13391
+ const verified = req.agentVerification?.identityVerified ? req.agentVerification.agent?.astraId : void 0;
13392
+ const callerId = options.callerId ? options.callerId(req) : verified;
13393
+ const response = await kit.handle(request, callerId ? { callerId } : void 0);
13394
+ res.status(response.status);
13395
+ response.headers.forEach((value, name) => {
13396
+ res.setHeader(name, value);
13397
+ });
13398
+ res.end(Buffer.from(await response.arrayBuffer()));
13399
+ } catch (err) {
13400
+ next(err);
13401
+ }
13402
+ };
12876
13403
  }
12877
13404
  // Annotate the CommonJS export names for ESM import in node:
12878
13405
  0 && (module.exports = {
12879
13406
  ACP_API_VERSION,
13407
+ ACP_SUPPORTED_API_VERSIONS,
12880
13408
  AEP_SPEC_VERSION,
12881
13409
  AP2_VCT,
12882
13410
  AepChain,
@@ -12897,6 +13425,7 @@ function createAcpCheckoutKit(opts) {
12897
13425
  MCP_VERIFIED_HOP_MAX_AGE_MS,
12898
13426
  MemoryCheckoutStore,
12899
13427
  MemoryIdempotencyStore,
13428
+ MemoryMppChallengeStore,
12900
13429
  PLATFORM_AGENT_SIGNATURES,
12901
13430
  RUNTIME_SIGNATURES,
12902
13431
  RedisIdempotencyStore,
@@ -12906,10 +13435,12 @@ function createAcpCheckoutKit(opts) {
12906
13435
  SDK_USER_AGENT,
12907
13436
  SettlementReportError,
12908
13437
  TRUST_LEVEL_RANGES,
13438
+ UCP_ORDER_CAPABILITY,
12909
13439
  UCP_VERSION,
12910
13440
  VERIFICATION_STATUS_HEADER,
12911
13441
  VERSION,
12912
13442
  _resetEdgeConfigCache,
13443
+ acpPaymentHandlers,
12913
13444
  aepCanonicalJson,
12914
13445
  aepHash,
12915
13446
  aepPrivateKeyFromSeed,
@@ -12924,9 +13455,12 @@ function createAcpCheckoutKit(opts) {
12924
13455
  authorizeSettlement,
12925
13456
  awaitStepUpApproval,
12926
13457
  beginIdempotent,
13458
+ buildAcpWellKnown,
12927
13459
  buildGuidance,
13460
+ buildMpp402Response,
12928
13461
  buildMppChallenge,
12929
13462
  buildSdkObservedMetadata,
13463
+ buildUcpOrder,
12930
13464
  buildUcpProfile,
12931
13465
  buildVerificationUnavailableBody,
12932
13466
  buildX402Challenge,
@@ -12950,6 +13484,7 @@ function createAcpCheckoutKit(opts) {
12950
13484
  encodeMppReceipt,
12951
13485
  encodeX402SettlementResponse,
12952
13486
  express,
13487
+ expressHandler,
12953
13488
  extractApiKeyFormat,
12954
13489
  extractCredentials,
12955
13490
  extractMcpCredentials,
@@ -12974,6 +13509,7 @@ function createAcpCheckoutKit(opts) {
12974
13509
  matchEdgePathRule,
12975
13510
  matchPlatformSignature,
12976
13511
  matchRoutePattern,
13512
+ mppBodyDigest,
12977
13513
  negotiateUcp,
12978
13514
  nextjs,
12979
13515
  normalizeDeclaredSource,