@astrasyncai/verification-gateway 5.17.1 → 5.18.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 (103) hide show
  1. package/CHANGELOG.md +1849 -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 +3 -1
  40. package/dist/bin/astrasync.js +21 -1
  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 +390 -12
  76. package/dist/index.d.ts +390 -12
  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.js +1 -1
  82. package/dist/registration/index.js.map +1 -1
  83. package/dist/registration/index.mjs +1 -1
  84. package/dist/registration/index.mjs.map +1 -1
  85. package/dist/transport/index.d.mts +4 -3
  86. package/dist/transport/index.d.ts +4 -3
  87. package/dist/transport/index.js +1 -1
  88. package/dist/transport/index.js.map +1 -1
  89. package/dist/transport/index.mjs +1 -1
  90. package/dist/transport/index.mjs.map +1 -1
  91. package/dist/{types-vNBvYD7Z.d.mts → types-3ObBpsnf.d.mts} +7 -0
  92. package/dist/{types-DA8LzAOB.d.ts → types-BoxZEYPS.d.ts} +7 -0
  93. package/dist/{types-DRb_plOh.d.mts → types-Cns4l8gX.d.mts} +17 -0
  94. package/dist/{types-DRb_plOh.d.ts → types-Cns4l8gX.d.ts} +17 -0
  95. package/dist/ui/index.d.mts +1 -1
  96. package/dist/ui/index.d.ts +1 -1
  97. package/dist/verify.d.mts +1 -1
  98. package/dist/verify.d.ts +1 -1
  99. package/dist/verify.js +1 -1
  100. package/dist/verify.js.map +1 -1
  101. package/dist/verify.mjs +1 -1
  102. package/dist/verify.mjs.map +1 -1
  103. package/package.json +8 -2
package/dist/index.mjs CHANGED
@@ -30,7 +30,7 @@ function getTrustLevel(score) {
30
30
  }
31
31
 
32
32
  // src/version.ts
33
- var SDK_VERSION = "5.17.1";
33
+ var SDK_VERSION = "5.18.0";
34
34
 
35
35
  // src/http.ts
36
36
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -1172,17 +1172,17 @@ function normalizeDeclaredSource(value) {
1172
1172
  return trimmed && DECLARED_SOURCE_PATTERN.test(trimmed) ? trimmed : void 0;
1173
1173
  }
1174
1174
  function byteLength(s) {
1175
- let bytes = 0;
1175
+ let bytes2 = 0;
1176
1176
  for (let i = 0; i < s.length; i++) {
1177
1177
  const code = s.charCodeAt(i);
1178
- if (code < 128) bytes += 1;
1179
- else if (code < 2048) bytes += 2;
1178
+ if (code < 128) bytes2 += 1;
1179
+ else if (code < 2048) bytes2 += 2;
1180
1180
  else if (code >= 55296 && code <= 56319) {
1181
- bytes += 4;
1181
+ bytes2 += 4;
1182
1182
  i++;
1183
- } else bytes += 3;
1183
+ } else bytes2 += 3;
1184
1184
  }
1185
- return bytes;
1185
+ return bytes2;
1186
1186
  }
1187
1187
  function extractApiKeyFormat(rawValue) {
1188
1188
  if (!rawValue) return void 0;
@@ -1359,9 +1359,9 @@ function decodeBase64Url(seg) {
1359
1359
  const b642 = seg.replace(/-/g, "+").replace(/_/g, "/");
1360
1360
  const padded = b642 + "=".repeat((4 - b642.length % 4) % 4);
1361
1361
  const binary2 = atob(padded);
1362
- const bytes = new Uint8Array(binary2.length);
1363
- for (let i = 0; i < binary2.length; i++) bytes[i] = binary2.charCodeAt(i);
1364
- return new TextDecoder().decode(bytes);
1362
+ const bytes2 = new Uint8Array(binary2.length);
1363
+ for (let i = 0; i < binary2.length; i++) bytes2[i] = binary2.charCodeAt(i);
1364
+ return new TextDecoder().decode(bytes2);
1365
1365
  }
1366
1366
  function sniffJwtHeader(compact) {
1367
1367
  try {
@@ -1400,6 +1400,23 @@ function detectCommerce(req, host) {
1400
1400
  }
1401
1401
  const signature = headerValue(req, "signature");
1402
1402
  const signatureInput = headerValue(req, "signature-input");
1403
+ if (/\/checkout[-_]sessions(\/|$)/.test(req.uri) && headerValue(req, "ucp-agent")) {
1404
+ const signed = Boolean(signature && signatureInput);
1405
+ return {
1406
+ protocol: "ucp",
1407
+ artifacts: {
1408
+ ucpRequest: { method, url, ...jsonBody && { body: jsonBody } },
1409
+ ...signed && {
1410
+ rfc9421: { headers, method, url, ...req.bodyText && { body: req.bodyText } }
1411
+ }
1412
+ },
1413
+ evidence: {
1414
+ path: req.uri,
1415
+ ...signed && { keyid: signatureInput.match(/keyid="([^"]+)"/)?.[1] },
1416
+ ...truncated
1417
+ }
1418
+ };
1419
+ }
1403
1420
  if (signature && signatureInput) {
1404
1421
  const signatureAgent = headerValue(req, "signature-agent");
1405
1422
  const registryHint = headerValue(req, "x-agent-registry");
@@ -1434,13 +1451,6 @@ function detectCommerce(req, host) {
1434
1451
  evidence: { hasTimestamp: Boolean(acpTimestamp), ...truncated }
1435
1452
  };
1436
1453
  }
1437
- if (/\/checkout[-_]sessions(\/|$)/.test(req.uri) && headerValue(req, "ucp-agent")) {
1438
- return {
1439
- protocol: "ucp",
1440
- artifacts: { ucpRequest: { method, url, ...jsonBody && { body: jsonBody } } },
1441
- evidence: { path: req.uri, ...truncated }
1442
- };
1443
- }
1444
1454
  if (jsonBody) {
1445
1455
  const intent = jsonBody.intent_mandate ?? jsonBody.intentMandate;
1446
1456
  const cart = jsonBody.cart_mandate ?? jsonBody.cartMandate;
@@ -2539,6 +2549,7 @@ function createMiddleware(options) {
2539
2549
  anonymousBeacon = true,
2540
2550
  trustVerifiedHop = false,
2541
2551
  verifiedHopMaxAgeMs,
2552
+ defaultRoutePolicy = "pass",
2542
2553
  ...config
2543
2554
  } = options;
2544
2555
  if (config.apiBaseUrl) {
@@ -2604,12 +2615,15 @@ function createMiddleware(options) {
2604
2615
  });
2605
2616
  }
2606
2617
  const correlationId = req.headers["x-request-id"] || req.headers["x-correlation-id"];
2607
- const routeConfig = findRouteConfig(cachedRoutes, req.path, req.method, {
2618
+ const matchedRoute = findRouteConfig(cachedRoutes, req.path, req.method, {
2608
2619
  caseInsensitive: caseInsensitiveRouteMatch,
2609
2620
  logShadowDivergence: true,
2610
2621
  correlationId
2611
2622
  });
2612
- if (!routeConfig) {
2623
+ const credentials = customExtractCredentials ? customExtractCredentials(req) : defaultExtractCredentials(req);
2624
+ const credentialed = Boolean(credentials.astraId || credentials.apiKey || credentials.jwt);
2625
+ const observeUnmatched = !matchedRoute && (config.evaluateAlwaysIfCredentialed === true && Boolean(credentials.astraId) || defaultRoutePolicy === "observe" && credentialed);
2626
+ if (!matchedRoute && !observeUnmatched) {
2613
2627
  if (config.setPassThroughHeader) {
2614
2628
  res.setHeader("X-Astra-Gateway-Mode", "unenforced");
2615
2629
  res.setHeader(
@@ -2620,10 +2634,14 @@ function createMiddleware(options) {
2620
2634
  beaconPassThrough?.(req);
2621
2635
  return next();
2622
2636
  }
2637
+ const routeConfig = matchedRoute ?? {
2638
+ pattern: req.path,
2639
+ method: req.method,
2640
+ observe: true
2641
+ };
2623
2642
  const wellKnownUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
2624
- const credentials = customExtractCredentials ? customExtractCredentials(req) : defaultExtractCredentials(req);
2625
2643
  const shouldEnforce = !routeConfig.observe;
2626
- if (routeConfig.observe && (!config.evaluateAlwaysIfCredentialed || !credentials.astraId)) {
2644
+ if (routeConfig.observe && !observeUnmatched && (!config.evaluateAlwaysIfCredentialed || !credentials.astraId)) {
2627
2645
  if (config.setPassThroughHeader) {
2628
2646
  res.setHeader("X-Astra-Gateway-Mode", "unenforced");
2629
2647
  res.setHeader("X-Astra-Gateway-Reason", "route-observe");
@@ -4105,8 +4123,8 @@ function extractBase64(value) {
4105
4123
  }
4106
4124
  return null;
4107
4125
  }
4108
- function bufferToBase64(bytes) {
4109
- return Buffer.from(bytes).toString("base64");
4126
+ function bufferToBase64(bytes2) {
4127
+ return Buffer.from(bytes2).toString("base64");
4110
4128
  }
4111
4129
 
4112
4130
  // src/transport/rfc9421-verify.ts
@@ -5273,9 +5291,9 @@ async function tryVerify(subtle, jwk, signature, body, alg) {
5273
5291
  }
5274
5292
  return false;
5275
5293
  }
5276
- function toBuf(bytes) {
5277
- const out = new ArrayBuffer(bytes.byteLength);
5278
- new Uint8Array(out).set(bytes);
5294
+ function toBuf(bytes2) {
5295
+ const out = new ArrayBuffer(bytes2.byteLength);
5296
+ new Uint8Array(out).set(bytes2);
5279
5297
  return out;
5280
5298
  }
5281
5299
  function checkTimestamp(headerValue3, toleranceSec, nowFn) {
@@ -5801,11 +5819,11 @@ async function thumbprintsMatch(a, b) {
5801
5819
  }
5802
5820
  async function jwkThumbprint(jwk) {
5803
5821
  const canonical2 = canonicalJwk(jwk);
5804
- const bytes = new TextEncoder().encode(JSON.stringify(canonical2));
5822
+ const bytes2 = new TextEncoder().encode(JSON.stringify(canonical2));
5805
5823
  const subtle = webcrypto.subtle;
5806
5824
  const buffer = await new Promise((resolve, reject) => {
5807
- const source = new ArrayBuffer(bytes.byteLength);
5808
- new Uint8Array(source).set(bytes);
5825
+ const source = new ArrayBuffer(bytes2.byteLength);
5826
+ new Uint8Array(source).set(bytes2);
5809
5827
  subtle.digest("SHA-256", source).then(resolve).catch(reject);
5810
5828
  });
5811
5829
  return Buffer.from(new Uint8Array(buffer)).toString("base64url").replace(/=+$/, "");
@@ -6198,14 +6216,14 @@ function createMastercardRegistry(options = {}) {
6198
6216
  const cache4 = /* @__PURE__ */ new Map();
6199
6217
  const ttlSec = options.cacheTtlSec ?? 3600;
6200
6218
  const fetchFn = options.fetch ?? globalThis.fetch;
6201
- let warned = false;
6219
+ let warned2 = false;
6202
6220
  return {
6203
6221
  name: "mastercard",
6204
6222
  async resolve(kid) {
6205
6223
  if (!kid) return null;
6206
6224
  if (!options.registryUrl) {
6207
- if (!warned && !options.silent) {
6208
- warned = true;
6225
+ if (!warned2 && !options.silent) {
6226
+ warned2 = true;
6209
6227
  console.warn(
6210
6228
  "[mastercard-registry] registryUrl not configured \u2014 key resolution disabled. Kid lookups will return null until a partnership registry is supplied."
6211
6229
  );
@@ -10758,6 +10776,7 @@ function astrasyncPaymentHandlers(handlers, specBase = "https://astrasync.ai/ucp
10758
10776
  }
10759
10777
  return out;
10760
10778
  }
10779
+ var UCP_ORDER_CAPABILITY = "dev.ucp.shopping.order";
10761
10780
  function buildUcpProfile(opts) {
10762
10781
  const capabilities = {
10763
10782
  "dev.ucp.shopping.checkout": [
@@ -10778,6 +10797,15 @@ function buildUcpProfile(opts) {
10778
10797
  }
10779
10798
  ]
10780
10799
  },
10800
+ ...opts.orders && {
10801
+ [UCP_ORDER_CAPABILITY]: [
10802
+ {
10803
+ version: UCP_VERSION,
10804
+ spec: `${SPEC}/shopping/order`,
10805
+ schema: `${SCHEMAS}/shopping/order.json`
10806
+ }
10807
+ ]
10808
+ },
10781
10809
  ...opts.capabilities ?? {}
10782
10810
  };
10783
10811
  return {
@@ -11154,7 +11182,7 @@ async function sendUcpWebhook(opts) {
11154
11182
  }
11155
11183
 
11156
11184
  // src/ucp/checkout-kit.ts
11157
- import { randomUUID as randomUUID2 } from "crypto";
11185
+ import { createHash as createHash10, randomUUID as randomUUID2 } from "crypto";
11158
11186
  import { CompactSign as CompactSign2 } from "jose";
11159
11187
 
11160
11188
  // src/mandates/sd-jwt.ts
@@ -11559,8 +11587,8 @@ function requestFingerprint(body, profile = "ucp") {
11559
11587
  }
11560
11588
  return createHash8("sha256").update(text).digest("hex");
11561
11589
  }
11562
- function scopeOf(authorization) {
11563
- return authorization ? createHash8("sha256").update(authorization).digest("hex").slice(0, 16) : "anon";
11590
+ function scopeOf(caller) {
11591
+ return caller ? createHash8("sha256").update(caller).digest("hex").slice(0, 16) : null;
11564
11592
  }
11565
11593
  async function beginIdempotent(opts, req) {
11566
11594
  const methods = opts.methods ?? ["POST", "PUT", "PATCH", "DELETE"];
@@ -11581,7 +11609,16 @@ async function beginIdempotent(opts, req) {
11581
11609
  message: `Idempotency-Key must be at most ${MAX_KEY_LENGTH} characters.`
11582
11610
  };
11583
11611
  }
11584
- const storeKey = `${scopeOf(req.auth)}:${req.method.toUpperCase()} ${req.path}:${req.key}`;
11612
+ const scope = scopeOf(req.auth);
11613
+ if (!scope) {
11614
+ return opts.required ? {
11615
+ kind: "reject",
11616
+ status: 401,
11617
+ code: "idempotency_caller_required",
11618
+ message: "Idempotency keys are scoped to the caller: send Authorization."
11619
+ } : { kind: "skip" };
11620
+ }
11621
+ const storeKey = `${scope}:${req.method.toUpperCase()} ${req.path}:${req.key}`;
11585
11622
  const requestHash = requestFingerprint(req.body, opts.profile ?? "ucp");
11586
11623
  const ttl = opts.ttlMs ?? DEFAULT_IDEMPOTENCY_TTL_MS;
11587
11624
  try {
@@ -11657,7 +11694,7 @@ function idempotencyMiddleware(opts) {
11657
11694
  path: (req.originalUrl ?? req.url).split("?")[0],
11658
11695
  body,
11659
11696
  key: headerOf(req.headers[header]),
11660
- auth: headerOf(req.headers.authorization) ?? headerOf(req.headers["x-api-key"])
11697
+ auth: headerOf(req.headers.authorization) ?? headerOf(req.headers["x-api-key"]) ?? headerOf(req.headers["x-astra-id"])
11661
11698
  });
11662
11699
  if (decision.kind === "skip") return next();
11663
11700
  const key = headerOf(req.headers[header]);
@@ -11696,7 +11733,7 @@ function withIdempotency(opts, handler) {
11696
11733
  path: new URL(req.url).pathname,
11697
11734
  body,
11698
11735
  key: req.headers.get(header) ?? void 0,
11699
- auth: req.headers.get("authorization") ?? req.headers.get("x-api-key") ?? void 0
11736
+ auth: req.headers.get("authorization") ?? req.headers.get("x-api-key") ?? req.headers.get("x-astra-id") ?? void 0
11700
11737
  });
11701
11738
  if (decision.kind === "skip") return handler(req);
11702
11739
  const key = req.headers.get(header) ?? void 0;
@@ -11753,18 +11790,95 @@ function orderReportAmount(result, fallback) {
11753
11790
  return fallback;
11754
11791
  }
11755
11792
 
11793
+ // src/ucp/kit-shared.ts
11794
+ import { createHash as createHash9 } from "crypto";
11795
+ function isHeld(result) {
11796
+ const r = result;
11797
+ return Boolean(
11798
+ r.requiresStepUp || r.requiresApproval || r.recommendation === "step_up_required" || r.settlementOutcome?.status === "requires_approval" || r.stepUpApproval && r.stepUpApproval.status !== "consumed"
11799
+ );
11800
+ }
11801
+ var scopeHash = (kind, value) => `${kind}:${createHash9("sha256").update(`${kind}
11802
+ ${value}`).digest("hex").slice(0, 32)}`;
11803
+ async function resolveCallerScope(req, ctx, resolve) {
11804
+ const verified = ctx?.callerId ?? (resolve ? await resolve(req) : void 0);
11805
+ if (verified) return scopeHash("astra", verified);
11806
+ const auth = req.headers.get("authorization");
11807
+ if (auth) return scopeHash("auth", auth);
11808
+ const astraId = req.headers.get("x-astra-id");
11809
+ if (astraId) return scopeHash("astra-header", astraId);
11810
+ return void 0;
11811
+ }
11812
+ var fallbackScope = (kind, value) => scopeHash(kind, value);
11813
+ function pendingResult(result) {
11814
+ const {
11815
+ settlementToken: _t,
11816
+ settlement: _s,
11817
+ mandates: _m,
11818
+ kyaToken: _k,
11819
+ aep: _a,
11820
+ ...rest
11821
+ } = result;
11822
+ return rest;
11823
+ }
11824
+ var warned = /* @__PURE__ */ new Set();
11825
+ function warnOnce(key, message) {
11826
+ if (warned.has(key) || process.env.NODE_ENV === "test") return;
11827
+ warned.add(key);
11828
+ console.warn(message);
11829
+ }
11830
+ function warnInMemoryStores(kit, missing) {
11831
+ if (!missing.length || process.env.NODE_ENV === "test") return;
11832
+ console.warn(
11833
+ `[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.`
11834
+ );
11835
+ }
11836
+
11756
11837
  // src/ucp/checkout-kit.ts
11757
11838
  var MemoryCheckoutStore = class {
11758
11839
  constructor() {
11759
11840
  this.m = /* @__PURE__ */ new Map();
11841
+ this.orders = /* @__PURE__ */ new Map();
11760
11842
  }
11761
11843
  async get(id) {
11762
11844
  return this.m.get(id) ?? null;
11763
11845
  }
11764
11846
  async put(s) {
11765
11847
  this.m.set(s.id, s);
11848
+ if (s.order) this.orders.set(s.order.id, s.id);
11849
+ }
11850
+ async claim(id, from, to) {
11851
+ const s = this.m.get(id);
11852
+ if (!s || s.status !== from) return false;
11853
+ s.status = to;
11854
+ return true;
11855
+ }
11856
+ async findByOrderId(orderId) {
11857
+ const id = this.orders.get(orderId);
11858
+ return id ? this.m.get(id) ?? null : null;
11766
11859
  }
11767
11860
  };
11861
+ function buildUcpOrder(session, opts = {}) {
11862
+ if (!session.order) return null;
11863
+ const version = opts.version ?? UCP_VERSION;
11864
+ return {
11865
+ ucp: { version, capabilities: { [UCP_ORDER_CAPABILITY]: [{ version }] } },
11866
+ id: session.order.id,
11867
+ checkout_id: session.id,
11868
+ ...session.order.permalink_url && { permalink_url: session.order.permalink_url },
11869
+ currency: session.currency,
11870
+ line_items: session.line_items.map((l) => ({
11871
+ id: l.id,
11872
+ item: l.item,
11873
+ quantity: { total: l.quantity, fulfilled: 0 },
11874
+ totals: l.totals,
11875
+ status: "processing"
11876
+ })),
11877
+ fulfillment: { expectations: [], events: [] },
11878
+ adjustments: [],
11879
+ totals: session.totals
11880
+ };
11881
+ }
11768
11882
  var json2 = (status, body, headers = {}) => new Response(JSON.stringify(body), {
11769
11883
  status,
11770
11884
  headers: { "content-type": "application/json", ...headers }
@@ -11777,6 +11891,24 @@ function createUcpCheckoutKit(opts) {
11777
11891
  store: opts.idempotencyStore ?? new MemoryIdempotencyStore(),
11778
11892
  profile: "ucp"
11779
11893
  };
11894
+ warnInMemoryStores("UCP", [
11895
+ ...opts.store ? [] : ["checkout store"],
11896
+ ...opts.idempotencyStore ? [] : ["idempotency store"]
11897
+ ]);
11898
+ if (!store.claim) {
11899
+ warnOnce(
11900
+ "ucp-claim",
11901
+ "[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."
11902
+ );
11903
+ }
11904
+ const orderIndex = /* @__PURE__ */ new Map();
11905
+ const ordersOn = Boolean(profile.ucp.capabilities?.[UCP_ORDER_CAPABILITY]);
11906
+ const handlerIds = new Set(
11907
+ Object.entries(profile.ucp.payment_handlers).flatMap(([name, entries]) => [
11908
+ name,
11909
+ ...entries.map((e) => e.id)
11910
+ ])
11911
+ );
11780
11912
  const fetchProfile = opts.fetchProfile ?? (async (url, fresh) => {
11781
11913
  const r = await fetchUcpProfile(url, { fresh });
11782
11914
  return r.ok ? r.profile : r;
@@ -11788,12 +11920,12 @@ function createUcpCheckoutKit(opts) {
11788
11920
  const [h, , sig] = attached.split(".");
11789
11921
  return { detached: `${h}..${sig}`, attached };
11790
11922
  }
11791
- async function render(s, status = 200) {
11923
+ async function render(s, status = 200, mode = "persist") {
11792
11924
  const view = {
11793
11925
  ucp: {
11794
11926
  version: profile.ucp.version,
11795
11927
  capabilities: Object.fromEntries(
11796
- Object.keys(profile.ucp.capabilities ?? {}).filter((k) => k !== "dev.ucp.common.payment.ap2_mandate" || s._ap2).map((k) => [k, [{ version: profile.ucp.version }]])
11928
+ 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 }]])
11797
11929
  ),
11798
11930
  payment_handlers: profile.ucp.payment_handlers
11799
11931
  },
@@ -11802,21 +11934,132 @@ function createUcpCheckoutKit(opts) {
11802
11934
  currency: s.currency,
11803
11935
  line_items: s.line_items,
11804
11936
  totals: s.totals,
11937
+ ...opts.links?.length && { links: opts.links },
11805
11938
  ...s.buyer && { buyer: s.buyer },
11806
11939
  ...s.messages?.length && { messages: s.messages },
11807
11940
  ...s.continue_url && s.status === "requires_escalation" && { continue_url: s.continue_url },
11808
11941
  ...s.order && { order: s.order }
11809
11942
  };
11810
- if (s._ap2) {
11811
- const auth = await merchantAuthorization(view);
11812
- if (auth) {
11813
- view.ap2 = { merchant_authorization: auth.detached };
11814
- s._checkoutJwt = auth.attached;
11815
- await store.put(s);
11943
+ if (s._ap2 && opts.signingKey) {
11944
+ const viewHash = createHash10("sha256").update(canonicalJson2(view)).digest("hex");
11945
+ if (mode === "reuse" && s._merchantAuth && s._merchantAuthFor === viewHash) {
11946
+ view.ap2 = { merchant_authorization: s._merchantAuth };
11947
+ } else {
11948
+ const auth = await merchantAuthorization(view);
11949
+ if (auth) {
11950
+ view.ap2 = { merchant_authorization: auth.detached };
11951
+ if (mode !== "ephemeral") {
11952
+ s._checkoutJwt = auth.attached;
11953
+ s._merchantAuth = auth.detached;
11954
+ s._merchantAuthFor = viewHash;
11955
+ await store.put(s);
11956
+ }
11957
+ }
11816
11958
  }
11817
11959
  }
11818
11960
  return json2(status, view);
11819
11961
  }
11962
+ function busy(s) {
11963
+ return render(
11964
+ {
11965
+ ...s,
11966
+ messages: [
11967
+ ...s.messages ?? [],
11968
+ ucpError(
11969
+ "checkout_in_progress",
11970
+ "This checkout is already being completed. Retry shortly.",
11971
+ "recoverable"
11972
+ )
11973
+ ]
11974
+ },
11975
+ 200,
11976
+ "ephemeral"
11977
+ );
11978
+ }
11979
+ async function save(s) {
11980
+ if (s.order) orderIndex.set(s.order.id, s.id);
11981
+ await store.put(s);
11982
+ }
11983
+ function webhookTarget(s) {
11984
+ const w = opts.webhook;
11985
+ if (!w) return null;
11986
+ const url = w.url ?? s._webhookUrl;
11987
+ const privateKey = w.privateKey ?? opts.signingKey?.privateKey ?? void 0;
11988
+ const keyId = w.keyId ?? opts.signingKey?.kid;
11989
+ if (!url || !privateKey || !keyId) return null;
11990
+ return { w, url, privateKey, keyId };
11991
+ }
11992
+ async function deliverOrder(s) {
11993
+ const target = webhookTarget(s);
11994
+ const order = buildUcpOrder(s, { version: profile.ucp.version });
11995
+ if (!target || !order) return null;
11996
+ const r = await sendUcpWebhook({
11997
+ url: target.url,
11998
+ body: order,
11999
+ privateKey: target.privateKey,
12000
+ keyId: target.keyId,
12001
+ businessProfileUrl: target.w.businessProfileUrl ?? new URL("/.well-known/ucp", opts.profile.endpoint).toString(),
12002
+ ...target.w.attempts && { attempts: target.w.attempts },
12003
+ ...target.w.fetch && { fetch: target.w.fetch }
12004
+ });
12005
+ target.w.onResult?.(r, order);
12006
+ return r;
12007
+ }
12008
+ function emitOrder(s) {
12009
+ void deliverOrder(s).catch(() => void 0);
12010
+ }
12011
+ async function reportOutcome(p, settled) {
12012
+ if (!opts.reportSettlement || !p.orderId) return;
12013
+ await opts.reportSettlement({
12014
+ orderId: p.orderId,
12015
+ status: settled.status,
12016
+ ...p.report,
12017
+ ...settled.status === "settled" && settled.processorRef && { processorRef: settled.processorRef },
12018
+ ...settled.status === "settled" && settled.txHash && { txHash: settled.txHash, chainId: settled.chainId },
12019
+ ...settled.status === "failed" && settled.failureCode && { failureCode: settled.failureCode }
12020
+ }).catch(() => void 0);
12021
+ }
12022
+ async function finish(s, result, p, settled) {
12023
+ await reportOutcome(p, settled);
12024
+ delete s._pending;
12025
+ if (settled.status === "failed") {
12026
+ s.status = "ready_for_complete";
12027
+ s.messages = [
12028
+ ucpError("payment_failed", "The payment did not go through.", "requires_buyer_input")
12029
+ ];
12030
+ await save(s);
12031
+ return;
12032
+ }
12033
+ const order = await opts.fulfil({ session: s, result });
12034
+ s.order = {
12035
+ ...order,
12036
+ ...!order.permalink_url && opts.permalinkBase && {
12037
+ permalink_url: `${opts.permalinkBase.replace(/\/+$/, "")}/${encodeURIComponent(order.id)}`
12038
+ }
12039
+ };
12040
+ s.status = "completed";
12041
+ s.messages = [];
12042
+ await save(s);
12043
+ emitOrder(s);
12044
+ }
12045
+ async function claim2(s, from) {
12046
+ if (store.claim) {
12047
+ if (!await store.claim(s.id, from, "complete_in_progress")) return false;
12048
+ s.status = "complete_in_progress";
12049
+ return true;
12050
+ }
12051
+ s.status = "complete_in_progress";
12052
+ await store.put(s);
12053
+ return true;
12054
+ }
12055
+ async function bindAgent(req, result) {
12056
+ const mode = opts.ucpAgentBinding ?? "registered";
12057
+ if (mode === "off") return null;
12058
+ const identities = opts.resolveIdentities ? await opts.resolveIdentities(req, result) : result.agent?.protocolIdentities;
12059
+ if (mode === "registered" && !identities?.ucpProfile) return null;
12060
+ const b = checkUcpAgentBinding(req.headers.get("ucp-agent"), identities);
12061
+ return b.ok ? null : b;
12062
+ }
11820
12063
  async function priceLines(lines) {
11821
12064
  const out = [];
11822
12065
  const messages = [];
@@ -11909,13 +12152,13 @@ function createUcpCheckoutKit(opts) {
11909
12152
  ap2: Boolean(neg.capabilities["dev.ucp.common.payment.ap2_mandate"])
11910
12153
  };
11911
12154
  }
11912
- async function withIdempotency2(req, body, path, run) {
12155
+ async function withIdempotency2(req, body, path, caller, run) {
11913
12156
  const d = await beginIdempotent(idem, {
11914
12157
  method: req.method,
11915
12158
  path,
11916
12159
  body,
11917
12160
  key: req.headers.get("idempotency-key") ?? void 0,
11918
- auth: req.headers.get("ucp-agent") ?? void 0
12161
+ auth: caller
11919
12162
  });
11920
12163
  if (d.kind === "reject")
11921
12164
  return json2(d.status, { type: "invalid_request", code: d.code, message: d.message });
@@ -11936,6 +12179,7 @@ function createUcpCheckoutKit(opts) {
11936
12179
  }
11937
12180
  async function complete(req, s, body) {
11938
12181
  if (s.status === "completed" || s.status === "canceled") return render(s);
12182
+ if (s.status === "complete_in_progress") return busy(s);
11939
12183
  const ap2 = body.ap2;
11940
12184
  const checkoutJwt = s._ap2 ? s._checkoutJwt : void 0;
11941
12185
  if (s._ap2) {
@@ -11947,6 +12191,7 @@ function createUcpCheckoutKit(opts) {
11947
12191
  "unrecoverable"
11948
12192
  )
11949
12193
  ];
12194
+ await store.put(s);
11950
12195
  return render(s);
11951
12196
  }
11952
12197
  const kidResolver = async (kid) => opts.mandateKey ? opts.mandateKey(kid, s) : (s._platformKeys ?? []).find((k) => k.kid === kid) ?? null;
@@ -11957,71 +12202,106 @@ function createUcpCheckoutKit(opts) {
11957
12202
  });
11958
12203
  if (!v.ok) {
11959
12204
  s.messages = [ucpError(v.code, v.error, "unrecoverable")];
12205
+ await store.put(s);
11960
12206
  return render(s);
11961
12207
  }
11962
12208
  }
11963
- const result = await opts.verifyPurchase({
11964
- session: s,
11965
- checkoutSessionId: s._astraCheckoutSessionId ?? s.id,
11966
- astraId: req.headers.get("x-astra-id") ?? void 0,
11967
- attemptId: req.headers.get("x-astra-attempt-id") ?? void 0,
11968
- ...checkoutJwt && { checkoutJwt },
11969
- valueMajor: total(s) / 100
11970
- });
11971
- const held = result.settlementOutcome?.status === "requires_approval" || result.stepUpApproval && result.stepUpApproval.status === "pending";
11972
- if (held) {
11973
- s.status = "requires_escalation";
11974
- s.continue_url = result.stepUpApproval?.approvalUrl ?? opts.fallbackContinueUrl;
11975
- s.messages = [
11976
- ucpError(
11977
- "buyer_approval_required",
11978
- "The buyer needs to approve this purchase.",
11979
- "requires_buyer_review"
11980
- )
11981
- ];
11982
- await store.put(s);
11983
- return render(s);
11984
- }
11985
- if (!result.identityVerified || !result.policyAllowed) {
12209
+ const instruments = body.payment?.instruments;
12210
+ const instrument = Array.isArray(instruments) && instruments.length === 1 ? instruments[0] : null;
12211
+ if (!instrument || typeof instrument.handler_id !== "string" || !handlerIds.has(instrument.handler_id)) {
11986
12212
  s.messages = [
11987
12213
  ucpError(
11988
- "agent_not_authorized",
11989
- "The agent is not authorized for this purchase.",
11990
- "unrecoverable"
12214
+ "payment_failed",
12215
+ "Send exactly one payment instrument whose handler_id is one of this business\u2019s payment_handlers.",
12216
+ "recoverable",
12217
+ "$.payment.instruments"
11991
12218
  )
11992
12219
  ];
11993
12220
  await store.put(s);
11994
12221
  return render(s);
11995
12222
  }
11996
- s.status = "complete_in_progress";
11997
- const instruments = body.payment?.instruments;
11998
- const settled = await opts.settle({ session: s, result, instrument: instruments?.[0] });
11999
- const orderId = result.settlementOutcome?.orderId;
12000
- if (opts.reportSettlement && orderId && settled.status !== "pending") {
12001
- await opts.reportSettlement({
12002
- orderId,
12003
- status: settled.status === "settled" ? "settled" : "failed",
12004
- ...orderReportAmount(result, { amountMinor: total(s), currency: s.currency }),
12005
- ...settled.status === "settled" && settled.processorRef && { processorRef: settled.processorRef },
12006
- ...settled.status === "settled" && settled.txHash && { txHash: settled.txHash, chainId: settled.chainId },
12007
- ...settled.status === "failed" && settled.failureCode && { failureCode: settled.failureCode }
12008
- }).catch(() => void 0);
12223
+ const from = s.status;
12224
+ if (!await claim2(s, from)) {
12225
+ const current = await store.get(s.id);
12226
+ return busy(current ?? s);
12009
12227
  }
12010
- if (settled.status === "failed") {
12011
- s.status = "ready_for_complete";
12012
- s.messages = [
12013
- ucpError("payment_failed", "The payment did not go through.", "requires_buyer_input")
12014
- ];
12015
- await store.put(s);
12228
+ try {
12229
+ const result = await opts.verifyPurchase({
12230
+ session: s,
12231
+ checkoutSessionId: s._astraCheckoutSessionId ?? s.id,
12232
+ astraId: req.headers.get("x-astra-id") ?? void 0,
12233
+ attemptId: req.headers.get("x-astra-attempt-id") ?? void 0,
12234
+ ...checkoutJwt && { checkoutJwt },
12235
+ valueMajor: total(s) / 100
12236
+ });
12237
+ const binding2 = await bindAgent(req, result);
12238
+ if (binding2) {
12239
+ s.status = from;
12240
+ s.messages = [ucpError(binding2.code, binding2.message, "unrecoverable")];
12241
+ await store.put(s);
12242
+ return render(s);
12243
+ }
12244
+ if (isHeld(result)) {
12245
+ s.status = "requires_escalation";
12246
+ s.continue_url = result.stepUpApproval?.approvalUrl ?? opts.fallbackContinueUrl;
12247
+ s.messages = [
12248
+ ucpError(
12249
+ "buyer_approval_required",
12250
+ "The buyer needs to approve this purchase.",
12251
+ "requires_buyer_review"
12252
+ )
12253
+ ];
12254
+ await store.put(s);
12255
+ return render(s);
12256
+ }
12257
+ if (!result.identityVerified || !result.policyAllowed) {
12258
+ s.status = from;
12259
+ s.messages = [
12260
+ ucpError(
12261
+ "agent_not_authorized",
12262
+ "The agent is not authorized for this purchase.",
12263
+ "unrecoverable"
12264
+ )
12265
+ ];
12266
+ await store.put(s);
12267
+ return render(s);
12268
+ }
12269
+ const settled = await opts.settle({ session: s, result, instrument });
12270
+ const pending = {
12271
+ ...result.settlementOutcome?.orderId && {
12272
+ orderId: result.settlementOutcome.orderId
12273
+ },
12274
+ report: orderReportAmount(result, { amountMinor: total(s), currency: s.currency }),
12275
+ result: pendingResult(result)
12276
+ };
12277
+ if (settled.status === "pending") {
12278
+ s._pending = pending;
12279
+ s.messages = [];
12280
+ await store.put(s);
12281
+ return render(s);
12282
+ }
12283
+ await finish(s, result, pending, settled);
12016
12284
  return render(s);
12285
+ } catch (e) {
12286
+ if (s.status === "complete_in_progress" && !s._pending) {
12287
+ s.status = from;
12288
+ await store.put(s).catch(() => void 0);
12289
+ }
12290
+ throw e;
12017
12291
  }
12018
- s.order = await opts.fulfil({ session: s, result });
12019
- s.status = "completed";
12020
- s.messages = [];
12021
- await store.put(s);
12022
- return render(s);
12023
12292
  }
12024
- async function handle(req) {
12293
+ async function resolvePending(sessionId, outcome) {
12294
+ const s = await store.get(sessionId);
12295
+ if (!s) return null;
12296
+ if (s.status !== "complete_in_progress" || !s._pending) return s;
12297
+ await finish(s, s._pending.result, s._pending, outcome);
12298
+ return s;
12299
+ }
12300
+ async function sendOrderWebhook(sessionId) {
12301
+ const s = await store.get(sessionId);
12302
+ return s ? deliverOrder(s) : null;
12303
+ }
12304
+ async function handle(req, reqCtx) {
12025
12305
  const base = new URL(opts.profile.endpoint).pathname.replace(/\/+$/, "");
12026
12306
  const url = new URL(req.url);
12027
12307
  const path = url.pathname.startsWith(base) ? url.pathname.slice(base.length) : url.pathname;
@@ -12035,9 +12315,11 @@ function createUcpCheckoutKit(opts) {
12035
12315
  const p = await platform(req, body);
12036
12316
  if ("error" in p && p.error) return p.error;
12037
12317
  const ctx = p;
12318
+ const caller = await resolveCallerScope(req, reqCtx, opts.resolveCallerId) ?? fallbackScope("ucp-profile", ctx.profileUrl);
12038
12319
  if (req.method === "POST" && path === "/checkout-sessions") {
12039
- return withIdempotency2(req, body, path, async () => {
12320
+ return withIdempotency2(req, body, path, caller, async () => {
12040
12321
  const priced = await priceLines(parsed.line_items ?? []);
12322
+ const webhookUrl = ctx.plat.ucp.capabilities?.[UCP_ORDER_CAPABILITY]?.[0]?.config?.webhook_url;
12041
12323
  const s2 = {
12042
12324
  id: `chk_${randomUUID2().replace(/-/g, "")}`,
12043
12325
  status: priced.lines.length && !priced.messages.length ? "ready_for_complete" : "incomplete",
@@ -12046,9 +12328,11 @@ function createUcpCheckoutKit(opts) {
12046
12328
  totals: priced.totals,
12047
12329
  messages: priced.messages,
12048
12330
  ...parsed.buyer ? { buyer: parsed.buyer } : {},
12331
+ _caller: caller,
12049
12332
  _platformProfile: ctx.profileUrl,
12050
12333
  _ap2: ctx.ap2,
12051
12334
  _platformKeys: ctx.plat.keys ?? [],
12335
+ ...typeof webhookUrl === "string" && /^https:\/\//.test(webhookUrl) && { _webhookUrl: webhookUrl },
12052
12336
  ...req.headers.get("x-astra-checkout-session") && {
12053
12337
  _astraCheckoutSessionId: req.headers.get("x-astra-checkout-session").slice(0, 64)
12054
12338
  }
@@ -12067,16 +12351,28 @@ function createUcpCheckoutKit(opts) {
12067
12351
  return render(s2, 201);
12068
12352
  });
12069
12353
  }
12354
+ const notFound = (what = "checkout session") => json2(404, { code: "not_found", content: `Unknown ${what}.` });
12355
+ const om = /^\/orders\/([^/]+)$/.exec(path);
12356
+ if (om && ordersOn) {
12357
+ if (req.method !== "GET")
12358
+ return json2(405, { code: "method_not_allowed", content: "Method not allowed." });
12359
+ const orderId = decodeURIComponent(om[1]);
12360
+ const viaIndex = orderIndex.get(orderId);
12361
+ const s2 = store.findByOrderId ? await store.findByOrderId(orderId) : viaIndex ? await store.get(viaIndex) : null;
12362
+ if (!s2 || s2._caller !== caller || s2._platformProfile !== ctx.profileUrl)
12363
+ return notFound("order");
12364
+ const order = buildUcpOrder(s2, { version: profile.ucp.version });
12365
+ return order ? json2(200, order) : notFound("order");
12366
+ }
12070
12367
  const m = /^\/checkout-sessions\/([^/]+)(\/complete|\/cancel)?$/.exec(path);
12071
12368
  if (!m) return json2(404, { code: "not_found", content: "Unknown operation." });
12072
12369
  const s = await store.get(m[1]);
12073
- if (!s) return json2(404, { code: "not_found", content: "Unknown checkout session." });
12074
- if (s._platformProfile !== ctx.profileUrl)
12075
- return json2(404, { code: "not_found", content: "Unknown checkout session." });
12076
- if (req.method === "GET" && !m[2]) return render(s);
12370
+ if (!s || s._caller !== caller || s._platformProfile !== ctx.profileUrl) return notFound();
12371
+ if (req.method === "GET" && !m[2]) return render(s, 200, "reuse");
12077
12372
  if (req.method === "PUT" && !m[2]) {
12078
- return withIdempotency2(req, body, path, async () => {
12373
+ return withIdempotency2(req, body, path, caller, async () => {
12079
12374
  if (s.status === "completed" || s.status === "canceled") return render(s);
12375
+ if (s.status === "complete_in_progress") return busy(s);
12080
12376
  if (parsed.line_items) {
12081
12377
  const priced = await priceLines(parsed.line_items);
12082
12378
  s.line_items = priced.lines;
@@ -12090,11 +12386,17 @@ function createUcpCheckoutKit(opts) {
12090
12386
  });
12091
12387
  }
12092
12388
  if (req.method === "POST" && m[2] === "/complete") {
12093
- return withIdempotency2(req, body, path, () => complete(req, s, parsed));
12389
+ return withIdempotency2(req, body, path, caller, () => complete(req, s, parsed));
12094
12390
  }
12095
12391
  if (req.method === "POST" && m[2] === "/cancel") {
12096
- return withIdempotency2(req, body, path, async () => {
12097
- if (s.status !== "completed") s.status = "canceled";
12392
+ return withIdempotency2(req, body, path, caller, async () => {
12393
+ if (s.status === "completed" || s.status === "canceled")
12394
+ return json2(405, {
12395
+ code: "method_not_allowed",
12396
+ content: `A ${s.status} checkout can't be canceled.`
12397
+ });
12398
+ if (s.status === "complete_in_progress") return busy(s);
12399
+ s.status = "canceled";
12098
12400
  await store.put(s);
12099
12401
  return render(s);
12100
12402
  });
@@ -12104,7 +12406,9 @@ function createUcpCheckoutKit(opts) {
12104
12406
  return {
12105
12407
  handle,
12106
12408
  profile,
12107
- profileHandler: () => json2(200, profile, { "cache-control": "public, max-age=300" })
12409
+ profileHandler: () => json2(200, profile, { "cache-control": "public, max-age=300" }),
12410
+ resolvePending,
12411
+ sendOrderWebhook
12108
12412
  };
12109
12413
  }
12110
12414
 
@@ -12157,7 +12461,7 @@ function x402PaymentMatchesOffer(payment, offered) {
12157
12461
  }
12158
12462
 
12159
12463
  // src/payments/mpp.ts
12160
- import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
12464
+ import { createHash as createHash11, createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
12161
12465
  var b64url = (s) => Buffer.from(s, "utf8").toString("base64url");
12162
12466
  function canonical(value) {
12163
12467
  if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
@@ -12166,27 +12470,29 @@ function canonical(value) {
12166
12470
  }
12167
12471
  return JSON.stringify(value);
12168
12472
  }
12169
- var BOUND = [
12170
- "realm",
12171
- "method",
12172
- "intent",
12173
- "request",
12174
- "expires",
12175
- "digest",
12176
- "opaque",
12177
- "header"
12178
- ];
12473
+ var requestSlot = (request) => typeof request === "string" ? request : b64url(canonical(request ?? {}));
12179
12474
  function binding(c, secret) {
12180
- const material = BOUND.map(
12181
- (k) => `${k}=${c[k] ?? ""}`
12182
- ).join("\n");
12183
- return createHmac2("sha256", secret).update(material).digest("base64url");
12475
+ const slots = [
12476
+ c.realm,
12477
+ c.method,
12478
+ c.intent,
12479
+ requestSlot(c.request),
12480
+ c.expires ?? "",
12481
+ c.digest ?? ""
12482
+ ];
12483
+ if (c.header && c.header.toLowerCase() !== "authorization") slots.push(c.header);
12484
+ slots.push(c.opaque ?? "");
12485
+ return createHmac2("sha256", secret).update(slots.join("|")).digest("base64url");
12184
12486
  }
12185
12487
  var quote = (v) => `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
12186
12488
  function buildMppChallenge(input, hmacSecret) {
12187
12489
  if (!/^[a-z]+$/.test(input.method)) throw new Error("mpp: method must be lowercase ASCII");
12188
- const { request, ...rest } = input;
12189
- const c = { ...rest, request: b64url(canonical(request)) };
12490
+ const { request, meta, ...rest } = input;
12491
+ const c = {
12492
+ ...rest,
12493
+ request: b64url(canonical(request)),
12494
+ ...rest.opaque === void 0 && meta !== void 0 && { opaque: b64url(canonical(meta)) }
12495
+ };
12190
12496
  const challenge = { id: binding(c, hmacSecret), ...c };
12191
12497
  const parts = Object.keys(challenge).filter((k) => challenge[k] !== void 0).map((k) => `${k}=${quote(String(challenge[k]))}`);
12192
12498
  return { challenge, header: `Payment ${parts.join(", ")}` };
@@ -12203,13 +12509,71 @@ function parseMppCredential(header) {
12203
12509
  return null;
12204
12510
  }
12205
12511
  }
12206
- function verifyMppChallenge(challenge, hmacSecret, now = /* @__PURE__ */ new Date()) {
12512
+ var MemoryMppChallengeStore = class {
12513
+ constructor(defaultTtlMs = 60 * 60 * 1e3, now = Date.now) {
12514
+ this.defaultTtlMs = defaultTtlMs;
12515
+ this.now = now;
12516
+ this.seen = /* @__PURE__ */ new Map();
12517
+ }
12518
+ async consume(id, expiresAt) {
12519
+ const t = this.now();
12520
+ for (const [k, exp] of this.seen) if (exp <= t) this.seen.delete(k);
12521
+ if (this.seen.has(id)) return false;
12522
+ this.seen.set(id, Number.isFinite(expiresAt) ? expiresAt : t + this.defaultTtlMs);
12523
+ return true;
12524
+ }
12525
+ };
12526
+ function mppBodyDigest(body) {
12527
+ return `sha-256=${createHash11("sha256").update(body).digest("base64")}`;
12528
+ }
12529
+ function checkChallenge(challenge, hmacSecret, now) {
12207
12530
  const { id, ...rest } = challenge;
12208
12531
  const expected = Buffer.from(binding(rest, hmacSecret));
12209
12532
  const got = Buffer.from(String(id ?? ""));
12210
- if (expected.length !== got.length || !timingSafeEqual2(expected, got)) return false;
12211
- if (challenge.expires && Date.parse(challenge.expires) < now.getTime()) return false;
12212
- return true;
12533
+ if (expected.length !== got.length || !timingSafeEqual2(expected, got))
12534
+ return { ok: false, reason: "invalid_id" };
12535
+ if (challenge.expires && Date.parse(challenge.expires) < now.getTime())
12536
+ return { ok: false, reason: "expired" };
12537
+ return { ok: true };
12538
+ }
12539
+ function verifyMppChallenge(challenge, hmacSecret, nowOrOptions = /* @__PURE__ */ new Date()) {
12540
+ if (nowOrOptions instanceof Date) return checkChallenge(challenge, hmacSecret, nowOrOptions).ok;
12541
+ const opts = nowOrOptions;
12542
+ return (async () => {
12543
+ const base = checkChallenge(challenge, hmacSecret, opts.now ?? /* @__PURE__ */ new Date());
12544
+ if (!base.ok) return base;
12545
+ if (challenge.digest) {
12546
+ if (opts.body === void 0) return { ok: false, reason: "body_required" };
12547
+ const want = Buffer.from(challenge.digest);
12548
+ const got = Buffer.from(mppBodyDigest(opts.body));
12549
+ if (want.length !== got.length || !timingSafeEqual2(want, got))
12550
+ return { ok: false, reason: "digest_mismatch" };
12551
+ }
12552
+ if (opts.store) {
12553
+ const expiresAt = challenge.expires ? Date.parse(challenge.expires) : NaN;
12554
+ if (!await opts.store.consume(challenge.id, expiresAt))
12555
+ return { ok: false, reason: "already_used" };
12556
+ }
12557
+ return { ok: true };
12558
+ })();
12559
+ }
12560
+ function buildMpp402Response(challenges, problem = {}) {
12561
+ const list = Array.isArray(challenges) ? challenges : [challenges];
12562
+ const headers = new Headers({
12563
+ "content-type": "application/problem+json",
12564
+ "cache-control": "no-store"
12565
+ });
12566
+ for (const c of list) headers.append("www-authenticate", typeof c === "string" ? c : c.header);
12567
+ return new Response(
12568
+ JSON.stringify({
12569
+ type: problem.type ?? "about:blank",
12570
+ title: problem.title ?? "Payment Required",
12571
+ status: 402,
12572
+ ...problem.detail && { detail: problem.detail },
12573
+ ...problem.instance && { instance: problem.instance }
12574
+ }),
12575
+ { status: 402, headers }
12576
+ );
12213
12577
  }
12214
12578
  function decodeMppRequest(challenge) {
12215
12579
  try {
@@ -12292,7 +12656,7 @@ async function verifyKyaPayTokens(header, opts) {
12292
12656
 
12293
12657
  // src/aep/core.ts
12294
12658
  import {
12295
- createHash as createHash9,
12659
+ createHash as createHash12,
12296
12660
  createPrivateKey,
12297
12661
  createPublicKey,
12298
12662
  sign,
@@ -12318,7 +12682,7 @@ function aepCanonicalJson(value) {
12318
12682
  throw new Error(`AEP canonical JSON: unsupported value of type ${typeof value}`);
12319
12683
  }
12320
12684
  function digest(input) {
12321
- return createHash9("sha256").update(aepCanonicalJson(input), "utf8").digest();
12685
+ return createHash12("sha256").update(aepCanonicalJson(input), "utf8").digest();
12322
12686
  }
12323
12687
  function aepHash(input) {
12324
12688
  return `sha256:${digest(input).toString("hex")}`;
@@ -12338,10 +12702,10 @@ function aepPrivateKeyFromSeed(seedBase64) {
12338
12702
  });
12339
12703
  }
12340
12704
  function aepPublicKeyFromBase64(raw) {
12341
- const bytes = Buffer.from(raw, "base64");
12342
- if (bytes.length !== 32) throw new Error("Ed25519 public key must be 32 bytes");
12705
+ const bytes2 = Buffer.from(raw, "base64");
12706
+ if (bytes2.length !== 32) throw new Error("Ed25519 public key must be 32 bytes");
12343
12707
  return createPublicKey({
12344
- key: Buffer.concat([ED25519_SPKI_PREFIX, bytes]),
12708
+ key: Buffer.concat([ED25519_SPKI_PREFIX, bytes2]),
12345
12709
  format: "der",
12346
12710
  type: "spki"
12347
12711
  });
@@ -12460,6 +12824,31 @@ function aepPublicKeyDocument(keys, revocations = []) {
12460
12824
  // src/acp/checkout-kit.ts
12461
12825
  import { randomUUID as randomUUID3 } from "crypto";
12462
12826
  var ACP_API_VERSION = "2026-04-17";
12827
+ var ACP_SUPPORTED_API_VERSIONS = [ACP_API_VERSION];
12828
+ function acpPaymentHandlers(names) {
12829
+ return names.map((name, i) => ({
12830
+ id: name.split(".").pop(),
12831
+ name,
12832
+ version: ACP_API_VERSION,
12833
+ spec: `https://astrasync.ai/ucp/handlers/${name}`,
12834
+ requires_delegate_payment: false,
12835
+ requires_pci_compliance: false,
12836
+ psp: "astrasync",
12837
+ config_schema: `https://astrasync.ai/ucp/handlers/${name}.json`,
12838
+ instrument_schemas: [],
12839
+ config: {},
12840
+ display_order: i
12841
+ }));
12842
+ }
12843
+ function buildAcpWellKnown(opts) {
12844
+ return {
12845
+ protocol: "acp",
12846
+ api_versions: [...opts.apiVersions ?? ACP_SUPPORTED_API_VERSIONS],
12847
+ endpoint: opts.endpoint,
12848
+ capabilities: { payment: { handlers: acpPaymentHandlers(opts.paymentHandlers) } },
12849
+ ...opts.links?.length && { links: opts.links }
12850
+ };
12851
+ }
12463
12852
  var json3 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
12464
12853
  var apiError = (status, code, message, param) => json3(status, { type: "invalid_request", code, message, ...param && { param } });
12465
12854
  var total2 = (s) => s.totals.find((t) => t.type === "total")?.amount ?? 0;
@@ -12467,26 +12856,30 @@ function createAcpCheckoutKit(opts) {
12467
12856
  const mem = /* @__PURE__ */ new Map();
12468
12857
  const store = opts.store ?? {
12469
12858
  get: async (id) => mem.get(id) ?? null,
12470
- put: async (s) => void mem.set(s.id, s)
12859
+ put: async (s) => void mem.set(s.id, s),
12860
+ claim: async (id, from, to) => {
12861
+ const s = mem.get(id);
12862
+ if (!s || s.status !== from) return false;
12863
+ s.status = to;
12864
+ return true;
12865
+ }
12471
12866
  };
12472
12867
  const idem = {
12473
12868
  store: opts.idempotencyStore ?? new MemoryIdempotencyStore(),
12474
12869
  profile: "acp",
12475
12870
  required: true
12476
12871
  };
12477
- const handlers = opts.paymentHandlers.map((name, i) => ({
12478
- id: name.split(".").pop(),
12479
- name,
12480
- version: ACP_API_VERSION,
12481
- spec: `https://astrasync.ai/ucp/handlers/${name}`,
12482
- requires_delegate_payment: false,
12483
- requires_pci_compliance: false,
12484
- psp: "astrasync",
12485
- config_schema: `https://astrasync.ai/ucp/handlers/${name}.json`,
12486
- instrument_schemas: [],
12487
- config: {},
12488
- display_order: i
12489
- }));
12872
+ warnInMemoryStores("ACP", [
12873
+ ...opts.store ? [] : ["checkout store"],
12874
+ ...opts.idempotencyStore ? [] : ["idempotency store"]
12875
+ ]);
12876
+ if (!store.claim) {
12877
+ warnOnce(
12878
+ "acp-claim",
12879
+ "[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."
12880
+ );
12881
+ }
12882
+ const handlers = acpPaymentHandlers(opts.paymentHandlers);
12490
12883
  function view(s) {
12491
12884
  return {
12492
12885
  id: s.id,
@@ -12541,13 +12934,13 @@ function createAcpCheckoutKit(opts) {
12541
12934
  ]
12542
12935
  };
12543
12936
  }
12544
- async function idempotent(req, body, path, run) {
12937
+ async function idempotent(req, body, path, caller, run) {
12545
12938
  const d = await beginIdempotent(idem, {
12546
12939
  method: req.method,
12547
12940
  path,
12548
12941
  body,
12549
12942
  key: req.headers.get("idempotency-key") ?? void 0,
12550
- auth: req.headers.get("authorization") ?? void 0
12943
+ auth: caller
12551
12944
  });
12552
12945
  if (d.kind === "reject") return apiError(d.status, d.code, d.message);
12553
12946
  if (d.kind === "replay")
@@ -12564,63 +12957,21 @@ function createAcpCheckoutKit(opts) {
12564
12957
  });
12565
12958
  return res;
12566
12959
  }
12567
- async function complete(req, s, body) {
12568
- if (s.status === "completed" || s.status === "canceled") return json3(200, view(s));
12569
- if (!body.payment_data)
12570
- return apiError(400, "missing", "payment_data is required.", "$.payment_data");
12571
- const result = await opts.verifyPurchase({
12572
- session: s,
12573
- checkoutSessionId: s._astraCheckoutSessionId ?? s.id,
12574
- astraId: req.headers.get("x-astra-id") ?? void 0,
12575
- attemptId: req.headers.get("x-astra-attempt-id") ?? void 0,
12576
- valueMajor: total2(s) / 100
12577
- });
12578
- const held = result.settlementOutcome?.status === "requires_approval" || result.stepUpApproval?.status === "pending";
12579
- if (held) {
12580
- s.status = "pending_approval";
12581
- s.continue_url = result.stepUpApproval?.approvalUrl ?? opts.fallbackContinueUrl;
12582
- s.messages = [
12583
- {
12584
- type: "error",
12585
- code: "approval_required",
12586
- content: "The buyer needs to approve this purchase.",
12587
- severity: "medium",
12588
- resolution: "requires_buyer_review"
12589
- }
12590
- ];
12591
- await store.put(s);
12592
- return json3(200, view(s));
12593
- }
12594
- if (!result.identityVerified || !result.policyAllowed) {
12595
- s.messages = [
12596
- {
12597
- type: "error",
12598
- code: "intervention_required",
12599
- content: "The agent is not authorized for this purchase.",
12600
- severity: "high",
12601
- resolution: "requires_buyer_review"
12602
- }
12603
- ];
12604
- await store.put(s);
12605
- return json3(200, view(s));
12606
- }
12607
- s.status = "complete_in_progress";
12608
- const settled = await opts.settle({
12609
- session: s,
12610
- result,
12611
- paymentData: body.payment_data
12612
- });
12613
- const orderId = result.settlementOutcome?.orderId;
12614
- if (opts.reportSettlement && orderId && settled.status !== "pending") {
12615
- await opts.reportSettlement({
12616
- orderId,
12617
- status: settled.status === "settled" ? "settled" : "failed",
12618
- ...orderReportAmount(result, { amountMinor: total2(s), currency: s.currency }),
12619
- ...settled.status === "settled" && settled.processorRef && { processorRef: settled.processorRef },
12620
- ...settled.status === "settled" && settled.txHash && { txHash: settled.txHash, chainId: settled.chainId },
12621
- ...settled.status === "failed" && settled.failureCode && { failureCode: settled.failureCode }
12622
- }).catch(() => void 0);
12623
- }
12960
+ const inProgress = () => apiError(409, "checkout_in_progress", "This checkout session is already being completed.");
12961
+ async function reportOutcome(p, settled) {
12962
+ if (!opts.reportSettlement || !p.orderId) return;
12963
+ await opts.reportSettlement({
12964
+ orderId: p.orderId,
12965
+ status: settled.status,
12966
+ ...p.report,
12967
+ ...settled.status === "settled" && settled.processorRef && { processorRef: settled.processorRef },
12968
+ ...settled.status === "settled" && settled.txHash && { txHash: settled.txHash, chainId: settled.chainId },
12969
+ ...settled.status === "failed" && settled.failureCode && { failureCode: settled.failureCode }
12970
+ }).catch(() => void 0);
12971
+ }
12972
+ async function finish(s, result, p, settled) {
12973
+ await reportOutcome(p, settled);
12974
+ delete s._pending;
12624
12975
  if (settled.status === "failed") {
12625
12976
  s.status = "ready_for_payment";
12626
12977
  s.messages = [
@@ -12633,26 +12984,126 @@ function createAcpCheckoutKit(opts) {
12633
12984
  }
12634
12985
  ];
12635
12986
  await store.put(s);
12636
- return json3(200, view(s));
12987
+ return;
12637
12988
  }
12638
12989
  const order = await opts.fulfil({ session: s, result });
12990
+ const permalink = order.permalink_url ?? (opts.permalinkBase && `${opts.permalinkBase.replace(/\/+$/, "")}/${encodeURIComponent(order.id)}`);
12639
12991
  s.order = {
12640
12992
  id: order.id,
12993
+ checkout_session_id: s.id,
12641
12994
  status: "confirmed",
12642
12995
  currency: s.currency.toLowerCase(),
12643
12996
  total: total2(s),
12644
- line_items: [],
12997
+ line_items: s.line_items.map((l) => ({
12998
+ id: l.id,
12999
+ item: l.item,
13000
+ name: l.name,
13001
+ quantity: l.quantity,
13002
+ unit_amount: l.unit_amount,
13003
+ totals: l.totals
13004
+ })),
12645
13005
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
12646
- ...order.permalink_url && { permalink_url: order.permalink_url }
13006
+ ...permalink && { permalink_url: permalink }
12647
13007
  };
12648
13008
  s.status = "completed";
12649
13009
  s.messages = [];
12650
13010
  await store.put(s);
12651
- return json3(200, view(s));
12652
13011
  }
12653
- async function handle(req) {
12654
- if (!req.headers.get("api-version"))
12655
- return apiError(400, "missing", "The API-Version header is required.");
13012
+ async function complete(req, s, body) {
13013
+ if (s.status === "completed" || s.status === "canceled") return json3(200, view(s));
13014
+ if (s.status === "complete_in_progress") return inProgress();
13015
+ if (!body.payment_data)
13016
+ return apiError(400, "missing", "payment_data is required.", "$.payment_data");
13017
+ const from = s.status;
13018
+ if (store.claim) {
13019
+ if (!await store.claim(s.id, from, "complete_in_progress")) return inProgress();
13020
+ s.status = "complete_in_progress";
13021
+ } else {
13022
+ s.status = "complete_in_progress";
13023
+ await store.put(s);
13024
+ }
13025
+ try {
13026
+ const result = await opts.verifyPurchase({
13027
+ session: s,
13028
+ checkoutSessionId: s._astraCheckoutSessionId ?? s.id,
13029
+ astraId: req.headers.get("x-astra-id") ?? void 0,
13030
+ attemptId: req.headers.get("x-astra-attempt-id") ?? void 0,
13031
+ valueMajor: total2(s) / 100
13032
+ });
13033
+ if (isHeld(result)) {
13034
+ s.status = "pending_approval";
13035
+ s.continue_url = result.stepUpApproval?.approvalUrl ?? opts.fallbackContinueUrl;
13036
+ s.messages = [
13037
+ {
13038
+ type: "error",
13039
+ code: "approval_required",
13040
+ content: "The buyer needs to approve this purchase.",
13041
+ severity: "medium",
13042
+ resolution: "requires_buyer_review"
13043
+ }
13044
+ ];
13045
+ await store.put(s);
13046
+ return json3(200, view(s));
13047
+ }
13048
+ if (!result.identityVerified || !result.policyAllowed) {
13049
+ s.status = from;
13050
+ s.messages = [
13051
+ {
13052
+ type: "error",
13053
+ code: "intervention_required",
13054
+ content: "The agent is not authorized for this purchase.",
13055
+ severity: "high",
13056
+ resolution: "requires_buyer_review"
13057
+ }
13058
+ ];
13059
+ await store.put(s);
13060
+ return json3(200, view(s));
13061
+ }
13062
+ const settled = await opts.settle({
13063
+ session: s,
13064
+ result,
13065
+ paymentData: body.payment_data
13066
+ });
13067
+ const orderId = result.settlementOutcome?.orderId;
13068
+ const pending = {
13069
+ ...orderId && { orderId },
13070
+ report: orderReportAmount(result, { amountMinor: total2(s), currency: s.currency }),
13071
+ result: pendingResult(result)
13072
+ };
13073
+ if (settled.status === "pending") {
13074
+ s._pending = pending;
13075
+ s.messages = [];
13076
+ await store.put(s);
13077
+ return json3(200, view(s));
13078
+ }
13079
+ await finish(s, result, pending, settled);
13080
+ return json3(200, view(s));
13081
+ } catch (e) {
13082
+ if (s.status === "complete_in_progress" && !s._pending) {
13083
+ s.status = from;
13084
+ await store.put(s).catch(() => void 0);
13085
+ }
13086
+ throw e;
13087
+ }
13088
+ }
13089
+ async function resolvePending(sessionId, outcome) {
13090
+ const s = await store.get(sessionId);
13091
+ if (!s) return null;
13092
+ if (s.status !== "complete_in_progress" || !s._pending) return s;
13093
+ await finish(s, s._pending.result, s._pending, outcome);
13094
+ return s;
13095
+ }
13096
+ async function route(req, reqCtx) {
13097
+ const version = req.headers.get("api-version");
13098
+ if (!version) return apiError(400, "missing", "The API-Version header is required.");
13099
+ if (!ACP_SUPPORTED_API_VERSIONS.includes(version)) {
13100
+ return json3(400, {
13101
+ type: "invalid_request",
13102
+ code: "unsupported_api_version",
13103
+ message: `API-Version ${version} is not supported.`,
13104
+ supported_versions: [...ACP_SUPPORTED_API_VERSIONS]
13105
+ });
13106
+ }
12656
13107
  const url = new URL(req.url);
12657
13108
  const base = (opts.basePath ?? "").replace(/\/+$/, "");
12658
13109
  const path = url.pathname.startsWith(base) ? url.pathname.slice(base.length) : url.pathname;
@@ -12663,9 +13114,11 @@ function createAcpCheckoutKit(opts) {
12663
13114
  } catch {
12664
13115
  return apiError(400, "invalid", "Body is not JSON.");
12665
13116
  }
12666
- const caller = req.headers.get("authorization") ?? "";
13117
+ const caller = await resolveCallerScope(req, reqCtx, opts.resolveCallerId);
13118
+ if (!caller)
13119
+ return apiError(401, "unauthorized", "Send an Authorization header (or X-Astra-Id).");
12667
13120
  if (req.method === "POST" && path === "/checkout_sessions") {
12668
- return idempotent(req, body, path, async () => {
13121
+ return idempotent(req, body, path, caller, async () => {
12669
13122
  const items = parsed.line_items ?? parsed.items;
12670
13123
  if (!items?.length)
12671
13124
  return apiError(400, "missing", "line_items is required.", "$.line_items");
@@ -12693,8 +13146,9 @@ function createAcpCheckoutKit(opts) {
12693
13146
  if (req.method === "GET" && !m[2]) return json3(200, view(s));
12694
13147
  if (req.method !== "POST") return apiError(405, "unsupported", "Method not allowed.");
12695
13148
  if (!m[2]) {
12696
- return idempotent(req, body, path, async () => {
13149
+ return idempotent(req, body, path, caller, async () => {
12697
13150
  if (s.status === "completed" || s.status === "canceled") return json3(200, view(s));
13151
+ if (s.status === "complete_in_progress") return inProgress();
12698
13152
  const items = parsed.line_items ?? parsed.items;
12699
13153
  if (items) {
12700
13154
  const p = await price(items);
@@ -12709,17 +13163,82 @@ function createAcpCheckoutKit(opts) {
12709
13163
  return json3(200, view(s));
12710
13164
  });
12711
13165
  }
12712
- if (m[2] === "/complete") return idempotent(req, body, path, () => complete(req, s, parsed));
12713
- return idempotent(req, body, path, async () => {
12714
- if (s.status !== "completed") s.status = "canceled";
13166
+ if (m[2] === "/complete")
13167
+ return idempotent(req, body, path, caller, () => complete(req, s, parsed));
13168
+ return idempotent(req, body, path, caller, async () => {
13169
+ if (s.status === "completed" || s.status === "canceled")
13170
+ return apiError(405, "method_not_allowed", `A ${s.status} session can't be canceled.`);
13171
+ if (s.status === "complete_in_progress") return inProgress();
13172
+ s.status = "canceled";
12715
13173
  await store.put(s);
12716
13174
  return json3(200, view(s));
12717
13175
  });
12718
13176
  }
12719
- return { handle };
13177
+ async function handle(req, reqCtx) {
13178
+ const res = await route(req, reqCtx);
13179
+ const key = req.headers.get("idempotency-key");
13180
+ const requestId = req.headers.get("request-id");
13181
+ if (!key && !requestId) return res;
13182
+ const headers = new Headers(res.headers);
13183
+ if (key) headers.set("idempotency-key", key);
13184
+ if (requestId) headers.set("request-id", requestId);
13185
+ return new Response(res.body, { status: res.status, headers });
13186
+ }
13187
+ return { handle, resolvePending };
13188
+ }
13189
+
13190
+ // src/adapters/kit-express.ts
13191
+ var bytes = (b) => b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
13192
+ function rawBodyOf(req) {
13193
+ if (req.method === "GET" || req.method === "HEAD") return void 0;
13194
+ const raw = req.rawBody;
13195
+ if (Buffer.isBuffer(raw)) return bytes(raw);
13196
+ if (typeof raw === "string") return raw;
13197
+ if (Buffer.isBuffer(req.body)) return bytes(req.body);
13198
+ if (typeof req.body === "string") return req.body;
13199
+ if (req.body !== void 0 && req.body !== null && typeof req.body === "object") {
13200
+ if (Object.keys(req.body).length === 0) return void 0;
13201
+ warnOnce(
13202
+ "kit-express-parsed-body",
13203
+ "[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."
13204
+ );
13205
+ return JSON.stringify(req.body);
13206
+ }
13207
+ return void 0;
13208
+ }
13209
+ function expressHandler(kit, options = {}) {
13210
+ return async (req, res, next) => {
13211
+ try {
13212
+ const host = req.get?.("host") ?? req.headers.host ?? "localhost";
13213
+ const url = `${req.protocol ?? "http"}://${host}${req.originalUrl ?? req.url}`;
13214
+ const headers = new Headers();
13215
+ for (const [k, v] of Object.entries(req.headers)) {
13216
+ if (v === void 0) continue;
13217
+ if (Array.isArray(v)) v.forEach((x) => headers.append(k, x));
13218
+ else headers.set(k, v);
13219
+ }
13220
+ const body = rawBodyOf(req);
13221
+ const request = new Request(url, {
13222
+ method: req.method,
13223
+ headers,
13224
+ ...body !== void 0 && { body }
13225
+ });
13226
+ const verified = req.agentVerification?.identityVerified ? req.agentVerification.agent?.astraId : void 0;
13227
+ const callerId = options.callerId ? options.callerId(req) : verified;
13228
+ const response = await kit.handle(request, callerId ? { callerId } : void 0);
13229
+ res.status(response.status);
13230
+ response.headers.forEach((value, name) => {
13231
+ res.setHeader(name, value);
13232
+ });
13233
+ res.end(Buffer.from(await response.arrayBuffer()));
13234
+ } catch (err) {
13235
+ next(err);
13236
+ }
13237
+ };
12720
13238
  }
12721
13239
  export {
12722
13240
  ACP_API_VERSION,
13241
+ ACP_SUPPORTED_API_VERSIONS,
12723
13242
  AEP_SPEC_VERSION,
12724
13243
  AP2_VCT,
12725
13244
  AepChain,
@@ -12740,6 +13259,7 @@ export {
12740
13259
  MCP_VERIFIED_HOP_MAX_AGE_MS,
12741
13260
  MemoryCheckoutStore,
12742
13261
  MemoryIdempotencyStore,
13262
+ MemoryMppChallengeStore,
12743
13263
  PLATFORM_AGENT_SIGNATURES,
12744
13264
  RUNTIME_SIGNATURES,
12745
13265
  RedisIdempotencyStore,
@@ -12749,10 +13269,12 @@ export {
12749
13269
  SDK_USER_AGENT,
12750
13270
  SettlementReportError,
12751
13271
  TRUST_LEVEL_RANGES,
13272
+ UCP_ORDER_CAPABILITY,
12752
13273
  UCP_VERSION,
12753
13274
  VERIFICATION_STATUS_HEADER,
12754
13275
  SDK_VERSION as VERSION,
12755
13276
  _resetEdgeConfigCache,
13277
+ acpPaymentHandlers,
12756
13278
  aepCanonicalJson,
12757
13279
  aepHash,
12758
13280
  aepPrivateKeyFromSeed,
@@ -12767,9 +13289,12 @@ export {
12767
13289
  authorizeSettlement,
12768
13290
  awaitStepUpApproval,
12769
13291
  beginIdempotent,
13292
+ buildAcpWellKnown,
12770
13293
  buildGuidance,
13294
+ buildMpp402Response,
12771
13295
  buildMppChallenge,
12772
13296
  buildSdkObservedMetadata,
13297
+ buildUcpOrder,
12773
13298
  buildUcpProfile,
12774
13299
  buildVerificationUnavailableBody,
12775
13300
  buildX402Challenge,
@@ -12793,6 +13318,7 @@ export {
12793
13318
  encodeMppReceipt,
12794
13319
  encodeX402SettlementResponse,
12795
13320
  express_exports as express,
13321
+ expressHandler,
12796
13322
  extractApiKeyFormat,
12797
13323
  extractCredentials,
12798
13324
  extractMcpCredentials,
@@ -12817,6 +13343,7 @@ export {
12817
13343
  matchEdgePathRule,
12818
13344
  matchPlatformSignature,
12819
13345
  matchRoutePattern,
13346
+ mppBodyDigest,
12820
13347
  negotiateUcp,
12821
13348
  nextjs_exports as nextjs,
12822
13349
  normalizeDeclaredSource,