@agent-cards/checkout 0.12.0 → 0.13.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.
- package/CHANGELOG.md +12 -0
- package/PREFLIGHT.md +12 -5
- package/README.md +2 -0
- package/dist/client.js +20 -1
- package/dist/lifecycle.js +6 -1
- package/dist/preflight-catalog.json +533 -8
- package/dist/preflight-playwright.js +143 -50
- package/dist/preflight-schemas.json +48 -7
- package/dist/preflight.generated.d.ts +11 -1
- package/dist/preflight.generated.js +82 -8
- package/examples/preflight/kernel-native/inventory.json +2 -2
- package/examples/preflight/kernel-profile.empty.json +1 -1
- package/examples/preflight/mollie-hosted.result.json +2 -2
- package/examples/preflight/stripe-script.direct.result.json +2 -2
- package/examples/preflight/stripe-script.result.json +2 -2
- package/package.json +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { normalizeCheckoutSignals, assessCheckoutSupport, } from './preflight.js';
|
|
1
|
+
import { normalizeCheckoutSignals, assessCheckoutSupport, cardEntryAttributable, } from './preflight.js';
|
|
2
2
|
const EVENTS = ['framenavigated', 'frameattached', 'framedetached'];
|
|
3
3
|
const DEADLINE = Symbol('checkout inspection deadline');
|
|
4
4
|
const DEFAULTS = { timeoutMs: 1_500, maxFrames: 64, maxSignals: 2_048, maxSnapshotBytes: 262_144 };
|
|
5
|
+
/** Frames at one nesting depth are read together. A remote CDP connection pays per round trip, not per frame. */
|
|
6
|
+
const CONCURRENCY = 8;
|
|
5
7
|
const encoder = new TextEncoder();
|
|
6
8
|
function bounded(value, fallback, maximum, minimum = 1) {
|
|
7
9
|
if (value === undefined)
|
|
@@ -27,6 +29,28 @@ async function beforeDeadline(operation, deadline) {
|
|
|
27
29
|
clearTimeout(timer);
|
|
28
30
|
}
|
|
29
31
|
}
|
|
32
|
+
function httpOrigin(url) {
|
|
33
|
+
if (!url || !/^https?:/i.test(url))
|
|
34
|
+
return null;
|
|
35
|
+
try {
|
|
36
|
+
return new URL(url).origin;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Run `worker` over `items` with bounded concurrency; results keep the input order. */
|
|
43
|
+
async function inBatches(items, size, worker) {
|
|
44
|
+
const results = new Array(items.length);
|
|
45
|
+
let next = 0;
|
|
46
|
+
await Promise.all(Array.from({ length: Math.min(size, items.length) }, async () => {
|
|
47
|
+
while (next < items.length) {
|
|
48
|
+
const index = next++;
|
|
49
|
+
results[index] = await worker(items[index]);
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
return results;
|
|
53
|
+
}
|
|
30
54
|
/** Read only frame, external asset and form locations; return reviewed rule IDs, never raw URLs. */
|
|
31
55
|
export async function collectCheckoutSignals(page, options = {}) {
|
|
32
56
|
const limits = {
|
|
@@ -41,6 +65,7 @@ export async function collectCheckoutSignals(page, options = {}) {
|
|
|
41
65
|
const registered = [];
|
|
42
66
|
let transientBytes = 0;
|
|
43
67
|
let truncated = false;
|
|
68
|
+
let stopped = false;
|
|
44
69
|
const changed = () => { reasons.add('navigation_changed'); };
|
|
45
70
|
const limit = (reason) => { reasons.add(reason); truncated = true; };
|
|
46
71
|
const append = (signal) => {
|
|
@@ -58,6 +83,24 @@ export async function collectCheckoutSignals(page, options = {}) {
|
|
|
58
83
|
signals.push(signal);
|
|
59
84
|
return true;
|
|
60
85
|
};
|
|
86
|
+
const stop = () => {
|
|
87
|
+
if (stopped)
|
|
88
|
+
return true;
|
|
89
|
+
if (performance.now() >= deadline) {
|
|
90
|
+
reasons.add('collection_timeout');
|
|
91
|
+
stopped = true;
|
|
92
|
+
}
|
|
93
|
+
else if (signals.length >= limits.maxSignals) {
|
|
94
|
+
limit('signal_limit');
|
|
95
|
+
stopped = true;
|
|
96
|
+
}
|
|
97
|
+
else if (truncated && reasons.has('snapshot_limit'))
|
|
98
|
+
stopped = true;
|
|
99
|
+
return stopped;
|
|
100
|
+
};
|
|
101
|
+
// Hostnames of visible nested card-entry frames outside the top document's origin. They stay
|
|
102
|
+
// local to this call; only the attribution verdict reaches the observation.
|
|
103
|
+
const cardEntryHosts = new Set();
|
|
61
104
|
try {
|
|
62
105
|
for (const event of EVENTS) {
|
|
63
106
|
page.on(event, changed);
|
|
@@ -73,29 +116,25 @@ export async function collectCheckoutSignals(page, options = {}) {
|
|
|
73
116
|
const ids = new Map(frames.map((frame, index) => [frame, index]));
|
|
74
117
|
if (frames.length > limits.maxFrames)
|
|
75
118
|
limit('frame_limit');
|
|
119
|
+
const topOrigin = frames.length ? httpOrigin(initialUrls.get(frames[0])) : null;
|
|
76
120
|
const visibility = new Map();
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
frame = frame.parentFrame();
|
|
83
|
-
}
|
|
84
|
-
return visited.size;
|
|
85
|
-
};
|
|
86
|
-
return depth(a) - depth(b);
|
|
87
|
-
});
|
|
88
|
-
for (const frame of ordered) {
|
|
89
|
-
if (performance.now() >= deadline) {
|
|
90
|
-
reasons.add('collection_timeout');
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
if (signals.length >= limits.maxSignals) {
|
|
94
|
-
limit('signal_limit');
|
|
95
|
-
break;
|
|
121
|
+
const depth = (frame) => {
|
|
122
|
+
const visited = new Set();
|
|
123
|
+
while (frame.parentFrame() && !visited.has(frame)) {
|
|
124
|
+
visited.add(frame);
|
|
125
|
+
frame = frame.parentFrame();
|
|
96
126
|
}
|
|
97
|
-
|
|
98
|
-
|
|
127
|
+
return visited.size;
|
|
128
|
+
};
|
|
129
|
+
// Parents settle before their children so visibility can propagate; siblings share round trips.
|
|
130
|
+
const levels = new Map();
|
|
131
|
+
for (const frame of frames.slice(0, limits.maxFrames)) {
|
|
132
|
+
const level = depth(frame);
|
|
133
|
+
levels.set(level, [...(levels.get(level) ?? []), frame]);
|
|
134
|
+
}
|
|
135
|
+
const readFrame = async (frame) => {
|
|
136
|
+
if (stop())
|
|
137
|
+
return null;
|
|
99
138
|
const parent = frame.parentFrame();
|
|
100
139
|
const frame_id = ids.get(frame);
|
|
101
140
|
const parent_frame_id = parent ? ids.get(parent) ?? null : null;
|
|
@@ -103,8 +142,9 @@ export async function collectCheckoutSignals(page, options = {}) {
|
|
|
103
142
|
try {
|
|
104
143
|
if (frame.isDetached()) {
|
|
105
144
|
reasons.add('frame_unavailable');
|
|
106
|
-
|
|
145
|
+
return null;
|
|
107
146
|
}
|
|
147
|
+
let released;
|
|
108
148
|
if (parent) {
|
|
109
149
|
if (!ids.has(parent))
|
|
110
150
|
reasons.add('frame_unavailable');
|
|
@@ -139,8 +179,9 @@ export async function collectCheckoutSignals(page, options = {}) {
|
|
|
139
179
|
visible = visible === false || ownVisibility === false ? false : visible === null || ownVisibility === null ? null : true;
|
|
140
180
|
}
|
|
141
181
|
finally {
|
|
142
|
-
// Disposal only releases our automation handle
|
|
143
|
-
|
|
182
|
+
// Disposal only releases our automation handle and overlaps the document read below.
|
|
183
|
+
// A stalled disposal still cannot extend the inspection deadline.
|
|
184
|
+
released = beforeDeadline(() => element.dispose(), deadline).then(() => undefined, error => error);
|
|
144
185
|
}
|
|
145
186
|
}
|
|
146
187
|
visibility.set(frame, visible);
|
|
@@ -149,6 +190,19 @@ export async function collectCheckoutSignals(page, options = {}) {
|
|
|
149
190
|
let bytes = 0;
|
|
150
191
|
let signalLimit = false;
|
|
151
192
|
let byteLimit = false;
|
|
193
|
+
const shown = (element) => {
|
|
194
|
+
if (!element.isConnected)
|
|
195
|
+
return false;
|
|
196
|
+
for (let current = element; current;) {
|
|
197
|
+
const style = getComputedStyle(current);
|
|
198
|
+
if (style.display === 'none' || style.visibility === 'hidden' || style.visibility === 'collapse' || style.opacity === '0')
|
|
199
|
+
return false;
|
|
200
|
+
const root = current.getRootNode();
|
|
201
|
+
current = current.parentElement ?? (root instanceof ShadowRoot ? root.host : null);
|
|
202
|
+
}
|
|
203
|
+
const rect = element.getBoundingClientRect();
|
|
204
|
+
return rect.width > 0 && rect.height > 0;
|
|
205
|
+
};
|
|
152
206
|
const elements = document.querySelectorAll('script[src], iframe[src], form[action]');
|
|
153
207
|
for (let index = 0; index < elements.length; index++) {
|
|
154
208
|
if (index >= remaining) {
|
|
@@ -176,31 +230,63 @@ export async function collectCheckoutSignals(page, options = {}) {
|
|
|
176
230
|
byteLimit = true;
|
|
177
231
|
break;
|
|
178
232
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
233
|
+
collected.push({ kind, url, visible: kind === 'script' ? null : shown(element) });
|
|
234
|
+
}
|
|
235
|
+
// Card-entry detection reads field metadata only: the autocomplete token or the
|
|
236
|
+
// control's name and id. Values, labels and page text are never read.
|
|
237
|
+
let cardEntry = false;
|
|
238
|
+
const inputs = document.querySelectorAll('input');
|
|
239
|
+
for (let index = 0; index < inputs.length && index < 256 && !cardEntry; index++) {
|
|
240
|
+
const input = inputs[index];
|
|
241
|
+
const type = (input.getAttribute('type') || 'text').toLowerCase();
|
|
242
|
+
if (['hidden', 'submit', 'button', 'checkbox', 'radio', 'image', 'reset', 'file', 'range', 'color'].includes(type))
|
|
243
|
+
continue;
|
|
244
|
+
const autocomplete = (input.getAttribute('autocomplete') || '').trim().toLowerCase();
|
|
245
|
+
const label = `${input.getAttribute('name') || ''} ${input.getAttribute('id') || ''}`.toLowerCase().replace(/[^a-z0-9]+/g, '');
|
|
246
|
+
const cardField = /^cc-(?:number|csc|exp|exp-month|exp-year)$/.test(autocomplete)
|
|
247
|
+
|| !label.includes('gift') && /cardnumber|cardnum|cardno|ccnumber|ccnum|ccno|cvv|cvc|csc|securitycode|cardcode/.test(label);
|
|
248
|
+
if (cardField && shown(input))
|
|
249
|
+
cardEntry = true;
|
|
193
250
|
}
|
|
194
251
|
const documentUrl = document.URL;
|
|
195
252
|
if (documentUrl.length * 3 > byteBudget)
|
|
196
253
|
byteLimit = true;
|
|
197
|
-
return { documentUrl: byteLimit && documentUrl.length * 3 > byteBudget ? '' : documentUrl, collected, signalLimit, byteLimit };
|
|
254
|
+
return { documentUrl: byteLimit && documentUrl.length * 3 > byteBudget ? '' : documentUrl, collected, signalLimit, byteLimit, cardEntry };
|
|
198
255
|
}, { remaining: Math.max(0, limits.maxSignals - signals.length - 1), byteBudget: Math.max(0, limits.maxSnapshotBytes - transientBytes) }), deadline);
|
|
256
|
+
const releaseError = released ? await released : undefined;
|
|
257
|
+
if (releaseError !== undefined)
|
|
258
|
+
throw releaseError;
|
|
199
259
|
if (frame.isDetached())
|
|
200
260
|
reasons.add('frame_unavailable');
|
|
201
|
-
|
|
261
|
+
// Committed URLs are compared with committed URLs. The document URL is compared only for a
|
|
262
|
+
// frame that committed an http(s) document: a script-created frame written through
|
|
263
|
+
// document.open() inherits the merchant URL without navigating anywhere.
|
|
264
|
+
const initial = initialUrls.get(frame) ?? '';
|
|
265
|
+
if (frame.url() !== initial || (/^https?:/i.test(initial) && data.documentUrl !== '' && data.documentUrl !== initial))
|
|
202
266
|
reasons.add('navigation_changed');
|
|
203
|
-
|
|
267
|
+
return { frame_id, parent_frame_id, nested: parent !== null, visible, data };
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
if (error === DEADLINE) {
|
|
271
|
+
reasons.add('collection_timeout');
|
|
272
|
+
stopped = true;
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
// Browser errors can include complete merchant URLs. Return a bounded reason instead.
|
|
276
|
+
reasons.add('frame_unavailable');
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
for (const level of [...levels.keys()].sort((a, b) => a - b)) {
|
|
281
|
+
if (stop())
|
|
282
|
+
break;
|
|
283
|
+
const outcomes = await inBatches(levels.get(level), CONCURRENCY, readFrame);
|
|
284
|
+
// Append in frame order so identical pages produce identical observations.
|
|
285
|
+
for (const outcome of outcomes) {
|
|
286
|
+
if (!outcome)
|
|
287
|
+
continue;
|
|
288
|
+
const { data, frame_id, parent_frame_id, nested, visible } = outcome;
|
|
289
|
+
append({ kind: nested ? 'frame' : 'document', url: data.documentUrl, frame_id, parent_frame_id, visible });
|
|
204
290
|
for (const signal of data.collected) {
|
|
205
291
|
if (!append({ ...signal, frame_id, parent_frame_id, visible: visible === false ? false : signal.kind === 'script' ? visible : visible === null ? null : signal.visible }))
|
|
206
292
|
break;
|
|
@@ -209,14 +295,11 @@ export async function collectCheckoutSignals(page, options = {}) {
|
|
|
209
295
|
limit('signal_limit');
|
|
210
296
|
if (data.byteLimit)
|
|
211
297
|
limit('snapshot_limit');
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
break;
|
|
298
|
+
if (nested && visible === true && data.cardEntry === true) {
|
|
299
|
+
const origin = httpOrigin(data.documentUrl);
|
|
300
|
+
if (origin && origin !== topOrigin)
|
|
301
|
+
cardEntryHosts.add(new URL(origin).hostname);
|
|
217
302
|
}
|
|
218
|
-
// Browser errors can include complete merchant URLs. Return a bounded reason instead.
|
|
219
|
-
reasons.add('frame_unavailable');
|
|
220
303
|
}
|
|
221
304
|
}
|
|
222
305
|
if (performance.now() >= deadline)
|
|
@@ -239,7 +322,17 @@ export async function collectCheckoutSignals(page, options = {}) {
|
|
|
239
322
|
}
|
|
240
323
|
}
|
|
241
324
|
}
|
|
242
|
-
|
|
325
|
+
const observation = () => ({ complete: reasons.size === 0, truncated, reasons: [...reasons] });
|
|
326
|
+
let snapshot = normalizeCheckoutSignals({ signals, observation: observation() });
|
|
327
|
+
// Visible card controls on a site no evidenced processor owns mean the card form belongs to
|
|
328
|
+
// something the catalog has not reviewed. The observation is then incomplete, so a co-loaded
|
|
329
|
+
// SDK script stays a candidate instead of becoming the selected processor. A snapshot past the
|
|
330
|
+
// byte budget is already incomplete and is bounded below instead.
|
|
331
|
+
if (encoder.encode(JSON.stringify(snapshot)).length <= limits.maxSnapshotBytes
|
|
332
|
+
&& [...cardEntryHosts].some(host => !cardEntryAttributable(snapshot, host))) {
|
|
333
|
+
reasons.add('card_entry_unrecognized');
|
|
334
|
+
snapshot = normalizeCheckoutSignals({ signals, observation: observation() });
|
|
335
|
+
}
|
|
243
336
|
if (encoder.encode(JSON.stringify(snapshot)).length > limits.maxSnapshotBytes) {
|
|
244
337
|
limit('snapshot_limit');
|
|
245
338
|
const boundedSnapshot = normalizeCheckoutSignals({ signals: [], observation: { complete: false, truncated, reasons: [...reasons] } });
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"url": {
|
|
58
58
|
"type": "string",
|
|
59
|
-
"maxLength":
|
|
59
|
+
"maxLength": 65536
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -90,6 +90,7 @@
|
|
|
90
90
|
"navigation_changed",
|
|
91
91
|
"invalid_observation",
|
|
92
92
|
"invalid_url",
|
|
93
|
+
"card_entry_unrecognized",
|
|
93
94
|
"collection_incomplete"
|
|
94
95
|
]
|
|
95
96
|
}
|
|
@@ -162,7 +163,7 @@
|
|
|
162
163
|
"pattern": "^[A-Za-z0-9][A-Za-z0-9._+-]{0,119}$"
|
|
163
164
|
},
|
|
164
165
|
"catalog_version": {
|
|
165
|
-
"const": "2026-09-
|
|
166
|
+
"const": "2026-09-15.1"
|
|
166
167
|
},
|
|
167
168
|
"integration": {
|
|
168
169
|
"type": "object",
|
|
@@ -358,7 +359,7 @@
|
|
|
358
359
|
"const": 1
|
|
359
360
|
},
|
|
360
361
|
"catalog_version": {
|
|
361
|
-
"const": "2026-09-
|
|
362
|
+
"const": "2026-09-15.1"
|
|
362
363
|
},
|
|
363
364
|
"signals": {
|
|
364
365
|
"type": "array",
|
|
@@ -450,10 +451,29 @@
|
|
|
450
451
|
"bambora-na-fields-1-0-5-39",
|
|
451
452
|
"global-payments-js-4-1-3",
|
|
452
453
|
"global-payments-js-v1-alias",
|
|
454
|
+
"tranzila-terminal-hosted-page",
|
|
455
|
+
"square-card-element-frame",
|
|
456
|
+
"authorize-net-sim-hosted-form",
|
|
457
|
+
"worldpay-hosted-payment-page",
|
|
458
|
+
"rapyd-hosted-checkout-page",
|
|
459
|
+
"dlocal-smart-fields-parent",
|
|
460
|
+
"dlocal-smart-card-fields",
|
|
461
|
+
"mercado-pago-secure-fields-frame",
|
|
462
|
+
"mercado-pago-checkout-pro-card-form",
|
|
453
463
|
"square-web-sdk-sandbox",
|
|
454
464
|
"paysafe-js-test",
|
|
455
465
|
"worldpay-checkout-sdk-try",
|
|
456
|
-
"authorize-net-accept-test"
|
|
466
|
+
"authorize-net-accept-test",
|
|
467
|
+
"tranzila-terminal-hosted-page-directng",
|
|
468
|
+
"square-card-element-frame-sandbox",
|
|
469
|
+
"authorize-net-sim-hosted-form-secure2",
|
|
470
|
+
"worldpay-hosted-payment-page-test",
|
|
471
|
+
"rapyd-hosted-checkout-page-sandbox",
|
|
472
|
+
"adyen-secured-fields-live-us",
|
|
473
|
+
"adyen-secured-fields-live-au",
|
|
474
|
+
"adyen-secured-fields-live-apse",
|
|
475
|
+
"adyen-secured-fields-live-in",
|
|
476
|
+
"adyen-secured-fields-live-nea"
|
|
457
477
|
]
|
|
458
478
|
},
|
|
459
479
|
"kind": {
|
|
@@ -520,6 +540,7 @@
|
|
|
520
540
|
"navigation_changed",
|
|
521
541
|
"invalid_observation",
|
|
522
542
|
"invalid_url",
|
|
543
|
+
"card_entry_unrecognized",
|
|
523
544
|
"collection_incomplete"
|
|
524
545
|
]
|
|
525
546
|
}
|
|
@@ -550,7 +571,7 @@
|
|
|
550
571
|
"pattern": "^[A-Za-z0-9][A-Za-z0-9._+-]{0,119}$"
|
|
551
572
|
},
|
|
552
573
|
"catalog_version": {
|
|
553
|
-
"const": "2026-09-
|
|
574
|
+
"const": "2026-09-15.1"
|
|
554
575
|
},
|
|
555
576
|
"integration": {
|
|
556
577
|
"type": "object",
|
|
@@ -753,7 +774,7 @@
|
|
|
753
774
|
"const": 1
|
|
754
775
|
},
|
|
755
776
|
"catalog_version": {
|
|
756
|
-
"const": "2026-09-
|
|
777
|
+
"const": "2026-09-15.1"
|
|
757
778
|
},
|
|
758
779
|
"integration": {
|
|
759
780
|
"type": "object",
|
|
@@ -947,10 +968,29 @@
|
|
|
947
968
|
"bambora-na-fields-1-0-5-39",
|
|
948
969
|
"global-payments-js-4-1-3",
|
|
949
970
|
"global-payments-js-v1-alias",
|
|
971
|
+
"tranzila-terminal-hosted-page",
|
|
972
|
+
"square-card-element-frame",
|
|
973
|
+
"authorize-net-sim-hosted-form",
|
|
974
|
+
"worldpay-hosted-payment-page",
|
|
975
|
+
"rapyd-hosted-checkout-page",
|
|
976
|
+
"dlocal-smart-fields-parent",
|
|
977
|
+
"dlocal-smart-card-fields",
|
|
978
|
+
"mercado-pago-secure-fields-frame",
|
|
979
|
+
"mercado-pago-checkout-pro-card-form",
|
|
950
980
|
"square-web-sdk-sandbox",
|
|
951
981
|
"paysafe-js-test",
|
|
952
982
|
"worldpay-checkout-sdk-try",
|
|
953
|
-
"authorize-net-accept-test"
|
|
983
|
+
"authorize-net-accept-test",
|
|
984
|
+
"tranzila-terminal-hosted-page-directng",
|
|
985
|
+
"square-card-element-frame-sandbox",
|
|
986
|
+
"authorize-net-sim-hosted-form-secure2",
|
|
987
|
+
"worldpay-hosted-payment-page-test",
|
|
988
|
+
"rapyd-hosted-checkout-page-sandbox",
|
|
989
|
+
"adyen-secured-fields-live-us",
|
|
990
|
+
"adyen-secured-fields-live-au",
|
|
991
|
+
"adyen-secured-fields-live-apse",
|
|
992
|
+
"adyen-secured-fields-live-in",
|
|
993
|
+
"adyen-secured-fields-live-nea"
|
|
954
994
|
]
|
|
955
995
|
},
|
|
956
996
|
"kind": {
|
|
@@ -1046,6 +1086,7 @@
|
|
|
1046
1086
|
"navigation_changed",
|
|
1047
1087
|
"invalid_observation",
|
|
1048
1088
|
"invalid_url",
|
|
1089
|
+
"card_entry_unrecognized",
|
|
1049
1090
|
"collection_incomplete"
|
|
1050
1091
|
]
|
|
1051
1092
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/** Advisory page discovery. These rules never admit a payment destination. */
|
|
2
2
|
export declare const CHECKOUT_PREFLIGHT_VERSION = "1";
|
|
3
|
-
export declare const CHECKOUT_PREFLIGHT_CATALOG_VERSION = "2026-09-
|
|
3
|
+
export declare const CHECKOUT_PREFLIGHT_CATALOG_VERSION = "2026-09-15.1";
|
|
4
4
|
export declare const CHECKOUT_PREFLIGHT_LIMITS: Readonly<{
|
|
5
5
|
frames: 64;
|
|
6
6
|
signals: 2048;
|
|
7
7
|
snapshot_bytes: 262144;
|
|
8
8
|
url_length: 4096;
|
|
9
|
+
raw_url_length: 65536;
|
|
9
10
|
}>;
|
|
10
11
|
export type CheckoutSignalKind = 'document' | 'frame' | 'script' | 'iframe' | 'form';
|
|
11
12
|
export type CheckoutScenario = 'one_time' | 'save_card' | 'subscription_initial' | 'subscription_renewal';
|
|
@@ -126,6 +127,7 @@ export declare function getCheckoutPreflightCatalog(): {
|
|
|
126
127
|
signals: 2048;
|
|
127
128
|
snapshot_bytes: 262144;
|
|
128
129
|
url_length: 4096;
|
|
130
|
+
raw_url_length: 65536;
|
|
129
131
|
}>;
|
|
130
132
|
known_psps: {
|
|
131
133
|
psp: string;
|
|
@@ -1347,6 +1349,14 @@ export declare function getCheckoutPreflightCatalog(): {
|
|
|
1347
1349
|
};
|
|
1348
1350
|
observation_reason_codes: string[];
|
|
1349
1351
|
};
|
|
1352
|
+
/**
|
|
1353
|
+
* Whether a visible card-entry frame on `hostname` can belong to a processor already evidenced in the
|
|
1354
|
+
* snapshot. A collector reports `card_entry_unrecognized` when this is false, so co-loaded SDK scripts
|
|
1355
|
+
* cannot be presented as the processor behind card controls that no reviewed fingerprint explains, and
|
|
1356
|
+
* a card form with no processor evidence at all is reported rather than passing as an empty page.
|
|
1357
|
+
* The hostname stays local to the caller; nothing here enters evidence.
|
|
1358
|
+
*/
|
|
1359
|
+
export declare function cardEntryAttributable(input: CheckoutSignalSnapshot | unknown, hostname: unknown): boolean;
|
|
1350
1360
|
/** Classify a sanitized snapshot. No browser, HTTP, or payment operations occur here. */
|
|
1351
1361
|
export declare function assessCheckoutSupport(input: CheckoutSignalSnapshot | unknown, options?: CheckoutPreflightOptions): CheckoutSupportResult;
|
|
1352
1362
|
/** Machine-readable transport shapes; semantic compatibility is checked by the classifier. */
|