@agent-cards/checkout 0.19.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 -753
- 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 -132
- package/PREFLIGHT.md +0 -312
- package/dist/adyen-merchant-hosted.generated.d.ts +0 -277
- package/dist/adyen-merchant-hosted.generated.js +0 -1902
- 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 -192
- package/dist/cdp.js +0 -2393
- package/dist/checkout-com.generated.d.ts +0 -4
- package/dist/checkout-com.generated.js +0 -183
- package/dist/client.d.ts +0 -849
- package/dist/client.js +0 -1754
- package/dist/cse-body.d.ts +0 -25
- package/dist/cse-body.js +0 -41
- package/dist/fiserv.d.ts +0 -65
- package/dist/fiserv.generated.d.ts +0 -73
- package/dist/fiserv.generated.js +0 -830
- package/dist/fiserv.js +0 -104
- package/dist/hosted-form.d.ts +0 -44
- package/dist/hosted-form.js +0 -78
- package/dist/index.d.ts +0 -18
- package/dist/index.js +0 -10
- package/dist/lifecycle.d.ts +0 -179
- package/dist/lifecycle.js +0 -395
- 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/merchant-handoff.d.ts +0 -54
- package/dist/merchant-handoff.js +0 -100
- package/dist/merchant-hosted.d.ts +0 -140
- package/dist/merchant-hosted.js +0 -170
- package/dist/merchant-total-watch.d.ts +0 -115
- package/dist/merchant-total-watch.js +0 -268
- package/dist/merchant-total.d.ts +0 -257
- package/dist/merchant-total.js +0 -383
- 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/pre-claim.d.ts +0 -123
- package/dist/pre-claim.js +0 -386
- package/dist/preflight-capabilities.generated.d.ts +0 -1253
- package/dist/preflight-capabilities.generated.js +0 -1929
- package/dist/preflight-catalog.json +0 -4727
- package/dist/preflight-playwright.d.ts +0 -34
- package/dist/preflight-playwright.js +0 -355
- package/dist/preflight-schemas.json +0 -1122
- package/dist/preflight.d.ts +0 -1
- package/dist/preflight.generated.d.ts +0 -1965
- package/dist/preflight.generated.js +0 -570
- package/dist/preflight.js +0 -2
- package/dist/preparation.d.ts +0 -38
- package/dist/preparation.js +0 -191
- package/dist/prepared-processor.d.ts +0 -43
- package/dist/prepared-processor.js +0 -172
- package/dist/recurly.generated.d.ts +0 -1
- package/dist/recurly.generated.js +0 -87
- package/dist/registry.d.ts +0 -121
- package/dist/registry.js +0 -310
- 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 -1093
- 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 -11
- package/dist/substitutions.generated.js +0 -818
- 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
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/** Adyen Web Sessions payments request, before it consumes an approval.
|
|
2
|
-
* adyen-web (Drop-in and the Card component in the Sessions flow) posts one
|
|
3
|
-
* JSON body to /checkoutshopper/v1/sessions/{id}/payments on the merchant's
|
|
4
|
-
* Adyen host, with `clientKey` as its only query parameter, and carries the
|
|
5
|
-
* card as four JWE blobs under paymentMethod. Those four blobs are what the
|
|
6
|
-
* cardholder's device replaces; nothing else in the body is rewritten. Test
|
|
7
|
-
* and live are separate host families AND separate client-key prefixes, so a
|
|
8
|
-
* preparation names one of them and the request must agree on both. A body
|
|
9
|
-
* that pays with a stored card, carries a single encrypted blob, or asks
|
|
10
|
-
* Adyen to store the card is not the purchase the person approved and never
|
|
11
|
-
* consumes readiness. This module never rewrites a body.
|
|
12
|
-
*/
|
|
13
|
-
export type AdyenPreparationEnvironment = 'production' | 'sandbox';
|
|
14
|
-
export declare const ADYEN_ENCRYPTED_CARD_FIELDS: readonly ["encryptedCardNumber", "encryptedExpiryMonth", "encryptedExpiryYear", "encryptedSecurityCode"];
|
|
15
|
-
/** Which preparation environment a hostname belongs to: Adyen's test host, one of its live host families, or neither. */
|
|
16
|
-
export declare function adyenPreparationEnvironment(hostname: string): AdyenPreparationEnvironment | null;
|
|
17
|
-
/** The URL a preparation advertises for the approved host family: every registry recognizes it, and it is never a real session. */
|
|
18
|
-
export declare function adyenPreparationUrl(environment: AdyenPreparationEnvironment): string;
|
|
19
|
-
/** A Sessions payments URL on the approved host family whose only query is a client key of that same environment. */
|
|
20
|
-
export declare function isPreparedAdyenUrl(rawUrl: string, environment: AdyenPreparationEnvironment): boolean;
|
|
21
|
-
/** A fresh scheme card carrying the four encrypted fields the device replaces; stored cards, single-blob encryption and store requests never consume consent. */
|
|
22
|
-
export declare function isPreparedAdyenBody(body: string | null | undefined): boolean;
|
|
23
|
-
/** Processor identity, environment and request shape are part of the device's prior consent. */
|
|
24
|
-
export declare function isPreparedAdyenRequest(rawUrl: string, method: string, body: string | null | undefined, environment: AdyenPreparationEnvironment): boolean;
|
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$"], "sandboxHosts": ["^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$"], "sandboxHosts": ["^api\\.sandbox\\.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$"], "sandboxHosts": ["^checkoutshopper-test\\.adyen\\.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$"], "sandboxHosts": ["^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$"], "sandboxHosts": ["^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$"], "sandboxHosts": ["^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$"], "sandboxHosts": ["^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$"], "sandboxHosts": ["^checkout\\.sandbox\\.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$"], "sandboxHosts": ["^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$"], "sandboxHosts": ["^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$"], "sandboxHosts": ["^sandbox\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.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$"], "sandboxHosts": ["^(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$"], "sandboxHosts": ["^api\\.test\\.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$"], "sandboxHosts": ["^esqa\\.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$"], "sandboxHosts": ["^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.sandbox\\.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 };
|