@agent-cards/checkout 0.18.0 → 0.21.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/README.md +6 -669
- package/cdp.d.ts +1 -0
- package/cdp.js +2 -0
- package/index.d.ts +1 -0
- package/index.js +2 -0
- package/package.json +33 -33
- package/playwright.d.ts +1 -0
- package/playwright.js +2 -0
- package/preflight.d.ts +1 -0
- package/preflight.js +2 -0
- package/CHANGELOG.md +0 -124
- package/PREFLIGHT.md +0 -308
- package/dist/adyen.generated.d.ts +0 -24
- package/dist/adyen.generated.js +0 -64
- package/dist/attachment.d.ts +0 -11
- package/dist/attachment.js +0 -50
- package/dist/braintree.d.ts +0 -2
- package/dist/braintree.generated.d.ts +0 -10
- package/dist/braintree.generated.js +0 -302
- package/dist/braintree.js +0 -2
- package/dist/builtin-registry.generated.d.ts +0 -2
- package/dist/builtin-registry.generated.js +0 -1
- package/dist/card-fields.generated.d.ts +0 -3
- package/dist/card-fields.generated.js +0 -46
- package/dist/cdp.d.ts +0 -189
- package/dist/cdp.js +0 -2194
- package/dist/checkout-com.generated.d.ts +0 -4
- package/dist/checkout-com.generated.js +0 -183
- package/dist/client.d.ts +0 -618
- package/dist/client.js +0 -1251
- package/dist/hosted-form.d.ts +0 -44
- package/dist/hosted-form.js +0 -78
- package/dist/index.d.ts +0 -13
- package/dist/index.js +0 -6
- package/dist/lifecycle.d.ts +0 -165
- package/dist/lifecycle.js +0 -370
- package/dist/mercado-checkout.d.ts +0 -20
- package/dist/mercado-checkout.generated.d.ts +0 -52
- package/dist/mercado-checkout.generated.js +0 -198
- package/dist/mercado-checkout.js +0 -108
- package/dist/owned-shop.generated.d.ts +0 -24
- package/dist/owned-shop.generated.js +0 -108
- package/dist/paysafe.generated.d.ts +0 -12
- package/dist/paysafe.generated.js +0 -87
- package/dist/playwright.d.ts +0 -3
- package/dist/playwright.js +0 -3
- package/dist/preflight-capabilities.generated.d.ts +0 -1253
- package/dist/preflight-capabilities.generated.js +0 -1929
- package/dist/preflight-catalog.json +0 -4595
- package/dist/preflight-playwright.d.ts +0 -34
- package/dist/preflight-playwright.js +0 -355
- package/dist/preflight-schemas.json +0 -1110
- package/dist/preflight.d.ts +0 -1
- package/dist/preflight.generated.d.ts +0 -1965
- package/dist/preflight.generated.js +0 -556
- package/dist/preflight.js +0 -2
- package/dist/preparation.d.ts +0 -31
- package/dist/preparation.js +0 -164
- package/dist/prepared-processor.d.ts +0 -10
- package/dist/prepared-processor.js +0 -122
- package/dist/recurly.generated.d.ts +0 -1
- package/dist/recurly.generated.js +0 -87
- package/dist/registry.d.ts +0 -76
- package/dist/registry.js +0 -296
- package/dist/spreedly.generated.d.ts +0 -10
- package/dist/spreedly.generated.js +0 -332
- package/dist/stripe-checkout.d.ts +0 -81
- package/dist/stripe-checkout.generated.d.ts +0 -82
- package/dist/stripe-checkout.generated.js +0 -1004
- package/dist/stripe-checkout.js +0 -140
- package/dist/substitute.d.ts +0 -38
- package/dist/substitute.js +0 -23
- package/dist/substitutions.generated.d.ts +0 -10
- package/dist/substitutions.generated.js +0 -66
- package/examples/existing-browser.mjs +0 -63
- package/examples/preflight/classify-direct.mjs +0 -21
- package/examples/preflight/classify-kernel.mjs +0 -30
- package/examples/preflight/inspect-browser.mjs +0 -44
- package/examples/preflight/kernel-native/README.md +0 -112
- package/examples/preflight/kernel-native/documented-adapters.json +0 -113
- package/examples/preflight/kernel-native/inventory.json +0 -233
- package/examples/preflight/kernel-native/qualification.mjs +0 -182
- package/examples/preflight/kernel-profile.empty.json +0 -11
- package/examples/preflight/mollie-hosted.observations.json +0 -23
- package/examples/preflight/mollie-hosted.result.json +0 -103
- package/examples/preflight/stripe-script.direct.result.json +0 -92
- package/examples/preflight/stripe-script.observations.json +0 -16
- package/examples/preflight/stripe-script.result.json +0 -87
package/dist/adyen.generated.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
export const ADYEN_ENCRYPTED_CARD_FIELDS = ['encryptedCardNumber', 'encryptedExpiryMonth', 'encryptedExpiryYear', 'encryptedSecurityCode'];
|
|
2
|
-
const SESSIONS_PAYMENTS = /^\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]{1,256}\/payments$/;
|
|
3
|
-
const TEST_HOST = /^checkoutshopper-test\.adyen\.com$/;
|
|
4
|
-
const LIVE_HOST = /^(?:checkoutshopper-live(?:-[a-z]+)?\.adyen\.com|(?:[a-z0-9-]+\.)*adyenpayments\.com)$/;
|
|
5
|
-
const CLIENT_KEY = /^(test|live)_[A-Za-z0-9]{32}$/;
|
|
6
|
-
const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
7
|
-
const text = (value, max = 4096) => typeof value === 'string' && value.length > 0 && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
|
|
8
|
-
/** Which preparation environment a hostname belongs to: Adyen's test host, one of its live host families, or neither. */
|
|
9
|
-
export function adyenPreparationEnvironment(hostname) {
|
|
10
|
-
const host = hostname.toLowerCase();
|
|
11
|
-
if (TEST_HOST.test(host))
|
|
12
|
-
return 'sandbox';
|
|
13
|
-
if (LIVE_HOST.test(host))
|
|
14
|
-
return 'production';
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
/** The URL a preparation advertises for the approved host family: every registry recognizes it, and it is never a real session. */
|
|
18
|
-
export function adyenPreparationUrl(environment) {
|
|
19
|
-
return `https://checkoutshopper-${environment === 'production' ? 'live' : 'test'}.adyen.com/checkoutshopper/v1/sessions/prepared/payments`;
|
|
20
|
-
}
|
|
21
|
-
/** A Sessions payments URL on the approved host family whose only query is a client key of that same environment. */
|
|
22
|
-
export function isPreparedAdyenUrl(rawUrl, environment) {
|
|
23
|
-
let url;
|
|
24
|
-
try {
|
|
25
|
-
url = new URL(rawUrl);
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
if (url.protocol !== 'https:' || url.port || url.username || url.password || url.hash)
|
|
31
|
-
return false;
|
|
32
|
-
if (adyenPreparationEnvironment(url.hostname) !== environment || !SESSIONS_PAYMENTS.test(url.pathname))
|
|
33
|
-
return false;
|
|
34
|
-
const names = [...url.searchParams.keys()];
|
|
35
|
-
if (names.length !== 1 || names[0] !== 'clientKey')
|
|
36
|
-
return false;
|
|
37
|
-
const key = url.searchParams.get('clientKey') ?? '';
|
|
38
|
-
const match = CLIENT_KEY.exec(key);
|
|
39
|
-
return !!match && match[0] === key && (match[1] === 'live') === (environment === 'production');
|
|
40
|
-
}
|
|
41
|
-
/** A fresh scheme card carrying the four encrypted fields the device replaces; stored cards, single-blob encryption and store requests never consume consent. */
|
|
42
|
-
export function isPreparedAdyenBody(body) {
|
|
43
|
-
if (typeof body !== 'string' || !body || body.length > 65536)
|
|
44
|
-
return false;
|
|
45
|
-
let parsed;
|
|
46
|
-
try {
|
|
47
|
-
parsed = JSON.parse(body);
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
if (!record(parsed) || parsed.storePaymentMethod === true)
|
|
53
|
-
return false;
|
|
54
|
-
const method = parsed.paymentMethod;
|
|
55
|
-
if (!record(method) || method.type !== 'scheme')
|
|
56
|
-
return false;
|
|
57
|
-
if ('storedPaymentMethodId' in method || 'encryptedCard' in method || 'recurringDetailReference' in method)
|
|
58
|
-
return false;
|
|
59
|
-
return ADYEN_ENCRYPTED_CARD_FIELDS.every(field => text(method[field]));
|
|
60
|
-
}
|
|
61
|
-
/** Processor identity, environment and request shape are part of the device's prior consent. */
|
|
62
|
-
export function isPreparedAdyenRequest(rawUrl, method, body, environment) {
|
|
63
|
-
return method.toUpperCase() === 'POST' && isPreparedAdyenUrl(rawUrl, environment) && isPreparedAdyenBody(body);
|
|
64
|
-
}
|
package/dist/attachment.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/** An attachment failure has no payment or browser credentials in its message. */
|
|
2
|
-
export declare class CheckoutAttachmentError extends Error {
|
|
3
|
-
readonly reason: 'timeout' | 'closed' | 'unavailable';
|
|
4
|
-
readonly code = "checkout_attachment_failed";
|
|
5
|
-
constructor(reason: 'timeout' | 'closed' | 'unavailable');
|
|
6
|
-
}
|
|
7
|
-
/** Preserve a known setup cause without exposing raw transport errors. */
|
|
8
|
-
export declare function attachmentFailure(error: unknown): CheckoutAttachmentError;
|
|
9
|
-
export declare function attachmentDeadline(value: number | undefined): number;
|
|
10
|
-
/** Stop waiting, then reject every late continuation before its next command. */
|
|
11
|
-
export declare function withinAttachmentDeadline(arm: (assertActive: () => void) => Promise<void>, timeoutMs: number, signal: AbortSignal): Promise<void>;
|
package/dist/attachment.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/** An attachment failure has no payment or browser credentials in its message. */
|
|
2
|
-
export class CheckoutAttachmentError extends Error {
|
|
3
|
-
reason;
|
|
4
|
-
code = 'checkout_attachment_failed';
|
|
5
|
-
constructor(reason) {
|
|
6
|
-
super('Could not attach checkout interception. Close this checkout page and retry in a fresh browser context.');
|
|
7
|
-
this.reason = reason;
|
|
8
|
-
this.name = 'CheckoutAttachmentError';
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
/** Preserve a known setup cause without exposing raw transport errors. */
|
|
12
|
-
export function attachmentFailure(error) {
|
|
13
|
-
return error instanceof CheckoutAttachmentError ? error : new CheckoutAttachmentError('unavailable');
|
|
14
|
-
}
|
|
15
|
-
export function attachmentDeadline(value) {
|
|
16
|
-
if (value === undefined)
|
|
17
|
-
return 30_000;
|
|
18
|
-
if (!Number.isSafeInteger(value) || value <= 0 || value > 300_000) {
|
|
19
|
-
throw new Error('attachmentTimeoutMs must be an integer between 1 and 300000.');
|
|
20
|
-
}
|
|
21
|
-
return value;
|
|
22
|
-
}
|
|
23
|
-
/** Stop waiting, then reject every late continuation before its next command. */
|
|
24
|
-
export async function withinAttachmentDeadline(arm, timeoutMs, signal) {
|
|
25
|
-
let stopped;
|
|
26
|
-
let timer;
|
|
27
|
-
let onAbort = () => { };
|
|
28
|
-
const assertActive = () => { if (stopped)
|
|
29
|
-
throw stopped; };
|
|
30
|
-
const deadline = new Promise((_resolve, reject) => {
|
|
31
|
-
const stop = (failure) => { stopped ??= failure; reject(stopped); };
|
|
32
|
-
onAbort = () => stop(attachmentFailure(signal.reason));
|
|
33
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
34
|
-
if (signal.aborted)
|
|
35
|
-
onAbort();
|
|
36
|
-
else
|
|
37
|
-
timer = setTimeout(() => stop(new CheckoutAttachmentError('timeout')), timeoutMs);
|
|
38
|
-
});
|
|
39
|
-
try {
|
|
40
|
-
await Promise.race([deadline, Promise.resolve().then(() => { assertActive(); return arm(assertActive); })]);
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
stopped ??= attachmentFailure(error);
|
|
44
|
-
throw stopped;
|
|
45
|
-
}
|
|
46
|
-
finally {
|
|
47
|
-
clearTimeout(timer);
|
|
48
|
-
signal.removeEventListener('abort', onAbort);
|
|
49
|
-
}
|
|
50
|
-
}
|
package/dist/braintree.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/** Classify native Braintree GraphQL requests without changing their bytes. */
|
|
2
|
-
export type BraintreeEnvironment = 'production' | 'sandbox';
|
|
3
|
-
export type BraintreeRequestKind = 'configuration' | 'tokenization' | 'invalid';
|
|
4
|
-
export declare function braintreeEnvironment(url: string): BraintreeEnvironment | undefined;
|
|
5
|
-
/** Bounded duplicate-aware JSON for fresh-card preparation bodies. */
|
|
6
|
-
export declare function readTokenizationJson(body: string | null): Record<string, unknown> | null;
|
|
7
|
-
/** Undefined belongs to another processor; invalid Braintree requests stay blocked. */
|
|
8
|
-
export declare function classifyBraintreeRequest(url: string, method: string, body: string | null): BraintreeRequestKind | undefined;
|
|
9
|
-
/** Preparation accepts guest tokenization only; ordinary interception stays broader. */
|
|
10
|
-
export declare function isPreparedBraintreeRequest(body: string | null): boolean;
|
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
const PRODUCTION = 'https://payments.braintree-api.com/graphql';
|
|
2
|
-
const SANDBOX = 'https://payments.sandbox.braintree-api.com/graphql';
|
|
3
|
-
const MAX_BODY_LENGTH = 128 * 1024;
|
|
4
|
-
const MAX_DEPTH = 32;
|
|
5
|
-
const MAX_NODES = 10_000;
|
|
6
|
-
export function braintreeEnvironment(url) {
|
|
7
|
-
return url === PRODUCTION ? 'production' : url === SANDBOX ? 'sandbox' : undefined;
|
|
8
|
-
}
|
|
9
|
-
function invalid() { throw new Error('invalid_braintree_request'); }
|
|
10
|
-
function record(value) {
|
|
11
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
12
|
-
}
|
|
13
|
-
/** JSON.parse discards duplicate keys, so validate keys before using any value. */
|
|
14
|
-
class JsonReader {
|
|
15
|
-
text;
|
|
16
|
-
at = 0;
|
|
17
|
-
nodes = 0;
|
|
18
|
-
constructor(text) {
|
|
19
|
-
this.text = text;
|
|
20
|
-
}
|
|
21
|
-
read() {
|
|
22
|
-
const value = this.value(0);
|
|
23
|
-
this.space();
|
|
24
|
-
if (this.at !== this.text.length)
|
|
25
|
-
invalid();
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
space() {
|
|
29
|
-
while (' \t\r\n'.includes(this.text[this.at] ?? '\0'))
|
|
30
|
-
this.at++;
|
|
31
|
-
}
|
|
32
|
-
take(expected) {
|
|
33
|
-
this.space();
|
|
34
|
-
if (this.text[this.at++] !== expected)
|
|
35
|
-
invalid();
|
|
36
|
-
}
|
|
37
|
-
string() {
|
|
38
|
-
this.space();
|
|
39
|
-
const start = this.at;
|
|
40
|
-
if (this.text[this.at++] !== '"')
|
|
41
|
-
invalid();
|
|
42
|
-
while (this.at < this.text.length) {
|
|
43
|
-
const char = this.text[this.at++];
|
|
44
|
-
if (char === '\\') {
|
|
45
|
-
this.at++;
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
if (char === '"')
|
|
49
|
-
return JSON.parse(this.text.slice(start, this.at));
|
|
50
|
-
}
|
|
51
|
-
return invalid();
|
|
52
|
-
}
|
|
53
|
-
value(depth) {
|
|
54
|
-
if (depth > MAX_DEPTH || ++this.nodes > MAX_NODES)
|
|
55
|
-
invalid();
|
|
56
|
-
this.space();
|
|
57
|
-
const char = this.text[this.at];
|
|
58
|
-
if (char === '"')
|
|
59
|
-
return this.string();
|
|
60
|
-
if (char === '{') {
|
|
61
|
-
this.at++;
|
|
62
|
-
const result = Object.create(null);
|
|
63
|
-
const keys = new Set();
|
|
64
|
-
this.space();
|
|
65
|
-
if (this.text[this.at] === '}') {
|
|
66
|
-
this.at++;
|
|
67
|
-
return result;
|
|
68
|
-
}
|
|
69
|
-
while (true) {
|
|
70
|
-
const key = this.string();
|
|
71
|
-
if (keys.has(key))
|
|
72
|
-
invalid();
|
|
73
|
-
keys.add(key);
|
|
74
|
-
this.take(':');
|
|
75
|
-
result[key] = this.value(depth + 1);
|
|
76
|
-
this.space();
|
|
77
|
-
const next = this.text[this.at++];
|
|
78
|
-
if (next === '}')
|
|
79
|
-
return result;
|
|
80
|
-
if (next !== ',')
|
|
81
|
-
invalid();
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (char === '[') {
|
|
85
|
-
this.at++;
|
|
86
|
-
const result = [];
|
|
87
|
-
this.space();
|
|
88
|
-
if (this.text[this.at] === ']') {
|
|
89
|
-
this.at++;
|
|
90
|
-
return result;
|
|
91
|
-
}
|
|
92
|
-
while (true) {
|
|
93
|
-
result.push(this.value(depth + 1));
|
|
94
|
-
this.space();
|
|
95
|
-
const next = this.text[this.at++];
|
|
96
|
-
if (next === ']')
|
|
97
|
-
return result;
|
|
98
|
-
if (next !== ',')
|
|
99
|
-
invalid();
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
for (const [literal, value] of [['true', true], ['false', false], ['null', null]]) {
|
|
103
|
-
if (this.text.startsWith(literal, this.at)) {
|
|
104
|
-
this.at += literal.length;
|
|
105
|
-
return value;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
const number = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/.exec(this.text.slice(this.at));
|
|
109
|
-
if (!number)
|
|
110
|
-
invalid();
|
|
111
|
-
this.at += number[0].length;
|
|
112
|
-
const value = Number(number[0]);
|
|
113
|
-
if (!Number.isFinite(value))
|
|
114
|
-
invalid();
|
|
115
|
-
return value;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
/** Bounded duplicate-aware JSON for fresh-card preparation bodies. */
|
|
119
|
-
export function readTokenizationJson(body) {
|
|
120
|
-
if (typeof body !== 'string' || body.length === 0 || body.length > MAX_BODY_LENGTH)
|
|
121
|
-
return null;
|
|
122
|
-
try {
|
|
123
|
-
const value = new JsonReader(body).read();
|
|
124
|
-
return record(value) ? value : null;
|
|
125
|
-
}
|
|
126
|
-
catch {
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/** Native operations need names, punctuation and variables, never literals. */
|
|
131
|
-
function lex(query) {
|
|
132
|
-
const tokens = [];
|
|
133
|
-
let at = 0;
|
|
134
|
-
while (at < query.length) {
|
|
135
|
-
const char = query[at];
|
|
136
|
-
if (' \t\r\n,'.includes(char)) {
|
|
137
|
-
at++;
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
if ('!$():{}'.includes(char)) {
|
|
141
|
-
tokens.push(char);
|
|
142
|
-
at++;
|
|
143
|
-
}
|
|
144
|
-
else if (/[A-Za-z_]/.test(char)) {
|
|
145
|
-
const start = at++;
|
|
146
|
-
while (at < query.length && /[A-Za-z_0-9]/.test(query[at]))
|
|
147
|
-
at++;
|
|
148
|
-
tokens.push(query.slice(start, at));
|
|
149
|
-
}
|
|
150
|
-
else
|
|
151
|
-
invalid(); // Includes comments, strings, fragments and directives.
|
|
152
|
-
if (tokens.length > MAX_NODES)
|
|
153
|
-
invalid();
|
|
154
|
-
}
|
|
155
|
-
return tokens;
|
|
156
|
-
}
|
|
157
|
-
class GraphqlReader {
|
|
158
|
-
tokens;
|
|
159
|
-
at = 0;
|
|
160
|
-
constructor(tokens) {
|
|
161
|
-
this.tokens = tokens;
|
|
162
|
-
}
|
|
163
|
-
peek() { return this.tokens[this.at]; }
|
|
164
|
-
take(expected) {
|
|
165
|
-
if (this.tokens[this.at++] !== expected)
|
|
166
|
-
invalid();
|
|
167
|
-
}
|
|
168
|
-
name() {
|
|
169
|
-
const value = this.tokens[this.at++];
|
|
170
|
-
if (!value || !/^[A-Za-z_][A-Za-z_0-9]*$/.test(value))
|
|
171
|
-
invalid();
|
|
172
|
-
return value;
|
|
173
|
-
}
|
|
174
|
-
read() {
|
|
175
|
-
const kind = this.name();
|
|
176
|
-
const name = this.name();
|
|
177
|
-
let inputDefinition = false;
|
|
178
|
-
if (this.peek() === '(') {
|
|
179
|
-
// No defaults, alternate types, unused variables or duplicate definitions.
|
|
180
|
-
for (const token of ['(', '$', 'input', ':', 'TokenizeCreditCardInput', '!', ')'])
|
|
181
|
-
this.take(token);
|
|
182
|
-
inputDefinition = true;
|
|
183
|
-
}
|
|
184
|
-
const fields = this.selection(0);
|
|
185
|
-
if (this.at !== this.tokens.length)
|
|
186
|
-
invalid();
|
|
187
|
-
return { kind, name, inputDefinition, fields };
|
|
188
|
-
}
|
|
189
|
-
selection(depth) {
|
|
190
|
-
if (depth > MAX_DEPTH)
|
|
191
|
-
invalid();
|
|
192
|
-
this.take('{');
|
|
193
|
-
const fields = [];
|
|
194
|
-
const names = new Set();
|
|
195
|
-
while (this.peek() !== '}') {
|
|
196
|
-
const name = this.name();
|
|
197
|
-
if (names.has(name))
|
|
198
|
-
invalid();
|
|
199
|
-
names.add(name);
|
|
200
|
-
const args = [];
|
|
201
|
-
if (this.peek() === '(') {
|
|
202
|
-
if (depth !== 0)
|
|
203
|
-
invalid();
|
|
204
|
-
this.take('(');
|
|
205
|
-
const argument = this.name();
|
|
206
|
-
this.take(':');
|
|
207
|
-
this.take('$');
|
|
208
|
-
args.push({ name: argument, variable: this.name() });
|
|
209
|
-
this.take(')');
|
|
210
|
-
}
|
|
211
|
-
const children = this.peek() === '{' ? this.selection(depth + 1) : [];
|
|
212
|
-
fields.push({ name, arguments: args, fields: children });
|
|
213
|
-
// Aliases, directives and other punctuation cannot begin the next field.
|
|
214
|
-
}
|
|
215
|
-
this.take('}');
|
|
216
|
-
if (fields.length === 0)
|
|
217
|
-
invalid();
|
|
218
|
-
return fields;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
/** Undefined belongs to another processor; invalid Braintree requests stay blocked. */
|
|
222
|
-
export function classifyBraintreeRequest(url, method, body) {
|
|
223
|
-
let hostname;
|
|
224
|
-
try {
|
|
225
|
-
hostname = new URL(url).hostname;
|
|
226
|
-
}
|
|
227
|
-
catch {
|
|
228
|
-
return undefined;
|
|
229
|
-
}
|
|
230
|
-
if (hostname !== 'payments.braintree-api.com' && hostname !== 'payments.sandbox.braintree-api.com')
|
|
231
|
-
return undefined;
|
|
232
|
-
if (!braintreeEnvironment(url) || method !== 'POST' || typeof body !== 'string' || body.length === 0 || body.length > MAX_BODY_LENGTH)
|
|
233
|
-
return 'invalid';
|
|
234
|
-
try {
|
|
235
|
-
const envelope = new JsonReader(body).read();
|
|
236
|
-
if (!record(envelope) || typeof envelope.query !== 'string' || typeof envelope.operationName !== 'string')
|
|
237
|
-
return 'invalid';
|
|
238
|
-
if (Object.keys(envelope).some(key => !['query', 'operationName', 'variables', 'clientSdkMetadata'].includes(key)))
|
|
239
|
-
return 'invalid';
|
|
240
|
-
if (envelope.clientSdkMetadata !== undefined && !record(envelope.clientSdkMetadata))
|
|
241
|
-
return 'invalid';
|
|
242
|
-
const operation = new GraphqlReader(lex(envelope.query)).read();
|
|
243
|
-
if (operation.name !== envelope.operationName || operation.fields.length !== 1)
|
|
244
|
-
return 'invalid';
|
|
245
|
-
const root = operation.fields[0];
|
|
246
|
-
if (root.fields.length === 0)
|
|
247
|
-
return 'invalid';
|
|
248
|
-
const variables = envelope.variables === undefined ? Object.create(null) : envelope.variables;
|
|
249
|
-
if (!record(variables))
|
|
250
|
-
return 'invalid';
|
|
251
|
-
if (operation.kind === 'query' && ['ClientConfiguration', 'ClientConfigurationQuery'].includes(operation.name)
|
|
252
|
-
&& !operation.inputDefinition && root.name === 'clientConfiguration' && root.arguments.length === 0
|
|
253
|
-
&& Object.keys(variables).length === 0)
|
|
254
|
-
return 'configuration';
|
|
255
|
-
if (operation.kind !== 'mutation' || operation.name !== 'TokenizeCreditCard' || !operation.inputDefinition
|
|
256
|
-
|| root.name !== 'tokenizeCreditCard' || root.arguments.length !== 1
|
|
257
|
-
|| root.arguments[0].name !== 'input' || root.arguments[0].variable !== 'input'
|
|
258
|
-
|| Object.keys(variables).length !== 1 || !record(variables.input) || !record(variables.input.creditCard))
|
|
259
|
-
return 'invalid';
|
|
260
|
-
const card = variables.input.creditCard;
|
|
261
|
-
if (!['number', 'expirationMonth', 'expirationYear'].every(key => typeof card[key] === 'string' && card[key].trim().length > 0))
|
|
262
|
-
return 'invalid';
|
|
263
|
-
if (card.cvv !== undefined && (typeof card.cvv !== 'string' || card.cvv.trim().length === 0))
|
|
264
|
-
return 'invalid';
|
|
265
|
-
return 'tokenization';
|
|
266
|
-
}
|
|
267
|
-
catch {
|
|
268
|
-
return 'invalid';
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
/** Preparation accepts guest tokenization only; ordinary interception stays broader. */
|
|
272
|
-
export function isPreparedBraintreeRequest(body) {
|
|
273
|
-
if (classifyBraintreeRequest(PRODUCTION, 'POST', body) !== 'tokenization')
|
|
274
|
-
return false;
|
|
275
|
-
try {
|
|
276
|
-
const envelope = new JsonReader(body).read();
|
|
277
|
-
const variables = envelope.variables;
|
|
278
|
-
const input = variables.input;
|
|
279
|
-
if (Object.keys(input).some(key => key !== 'creditCard' && key !== 'options'))
|
|
280
|
-
return false;
|
|
281
|
-
const card = input.creditCard;
|
|
282
|
-
if (Object.keys(card).some(key => !['number', 'expirationMonth', 'expirationYear', 'cvv', 'cardholderName', 'billingAddress'].includes(key)))
|
|
283
|
-
return false;
|
|
284
|
-
if (Object.entries(card).some(([key, value]) => key !== 'billingAddress' && typeof value !== 'string'))
|
|
285
|
-
return false;
|
|
286
|
-
if (card.billingAddress !== undefined) {
|
|
287
|
-
if (!record(card.billingAddress))
|
|
288
|
-
return false;
|
|
289
|
-
const addressKeys = ['postalCode', 'firstName', 'lastName', 'company', 'streetAddress', 'extendedAddress',
|
|
290
|
-
'locality', 'region', 'countryCodeNumeric', 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryName'];
|
|
291
|
-
if (Object.entries(card.billingAddress).some(([key, value]) => !addressKeys.includes(key) || typeof value !== 'string'))
|
|
292
|
-
return false;
|
|
293
|
-
}
|
|
294
|
-
// Fingerprint authorization defaults validation on when the option is absent.
|
|
295
|
-
// Preparation permits only an explicit request for a transient card token.
|
|
296
|
-
return record(input.options) && Object.keys(input.options).every(key => key === 'validate')
|
|
297
|
-
&& input.options.validate === false;
|
|
298
|
-
}
|
|
299
|
-
catch {
|
|
300
|
-
return false;
|
|
301
|
-
}
|
|
302
|
-
}
|
package/dist/braintree.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const BUILTIN_REGISTRY = [{ "psp": "spreedly", "match": /core\.spreedly\.com\/v1\/payment_methods\/restricted\.json/i, "hosts": ["^core\\.spreedly\\.com$"], "encoding": "json", "passthroughHeaders": [/^spreedly-environment-key$/i] }, { "psp": "shopify", "match": /(checkout\.pci\.shopifyinc\.com|deposit\.[a-z0-9-]+\.shopifycs\.com)\/sessions/i, "hosts": ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"], "encoding": "json", "passthroughHeaders": [/^shopify-identification-signature$/i] }, { "psp": "stripe", "match": /api\.stripe\.com\/v1\/(payment_methods|tokens|sources|confirmation_tokens|setup_intents\/seti_\w+\/confirm|payment_intents\/pi_\w+\/confirm|payment_pages\/cs_(?:test|live)_\w+\/confirm)/i, "hosts": ["^api\\.stripe\\.com$"], "encoding": "form", "passthroughHeaders": [/^authorization$/i, /^stripe-version$/i, /^x-stripe-client-user-agent$/i], "cardFields": ["card[number]", "payment_method_data[card][number]"], "passWithoutCard": "api\\.stripe\\.com/v1/(sources|confirmation_tokens|payment_pages/cs_(?:test|live)_\\w+/confirm)" }, { "psp": "braintree", "match": /payments(\.sandbox)?\.braintree-api\.com\/graphql/i, "hosts": ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^braintree-version$/i] }, { "psp": "checkout_com", "match": /(api|card-acquisition-gateway)(\.sandbox)?\.checkout\.com\/tokens/i, "hosts": ["^api\\.checkout\\.com$", "^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "adyen", "match": /(checkoutshopper-(test|live(-[a-z]+)?)\.adyen\.com|([a-z0-9-]+\.)*adyenpayments\.com)\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]+\/payments/i, "hosts": ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"], "encoding": "json", "passthroughHeaders": [], "clientSideEncrypted": true, "mode": "cse" }, { "psp": "tranzila", "match": /(direct\.tranzila\.com\/process\/n\/|directng\.tranzila\.com\/process\/?)/i, "hosts": ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"], "encoding": "form", "passthroughHeaders": [], "mode": "hosted_form" }, { "psp": "square", "match": /pci-connect\.squareup(?:sandbox)?\.com\/v2\/card-nonce/i, "hosts": ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "authorize_net", "match": /(api2|apitest)\.authorize\.net\/xml\/v1\/request\.api/i, "hosts": ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "worldpay", "match": /(try\.)?access\.worldpay\.com\/(sessions\/card|verifiedTokens\/sessions)/i, "hosts": ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^accept$/i] }, { "psp": "nuvei", "match": /(secure|ppp-test)\.safecharge\.com\/ppp\/api\/v1\/(?:cardTokenization|clientPayment|websdk\/initPaymentWithCardTokenization)\.do/i, "hosts": ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "airwallex", "match": /(checkout(\.sandbox)?\.airwallex\.com|pci-api(\.sandbox)?\.airwallex\.com)\/api\/v1\/pa\/(payment_intents\/[^/?#]+\/confirm|payment_consents\/[^/?#]+\/verify|payment_methods\/create)(\?|$)/i, "hosts": ["^checkout\\.airwallex\\.com$", "^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "encoding": "json", "passthroughHeaders": [/^client-secret$/i, /^authorization$/i, /^x-auth-token$/i, /^x-on-behalf-of$/i, /^x-api-version$/i] }, { "psp": "rapyd", "match": /(api|sandboxapi)\.rapyd\.net\/v1\/(?:hosted\/collect\/card\/[^/?#]+\/payment_method|checkout\/checkout_[A-Za-z0-9]+)(?:[?#]|$)/i, "hosts": ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "dlocal", "match": /ppmcc(-sandbox)?\.dlocal\.com\/cvault\/credit-card\/temporal/i, "hosts": ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-fields-api-key$/i, /^x-uow$/i, /^x-dlocal-infrav2$/i] }, { "psp": "ebanx", "match": /(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\.ebanx\.com|(?:api-diamond|api|sandbox)\.ebanxpay\.com)\/ws\/token/i, "hosts": ["^customer\\.ebanx\\.com$", "^customer-frontier\\.ebanx\\.com$", "^api\\.ebanx\\.com$", "^sandbox\\.ebanx\\.com$", "^api-local-latam\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^api-diamond\\.ebanxpay\\.com$", "^api\\.ebanxpay\\.com$", "^sandbox\\.ebanxpay\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "mercado_pago", "match": /api\.mercadopago\.com\/v1\/card_tokens(?![\w/-])/i, "hosts": ["^api\\.mercadopago\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-product-id$/i] }, { "psp": "payu", "match": /(secure\.payu\.com|(merch-prod|secure)\.snd\.payu\.com)\/api\/front\/tokens/i, "hosts": ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "razorpay", "match": /api\.razorpay\.com\/v1\/(?:standard_checkout\/)?payments\/create\/(?:ajax|checkout|fees)/i, "hosts": ["^api\\.razorpay\\.com$"], "encoding": "form", "passthroughHeaders": [/^x-razorpay-sessionid$/i, /^x-customer-access-token$/i] }, { "psp": "mollie", "match": /api\.cc\.mollie\.com\/v1\/card-tokens/i, "hosts": ["^api\\.cc\\.mollie\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "paysafe", "match": /(?:api(\.test)?\.paysafe\.com\/(?:paymenthub\/v1\/singleusepaymenthandles|js\/api\/v1\/tokenize)|hosted(\.test)?\.paysafe\.com\/checkout\/api\/v1\/tokenize)(?![\w/-])/i, "hosts": ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-paysafe-credentials$/i, /^correlationid$/i] }, { "psp": "recurly", "match": /api(\.eu)?\.recurly\.com\/js\/v1\/token(?![\w-])/i, "hosts": ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"], "encoding": "form", "passthroughHeaders": [/^recurly-credential-checkout-hostname$/i] }, { "psp": "moneris", "match": /(www3|esqa|gateway|gatewayqa|gatewayt|gatewaydev)\.moneris\.com\/HPPtoken\/request\.php/i, "hosts": ["^www3\\.moneris\\.com$", "^esqa\\.moneris\\.com$", "^gateway\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"], "encoding": "form", "passthroughHeaders": [] }, { "psp": "bambora", "match": /(api\.na\.bambora\.com|api\.bam\.shift4api\.net)\/scripts\/tokenization\/tokens/i, "hosts": ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "global_payments", "match": /(?:api\.heartlandportico\.com\/SecureSubmit\.v1\/api\/token|cert\.api2\.heartlandportico\.com\/Hps\.Exchange\.PosGateway\.Hpf\.v1\/api\/token|apis(?:\.sandbox)?(?:\.eu)?\.globalpay\.com\/ucp\/(?:merchants\/[^/]+\/)?payment-methods)/i, "hosts": ["^api\\.heartlandportico\\.com$", "^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.globalpay\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.eu\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-gp-version$/i] }];
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256: ac6967df3f29912d12c310418d06b6583529d5a4d5346c362ef0a7987fd4f2ab
|
|
4
|
-
// src/card-fields.js
|
|
5
|
-
function carriesCardFields(recognizer, body) {
|
|
6
|
-
const fields = recognizer && recognizer.cardFields;
|
|
7
|
-
if (!Array.isArray(fields) || fields.length === 0)
|
|
8
|
-
return true;
|
|
9
|
-
if (recognizer.encoding !== "form")
|
|
10
|
-
return true;
|
|
11
|
-
if (typeof body !== "string" || body === "")
|
|
12
|
-
return false;
|
|
13
|
-
for (const chunk of body.split("&")) {
|
|
14
|
-
const eq = chunk.indexOf("=");
|
|
15
|
-
if (eq <= 0 || eq === chunk.length - 1)
|
|
16
|
-
continue;
|
|
17
|
-
let key;
|
|
18
|
-
try {
|
|
19
|
-
key = decodeURIComponent(chunk.slice(0, eq).replace(/\+/g, " "));
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
if (fields.includes(key))
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
function requestWithoutCard(recognizer, url, body) {
|
|
30
|
-
if (carriesCardFields(recognizer, body))
|
|
31
|
-
return null;
|
|
32
|
-
const pass = recognizer && recognizer.passWithoutCard;
|
|
33
|
-
if (typeof pass === "string" && pass !== "") {
|
|
34
|
-
let parsed;
|
|
35
|
-
try {
|
|
36
|
-
parsed = new URL(url);
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
return "refuse";
|
|
40
|
-
}
|
|
41
|
-
if (new RegExp(`^(?:${pass})$`, "i").test(`${parsed.hostname.toLowerCase()}${parsed.pathname}`))
|
|
42
|
-
return "continue";
|
|
43
|
-
}
|
|
44
|
-
return "refuse";
|
|
45
|
-
}
|
|
46
|
-
export { carriesCardFields, requestWithoutCard };
|