@agent-cards/sdk 0.23.27 → 0.24.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 +66 -0
  2. package/PREFLIGHT.md +7 -1
  3. package/README.md +137 -99
  4. package/dist/aci.d.ts +52 -22
  5. package/dist/aci.js +79 -15
  6. package/dist/adyen-merchant-hosted.generated.d.ts +1 -1
  7. package/dist/adyen-merchant-hosted.generated.js +459 -21
  8. package/dist/agent-amount.generated.js +1 -1
  9. package/dist/agentcard.d.ts +4 -5
  10. package/dist/agentcard.js +4 -5
  11. package/dist/amadeus.d.ts +9 -3
  12. package/dist/amadeus.js +33 -4
  13. package/dist/api-module.d.ts +1 -7
  14. package/dist/api-module.js +3 -5
  15. package/dist/{voice-retry.js → api-retry.js} +1 -1
  16. package/dist/api-types.d.ts +53 -0
  17. package/dist/{voice-types.js → api-types.js} +7 -2
  18. package/dist/approvals.d.ts +166 -0
  19. package/dist/approvals.js +213 -0
  20. package/dist/buy.d.ts +20 -101
  21. package/dist/buy.js +19 -74
  22. package/dist/capabilities.generated.js +1 -1
  23. package/dist/card-fields.generated.js +1398 -106
  24. package/dist/cdp.d.ts +3 -13
  25. package/dist/cdp.js +231 -175
  26. package/dist/client.d.ts +104 -22
  27. package/dist/client.js +215 -40
  28. package/dist/creditguard.generated.d.ts +4 -4
  29. package/dist/datatrans.generated.js +22 -1
  30. package/dist/elavon-converge.generated.js +5 -3
  31. package/dist/hold.d.ts +8 -0
  32. package/dist/hold.js +43 -2
  33. package/dist/hosted-entry.js +2 -0
  34. package/dist/index.d.ts +9 -15
  35. package/dist/index.js +6 -10
  36. package/dist/juspay-gate.js +2 -2
  37. package/dist/juspay.generated.d.ts +2 -2
  38. package/dist/juspay.generated.js +174 -4
  39. package/dist/lifecycle.d.ts +9 -61
  40. package/dist/lifecycle.js +24 -10
  41. package/dist/merchant-card-endpoints.generated.d.ts +6 -1
  42. package/dist/merchant-card-endpoints.generated.js +344 -19
  43. package/dist/merchant-hosted.d.ts +8 -1
  44. package/dist/nike-order.d.ts +51 -0
  45. package/dist/nike-order.js +81 -0
  46. package/dist/oceanpayment.d.ts +38 -0
  47. package/dist/oceanpayment.js +70 -0
  48. package/dist/paddle-tokenex.generated.js +105 -10
  49. package/dist/payplug.d.ts +41 -0
  50. package/dist/payplug.js +71 -0
  51. package/dist/payrails.generated.js +27 -7
  52. package/dist/payrexx.generated.js +176 -2
  53. package/dist/pre-claim.d.ts +9 -0
  54. package/dist/pre-claim.js +86 -7
  55. package/dist/preflight-capabilities.generated.d.ts +0 -4
  56. package/dist/preflight-capabilities.generated.js +0 -5
  57. package/dist/preflight-catalog.json +46 -5
  58. package/dist/preflight-playwright.d.ts +2 -2
  59. package/dist/preflight-playwright.js +63 -14
  60. package/dist/preflight-schemas.json +4 -0
  61. package/dist/preflight.generated.d.ts +0 -4
  62. package/dist/preflight.generated.js +24 -1
  63. package/dist/prepared-processor.d.ts +18 -5
  64. package/dist/prepared-processor.js +48 -8
  65. package/dist/primer.generated.js +1418 -120
  66. package/dist/processout.generated.d.ts +61 -0
  67. package/dist/processout.generated.js +577 -0
  68. package/dist/requests.js +1 -1
  69. package/dist/shiji-sts.generated.js +105 -10
  70. package/dist/stripe-checkout.generated.js +1405 -234
  71. package/dist/substitutions.generated.js +460 -26
  72. package/dist/tap-payments.generated.js +105 -10
  73. package/dist/twoc2p.generated.js +105 -10
  74. package/dist/url-masks/bluesnap.d.ts +2 -0
  75. package/dist/url-masks/bluesnap.js +15 -0
  76. package/dist/url-masks/oceanpayment.d.ts +2 -0
  77. package/dist/url-masks/oceanpayment.js +13 -0
  78. package/dist/url-masks/payplug.d.ts +2 -0
  79. package/dist/url-masks/payplug.js +13 -0
  80. package/dist/url-masks/vtex_payments.d.ts +2 -0
  81. package/dist/url-masks/vtex_payments.js +12 -0
  82. package/dist/url-masks.generated.d.ts +1 -1
  83. package/dist/url-masks.generated.js +6 -2
  84. package/dist/verifone.generated.js +42 -2
  85. package/dist/vtex-payments.generated.d.ts +127 -0
  86. package/dist/vtex-payments.generated.js +544 -0
  87. package/dist/webhooks.d.ts +2 -2
  88. package/dist/webhooks.js +2 -2
  89. package/dist/wspay.generated.d.ts +1 -1
  90. package/dist/wspay.generated.js +175 -3
  91. package/examples/preflight/kernel-native/inventory.json +1 -1
  92. package/package.json +6 -8
  93. package/dist/languages.d.ts +0 -23
  94. package/dist/languages.js +0 -76
  95. package/dist/voice-pkce.d.ts +0 -49
  96. package/dist/voice-pkce.js +0 -147
  97. package/dist/voice-stream.generated.d.ts +0 -118
  98. package/dist/voice-stream.generated.js +0 -170
  99. package/dist/voice-types.d.ts +0 -735
  100. package/dist/voice.d.ts +0 -373
  101. package/dist/voice.js +0 -815
  102. package/readme.test.mjs +0 -53
  103. /package/dist/{voice-retry.d.ts → api-retry.d.ts} +0 -0
@@ -1,11 +1,46 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: fedc712140db2d5651bed3391ac158d67dc04d8be58db9926ed328e9be20eb1b
3
+ // artifact-sha256: 4acbca9604c5eee50f5f73dc3dea5ab0d40c9cba1214ccda0d2074271b3554a5
4
4
  // src/json-path.js
5
5
  var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
6
6
  function isPlainObject(v) {
7
7
  return !!v && typeof v === "object" && !Array.isArray(v);
8
8
  }
9
+ function isTextStep(step) {
10
+ return isPlainObject(step) && Object.keys(step).length === 1 && step.text === "json";
11
+ }
12
+ function spellJson(parsed, form) {
13
+ const text = JSON.stringify(parsed);
14
+ if (form !== "php")
15
+ return text;
16
+ return text.replace(/\//g, "\\/").replace(/[\u0080-\uffff]/g, (unit) => `\\u${unit.charCodeAt(0).toString(16).padStart(4, "0")}`);
17
+ }
18
+ function jsonTextForm(value) {
19
+ if (typeof value !== "string" || !/^[[{]/.test(value))
20
+ return void 0;
21
+ let parsed;
22
+ try {
23
+ parsed = JSON.parse(value);
24
+ }
25
+ catch {
26
+ return void 0;
27
+ }
28
+ if (parsed === null || typeof parsed !== "object")
29
+ return void 0;
30
+ for (const form of ["js", "php"]) {
31
+ try {
32
+ if (spellJson(parsed, form) === value)
33
+ return { parsed, form };
34
+ }
35
+ catch {
36
+ return void 0;
37
+ }
38
+ }
39
+ return void 0;
40
+ }
41
+ function jsonText(value) {
42
+ return jsonTextForm(value)?.parsed;
43
+ }
9
44
  function jsonAt(root, path) {
10
45
  let node = root;
11
46
  for (const step of path) {
@@ -36,7 +71,12 @@ function valuesIgnoringCase(root, path) {
36
71
  for (const step of path) {
37
72
  const next = [];
38
73
  for (const node of nodes) {
39
- if (typeof step === "number") {
74
+ if (isTextStep(step)) {
75
+ const inner = jsonText(node);
76
+ if (inner !== void 0)
77
+ next.push(inner);
78
+ }
79
+ else if (typeof step === "number") {
40
80
  if (Array.isArray(node) && Number.isSafeInteger(step) && step >= 0 && step < node.length)
41
81
  next.push(node[step]);
42
82
  }
@@ -51,7 +91,7 @@ function valuesIgnoringCase(root, path) {
51
91
  return nodes;
52
92
  }
53
93
  function describePath(path) {
54
- return path.length ? path.map((step) => typeof step === "number" ? `[${step}]` : `.${step}`).join("").replace(/^\./, "") : "(root)";
94
+ return path.length ? path.map((step) => typeof step === "number" ? `[${step}]` : isTextStep(step) ? "<json>" : `.${step}`).join("").replace(/^\./, "") : "(root)";
55
95
  }
56
96
  // src/url-template.js
57
97
  var PLACEHOLDER_VALUE = /^[A-Za-z0-9._~@+-]{1,256}$/;
@@ -157,6 +197,192 @@ var SubstitutionError = class extends Error {
157
197
  this.name = "SubstitutionError";
158
198
  }
159
199
  };
200
+ // src/three-ds-step.js
201
+ var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
202
+ var PERCENT_LAYERS = 8;
203
+ function percentDecoded(text) {
204
+ let out = text;
205
+ for (let layer = 0; layer < PERCENT_LAYERS; layer += 1) {
206
+ const next = out.replace(/%([0-9A-Fa-f]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
207
+ if (next === out)
208
+ break;
209
+ out = next;
210
+ }
211
+ return out;
212
+ }
213
+ var NAMED = /* @__PURE__ */ Object.freeze({
214
+ amp: "&",
215
+ percnt: "%",
216
+ num: "#",
217
+ quest: "?",
218
+ semi: ";",
219
+ excl: "!",
220
+ dollar: "$",
221
+ lpar: "(",
222
+ rpar: ")",
223
+ ast: "*",
224
+ comma: ",",
225
+ commat: "@",
226
+ lsqb: "[",
227
+ rsqb: "]",
228
+ lcub: "{",
229
+ rcub: "}",
230
+ verbar: "|",
231
+ bsol: "\\",
232
+ hat: "^",
233
+ grave: "`",
234
+ plus: "+",
235
+ sol: "/",
236
+ equals: "=",
237
+ lowbar: "_",
238
+ period: ".",
239
+ colon: ":",
240
+ quot: '"',
241
+ apos: "'",
242
+ lt: "<",
243
+ gt: ">"
244
+ });
245
+ var NUMERIC = "#(?:[xX]([0-9a-fA-F]+)|([0-9]+));?";
246
+ var BARE_NAME = "(sup[123]|frac(?:12|14|34)|[A-Za-z]+);?";
247
+ var referenceText = (hex, decimal, name) => {
248
+ if (name !== void 0)
249
+ return Object.prototype.hasOwnProperty.call(NAMED, name.toLowerCase()) ? NAMED[name.toLowerCase()] : " ";
250
+ const point = hex === void 0 ? parseInt(decimal, 10) : parseInt(hex, 16);
251
+ return point <= 1114111 ? String.fromCodePoint(point) : "";
252
+ };
253
+ function referenceReadings(text) {
254
+ const read = (pattern) => text.replace(new RegExp(`&(?:${NUMERIC}|${pattern})`, "g"), (_, hex, decimal, name) => referenceText(hex, decimal, name));
255
+ return [read("([A-Za-z][A-Za-z0-9]*);"), read(BARE_NAME)];
256
+ }
257
+ function readings(text) {
258
+ const out = /* @__PURE__ */ new Set([text, percentDecoded(text)]);
259
+ for (const reading of [...out])
260
+ for (const decoded of referenceReadings(reading))
261
+ out.add(decoded).add(percentDecoded(decoded));
262
+ return out;
263
+ }
264
+ function base64Bytes(run, skip) {
265
+ let bits = 0;
266
+ let value = 0;
267
+ let out = "";
268
+ for (let i = skip; i < run.length; i += 1) {
269
+ const char = run[i];
270
+ value = value << 6 | (char === "-" ? 62 : char === "_" ? 63 : ALPHABET.indexOf(char));
271
+ bits += 6;
272
+ if (bits >= 8) {
273
+ bits -= 8;
274
+ out += String.fromCharCode(value >> bits);
275
+ value &= (1 << bits) - 1;
276
+ }
277
+ }
278
+ return out;
279
+ }
280
+ var base64Text = (value) => base64Bytes(value.replace(/=+$/, ""), 0);
281
+ // src/test-pans.js
282
+ function immutableSet(values, name) {
283
+ const set = new Set(values);
284
+ for (const method of ["add", "delete", "clear"]) {
285
+ set[method] = () => {
286
+ throw new TypeError(`${name} is immutable`);
287
+ };
288
+ }
289
+ return Object.freeze(set);
290
+ }
291
+ var DOCUMENTED_TEST_PANS = /* @__PURE__ */ immutableSet([
292
+ "4242424242424242",
293
+ "4000000000000002",
294
+ "4000002500003155",
295
+ "4111111111111111"
296
+ ], "DOCUMENTED_TEST_PANS");
297
+ // src/answer-card-leak.js
298
+ var ANSWER_HOLDS_CARD = "card_in_answer";
299
+ var LAYERS = 2;
300
+ var BASE64_RUN = /[A-Za-z0-9+/_-]{6,}/g;
301
+ var BASE64_PIECE = /[A-Za-z0-9+/_-]{2,}={0,2}/g;
302
+ var VALUE_END = /["'<>&=,;:{}[\]|\r\n\t]/;
303
+ var DECIMAL = /\p{Nd}/u;
304
+ var PIECE = 4;
305
+ function digitValue(char) {
306
+ const point = char.codePointAt(0);
307
+ let first = point;
308
+ while (DECIMAL.test(String.fromCodePoint(first - 1)))
309
+ first -= 1;
310
+ return String((point - first) % 10);
311
+ }
312
+ var ENCLOSED = /* @__PURE__ */ Object.freeze([[9451, 10, 11], [9461, 10, 1], [9471, 1, 0], [10102, 30, 1], [127243, 2, 0]]);
313
+ var ENCLOSED_CHAR = /[\u24eb-\u24ff\u2776-\u2793\u{1f10b}\u{1f10c}]/gu;
314
+ function enclosedValue(char) {
315
+ const point = char.codePointAt(0);
316
+ const [first, , start] = ENCLOSED.find(([from, size]) => point >= from && point < from + size);
317
+ return String(first === 127243 ? 0 : first === 10102 ? (point - first) % 10 + 1 : start + point - first);
318
+ }
319
+ var asciiDigits = (text) => (typeof text.normalize === "function" ? text.normalize("NFKC") : text).replace(ENCLOSED_CHAR, enclosedValue).replace(/\p{Nd}/gu, (char) => char >= "0" && char <= "9" ? char : digitValue(char));
320
+ var unescaped = (text) => text.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
321
+ var decodedInPlace = (text) => text.replace(BASE64_PIECE, (piece) => {
322
+ const bare = piece.replace(/=+$/, "");
323
+ for (let skip = 0; skip < 4 && skip < bare.length - 1; skip += 1) {
324
+ const bytes = base64Text(bare.slice(skip));
325
+ if (/^[\x20-\x7e]+$/.test(bytes))
326
+ return bare.slice(0, skip) + bytes;
327
+ }
328
+ return piece;
329
+ });
330
+ function collect(text, layers, streams, runs) {
331
+ const base = /* @__PURE__ */ new Set([...readings(text), ...readings(unescaped(text))]);
332
+ for (const reading of /* @__PURE__ */ new Set([...base, ...[...base].map(decodedInPlace)])) {
333
+ const digits = asciiDigits(reading);
334
+ const sequence = digits.match(/[0-9]+/g) ?? [];
335
+ streams.add(sequence);
336
+ for (const run of sequence)
337
+ runs.add(run);
338
+ for (const value of digits.split(VALUE_END))
339
+ if (/[0-9]/.test(value))
340
+ runs.add(value.replace(/[^0-9]/g, ""));
341
+ if (layers === 0)
342
+ continue;
343
+ for (const run of new Set(reading.match(BASE64_RUN))) {
344
+ for (let skip = 0; skip < 4; skip += 1)
345
+ collect(base64Text(run.slice(skip)), layers - 1, streams, runs);
346
+ }
347
+ }
348
+ }
349
+ function joined(target, runs, shown) {
350
+ const list = [...runs];
351
+ const hidden = (part, run) => !shown.has(part) && !shown.has(run);
352
+ const rest = (p, used, hides) => list.some((run) => !used.includes(run) && (run.startsWith(target.slice(p)) && (hides || hidden(target.slice(p), run)) || run.length >= PIECE && p + run.length <= target.length - PIECE && target.startsWith(run, p) && rest(p + run.length, [...used, run], hides || hidden(run, run))));
353
+ return list.some((first) => {
354
+ for (let p = PIECE; p <= target.length - PIECE && p <= first.length; p += 1) {
355
+ if (first.endsWith(target.slice(0, p)) && rest(p, [first], hidden(target.slice(0, p), first)))
356
+ return true;
357
+ }
358
+ return false;
359
+ });
360
+ }
361
+ function inOrder(targets, stream, shown) {
362
+ const digits = stream.join("");
363
+ if (digits.length < targets[targets.length - 1].length)
364
+ return false;
365
+ const hidden = stream.map((run) => (shown.has(run) ? "0" : "1").repeat(run.length)).join("");
366
+ return targets.some((target) => {
367
+ for (let at = digits.indexOf(target); at >= 0; at = digits.indexOf(target, at + 1)) {
368
+ if (hidden.slice(at, at + target.length).includes("1"))
369
+ return true;
370
+ }
371
+ return false;
372
+ });
373
+ }
374
+ function answerCardLeak(card, texts) {
375
+ const number = card && (typeof card.number === "string" || typeof card.number === "number") ? String(card.number).replace(/[^0-9]/g, "") : "";
376
+ if (number.length < 12 || number.length > 19 || DOCUMENTED_TEST_PANS.has(number))
377
+ return null;
378
+ const streams = /* @__PURE__ */ new Set();
379
+ const runs = /* @__PURE__ */ new Set();
380
+ for (const text of Array.isArray(texts) ? texts : [texts])
381
+ collect(typeof text === "string" ? text : JSON.stringify(text) ?? "", LAYERS, streams, runs);
382
+ const shown = new Set([6, 8].flatMap((bin) => [number.slice(0, bin), number.slice(0, bin) + number.slice(-4)]).concat(number.slice(-4)));
383
+ const targets = [number, number.slice(0, -1)];
384
+ return [...streams].some((stream) => inOrder(targets, stream, shown)) || targets.some((target) => joined(target, runs, shown)) ? ANSWER_HOLDS_CARD : null;
385
+ }
160
386
  // src/deep-freeze.js
161
387
  function deepFreeze(value) {
162
388
  if (value && typeof value === "object" && !Object.isFrozen(value)) {
@@ -2421,9 +2647,11 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
2421
2647
  "www.nike.com guest checkout, read live on Oct 23, 2026 in a Kernel browser with every non-GET request aborted before it left (a placeholder card, nothing sent): the card is typed into Nike's own card frame, https://paymentcc.nike.com/services/default?id=<creditCardInfoId>&ctx=…&language=…&maskerEnabled=…, a form #creditCardForm with #creditCardNumber, #expirationDate (MM/YY) and #cvNumber and hidden expirationMonth and expirationYear. No processor frame, tokenizer or client-side encryption is on the page.",
2422
2648
  "The frame's own script (/resources/js/common.js) posts the card once all three fields are valid, on a field's blur or keyup, with no button press: XHR POST /creditcardsubmit/<creditCardInfoId>/store, Content-Type application/json; charset=utf-8, body {accountNumber (digits), cardType, expirationMonth (MM), expirationYear ('20' + YY), creditCardInfoId (the frame's id query value, the same as the path), cvNumber}. The captured request carried the .nike.com cookies (Akamai's ak_bmsc and bm_sv among them), Origin https://paymentcc.nike.com, and no CSRF or auth header.",
2423
2649
  "cardType comes from the frame's own validator table, first match wins: AMERICANEXPRESS (34, 37), CARTASI (a list of Italian 4 and 5 BINs), CARTEBLEUE, DANKORT, DISCOVER, DINERS, DOMESTICMAESTRO, INTERNATIONALMAESTRO, JCB, MASTERCARD (51-55 and 2221-2720), VISAELECTRON, VISA (4).",
2424
- "After the post the frame asks GET /creditcardsubmit/<id>/isValid?mode=1 and tells the page its brand, last four and expiry by postMessage. A CVV-only change sends PUT /creditcardsubmit/<id>/update {cvNumber}. Continue to Order Review then posts https://api.nike.com/payment/preview/v3 with paymentInfo naming the creditCardInfoId, the brand, the last four and a masked accountNumber, beside the checkout total and currency: no card number leaves the page outside the store request."
2650
+ "After the post the frame asks GET /creditcardsubmit/<id>/isValid?mode=1 and tells the page its brand, last four and expiry by postMessage. A CVV-only change sends PUT /creditcardsubmit/<id>/update {cvNumber}. Continue to Order Review then posts https://api.nike.com/payment/preview/v3 with paymentInfo naming the creditCardInfoId, the brand, the last four and a masked accountNumber, beside the checkout total and currency: no card number leaves the page outside the store request.",
2651
+ "Felipe Abello's recorded www.nike.com guest purchase with a one-time Agentcard Mastercard, Oct 23, 2026 (he reviewed the HAR and sent its facts as text; no card value, cookie or HAR entry is in this table): the store post answered 201 Created with an empty body (Content-Length 0), its path and body naming one creditCardInfoId UUID; isValid?mode=1 answered 200 with the JSON booleans cc, cvv, exp and isValid; payment/preview/v3 named the card MASTERCARD in paymentInfo[0].",
2652
+ "The same purchase: a checkout preview job (GET /buy/checkout_previews_jobs/v3/…, the rest of that line not recorded) prices the order, and the page sends the total twice, both after the store post: in payment/preview/v3 (a top-level total and currency, and paymentInfo[] naming the creditCardInfoId), a pre-check that answered validForCheckout true and is3DSRequired false and charges nothing, and in PUT https://api.nike.com/buy/checkouts/v3/<checkoutId> (request.totals.total, a paymentToken, no card data), which answers 202 with a PENDING job. GET /buy/checkouts_jobs/v3/<checkoutId> then read COMPLETED with an orderId, a paymentApprovalId and paymentStatus ACCEPT: that PUT authorizes the card."
2425
2653
  ],
2426
- har: null,
2654
+ har: "Felipe Abello, Oct 23, 2026: one www.nike.com guest purchase he ran and recorded with a one-time Agentcard Mastercard and reviewed himself, its facts sent as text. It confirms the store post, its answer, isValid, the Mastercard code and the order calls after it. It ran in his own browser, so it shows nothing of how Akamai treats the relay's request.",
2427
2655
  checkout: "https://www.nike.com",
2428
2656
  checkout_frame: false,
2429
2657
  frame: "https://paymentcc.nike.com",
@@ -2447,7 +2675,10 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
2447
2675
  expiry: null,
2448
2676
  last4: null,
2449
2677
  leading: null,
2450
- brands: [{ at: ["cardType"], map: { visa: "VISA", mastercard: "MASTERCARD", amex: "AMERICANEXPRESS", discover: "DISCOVER" } }],
2678
+ // The recorded purchase shows MASTERCARD only. The frame's table writes other codes for other networks, but no
2679
+ // purchase shows Nike taking one, so the device refuses a card of another network and the body rules refuse a
2680
+ // placeholder the frame names otherwise: the agent types a Mastercard-range placeholder.
2681
+ brands: [{ at: ["cardType"], map: { mastercard: "MASTERCARD" } }],
2451
2682
  bins: null
2452
2683
  },
2453
2684
  merchant_key: null,
@@ -2457,9 +2688,12 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
2457
2688
  ],
2458
2689
  refuse: [],
2459
2690
  payment_method: null,
2460
- // The store request carries no total: the order's total first travels in payment/preview/v3, after it.
2691
+ // The store post carries no total: Nike's total travels after it, in payment/preview/v3 and the order's PUT, which the
2692
+ // checkout SDK holds to the approved amount (merchant-card-endpoint-nike-order.js).
2461
2693
  amount: null,
2462
2694
  currency: "USD",
2695
+ // Nike answers the store post 201 Created with an empty body: the card is stored under the creditCardInfoId and nothing
2696
+ // is charged until the order's PUT. The approve records the row approved on that answer, which means sent, never paid.
2463
2697
  response: { headers: ["content-type"], withhold: [] },
2464
2698
  resends: [
2465
2699
  // The frame's CVV-only update, which sends the typed security code again outside the store request.
@@ -2472,12 +2706,10 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
2472
2706
  timeout_ms: 3e4,
2473
2707
  charge_timing: "deferred",
2474
2708
  once_per: { path: "creditCardInfoId" },
2709
+ // What keeps Nike in observe. What only a purchase through the relay can show (Akamai's view of the relay's request,
2710
+ // a capture when the order ships) and the codes of other networks are open in PROOF.md, beside the attended purchase.
2475
2711
  pending: [
2476
- "the answer to the store request (status, body and headers) and to isValid, which this capture aborted before they were sent",
2477
- "a total to hold the agent's amount to: the store request names none, and Nike's checkout total travels only in payment/preview/v3 after it",
2478
- "which call authorizes the card (payment/preview/v3 or the order submit), and whether the charge when the order ships needs the card to stay open",
2479
- "a card the frame names CARTASI, CARTEBLEUE, DANKORT or a Maestro type, which the brand map writes as its network's code",
2480
- "whether Akamai accepts the relay's request with cookies the agent's browser minted"
2712
+ `a total to hold the agent's amount to, in the form Nike's order writes it: the checkout SDK holds the order's PUT to the approved amount (merchant-card-endpoint-nike-order.js), reading request.totals.total as a JSON number and request.currency, and refuses any other form. The recorded purchase's facts name that path and an amount ending "4.76 USD", and the paste cut the JSON type and where the order names its currency, which a person confirms from the recorded purchase`
2481
2713
  ]
2482
2714
  }
2483
2715
  ]);
@@ -3285,6 +3517,96 @@ function merchantCardEndpointFactsRefusal(profile, facts, agent) {
3285
3517
  }
3286
3518
  return null;
3287
3519
  }
3520
+ // src/merchant-card-endpoint-nike-order.js
3521
+ var NIKE_ORDER = /* @__PURE__ */ deepFreeze({
3522
+ profile: "nike_paymentcc_store",
3523
+ host: "api.nike.com",
3524
+ origin: "https://api.nike.com",
3525
+ method: "PUT",
3526
+ path: "/buy/checkouts/v3/:checkoutId",
3527
+ total: ["request", "totals", "total"],
3528
+ currency: ["request", "currency"]
3529
+ });
3530
+ var NIKE_ORDER_REFUSALS = /* @__PURE__ */ deepFreeze([
3531
+ "request_unrecognized",
3532
+ "body_unreadable",
3533
+ "total_unreadable",
3534
+ "currency_unreadable",
3535
+ "currency_mismatch",
3536
+ "amount_mismatch",
3537
+ "approval_unknown"
3538
+ ]);
3539
+ var BODY_MAX = 256 * 1024;
3540
+ var TOTAL_TEXT = /^\d{1,9}(?:\.\d{1,2})?$/;
3541
+ var READS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
3542
+ function nikeOrderHost(url) {
3543
+ try {
3544
+ return new URL(url).hostname.replace(/\.$/, "") === NIKE_ORDER.host;
3545
+ }
3546
+ catch {
3547
+ return false;
3548
+ }
3549
+ }
3550
+ function serverSegments(pathname) {
3551
+ let path = pathname;
3552
+ for (let round = 0; round < 4; round += 1) {
3553
+ let decoded;
3554
+ try {
3555
+ decoded = decodeURIComponent(path);
3556
+ }
3557
+ catch {
3558
+ return null;
3559
+ }
3560
+ if (decoded === path)
3561
+ break;
3562
+ path = decoded;
3563
+ }
3564
+ const segments = [];
3565
+ for (const raw of path.replace(/\\/g, "/").split("/")) {
3566
+ const segment = raw.split(";")[0].toLowerCase();
3567
+ if (segment === "..")
3568
+ segments.pop();
3569
+ else if (segment !== "" && segment !== ".")
3570
+ segments.push(segment);
3571
+ }
3572
+ return segments;
3573
+ }
3574
+ function nikeOrderRequest(url, method) {
3575
+ if (typeof method !== "string" || READS.has(method.toUpperCase()) || !nikeOrderHost(url))
3576
+ return false;
3577
+ const segments = serverSegments(new URL(url).pathname);
3578
+ return segments === null || segments[0] === "buy" && segments[1] === "checkouts";
3579
+ }
3580
+ function nikeOrderRefusal(url, method, body, approved) {
3581
+ const want = isPlainObject(approved) ? approved.amount : void 0;
3582
+ const currency = isPlainObject(approved) && typeof approved.currency === "string" ? approved.currency.toLowerCase() : "";
3583
+ if (!Number.isSafeInteger(want) || want <= 0 || !TWO_DECIMAL_CURRENCIES.has(currency))
3584
+ return "approval_unknown";
3585
+ const match = typeof method === "string" && method.toUpperCase() === NIKE_ORDER.method ? urlTemplateMatch({ path: NIKE_ORDER.path, query: {} }, url) : null;
3586
+ if (match?.origin !== NIKE_ORDER.origin)
3587
+ return "request_unrecognized";
3588
+ if (typeof body !== "string" || body.length > BODY_MAX)
3589
+ return "body_unreadable";
3590
+ let parsed;
3591
+ try {
3592
+ parsed = JSON.parse(body);
3593
+ }
3594
+ catch {
3595
+ return "body_unreadable";
3596
+ }
3597
+ if (!isPlainObject(parsed) || !roundTrips(parsed, body))
3598
+ return "body_unreadable";
3599
+ const total = jsonAt(parsed, NIKE_ORDER.total);
3600
+ if (!total.found || typeof total.value !== "number" || !TOTAL_TEXT.test(String(total.value)))
3601
+ return "total_unreadable";
3602
+ const named = jsonAt(parsed, NIKE_ORDER.currency);
3603
+ if (!named.found || typeof named.value !== "string" || !/^[A-Z]{3}$/.test(named.value))
3604
+ return "currency_unreadable";
3605
+ if (named.value !== currency.toUpperCase())
3606
+ return "currency_mismatch";
3607
+ const [whole, fraction = ""] = String(total.value).split(".");
3608
+ return Number(whole) * 100 + Number(fraction.padEnd(2, "0")) === want ? null : "amount_mismatch";
3609
+ }
3288
3610
  // src/merchant-card-endpoints.js
3289
3611
  var MERCHANT_CARD_ENDPOINT_LIMITS = /* @__PURE__ */ Object.freeze({
3290
3612
  bodyBytes: 128 * 1024,
@@ -3377,7 +3699,7 @@ function codeNearKey(text, code) {
3377
3699
  return true;
3378
3700
  for (const [run] of near.replace(BASE64_ESCAPE, unescapeBase64).matchAll(CODE_BASE64_RUN)) {
3379
3701
  for (let skip = 0; skip < 4; skip += 1)
3380
- if (code.test(base64Bytes(run, skip).replace(/\0/g, "")))
3702
+ if (code.test(base64Bytes2(run, skip).replace(/\0/g, "")))
3381
3703
  return true;
3382
3704
  }
3383
3705
  }
@@ -4415,13 +4737,13 @@ function answerParts(response) {
4415
4737
  }
4416
4738
  return pairs;
4417
4739
  }
4418
- var BASE64_RUN = /[A-Za-z0-9+/_-]{8,}/g;
4740
+ var BASE64_RUN2 = /[A-Za-z0-9+/_-]{8,}/g;
4419
4741
  var STREAM_RUN_LENGTH = 16;
4420
4742
  var CODE_BASE64_RUN = /[A-Za-z0-9+/_-]{4,}/g;
4421
4743
  var BASE64_ESCAPE = /\\\/|\\u002[bBfF]|%2[bBfF]|&#(?:43|47|x2[bBfF]);/g;
4422
4744
  var BASE64_ALPHABET2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4423
4745
  var unescapeBase64 = (escape) => /43|[bB]/.test(escape) ? "+" : "/";
4424
- function base64Bytes(run, skip) {
4746
+ function base64Bytes2(run, skip) {
4425
4747
  let bits = 0;
4426
4748
  let value = 0;
4427
4749
  let out = "";
@@ -4452,9 +4774,9 @@ function carriesCard(text, facts, pan, code, depth, seen) {
4452
4774
  return true;
4453
4775
  if (depth === 0)
4454
4776
  return false;
4455
- for (const [run] of text.replace(BASE64_ESCAPE, unescapeBase64).matchAll(BASE64_RUN)) {
4777
+ for (const [run] of text.replace(BASE64_ESCAPE, unescapeBase64).matchAll(BASE64_RUN2)) {
4456
4778
  for (let skip = 0; skip < 4; skip += 1) {
4457
- const bytes = base64Bytes(run, skip);
4779
+ const bytes = base64Bytes2(run, skip);
4458
4780
  if (run.length >= STREAM_RUN_LENGTH && compressedStream(bytes))
4459
4781
  seen.compressed = true;
4460
4782
  if (carriesCard(bytes.replace(/\0/g, ""), facts, pan, code, depth - 1, seen))
@@ -4468,10 +4790,13 @@ function responseCardLeak(card, response) {
4468
4790
  const pan = digitRun(facts.number);
4469
4791
  const code = codeRun(facts.cvc);
4470
4792
  const seen = { compressed: false };
4471
- for (const text of [response.bodyText, ...answerParts(response).flat()]) {
4793
+ const texts = [response.bodyText, ...answerParts(response).flat()];
4794
+ for (const text of texts) {
4472
4795
  if (carriesCard(text, facts, pan, code, 2, seen))
4473
4796
  return "card_in_response";
4474
4797
  }
4798
+ if (answerCardLeak(facts, texts))
4799
+ return "card_in_response";
4475
4800
  return seen.compressed ? "unreadable_in_response" : null;
4476
4801
  }
4477
4802
  var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
@@ -4532,4 +4857,4 @@ function merchantCardEndpointNavigationAnswer(profile, answer, requestUrl) {
4532
4857
  return { kind: "refused", reason: "redirect_location_invalid" };
4533
4858
  return { kind: "redirect", status, location: target.href };
4534
4859
  }
4535
- export { HILTON_CREATE_RESERVATION_DOCUMENTS, MERCHANT_CARD_ENDPOINT_CLIENT_TABLE, MERCHANT_CARD_ENDPOINT_FACTS_TEXT_MAX, MERCHANT_CARD_ENDPOINT_FACT_READERS, MERCHANT_CARD_ENDPOINT_LIMITS, MERCHANT_CARD_ENDPOINT_MODE, MERCHANT_CARD_ENDPOINT_PROFILES, MERCHANT_CARD_ENDPOINT_PSP, MERCHANT_CARD_ENDPOINT_RECOGNIZERS, SubstitutionError, assertMerchantCardEndpoint, encryptMerchantCardEndpointBody, encryptedMerchantEndpointRequest, matchMerchantCardEndpoint, matchMerchantCardEndpointResend, merchantCardEndpointAmount, merchantCardEndpointAnswer, merchantCardEndpointAnswerRefusal, merchantCardEndpointBodyRefusal, merchantCardEndpointById, merchantCardEndpointCarriesCard, merchantCardEndpointCheckoutKey, merchantCardEndpointEncrypts, merchantCardEndpointFactsRefusal, merchantCardEndpointFactsRole, merchantCardEndpointLive, merchantCardEndpointMatches, merchantCardEndpointNavigationAnswer, merchantCardEndpointPasses, merchantCardEndpointRecognizerById, merchantCardEndpointRequestOrigin, merchantCardEndpointSessionRefusal, merchantCardEndpointSessionTtl, merchantEndpointRequest, projectMerchantCardEndpointFacts, readMerchantCardEndpointFacts, responseCardLeak, substituteMerchantCardEndpoint, trustedMerchantCardEndpoint };
4860
+ export { HILTON_CREATE_RESERVATION_DOCUMENTS, MERCHANT_CARD_ENDPOINT_CLIENT_TABLE, MERCHANT_CARD_ENDPOINT_FACTS_TEXT_MAX, MERCHANT_CARD_ENDPOINT_FACT_READERS, MERCHANT_CARD_ENDPOINT_LIMITS, MERCHANT_CARD_ENDPOINT_MODE, MERCHANT_CARD_ENDPOINT_PROFILES, MERCHANT_CARD_ENDPOINT_PSP, MERCHANT_CARD_ENDPOINT_RECOGNIZERS, NIKE_ORDER, NIKE_ORDER_REFUSALS, SubstitutionError, assertMerchantCardEndpoint, encryptMerchantCardEndpointBody, encryptedMerchantEndpointRequest, matchMerchantCardEndpoint, matchMerchantCardEndpointResend, merchantCardEndpointAmount, merchantCardEndpointAnswer, merchantCardEndpointAnswerRefusal, merchantCardEndpointBodyRefusal, merchantCardEndpointById, merchantCardEndpointCarriesCard, merchantCardEndpointCheckoutKey, merchantCardEndpointEncrypts, merchantCardEndpointFactsRefusal, merchantCardEndpointFactsRole, merchantCardEndpointLive, merchantCardEndpointMatches, merchantCardEndpointNavigationAnswer, merchantCardEndpointPasses, merchantCardEndpointRecognizerById, merchantCardEndpointRequestOrigin, merchantCardEndpointSessionRefusal, merchantCardEndpointSessionTtl, merchantEndpointRequest, nikeOrderHost, nikeOrderRefusal, nikeOrderRequest, projectMerchantCardEndpointFacts, readMerchantCardEndpointFacts, responseCardLeak, substituteMerchantCardEndpoint, trustedMerchantCardEndpoint };
@@ -35,6 +35,12 @@ export type MerchantRequestVerdict = {
35
35
  };
36
36
  /** One step of a JSON path: an object key, or an index into an array. */
37
37
  export type JsonPathStep = string | number;
38
+ /** The step into a string that holds JSON text (payment-core json-path.js TEXT_STEP). */
39
+ export type JsonTextStep = {
40
+ text: 'json';
41
+ };
42
+ /** One step of a profile's holder path: a JSON path step, or the step into JSON text the body carries. */
43
+ export type ProfilePathStep = JsonPathStep | JsonTextStep;
38
44
  /** A card fact the API asks to write beside the ciphertext (reserved: no profile declares one yet). */
39
45
  export interface MerchantHostedFact {
40
46
  path: JsonPathStep[];
@@ -51,7 +57,8 @@ export interface MerchantHostedSubstitutions {
51
57
  encoding: 'json';
52
58
  kind: 'merchant_hosted';
53
59
  profile: string;
54
- path: JsonPathStep[];
60
+ /** The holder: it may step into JSON text the body carries (SCHEELS' `state`); a fact's path never does. */
61
+ path: ProfilePathStep[];
55
62
  fields: Record<string, string>;
56
63
  remove: string[];
57
64
  /** Card facts to write after the swap. No profile declares any yet, and the substitution refuses them. */
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Why the gate refuses Nike's order: payment-core's reasons; an earlier order under the same approval refused; another
3
+ * order than the one already let go under it; or an approval the checkout no longer names as under way (cancelled, a
4
+ * new attempt, a final state, a reconciliation running).
5
+ */
6
+ export type NikeOrderRefusal = 'request_unrecognized' | 'body_unreadable' | 'total_unreadable' | 'currency_unreadable' | 'currency_mismatch' | 'amount_mismatch' | 'approval_unknown' | 'earlier_order_refused' | 'another_order' | 'approval_not_current';
7
+ /** What an event may say about Nike's order: its profile and origin, never the order's URL or body. */
8
+ export declare const NIKE_ORDER_DETAIL: Readonly<{
9
+ profile: string;
10
+ origin: string;
11
+ }>;
12
+ /** Fetch.enable's patterns for Nike's API host in every spelling a browser sends; nikeOrderRequest is the exact check. */
13
+ export declare const NIKE_ORDER_PATTERNS: readonly string[];
14
+ /** Whether the Playwright adapter routes a URL, which it sees without its method: any URL on Nike's API host. */
15
+ export declare function nikeOrderRouted(url: string): boolean;
16
+ /** The approval Nike's order is held to on one attachment, once a card went through Nike's profile. */
17
+ export declare class NikeOrders {
18
+ private held;
19
+ /**
20
+ * The adapters are about to ask the cardholder to pay a card request to `profile`. From the create on, the device may
21
+ * store the card at Nike whatever this attachment then reads back, so for Nike's profile every order is held from
22
+ * here, and refused until the approved answer names its amount (remember). True for Nike's profile; another profile
23
+ * changes nothing.
24
+ */
25
+ asking(profile: string): boolean;
26
+ /**
27
+ * An approved card endpoint answer, read before it reaches the page: for Nike's profile, its approval is the one Nike's
28
+ * orders are held to, at the amount the API's read names (none, and every order is refused). It is the attempt
29
+ * asking() armed, so an order refused while the approval was on its way keeps the rest refused; only the next
30
+ * asking() clears that. Another profile's answer changes nothing.
31
+ */
32
+ remember(replay: {
33
+ authorizationId: string;
34
+ profile: string;
35
+ approved?: {
36
+ amount: number;
37
+ currency: string;
38
+ };
39
+ }): void;
40
+ /**
41
+ * The gate's verdict on a paused request, or null when it is left alone: no card went through Nike's profile here, or
42
+ * the request is not Nike's order. `current` says whether the checkout still names an approval as under way: once an
43
+ * approved answer is in, an order the checkout no longer names as under way is stopped before the rule reads it, so a
44
+ * late order never ends an attempt that came after. One order goes, and after it only a byte-identical retry; after
45
+ * one refusal every later order under that approval is refused.
46
+ */
47
+ judge(url: string, method: string, body: () => string | null, current: (authorizationId: string | null) => boolean): {
48
+ authorizationId: string | null;
49
+ refusal: NikeOrderRefusal | null;
50
+ } | null;
51
+ }
@@ -0,0 +1,81 @@
1
+ // Nike's order after its card request (payment-core merchant-card-endpoint-nike-order.js), Felipe's amount rule C.
2
+ //
3
+ // Nike's store post names no total: the cardholder's device stores the card under the checkout's id, and Nike charges
4
+ // nothing until the page sends its order, PUT https://api.nike.com/buy/checkouts/v3/<checkoutId>. So once the adapters
5
+ // ask the cardholder to pay a card request through Nike's profile on an attachment, they hand every write to Nike's API
6
+ // host to the pre-claim gate (pre-claim.ts nikeOrderGate). It lets one order go unchanged, only when its total and
7
+ // currency are the amount the cardholder approved and only while the checkout still names that approval as under way,
8
+ // and after it only a byte-identical retry. Before that, on any other attachment, and for every other request, nothing
9
+ // here holds anything. The order's URL and body are cardholder data: no event names them.
10
+ import { NIKE_ORDER as SHARED_ORDER, nikeOrderHost as sharedHost, nikeOrderRefusal as sharedRefusal, nikeOrderRequest as sharedRequest, } from './merchant-card-endpoints.generated.js';
11
+ const ORDER = SHARED_ORDER;
12
+ /** What an event may say about Nike's order: its profile and origin, never the order's URL or body. */
13
+ export const NIKE_ORDER_DETAIL = Object.freeze({ profile: ORDER.profile, origin: ORDER.origin });
14
+ /** Fetch.enable's patterns for Nike's API host in every spelling a browser sends; nikeOrderRequest is the exact check. */
15
+ export const NIKE_ORDER_PATTERNS = [`https://${ORDER.host}*`, `http://${ORDER.host}*`];
16
+ /** Whether the Playwright adapter routes a URL, which it sees without its method: any URL on Nike's API host. */
17
+ export function nikeOrderRouted(url) {
18
+ return sharedHost(url) === true;
19
+ }
20
+ /** The approval Nike's order is held to on one attachment, once a card went through Nike's profile. */
21
+ export class NikeOrders {
22
+ held = null;
23
+ /**
24
+ * The adapters are about to ask the cardholder to pay a card request to `profile`. From the create on, the device may
25
+ * store the card at Nike whatever this attachment then reads back, so for Nike's profile every order is held from
26
+ * here, and refused until the approved answer names its amount (remember). True for Nike's profile; another profile
27
+ * changes nothing.
28
+ */
29
+ asking(profile) {
30
+ if (profile !== ORDER.profile)
31
+ return false;
32
+ this.held = { authorizationId: null, approved: null, refused: false, released: null };
33
+ return true;
34
+ }
35
+ /**
36
+ * An approved card endpoint answer, read before it reaches the page: for Nike's profile, its approval is the one Nike's
37
+ * orders are held to, at the amount the API's read names (none, and every order is refused). It is the attempt
38
+ * asking() armed, so an order refused while the approval was on its way keeps the rest refused; only the next
39
+ * asking() clears that. Another profile's answer changes nothing.
40
+ */
41
+ remember(replay) {
42
+ if (replay.profile !== ORDER.profile)
43
+ return;
44
+ this.held = { authorizationId: replay.authorizationId, approved: replay.approved ?? null, refused: this.held?.refused ?? false, released: null };
45
+ }
46
+ /**
47
+ * The gate's verdict on a paused request, or null when it is left alone: no card went through Nike's profile here, or
48
+ * the request is not Nike's order. `current` says whether the checkout still names an approval as under way: once an
49
+ * approved answer is in, an order the checkout no longer names as under way is stopped before the rule reads it, so a
50
+ * late order never ends an attempt that came after. One order goes, and after it only a byte-identical retry; after
51
+ * one refusal every later order under that approval is refused.
52
+ */
53
+ judge(url, method, body, current) {
54
+ const held = this.held;
55
+ if (!held || sharedRequest(url, method) !== true)
56
+ return null;
57
+ const verdict = (refusal) => ({ authorizationId: held.authorizationId, refusal });
58
+ if (held.refused)
59
+ return verdict('earlier_order_refused');
60
+ // While the approval is still on its way, the rule refuses every order (approval_unknown), and that stands.
61
+ if (held.authorizationId !== null && !current(held.authorizationId))
62
+ return verdict('approval_not_current');
63
+ let text;
64
+ try {
65
+ text = body();
66
+ }
67
+ catch {
68
+ text = null;
69
+ }
70
+ const sent = held.released;
71
+ if (sent)
72
+ return verdict(sent.method === method.toUpperCase() && sent.url === url && sent.body === text ? null : 'another_order');
73
+ const refusal = sharedRefusal(url, method, text, held.approved);
74
+ if (refusal !== null) {
75
+ held.refused = true;
76
+ return verdict(refusal);
77
+ }
78
+ held.released = { method: method.toUpperCase(), url, body: text };
79
+ return verdict(null);
80
+ }
81
+ }