@agent-cards/sdk 0.23.28 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +84 -0
- package/PREFLIGHT.md +7 -1
- package/README.md +197 -102
- package/dist/aci.d.ts +52 -22
- package/dist/aci.js +79 -15
- package/dist/adyen-merchant-hosted.generated.d.ts +2 -2
- package/dist/adyen-merchant-hosted.generated.js +586 -55
- package/dist/agent-amount.generated.js +1 -1
- package/dist/agentcard.d.ts +4 -5
- package/dist/agentcard.js +4 -5
- package/dist/amadeus.d.ts +9 -3
- package/dist/amadeus.js +33 -4
- package/dist/api-module.d.ts +1 -7
- package/dist/api-module.js +3 -5
- package/dist/{voice-retry.js → api-retry.js} +1 -1
- package/dist/api-types.d.ts +53 -0
- package/dist/{voice-types.js → api-types.js} +7 -2
- package/dist/approvals.d.ts +166 -0
- package/dist/approvals.js +213 -0
- package/dist/aurus.d.ts +33 -0
- package/dist/aurus.js +75 -0
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/buy.d.ts +20 -101
- package/dist/buy.js +19 -74
- package/dist/capabilities.generated.js +1 -1
- package/dist/card-fields.generated.js +2864 -249
- package/dist/cardlink-vpos.d.ts +43 -0
- package/dist/cardlink-vpos.js +90 -0
- package/dist/cdp.d.ts +3 -13
- package/dist/cdp.js +244 -175
- package/dist/checkout-com.generated.js +5 -1
- package/dist/client.d.ts +170 -22
- package/dist/client.js +281 -40
- package/dist/creditguard.generated.d.ts +4 -4
- package/dist/cybersource-flex.generated.d.ts +39 -1
- package/dist/cybersource-flex.generated.js +100 -11
- package/dist/datatrans.generated.js +22 -1
- package/dist/eghl.d.ts +40 -0
- package/dist/eghl.js +72 -0
- package/dist/elavon-converge.generated.js +5 -3
- package/dist/gmo.generated.js +7 -2
- package/dist/hold.d.ts +8 -0
- package/dist/hold.js +43 -2
- package/dist/hosted-entry.js +4 -0
- package/dist/index.d.ts +9 -15
- package/dist/index.js +6 -10
- package/dist/lifecycle.d.ts +9 -61
- package/dist/lifecycle.js +24 -10
- package/dist/merchant-card-endpoints.generated.d.ts +6 -1
- package/dist/merchant-card-endpoints.generated.js +344 -19
- package/dist/merchant-hosted.d.ts +8 -1
- package/dist/nike-order.d.ts +51 -0
- package/dist/nike-order.js +81 -0
- package/dist/oceanpayment.d.ts +38 -0
- package/dist/oceanpayment.js +70 -0
- package/dist/paddle-tokenex.generated.js +111 -11
- package/dist/payplug.d.ts +41 -0
- package/dist/payplug.js +71 -0
- package/dist/payrails.generated.js +33 -8
- package/dist/pre-claim.d.ts +11 -0
- package/dist/pre-claim.js +130 -7
- package/dist/preflight-capabilities.generated.d.ts +0 -4
- package/dist/preflight-capabilities.generated.js +0 -5
- package/dist/preflight-catalog.json +200 -6
- package/dist/preflight-playwright.d.ts +2 -2
- package/dist/preflight-playwright.js +63 -14
- package/dist/preflight-schemas.json +18 -0
- package/dist/preflight.generated.d.ts +5 -4
- package/dist/preflight.generated.js +63 -3
- package/dist/preparation.d.ts +7 -0
- package/dist/preparation.js +21 -0
- package/dist/prepared-processor.d.ts +18 -5
- package/dist/prepared-processor.js +48 -8
- package/dist/primer.generated.js +2889 -268
- package/dist/processout.generated.d.ts +61 -0
- package/dist/processout.generated.js +577 -0
- package/dist/requests.js +1 -1
- package/dist/shiji-sts.generated.js +111 -11
- package/dist/stripe-checkout.generated.js +2761 -319
- package/dist/substitutions.generated.js +553 -26
- package/dist/tap-payments.generated.js +111 -11
- package/dist/twoc2p.generated.js +111 -11
- package/dist/url-masks/bluesnap.d.ts +2 -0
- package/dist/url-masks/bluesnap.js +15 -0
- package/dist/url-masks/oceanpayment.d.ts +2 -0
- package/dist/url-masks/oceanpayment.js +13 -0
- package/dist/url-masks/payplug.d.ts +2 -0
- package/dist/url-masks/payplug.js +13 -0
- package/dist/url-masks/vtex_payments.d.ts +2 -0
- package/dist/url-masks/vtex_payments.js +12 -0
- package/dist/url-masks.generated.d.ts +1 -1
- package/dist/url-masks.generated.js +6 -2
- package/dist/verifone.generated.js +42 -2
- package/dist/vtex-payments.generated.d.ts +127 -0
- package/dist/vtex-payments.generated.js +544 -0
- package/dist/webhooks.d.ts +2 -2
- package/dist/webhooks.js +2 -2
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +6 -8
- package/dist/languages.d.ts +0 -23
- package/dist/languages.js +0 -76
- package/dist/voice-pkce.d.ts +0 -49
- package/dist/voice-pkce.js +0 -147
- package/dist/voice-stream.generated.d.ts +0 -118
- package/dist/voice-stream.generated.js +0 -170
- package/dist/voice-types.d.ts +0 -735
- package/dist/voice.d.ts +0 -373
- package/dist/voice.js +0 -815
- package/readme.test.mjs +0 -53
- /package/dist/{voice-retry.d.ts → api-retry.d.ts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256:
|
|
3
|
+
// artifact-sha256: 7f066dc9de1de76fd2dce4d9ac940e653010b8cd3e9d45c3bb4fd04d006f7c16
|
|
4
4
|
// src/deep-freeze.js
|
|
5
5
|
function deepFreeze(value) {
|
|
6
6
|
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -16,9 +16,65 @@ function isPlainObject(v) {
|
|
|
16
16
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
17
17
|
}
|
|
18
18
|
var MAX_PATH_STEPS = 16;
|
|
19
|
+
var MAX_TEXT_STEPS = 2;
|
|
20
|
+
var TEXT_STEP = /* @__PURE__ */ Object.freeze({ text: "json" });
|
|
21
|
+
function isTextStep(step) {
|
|
22
|
+
return isPlainObject(step) && Object.keys(step).length === 1 && step.text === "json";
|
|
23
|
+
}
|
|
24
|
+
function sameStep(a, b) {
|
|
25
|
+
return a === b || isTextStep(a) && isTextStep(b);
|
|
26
|
+
}
|
|
27
|
+
function spellJson(parsed, form) {
|
|
28
|
+
const text = JSON.stringify(parsed);
|
|
29
|
+
if (form !== "php")
|
|
30
|
+
return text;
|
|
31
|
+
return text.replace(/\//g, "\\/").replace(/[\u0080-\uffff]/g, (unit) => `\\u${unit.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
32
|
+
}
|
|
33
|
+
function jsonTextForm(value) {
|
|
34
|
+
if (typeof value !== "string" || !/^[[{]/.test(value))
|
|
35
|
+
return void 0;
|
|
36
|
+
let parsed;
|
|
37
|
+
try {
|
|
38
|
+
parsed = JSON.parse(value);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
if (parsed === null || typeof parsed !== "object")
|
|
44
|
+
return void 0;
|
|
45
|
+
for (const form of ["js", "php"]) {
|
|
46
|
+
try {
|
|
47
|
+
if (spellJson(parsed, form) === value)
|
|
48
|
+
return { parsed, form };
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
function jsonText(value) {
|
|
57
|
+
return jsonTextForm(value)?.parsed;
|
|
58
|
+
}
|
|
19
59
|
function isJsonPath(path) {
|
|
20
60
|
return Array.isArray(path) && path.length <= MAX_PATH_STEPS && path.every((step) => typeof step === "string" ? step.length > 0 && !FORBIDDEN_KEYS.has(step) : Number.isSafeInteger(step) && step >= 0);
|
|
21
61
|
}
|
|
62
|
+
function isProfilePath(path) {
|
|
63
|
+
if (!Array.isArray(path) || path.length > MAX_PATH_STEPS)
|
|
64
|
+
return false;
|
|
65
|
+
let texts = 0;
|
|
66
|
+
for (const [i, step] of path.entries()) {
|
|
67
|
+
if (isTextStep(step)) {
|
|
68
|
+
texts += 1;
|
|
69
|
+
if (i === 0 || i === path.length - 1 || isTextStep(path[i - 1]) || texts > MAX_TEXT_STEPS)
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
else if (!isJsonPath([step])) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
22
78
|
function jsonAt(root, path) {
|
|
23
79
|
let node = root;
|
|
24
80
|
for (const step of path) {
|
|
@@ -33,6 +89,25 @@ function jsonAt(root, path) {
|
|
|
33
89
|
}
|
|
34
90
|
return { found: true, value: node };
|
|
35
91
|
}
|
|
92
|
+
function profileAt(root, path) {
|
|
93
|
+
if (!isProfilePath(path))
|
|
94
|
+
return { found: false };
|
|
95
|
+
let node = root;
|
|
96
|
+
for (const step of path) {
|
|
97
|
+
if (isTextStep(step)) {
|
|
98
|
+
const inner = jsonText(node);
|
|
99
|
+
if (inner === void 0)
|
|
100
|
+
return { found: false };
|
|
101
|
+
node = inner;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const at = jsonAt(node, [step]);
|
|
105
|
+
if (!at.found)
|
|
106
|
+
return { found: false };
|
|
107
|
+
node = at.value;
|
|
108
|
+
}
|
|
109
|
+
return { found: true, value: node };
|
|
110
|
+
}
|
|
36
111
|
function roundTrips(parsed, body) {
|
|
37
112
|
try {
|
|
38
113
|
return JSON.stringify(parsed) === body;
|
|
@@ -44,7 +119,7 @@ function roundTrips(parsed, body) {
|
|
|
44
119
|
function pathsOverlap(a, b) {
|
|
45
120
|
const n = Math.min(a.length, b.length);
|
|
46
121
|
for (let i = 0; i < n; i += 1)
|
|
47
|
-
if (a[i]
|
|
122
|
+
if (!sameStep(a[i], b[i]))
|
|
48
123
|
return false;
|
|
49
124
|
return true;
|
|
50
125
|
}
|
|
@@ -56,7 +131,12 @@ function valuesIgnoringCase(root, path) {
|
|
|
56
131
|
for (const step of path) {
|
|
57
132
|
const next = [];
|
|
58
133
|
for (const node of nodes) {
|
|
59
|
-
if (
|
|
134
|
+
if (isTextStep(step)) {
|
|
135
|
+
const inner = jsonText(node);
|
|
136
|
+
if (inner !== void 0)
|
|
137
|
+
next.push(inner);
|
|
138
|
+
}
|
|
139
|
+
else if (typeof step === "number") {
|
|
60
140
|
if (Array.isArray(node) && Number.isSafeInteger(step) && step >= 0 && step < node.length)
|
|
61
141
|
next.push(node[step]);
|
|
62
142
|
}
|
|
@@ -71,7 +151,7 @@ function valuesIgnoringCase(root, path) {
|
|
|
71
151
|
return nodes;
|
|
72
152
|
}
|
|
73
153
|
function describePath(path) {
|
|
74
|
-
return path.length ? path.map((step) => typeof step === "number" ? `[${step}]` : `.${step}`).join("").replace(/^\./, "") : "(root)";
|
|
154
|
+
return path.length ? path.map((step) => typeof step === "number" ? `[${step}]` : isTextStep(step) ? "<json>" : `.${step}`).join("").replace(/^\./, "") : "(root)";
|
|
75
155
|
}
|
|
76
156
|
// src/url-template.js
|
|
77
157
|
var PLACEHOLDER_VALUE = /^[A-Za-z0-9._~@+-]{1,256}$/;
|
|
@@ -316,6 +396,7 @@ function listRefusal(list, max, label, each, { optional = true, min = 0 } = {})
|
|
|
316
396
|
return null;
|
|
317
397
|
}
|
|
318
398
|
var pathRefusal = (path, label) => isJsonPath(path) && path.length > 0 ? null : `${label} is not a JSON path of keys and indices`;
|
|
399
|
+
var bodyPathRefusal = (path, label) => isProfilePath(path) && path.length > 0 ? null : `${label} is not a JSON path of keys and indices`;
|
|
319
400
|
function equalsRuleRefusal(rule, label, { root }) {
|
|
320
401
|
const byPath = root && isPlainObject(rule) && has(rule, "equalsPath");
|
|
321
402
|
if (byPath && has(rule, "equals"))
|
|
@@ -755,11 +836,11 @@ function merchantBodyAmountRefusal(profile) {
|
|
|
755
836
|
if (amount === null)
|
|
756
837
|
return null;
|
|
757
838
|
if (Array.isArray(amount))
|
|
758
|
-
return
|
|
839
|
+
return bodyPathRefusal(amount, "the body amount");
|
|
759
840
|
const members = membersRefusal(amount, BODY_AMOUNT_MEMBERS, BODY_AMOUNT_MEMBERS, "the body amount");
|
|
760
841
|
if (members)
|
|
761
842
|
return members;
|
|
762
|
-
const paths =
|
|
843
|
+
const paths = bodyPathRefusal(amount.value, "the body amount's value") ?? bodyPathRefusal(amount.currency, "the body amount's currency");
|
|
763
844
|
if (paths)
|
|
764
845
|
return paths;
|
|
765
846
|
if (pathsOverlap(amount.value, amount.currency))
|
|
@@ -780,16 +861,16 @@ function readMerchantBodyAmount(profile, body) {
|
|
|
780
861
|
if (amount === null)
|
|
781
862
|
return { kind: "unnamed" };
|
|
782
863
|
if (Array.isArray(amount)) {
|
|
783
|
-
const at =
|
|
864
|
+
const at = profileAt(body, amount);
|
|
784
865
|
if (!at.found)
|
|
785
866
|
return { kind: "absent" };
|
|
786
867
|
const named = adyenAmount(at.value);
|
|
787
868
|
return named ? { kind: "amount", amountCents: named.minor, currency: named.currency } : { kind: "unreadable" };
|
|
788
869
|
}
|
|
789
|
-
const value =
|
|
870
|
+
const value = profileAt(body, amount.value);
|
|
790
871
|
if (!value.found)
|
|
791
872
|
return { kind: "absent" };
|
|
792
|
-
const currency =
|
|
873
|
+
const currency = profileAt(body, amount.currency);
|
|
793
874
|
if (!currency.found || typeof currency.value !== "string" || !/^[A-Za-z]{3}$/.test(currency.value))
|
|
794
875
|
return { kind: "unreadable" };
|
|
795
876
|
const code = currency.value.toUpperCase();
|
|
@@ -1542,6 +1623,382 @@ var MERCHANT_HOSTED_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1542
1623
|
facts: [{ path: ["sign_request", "raw_card_bin"], fact: "bin6" }],
|
|
1543
1624
|
then: { method: "POST", path: "/cpp-paypage/v1/miles-plus-cash-authorise", query: {}, holder: [] },
|
|
1544
1625
|
adyen: { environment: "live", clientKey: null, publicKeySha256: null }
|
|
1626
|
+
},
|
|
1627
|
+
{
|
|
1628
|
+
// Read Oct 23, 2026, from teenage.engineering's shipped code and a card-free observe run on its
|
|
1629
|
+
// US checkout (every non-GET aborted once the card form was reached, the placeholder typed into
|
|
1630
|
+
// adyen-web's secured fields, nothing pressed). The store runs on Centra. Its checkout posts the
|
|
1631
|
+
// order form to /_api/mw/selections/<selection>/payment2 with paymentInitiateOnly (no card), and
|
|
1632
|
+
// Centra answers with the Adyen Drop-in's own script (adyen-web 6.41.1, environment live, this
|
|
1633
|
+
// client key, holder name required, enableStoreDetails false). That script's onSubmit hands
|
|
1634
|
+
// adyen-web's state.data to the page as paymentMethodSpecificFields (centra_checkout_payment_callback),
|
|
1635
|
+
// and the page's finishAdyenPayment (assets/shop.*.js, cart-api.*.js) posts
|
|
1636
|
+
// {paymentMethod: 'adyen-drop-in', address: <billing address>, paymentMethodSpecificFields:
|
|
1637
|
+
// state.data, termsAndConditions: true, affiliate} to the same path through fetch, with no
|
|
1638
|
+
// content type of its own (text/plain;charset=UTF-8 on the wire, a JSON text). The body names no
|
|
1639
|
+
// amount: Centra charges the selection the path names, so the total comes from the selection's
|
|
1640
|
+
// own answers below. The Drop-in's stored-payment update flow adds contracts. Public key read
|
|
1641
|
+
// from checkoutshopper-live.adyen.com on 2026-10-23 (UTC). Not yet seen on the wire: the card
|
|
1642
|
+
// request itself, which leaves only on the Pay press, so a press run confirms the body.
|
|
1643
|
+
id: "teenage_engineering_centra_payment",
|
|
1644
|
+
merchant: "teenage engineering",
|
|
1645
|
+
status: "observe",
|
|
1646
|
+
origins: [{ checkout: "https://teenage.engineering", endpoint: "https://teenage.engineering" }],
|
|
1647
|
+
method: "POST",
|
|
1648
|
+
path: "/_api/mw/selections/:selectionId/payment2",
|
|
1649
|
+
query: {},
|
|
1650
|
+
requires: [{ path: ["paymentMethod"], equals: "adyen-drop-in" }],
|
|
1651
|
+
holder: ["paymentMethodSpecificFields", "paymentMethod"],
|
|
1652
|
+
fields: {
|
|
1653
|
+
encryptedCardNumber: "encryptedCardNumber",
|
|
1654
|
+
encryptedExpiryMonth: "encryptedExpiryMonth",
|
|
1655
|
+
encryptedExpiryYear: "encryptedExpiryYear",
|
|
1656
|
+
encryptedSecurityCode: "encryptedSecurityCode"
|
|
1657
|
+
},
|
|
1658
|
+
// adyen-web 6.x stamps the dummy's brand after its bin lookup answers.
|
|
1659
|
+
remove: ["brand"],
|
|
1660
|
+
refuse: [
|
|
1661
|
+
{ path: ["paymentMethodSpecificFields", "storePaymentMethod"], when: "true" },
|
|
1662
|
+
{ path: ["paymentMethodSpecificFields", "paymentMethod", "storedPaymentMethodId"], when: "present" },
|
|
1663
|
+
{ path: ["paymentMethodSpecificFields", "paymentMethod", "recurringDetailReference"], when: "present" },
|
|
1664
|
+
// The order form's own post on the same path initiates the payment and carries no card.
|
|
1665
|
+
{ path: ["paymentInitiateOnly"], when: "true" },
|
|
1666
|
+
// The Drop-in's stored-payment update flow names subscription contracts the card would pay later.
|
|
1667
|
+
{ path: ["contracts"], when: "present" }
|
|
1668
|
+
],
|
|
1669
|
+
amount: null,
|
|
1670
|
+
// Read Oct 23, 2026 (the observe run above). Every selection call answers the whole selection
|
|
1671
|
+
// as JSON: the read, the country and VAT changes, item quantities and the address step (which
|
|
1672
|
+
// wraps it in `selection`). totals.grandTotalPriceAsNumber is what the Drop-in tells its own
|
|
1673
|
+
// wallets to charge, in major units (2497.29 in the run), and currency names the currency. The
|
|
1674
|
+
// card request's URL carries the same selection id, which binds each answer to it. Voucher,
|
|
1675
|
+
// VAT-number and item-removal answers were not seen in the run, so they are not read: any of
|
|
1676
|
+
// those calls after the chosen answer makes it stale.
|
|
1677
|
+
amountSource: {
|
|
1678
|
+
id: "teenage_engineering_selection_totals",
|
|
1679
|
+
origin: "endpoint",
|
|
1680
|
+
format: "json",
|
|
1681
|
+
responses: [
|
|
1682
|
+
{ method: "GET", path: "/_api/mw/selections/:selectionId", query: {} },
|
|
1683
|
+
{ method: "PUT", path: "/_api/mw/selections/:selectionId", query: {} },
|
|
1684
|
+
{ method: "PUT", path: "/_api/mw/selections/:selectionId/address", query: {}, at: ["selection"] },
|
|
1685
|
+
{ method: "DELETE", path: "/_api/mw/selections/:selectionId/vat", query: {} },
|
|
1686
|
+
{ method: "POST", path: "/_api/mw/selections/:selectionId/items/:itemId/quantity/:quantity", query: {} },
|
|
1687
|
+
{ method: "DELETE", path: "/_api/mw/selections/:selectionId/items/:itemId/quantity/:quantity", query: {} }
|
|
1688
|
+
],
|
|
1689
|
+
total: { path: ["totals", "grandTotalPriceAsNumber"], format: "number" },
|
|
1690
|
+
unit: "major",
|
|
1691
|
+
currency: "USD",
|
|
1692
|
+
currencyPath: ["currency"],
|
|
1693
|
+
bind: [{ placeholder: "selectionId", cardPlaceholder: "selectionId" }],
|
|
1694
|
+
voidedBy: [
|
|
1695
|
+
{ method: "POST", path: "/_api/mw/selections/:selectionId/vouchers/:voucherCode", query: {} },
|
|
1696
|
+
{ method: "DELETE", path: "/_api/mw/selections/:selectionId/vouchers", query: {} },
|
|
1697
|
+
{ method: "POST", path: "/_api/mw/selections/:selectionId/vat/:vatNumber", query: {} },
|
|
1698
|
+
{ method: "DELETE", path: "/_api/mw/selections/:selectionId/items/:itemId", query: {} }
|
|
1699
|
+
],
|
|
1700
|
+
maxAgeMs: 9e5
|
|
1701
|
+
},
|
|
1702
|
+
adyen: {
|
|
1703
|
+
environment: "live",
|
|
1704
|
+
clientKey: "live_FAQ7CUZM7JAO7K6CGXI67KMZ64K35AVH",
|
|
1705
|
+
publicKeySha256: "b99a44120544b609c01ede6041c82b481d23423d7e956355022a5d7f362b5349"
|
|
1706
|
+
}
|
|
1707
|
+
},
|
|
1708
|
+
{
|
|
1709
|
+
// Read Oct 23, 2026, from www.scheels.com's shipped checkout chunk (a Next.js page on commercetools
|
|
1710
|
+
// Frontastic) and a card-free observe run on its guest checkout (every non-GET aborted once the card
|
|
1711
|
+
// form was reached, the placeholder typed into adyen-web 5.5.3's secured fields, Review Order pressed:
|
|
1712
|
+
// the address save before it was aborted too, so the card request was not reached). The Card
|
|
1713
|
+
// component's onSubmit (showPayButton false; the page submits it on Review Order) sets
|
|
1714
|
+
// state.data.paymentMethod.brand from onBrand, then calls the Frontastic action
|
|
1715
|
+
// /cart/setAdyenCreditCard with {payment: {paymentMethod: 'CREDIT_CARD', bin (onBinValue's six
|
|
1716
|
+
// digits, or onFieldValid's issuerBin), lastFour (onFieldValid's endDigits), deviceFingerprint (when
|
|
1717
|
+
// the page has one)}, state: JSON.stringify(state)}. The SDK posts JSON.stringify(payload) to
|
|
1718
|
+
// apihub.scheels.com/frontastic/action/cart/setAdyenCreditCard (its path joiner collapses the
|
|
1719
|
+
// action's leading '/') with content-type application/json and the Frontastic-Session header. So the
|
|
1720
|
+
// four JWEs sit in adyen-web's state, inside the `state` text. bin and lastFour describe the dummy:
|
|
1721
|
+
// the approved card's BIN6 and last four are written there. The body names no amount: the cart the
|
|
1722
|
+
// session holds is charged when Place Order sends cart/checkout, which carries no card. Public key
|
|
1723
|
+
// read from checkoutshopper-live-us.adyen.com on 2026-10-23 (UTC).
|
|
1724
|
+
id: "scheels_frontastic_adyen_card",
|
|
1725
|
+
merchant: "SCHEELS",
|
|
1726
|
+
status: "observe",
|
|
1727
|
+
origins: [{ checkout: "https://www.scheels.com", endpoint: "https://apihub.scheels.com" }],
|
|
1728
|
+
method: "POST",
|
|
1729
|
+
path: "/frontastic/action/cart/setAdyenCreditCard",
|
|
1730
|
+
query: {},
|
|
1731
|
+
requires: [{ path: ["payment", "paymentMethod"], equals: "CREDIT_CARD" }],
|
|
1732
|
+
holder: ["state", TEXT_STEP, "data", "paymentMethod"],
|
|
1733
|
+
fields: {
|
|
1734
|
+
encryptedCardNumber: "encryptedCardNumber",
|
|
1735
|
+
encryptedExpiryMonth: "encryptedExpiryMonth",
|
|
1736
|
+
encryptedExpiryYear: "encryptedExpiryYear",
|
|
1737
|
+
encryptedSecurityCode: "encryptedSecurityCode"
|
|
1738
|
+
},
|
|
1739
|
+
// The page copies onBrand's code (the dummy's) into the state before it is stringified.
|
|
1740
|
+
remove: ["brand"],
|
|
1741
|
+
refuse: [
|
|
1742
|
+
// enableStoreDetails is on for a signed-in shopper only.
|
|
1743
|
+
{ path: ["state", TEXT_STEP, "data", "storePaymentMethod"], when: "true" },
|
|
1744
|
+
{ path: ["state", TEXT_STEP, "data", "paymentMethod", "storedPaymentMethodId"], when: "present" },
|
|
1745
|
+
{ path: ["state", TEXT_STEP, "data", "paymentMethod", "recurringDetailReference"], when: "present" }
|
|
1746
|
+
],
|
|
1747
|
+
amount: null,
|
|
1748
|
+
// Read Oct 23, 2026 (the observe run above). Every cart action on the checkout page answers the
|
|
1749
|
+
// whole cart, camelCase JSON at the root (addToCart wraps it in `cart`); summary.amountDue is what
|
|
1750
|
+
// the payment step shows as "Amount due" and what is left for the card, in cents (6538 in the run,
|
|
1751
|
+
// the run's total). The cart is the session's, so an answer is tied to the card request by its
|
|
1752
|
+
// page. A gift card or another tender lowers the amount due below the order's gross total and is
|
|
1753
|
+
// refused until a HAR shows which one the charge follows.
|
|
1754
|
+
amountSource: {
|
|
1755
|
+
id: "scheels_cart_amount_due",
|
|
1756
|
+
origin: "endpoint",
|
|
1757
|
+
format: "json",
|
|
1758
|
+
responses: [
|
|
1759
|
+
{ method: "POST", path: "/frontastic/action/cart/updateCart", query: {} },
|
|
1760
|
+
{ method: "POST", path: "/frontastic/action/cart/setCheckoutDefaults", query: {} },
|
|
1761
|
+
{ method: "POST", path: "/frontastic/action/cart/clean", query: {} },
|
|
1762
|
+
{ method: "POST", path: "/frontastic/action/cart/addToCart", query: {}, at: ["cart"] },
|
|
1763
|
+
{ method: "POST", path: "/frontastic/action/route/addRouteProtection", query: {} },
|
|
1764
|
+
{ method: "POST", path: "/frontastic/action/route/removeRouteProtection", query: {} }
|
|
1765
|
+
],
|
|
1766
|
+
total: { path: ["summary", "amountDue", "centAmount"], format: "number" },
|
|
1767
|
+
unit: "minor",
|
|
1768
|
+
currency: "USD",
|
|
1769
|
+
currencyPath: ["summary", "amountDue", "currencyCode"],
|
|
1770
|
+
bind: [{ samePage: true }],
|
|
1771
|
+
requires: [{ path: ["summary", "amountDue", "centAmount"], equalsPath: ["summary", "grossTotal", "centAmount"] }],
|
|
1772
|
+
refuse: [
|
|
1773
|
+
{ path: ["summary", "giftCardTotal", "centAmount"], when: "nonzero" },
|
|
1774
|
+
{ path: ["summary", "tenderTotal", "centAmount"], when: "nonzero" }
|
|
1775
|
+
],
|
|
1776
|
+
// Cart changes whose answers were not seen in the run: any of them after the chosen answer makes it stale.
|
|
1777
|
+
voidedBy: [
|
|
1778
|
+
{ method: "POST", path: "/frontastic/action/cart/redeemDiscountCode", query: {} },
|
|
1779
|
+
{ method: "POST", path: "/frontastic/action/cart/removeDiscountCode", query: {} },
|
|
1780
|
+
{ method: "POST", path: "/frontastic/action/cart/updateItem", query: {} },
|
|
1781
|
+
{ method: "POST", path: "/frontastic/action/cart/removeItem", query: {} },
|
|
1782
|
+
{ method: "POST", path: "/frontastic/action/cart/setShippingMethod", query: {} },
|
|
1783
|
+
{ method: "POST", path: "/frontastic/action/sameDay/setTip", query: {} }
|
|
1784
|
+
],
|
|
1785
|
+
maxAgeMs: 9e5
|
|
1786
|
+
},
|
|
1787
|
+
facts: [
|
|
1788
|
+
{ path: ["payment", "bin"], fact: "bin6" },
|
|
1789
|
+
{ path: ["payment", "lastFour"], fact: "last4" }
|
|
1790
|
+
],
|
|
1791
|
+
adyen: {
|
|
1792
|
+
environment: "live-us",
|
|
1793
|
+
clientKey: "live_7NQ5GGRX7ZD3PJCWBGV6WELTMMA3GBOO",
|
|
1794
|
+
publicKeySha256: "9d6aa672c539f6e76c082beed1fcbe089af526b87c9ea6878ab3671355089065"
|
|
1795
|
+
}
|
|
1796
|
+
},
|
|
1797
|
+
{
|
|
1798
|
+
// Read Oct 23, 2026, from www.buff.com's shipped US checkout (Magento on Hyvä Checkout with
|
|
1799
|
+
// Magewire, Adyen's Hyvä module, adyen-web 5.5.3) and a card-free observe run on its guest checkout
|
|
1800
|
+
// (every non-GET aborted once the card form was reached, the placeholder typed into the secured
|
|
1801
|
+
// fields, nothing pressed). The card renderer keeps adyen-web's state.data from onChange and the
|
|
1802
|
+
// brand code onBrand maps (amex AE, visa VI, mc MC). Place Order calls the adyen_cc component's
|
|
1803
|
+
// placeOrder({stateData, ccType, publicHash: null, extension_attributes}) through Magewire, which
|
|
1804
|
+
// posts JSON.stringify({fingerprint, serverMemo, updates: [{type: 'callMethod', payload: {id,
|
|
1805
|
+
// method: 'placeOrder', params: [that object]}}]}) to
|
|
1806
|
+
// /us/magewire/post/livewire/message/checkout.payment.methods.adyen_cc with content-type
|
|
1807
|
+
// application/json and X-CSRF-TOKEN. The server charges the quote there. ccType describes the
|
|
1808
|
+
// dummy: the approved card's code is written there. The body's own serverMemo, which Magewire signs
|
|
1809
|
+
// with its checksum, carries the component's paymentResponse as JSON text, and its
|
|
1810
|
+
// paymentMethodsExtraDetails.scheme.configuration.amount is the quote's grand total in cents: it
|
|
1811
|
+
// followed the run's total from 8600 to 9342 once shipping and tax were set. A saved card sends a
|
|
1812
|
+
// publicHash and enableStoreDetails is off for a guest. Public key read from
|
|
1813
|
+
// checkoutshopper-live.adyen.com on 2026-10-23 (UTC). Not yet seen on the wire: the card request
|
|
1814
|
+
// itself, which leaves only on Place Order.
|
|
1815
|
+
id: "buff_magewire_adyen_cc",
|
|
1816
|
+
merchant: "Buff",
|
|
1817
|
+
status: "observe",
|
|
1818
|
+
origins: [{ checkout: "https://www.buff.com", endpoint: "https://www.buff.com" }],
|
|
1819
|
+
method: "POST",
|
|
1820
|
+
path: "/us/magewire/post/livewire/message/checkout.payment.methods.adyen_cc",
|
|
1821
|
+
query: {},
|
|
1822
|
+
requires: [
|
|
1823
|
+
{ path: ["fingerprint", "name"], equals: "checkout.payment.methods.adyen_cc" },
|
|
1824
|
+
{ path: ["updates", 0, "type"], equals: "callMethod" },
|
|
1825
|
+
{ path: ["updates", 0, "payload", "method"], equals: "placeOrder" }
|
|
1826
|
+
],
|
|
1827
|
+
holder: ["updates", 0, "payload", "params", 0, "stateData", "paymentMethod"],
|
|
1828
|
+
fields: {
|
|
1829
|
+
encryptedCardNumber: "encryptedCardNumber",
|
|
1830
|
+
encryptedExpiryMonth: "encryptedExpiryMonth",
|
|
1831
|
+
encryptedExpiryYear: "encryptedExpiryYear",
|
|
1832
|
+
encryptedSecurityCode: "encryptedSecurityCode"
|
|
1833
|
+
},
|
|
1834
|
+
// adyen-web 5.x stamps the dummy's brand into state.data.
|
|
1835
|
+
remove: ["brand"],
|
|
1836
|
+
refuse: [
|
|
1837
|
+
// One call per message: a second update or a second argument is not the reviewed request.
|
|
1838
|
+
{ path: ["updates", 1], when: "present" },
|
|
1839
|
+
{ path: ["updates", 0, "payload", "params", 1], when: "present" },
|
|
1840
|
+
// A saved card's public hash, or a card kept for later.
|
|
1841
|
+
{ path: ["updates", 0, "payload", "params", 0, "publicHash"], when: "true" },
|
|
1842
|
+
{ path: ["updates", 0, "payload", "params", 0, "stateData", "storePaymentMethod"], when: "true" },
|
|
1843
|
+
{ path: ["updates", 0, "payload", "params", 0, "stateData", "paymentMethod", "storedPaymentMethodId"], when: "present" },
|
|
1844
|
+
{ path: ["updates", 0, "payload", "params", 0, "stateData", "paymentMethod", "recurringDetailReference"], when: "present" }
|
|
1845
|
+
],
|
|
1846
|
+
amount: ["serverMemo", "data", "paymentResponse", TEXT_STEP, "paymentMethodsExtraDetails", "scheme", "configuration", "amount"],
|
|
1847
|
+
facts: [
|
|
1848
|
+
// The renderer's own map (componentHandler.getCcCodeByAltCode); Buff's card list names these three.
|
|
1849
|
+
{ path: ["updates", 0, "payload", "params", 0, "ccType"], fact: "brand", map: { visa: "VI", mastercard: "MC", amex: "AE" } }
|
|
1850
|
+
],
|
|
1851
|
+
adyen: {
|
|
1852
|
+
environment: "live",
|
|
1853
|
+
clientKey: "live_5NIC7BWSNFDX3GBSKL5XCZ4B4YCIWLKP",
|
|
1854
|
+
publicKeySha256: "3f7e1740684cb4964aa8bb7185b9718564fc0957cfc83e096f7bf016d6a7d001"
|
|
1855
|
+
}
|
|
1856
|
+
},
|
|
1857
|
+
{
|
|
1858
|
+
// Read Oct 23, 2026, from www.plein.com's shipped US checkout (Salesforce Commerce Cloud SiteGenesis with Adyen's
|
|
1859
|
+
// int_adyen cartridge, adyen-web 4.9.0) and the press lane's stored copy of the card request the page sent with the
|
|
1860
|
+
// placeholder typed (aborted unsent by the checkout SDK's hold; values sanitized, field names and shape kept). The
|
|
1861
|
+
// card component's onSubmit writes JSON.stringify(state.data) into the hidden field dwfrm_adyPaydata_adyenStateData,
|
|
1862
|
+
// and CONTINUE TO PLACE ORDER submits the billing form: a top-level form post (application/x-www-form-urlencoded) to
|
|
1863
|
+
// /us/billing/. So the four JWEs sit in adyen-web's state, inside that field's JSON text, under paymentMethod
|
|
1864
|
+
// (adyen-web 4.9 writes no brand there; one a later version writes is dropped). Beside the state the page writes
|
|
1865
|
+
// onBrand's code into dwfrm_billing_paymentMethods_creditCard_type and twelve '*' with onFieldValid's last four into
|
|
1866
|
+
// dwfrm_billing_paymentMethods_creditCard_number, both describing the dummy: the approved card's are written there.
|
|
1867
|
+
// The form also carries its csrf_token and its submit button's name twice (dwfrm_billing_save); a repeated field
|
|
1868
|
+
// never reaches a rule. enableStoreDetails is on for a signed-in shopper only. The body names no amount, and the
|
|
1869
|
+
// page's total sits only in its analytics block (tc_vars.order_amount_ati_with_sf), which no reviewed rule reads,
|
|
1870
|
+
// so amountPending holds the profile in observe. Public key read from checkoutshopper-live-us.adyen.com on
|
|
1871
|
+
// 2026-10-23 (UTC).
|
|
1872
|
+
id: "plein_sfcc_billing_form",
|
|
1873
|
+
merchant: "Philipp Plein",
|
|
1874
|
+
status: "observe",
|
|
1875
|
+
origins: [{ checkout: "https://www.plein.com", endpoint: "https://www.plein.com" }],
|
|
1876
|
+
method: "POST",
|
|
1877
|
+
path: "/us/billing/",
|
|
1878
|
+
query: {},
|
|
1879
|
+
encoding: "form",
|
|
1880
|
+
requires: [
|
|
1881
|
+
{ path: ["dwfrm_billing_paymentMethods_selectedPaymentMethodID"], equals: "AdyenComponent" },
|
|
1882
|
+
{ path: ["brandCode"], equals: "scheme" }
|
|
1883
|
+
],
|
|
1884
|
+
holder: ["dwfrm_adyPaydata_adyenStateData", TEXT_STEP, "paymentMethod"],
|
|
1885
|
+
fields: {
|
|
1886
|
+
encryptedCardNumber: "encryptedCardNumber",
|
|
1887
|
+
encryptedExpiryMonth: "encryptedExpiryMonth",
|
|
1888
|
+
encryptedExpiryYear: "encryptedExpiryYear",
|
|
1889
|
+
encryptedSecurityCode: "encryptedSecurityCode"
|
|
1890
|
+
},
|
|
1891
|
+
remove: ["brand"],
|
|
1892
|
+
refuse: [
|
|
1893
|
+
{ path: ["dwfrm_adyPaydata_adyenStateData", TEXT_STEP, "storePaymentMethod"], when: "true" },
|
|
1894
|
+
{ path: ["dwfrm_adyPaydata_adyenStateData", TEXT_STEP, "paymentMethod", "storedPaymentMethodId"], when: "present" },
|
|
1895
|
+
{ path: ["dwfrm_adyPaydata_adyenStateData", TEXT_STEP, "paymentMethod", "recurringDetailReference"], when: "present" }
|
|
1896
|
+
],
|
|
1897
|
+
amount: null,
|
|
1898
|
+
amountPending: "the page names its total only in its analytics block (tc_vars.order_amount_ati_with_sf), which no reviewed rule reads",
|
|
1899
|
+
facts: [
|
|
1900
|
+
// onBrand's adyen-web codes for the networks a US checkout takes.
|
|
1901
|
+
{ path: ["dwfrm_billing_paymentMethods_creditCard_type"], fact: "brand", map: { visa: "visa", mastercard: "mc", amex: "amex", discover: "discover" } },
|
|
1902
|
+
{ path: ["dwfrm_billing_paymentMethods_creditCard_number"], fact: "masked_last4" }
|
|
1903
|
+
],
|
|
1904
|
+
adyen: {
|
|
1905
|
+
environment: "live-us",
|
|
1906
|
+
clientKey: "live_P7RRXHAHEVFTLPS3XUD3ML7ZWUEZ6BXN",
|
|
1907
|
+
publicKeySha256: "3e0d4366563b714feb233d12ce98adcef3b606381596e097ca3a8123b9a91d67"
|
|
1908
|
+
}
|
|
1909
|
+
},
|
|
1910
|
+
{
|
|
1911
|
+
// Read Nov 3, 2026, from www.lego.com's shipped US checkout (a Next.js app under /checkout, Apollo GraphQL) and a
|
|
1912
|
+
// card-free run on its guest checkout up to the card form (test contact details, no card typed, nothing pressed;
|
|
1913
|
+
// every non-GET to adyen.com aborted). The checkout's cart answer (CheckoutV2State, paymentMethodsV3.primary) names
|
|
1914
|
+
// CREDIT_CARD_ADYEN with environment 'live' and this client key, and the card form is adyen-web 6's secured fields
|
|
1915
|
+
// (securedFields 6.0.0 on checkoutshopper-live.adyen.com). Place order's handler (chunk 487, the CreditCard submit)
|
|
1916
|
+
// copies encryptedCardNumber, encryptedExpiryMonth, encryptedExpiryYear, encryptedSecurityCode, type and holderName
|
|
1917
|
+
// out of adyen-web's state.data.paymentMethod, with no brand, and sends the Apollo mutation AddAdyenPayment with
|
|
1918
|
+
// variables {input: {paymentReference: <the cart's orderNumber>, telephoneNumber, browserInfo, origin, paymentMethod,
|
|
1919
|
+
// returnUrl, storePaymentMethod, tracking}}. Apollo posts {operationName, variables, query} as JSON to the gateway
|
|
1920
|
+
// (www.lego.com/checkout/api/graphql), whose link adds the operation name to the path: every checkout operation in
|
|
1921
|
+
// the run went to /checkout/api/graphql/<operationName>. So the card request is Lego's own server, Adyen's Advanced
|
|
1922
|
+
// flow. A saved card goes as AddAdyenSavedCardPaymentV2, a gift card as AddGiftCardPaymentV2Mutation, each on its
|
|
1923
|
+
// own path. The body names no amount. Public key read from checkoutshopper-live.adyen.com on 2026-11-03 (UTC). Not
|
|
1924
|
+
// yet seen on the wire: the card request itself, which leaves only on Place order.
|
|
1925
|
+
// Enabled on Nov 4, 2026 under D1, so the Vault's bundled copy of this table carries it at the next Vault release. No
|
|
1926
|
+
// company is admitted yet: Agentcard's proof company joins through a flip in backend merchant-profile-orgs.ts after that
|
|
1927
|
+
// release. Felipe's approval of this change is his D1 decision for LEGO, and Karen's its PCI review.
|
|
1928
|
+
// No afterPayment reader yet: nobody has seen what LEGO answers after a placed order, so the charge is not compared with
|
|
1929
|
+
// the approved amount after payment and merchantChargeVerdict has nothing to read (Greptile on this change). The amount
|
|
1930
|
+
// is checked before payment (amountSource below). Until a reviewed afterPayment lands, built from the response the first
|
|
1931
|
+
// live run captures, Agentcard's proof company makes no purchase LEGO's Adyen approves: its runs use a card Adyen declines.
|
|
1932
|
+
id: "lego_checkout_add_adyen_payment",
|
|
1933
|
+
merchant: "LEGO",
|
|
1934
|
+
status: "enabled",
|
|
1935
|
+
origins: [{ checkout: "https://www.lego.com", endpoint: "https://www.lego.com" }],
|
|
1936
|
+
method: "POST",
|
|
1937
|
+
path: "/checkout/api/graphql/AddAdyenPayment",
|
|
1938
|
+
query: {},
|
|
1939
|
+
requires: [
|
|
1940
|
+
{ path: ["operationName"], equals: "AddAdyenPayment" },
|
|
1941
|
+
{ path: ["variables", "input", "paymentMethod", "type"], equals: "scheme" }
|
|
1942
|
+
],
|
|
1943
|
+
holder: ["variables", "input", "paymentMethod"],
|
|
1944
|
+
fields: {
|
|
1945
|
+
encryptedCardNumber: "encryptedCardNumber",
|
|
1946
|
+
encryptedExpiryMonth: "encryptedExpiryMonth",
|
|
1947
|
+
encryptedExpiryYear: "encryptedExpiryYear",
|
|
1948
|
+
encryptedSecurityCode: "encryptedSecurityCode"
|
|
1949
|
+
},
|
|
1950
|
+
// The page copies no brand out of adyen-web's state.
|
|
1951
|
+
remove: [],
|
|
1952
|
+
refuse: [
|
|
1953
|
+
{ path: ["variables", "input", "storePaymentMethod"], when: "true" },
|
|
1954
|
+
{ path: ["variables", "input", "paymentMethod", "storedPaymentMethodId"], when: "present" },
|
|
1955
|
+
{ path: ["variables", "input", "paymentMethod", "recurringDetailReference"], when: "present" }
|
|
1956
|
+
],
|
|
1957
|
+
amount: null,
|
|
1958
|
+
// Read Nov 3, 2026 (the run above). The checkout's cart answers carry the whole cart: CheckoutV2State at
|
|
1959
|
+
// data.me.cart, the address step at data.updateCartAddresses and the shipping step at data.setCartShippingMethod.
|
|
1960
|
+
// remainingTotalToPay is what is left for the card, in cents (1191 in the run, the page's Order Total $11.91), and
|
|
1961
|
+
// orderNumber is the paymentReference the card request sends, which ties each answer to it. A gift card already
|
|
1962
|
+
// applied shows in payments and lowers what is left below the order's total, so a cart with one is refused. Cart
|
|
1963
|
+
// changes whose answers were not seen in the run make the chosen answer stale.
|
|
1964
|
+
amountSource: {
|
|
1965
|
+
id: "lego_cart_remaining_total",
|
|
1966
|
+
origin: "endpoint",
|
|
1967
|
+
format: "json",
|
|
1968
|
+
responses: [
|
|
1969
|
+
{ method: "POST", path: "/checkout/api/graphql/CheckoutV2State", query: {}, at: ["data", "me", "cart"] },
|
|
1970
|
+
{ method: "POST", path: "/checkout/api/graphql/SetAddressMutation", query: {}, at: ["data", "updateCartAddresses"] },
|
|
1971
|
+
{ method: "POST", path: "/checkout/api/graphql/SetShippingMethodV2Mutation", query: {}, at: ["data", "setCartShippingMethod"] }
|
|
1972
|
+
],
|
|
1973
|
+
total: { path: ["remainingTotalToPay", "centAmount"], format: "number" },
|
|
1974
|
+
unit: "minor",
|
|
1975
|
+
currency: "USD",
|
|
1976
|
+
currencyPath: ["remainingTotalToPay", "currencyCode"],
|
|
1977
|
+
bind: [{ source: ["orderNumber"], card: ["variables", "input", "paymentReference"] }],
|
|
1978
|
+
refuse: [
|
|
1979
|
+
{ path: ["payments"], when: "nonEmpty" },
|
|
1980
|
+
// A GraphQL error result is not a total, even beside partial data.
|
|
1981
|
+
{ path: ["errors"], when: "present", root: true }
|
|
1982
|
+
],
|
|
1983
|
+
voidedBy: [
|
|
1984
|
+
{ method: "POST", path: "/checkout/api/graphql/AddGiftCardPaymentV2Mutation", query: {} },
|
|
1985
|
+
{ method: "POST", path: "/checkout/api/graphql/AddGiftCardOnlyPayment", query: {} },
|
|
1986
|
+
{ method: "POST", path: "/checkout/api/graphql/RemoveGiftCardPaymentMutations", query: {} },
|
|
1987
|
+
{ method: "POST", path: "/checkout/api/graphql/AddVipLineItemCheckout", query: {} },
|
|
1988
|
+
{ method: "POST", path: "/checkout/api/graphql/EditVipLineItemCheckout", query: {} },
|
|
1989
|
+
{ method: "POST", path: "/checkout/api/graphql/RemoveVipPointsLineItemCheckout", query: {} },
|
|
1990
|
+
{ method: "POST", path: "/checkout/api/graphql/SetShippingMethodOnlyMutation", query: {} },
|
|
1991
|
+
{ method: "POST", path: "/checkout/api/graphql/SetAddressMutationWithoutEmail", query: {} },
|
|
1992
|
+
{ method: "POST", path: "/checkout/api/graphql/SetAddressesMutationV2", query: {} },
|
|
1993
|
+
{ method: "POST", path: "/checkout/api/graphql/saveCollectionPointMutation", query: {} }
|
|
1994
|
+
],
|
|
1995
|
+
maxAgeMs: 9e5
|
|
1996
|
+
},
|
|
1997
|
+
adyen: {
|
|
1998
|
+
environment: "live",
|
|
1999
|
+
clientKey: "live_P34BZ2Q6ANHNHAUEKOFOX3IMHQHZ75ES",
|
|
2000
|
+
publicKeySha256: "5659f43faa157b82b45844fdf35382a8b83b7bb24662b5e0fbcca15fd120bcc2"
|
|
2001
|
+
}
|
|
1545
2002
|
}
|
|
1546
2003
|
]);
|
|
1547
2004
|
function adyenKeyHost(environment) {
|
|
@@ -1617,23 +2074,80 @@ function declaredEndpointRefusal(endpoint) {
|
|
|
1617
2074
|
return `${url.hostname} belongs to a merchant Agentcard reviewed, so it cannot be declared`;
|
|
1618
2075
|
return null;
|
|
1619
2076
|
}
|
|
1620
|
-
function
|
|
1621
|
-
const
|
|
1622
|
-
|
|
1623
|
-
|
|
2077
|
+
function textRefusal(p, parsed) {
|
|
2078
|
+
const amount = p.amount;
|
|
2079
|
+
const amountPaths = Array.isArray(amount) ? [amount] : isPlainObject(amount) ? [amount.value, amount.currency] : [];
|
|
2080
|
+
const paths = [p.holder, ...p.requires.map((rule) => rule.path), ...p.refuse.map((rule) => rule.path), ...amountPaths].filter(Array.isArray);
|
|
2081
|
+
for (const path of paths) {
|
|
2082
|
+
for (const [k, step] of path.entries()) {
|
|
2083
|
+
if (!isTextStep(step))
|
|
2084
|
+
continue;
|
|
2085
|
+
const before = path.slice(0, k);
|
|
2086
|
+
if (valuesIgnoringCase(parsed, before).some((value) => valuesIgnoringCase(value, [TEXT_STEP]).length === 0)) {
|
|
2087
|
+
return `the paused body's ${describePath(before)} is not JSON text written the way JSON.stringify or PHP's json_encode writes it, so ${p.merchant}'s rules cannot read it`;
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
return null;
|
|
2092
|
+
}
|
|
2093
|
+
function formBodyView(body) {
|
|
2094
|
+
if (typeof body !== "string" || body === "")
|
|
2095
|
+
return void 0;
|
|
2096
|
+
let params;
|
|
2097
|
+
try {
|
|
2098
|
+
params = new URLSearchParams(body);
|
|
2099
|
+
}
|
|
2100
|
+
catch {
|
|
2101
|
+
return void 0;
|
|
2102
|
+
}
|
|
2103
|
+
if (params.toString() !== body)
|
|
2104
|
+
return void 0;
|
|
2105
|
+
const view = /* @__PURE__ */ Object.create(null);
|
|
2106
|
+
for (const [name, value] of params) {
|
|
2107
|
+
if (!Object.prototype.hasOwnProperty.call(view, name))
|
|
2108
|
+
view[name] = value;
|
|
2109
|
+
else
|
|
2110
|
+
view[name] = Array.isArray(view[name]) ? [...view[name], value] : [view[name], value];
|
|
2111
|
+
}
|
|
2112
|
+
return view;
|
|
2113
|
+
}
|
|
2114
|
+
function readProfileBody(p, body) {
|
|
2115
|
+
if (p.encoding === "form") {
|
|
2116
|
+
const view = formBodyView(body);
|
|
2117
|
+
return view === void 0 ? { refusal: "the paused body is not a form written the way a browser writes it, so its rules cannot be read" } : { parsed: view };
|
|
2118
|
+
}
|
|
1624
2119
|
let parsed;
|
|
1625
2120
|
try {
|
|
1626
2121
|
parsed = typeof body === "string" ? JSON.parse(body) : void 0;
|
|
1627
2122
|
}
|
|
1628
2123
|
catch {
|
|
1629
|
-
return "the paused body is not JSON";
|
|
2124
|
+
return { refusal: "the paused body is not JSON" };
|
|
1630
2125
|
}
|
|
1631
2126
|
if (!isPlainObject(parsed))
|
|
1632
|
-
return "the paused body is not a JSON object";
|
|
2127
|
+
return { refusal: "the paused body is not a JSON object" };
|
|
1633
2128
|
if (!roundTrips(parsed, body))
|
|
1634
|
-
return "the paused body does not round-trip through JSON, so its rules cannot be read";
|
|
2129
|
+
return { refusal: "the paused body does not round-trip through JSON, so its rules cannot be read" };
|
|
2130
|
+
return { parsed };
|
|
2131
|
+
}
|
|
2132
|
+
function merchantProfileBody(profile, body) {
|
|
2133
|
+
const p = merchantProfileRecognizerById(typeof profile === "string" ? profile : profile?.id);
|
|
2134
|
+
if (!p)
|
|
2135
|
+
throw new Error("Refusing to pay: unknown merchant profile.");
|
|
2136
|
+
return readProfileBody(p, body).parsed;
|
|
2137
|
+
}
|
|
2138
|
+
function merchantBodyRefusal(profile, body) {
|
|
2139
|
+
const p = merchantProfileRecognizerById(typeof profile === "string" ? profile : profile?.id);
|
|
2140
|
+
if (!p)
|
|
2141
|
+
throw new Error("Refusing to pay: unknown merchant profile.");
|
|
2142
|
+
const read = readProfileBody(p, body);
|
|
2143
|
+
if (read.refusal)
|
|
2144
|
+
return read.refusal;
|
|
2145
|
+
const parsed = read.parsed;
|
|
2146
|
+
const unreadText = textRefusal(p, parsed);
|
|
2147
|
+
if (unreadText)
|
|
2148
|
+
return unreadText;
|
|
1635
2149
|
for (const rule of p.requires) {
|
|
1636
|
-
const at =
|
|
2150
|
+
const at = profileAt(parsed, rule.path);
|
|
1637
2151
|
if (!at.found || at.value !== rule.equals || valuesIgnoringCase(parsed, rule.path).some((value) => value !== rule.equals)) {
|
|
1638
2152
|
return `the paused body is not ${p.merchant}'s card request: ${describePath(rule.path)} is not ${JSON.stringify(rule.equals)}`;
|
|
1639
2153
|
}
|
|
@@ -1646,40 +2160,6 @@ function merchantBodyRefusal(profile, body) {
|
|
|
1646
2160
|
}
|
|
1647
2161
|
return null;
|
|
1648
2162
|
}
|
|
1649
|
-
// src/adyen-jwe.js
|
|
1650
|
-
var BASE64URL = /^[A-Za-z0-9_-]+$/;
|
|
1651
|
-
var ADYEN_WEB_JWE_FORMATS = /* @__PURE__ */ Object.freeze([
|
|
1652
|
-
/* @__PURE__ */ Object.freeze({ alg: "RSA-OAEP-256", enc: "A256GCM", iv: 16, tag: 22 }),
|
|
1653
|
-
/* @__PURE__ */ Object.freeze({ alg: "RSA-OAEP", enc: "A256CBC-HS512", iv: 22, tag: 43 })
|
|
1654
|
-
]);
|
|
1655
|
-
var ADYEN_WEB_CIPHERTEXT_MAX = 8192;
|
|
1656
|
-
var ADYEN_WEB_HEADER_MAX = 128;
|
|
1657
|
-
function adyenWebJweHeader(segment) {
|
|
1658
|
-
if (!BASE64URL.test(segment) || segment.length > ADYEN_WEB_HEADER_MAX)
|
|
1659
|
-
return null;
|
|
1660
|
-
let parsed;
|
|
1661
|
-
try {
|
|
1662
|
-
parsed = JSON.parse(new TextDecoder().decode(Uint8Array.from(atob(segment.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0))));
|
|
1663
|
-
}
|
|
1664
|
-
catch {
|
|
1665
|
-
return null;
|
|
1666
|
-
}
|
|
1667
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
1668
|
-
return null;
|
|
1669
|
-
return Object.keys(parsed).sort().join(",") === "alg,enc,version" && parsed.version === "1" ? parsed : null;
|
|
1670
|
-
}
|
|
1671
|
-
function isAdyenWebJweCompact(s) {
|
|
1672
|
-
if (typeof s !== "string" || s.length > ADYEN_WEB_HEADER_MAX + 342 + 22 + ADYEN_WEB_CIPHERTEXT_MAX + 43 + 4)
|
|
1673
|
-
return false;
|
|
1674
|
-
const parts = s.split(".");
|
|
1675
|
-
if (parts.length !== 5 || !parts.every((p) => p.length > 0 && BASE64URL.test(p)))
|
|
1676
|
-
return false;
|
|
1677
|
-
const [header, encryptedKey, iv, ciphertext, tag] = parts;
|
|
1678
|
-
if (encryptedKey.length !== 342 || ciphertext.length > ADYEN_WEB_CIPHERTEXT_MAX)
|
|
1679
|
-
return false;
|
|
1680
|
-
const parsed = adyenWebJweHeader(header);
|
|
1681
|
-
return parsed !== null && ADYEN_WEB_JWE_FORMATS.some((form) => parsed.alg === form.alg && parsed.enc === form.enc && iv.length === form.iv && tag.length === form.tag);
|
|
1682
|
-
}
|
|
1683
2163
|
// src/sha256.js
|
|
1684
2164
|
var K = /* @__PURE__ */ new Uint32Array([
|
|
1685
2165
|
1116352408,
|
|
@@ -1811,6 +2291,40 @@ function sha256Hex(input) {
|
|
|
1811
2291
|
}
|
|
1812
2292
|
return Array.from(H, (word) => word.toString(16).padStart(8, "0")).join("");
|
|
1813
2293
|
}
|
|
2294
|
+
// src/adyen-jwe.js
|
|
2295
|
+
var BASE64URL = /^[A-Za-z0-9_-]+$/;
|
|
2296
|
+
var ADYEN_WEB_JWE_FORMATS = /* @__PURE__ */ Object.freeze([
|
|
2297
|
+
/* @__PURE__ */ Object.freeze({ alg: "RSA-OAEP-256", enc: "A256GCM", iv: 16, tag: 22 }),
|
|
2298
|
+
/* @__PURE__ */ Object.freeze({ alg: "RSA-OAEP", enc: "A256CBC-HS512", iv: 22, tag: 43 })
|
|
2299
|
+
]);
|
|
2300
|
+
var ADYEN_WEB_CIPHERTEXT_MAX = 8192;
|
|
2301
|
+
var ADYEN_WEB_HEADER_MAX = 128;
|
|
2302
|
+
function adyenWebJweHeader(segment) {
|
|
2303
|
+
if (!BASE64URL.test(segment) || segment.length > ADYEN_WEB_HEADER_MAX)
|
|
2304
|
+
return null;
|
|
2305
|
+
let parsed;
|
|
2306
|
+
try {
|
|
2307
|
+
parsed = JSON.parse(new TextDecoder().decode(Uint8Array.from(atob(segment.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0))));
|
|
2308
|
+
}
|
|
2309
|
+
catch {
|
|
2310
|
+
return null;
|
|
2311
|
+
}
|
|
2312
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
2313
|
+
return null;
|
|
2314
|
+
return Object.keys(parsed).sort().join(",") === "alg,enc,version" && parsed.version === "1" ? parsed : null;
|
|
2315
|
+
}
|
|
2316
|
+
function isAdyenWebJweCompact(s) {
|
|
2317
|
+
if (typeof s !== "string" || s.length > ADYEN_WEB_HEADER_MAX + 342 + 22 + ADYEN_WEB_CIPHERTEXT_MAX + 43 + 4)
|
|
2318
|
+
return false;
|
|
2319
|
+
const parts = s.split(".");
|
|
2320
|
+
if (parts.length !== 5 || !parts.every((p) => p.length > 0 && BASE64URL.test(p)))
|
|
2321
|
+
return false;
|
|
2322
|
+
const [header, encryptedKey, iv, ciphertext, tag] = parts;
|
|
2323
|
+
if (encryptedKey.length !== 342 || ciphertext.length > ADYEN_WEB_CIPHERTEXT_MAX)
|
|
2324
|
+
return false;
|
|
2325
|
+
const parsed = adyenWebJweHeader(header);
|
|
2326
|
+
return parsed !== null && ADYEN_WEB_JWE_FORMATS.some((form) => parsed.alg === form.alg && parsed.enc === form.enc && iv.length === form.iv && tag.length === form.tag);
|
|
2327
|
+
}
|
|
1814
2328
|
// src/adyen-merchant-hosted.js
|
|
1815
2329
|
function matchProfileEndpoint(profile, rawUrl, method, opts) {
|
|
1816
2330
|
const p = trustedMerchantProfile(profile);
|
|
@@ -1949,7 +2463,7 @@ var PASS = /* @__PURE__ */ Object.freeze({ verdict: "pass" });
|
|
|
1949
2463
|
var PAUSE = /* @__PURE__ */ Object.freeze({ verdict: "pause" });
|
|
1950
2464
|
var abort = (reason) => Object.freeze({ verdict: "abort", reason });
|
|
1951
2465
|
function holderRefusal(profile, parsed) {
|
|
1952
|
-
const holder =
|
|
2466
|
+
const holder = profileAt(parsed, profile.holder);
|
|
1953
2467
|
if (!holder.found || !isPlainObject(holder.value)) {
|
|
1954
2468
|
return `the paused body carries no card object where ${profile.merchant}'s card request holds it`;
|
|
1955
2469
|
}
|
|
@@ -1967,6 +2481,8 @@ function holderRefusal(profile, parsed) {
|
|
|
1967
2481
|
function amountRefusal(profile, parsed) {
|
|
1968
2482
|
const priced = isPlainObject(profile.amountSource);
|
|
1969
2483
|
if (profile.amount === null || profile.amount === void 0) {
|
|
2484
|
+
if (!priced && typeof profile.amountPending === "string" && profile.amountPending !== "")
|
|
2485
|
+
return null;
|
|
1970
2486
|
return priced ? null : `${profile.merchant}'s card request names no amount, so it cannot be checked against the amount the cardholder approves`;
|
|
1971
2487
|
}
|
|
1972
2488
|
if (!Array.isArray(profile.amount)) {
|
|
@@ -1984,7 +2500,7 @@ function amountRefusal(profile, parsed) {
|
|
|
1984
2500
|
}
|
|
1985
2501
|
return `the amount where ${profile.merchant}'s card request holds it is not the amount Agentcard reviewed, so it cannot be checked`;
|
|
1986
2502
|
}
|
|
1987
|
-
const at =
|
|
2503
|
+
const at = profileAt(parsed, profile.amount);
|
|
1988
2504
|
if (!at.found) {
|
|
1989
2505
|
return priced ? null : `the paused body names no amount where ${profile.merchant}'s card request holds it, so it cannot be checked against the amount the cardholder approves`;
|
|
1990
2506
|
}
|
|
@@ -2002,7 +2518,7 @@ function classifyMerchantBody(profileId, body) {
|
|
|
2002
2518
|
const refusal = merchantBodyRefusal(profile.id, body);
|
|
2003
2519
|
if (refusal)
|
|
2004
2520
|
return abort(refusal);
|
|
2005
|
-
const parsed =
|
|
2521
|
+
const parsed = merchantProfileBody(profile.id, body);
|
|
2006
2522
|
const card = holderRefusal(profile, parsed) ?? amountRefusal(profile, parsed);
|
|
2007
2523
|
return card ? abort(card) : PAUSE;
|
|
2008
2524
|
}
|
|
@@ -2098,7 +2614,22 @@ function merchantBodyKeyPathSha256(body) {
|
|
|
2098
2614
|
parsed = JSON.parse(body);
|
|
2099
2615
|
}
|
|
2100
2616
|
catch {
|
|
2101
|
-
|
|
2617
|
+
parsed = void 0;
|
|
2618
|
+
}
|
|
2619
|
+
if (parsed === void 0) {
|
|
2620
|
+
const view = formBodyView(body);
|
|
2621
|
+
if (view === void 0)
|
|
2622
|
+
return null;
|
|
2623
|
+
parsed = Object.fromEntries(Object.entries(view).map(([name, value]) => {
|
|
2624
|
+
if (typeof value !== "string" || !/^[[{]/.test(value))
|
|
2625
|
+
return [name, value];
|
|
2626
|
+
try {
|
|
2627
|
+
return [name, JSON.parse(value)];
|
|
2628
|
+
}
|
|
2629
|
+
catch {
|
|
2630
|
+
return [name, value];
|
|
2631
|
+
}
|
|
2632
|
+
}));
|
|
2102
2633
|
}
|
|
2103
2634
|
const paths = /* @__PURE__ */ new Set();
|
|
2104
2635
|
keyPaths(parsed, paths);
|