@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.
Files changed (88) hide show
  1. package/README.md +6 -669
  2. package/cdp.d.ts +1 -0
  3. package/cdp.js +2 -0
  4. package/index.d.ts +1 -0
  5. package/index.js +2 -0
  6. package/package.json +33 -33
  7. package/playwright.d.ts +1 -0
  8. package/playwright.js +2 -0
  9. package/preflight.d.ts +1 -0
  10. package/preflight.js +2 -0
  11. package/CHANGELOG.md +0 -124
  12. package/PREFLIGHT.md +0 -308
  13. package/dist/adyen.generated.d.ts +0 -24
  14. package/dist/adyen.generated.js +0 -64
  15. package/dist/attachment.d.ts +0 -11
  16. package/dist/attachment.js +0 -50
  17. package/dist/braintree.d.ts +0 -2
  18. package/dist/braintree.generated.d.ts +0 -10
  19. package/dist/braintree.generated.js +0 -302
  20. package/dist/braintree.js +0 -2
  21. package/dist/builtin-registry.generated.d.ts +0 -2
  22. package/dist/builtin-registry.generated.js +0 -1
  23. package/dist/card-fields.generated.d.ts +0 -3
  24. package/dist/card-fields.generated.js +0 -46
  25. package/dist/cdp.d.ts +0 -189
  26. package/dist/cdp.js +0 -2194
  27. package/dist/checkout-com.generated.d.ts +0 -4
  28. package/dist/checkout-com.generated.js +0 -183
  29. package/dist/client.d.ts +0 -618
  30. package/dist/client.js +0 -1251
  31. package/dist/hosted-form.d.ts +0 -44
  32. package/dist/hosted-form.js +0 -78
  33. package/dist/index.d.ts +0 -13
  34. package/dist/index.js +0 -6
  35. package/dist/lifecycle.d.ts +0 -165
  36. package/dist/lifecycle.js +0 -370
  37. package/dist/mercado-checkout.d.ts +0 -20
  38. package/dist/mercado-checkout.generated.d.ts +0 -52
  39. package/dist/mercado-checkout.generated.js +0 -198
  40. package/dist/mercado-checkout.js +0 -108
  41. package/dist/owned-shop.generated.d.ts +0 -24
  42. package/dist/owned-shop.generated.js +0 -108
  43. package/dist/paysafe.generated.d.ts +0 -12
  44. package/dist/paysafe.generated.js +0 -87
  45. package/dist/playwright.d.ts +0 -3
  46. package/dist/playwright.js +0 -3
  47. package/dist/preflight-capabilities.generated.d.ts +0 -1253
  48. package/dist/preflight-capabilities.generated.js +0 -1929
  49. package/dist/preflight-catalog.json +0 -4595
  50. package/dist/preflight-playwright.d.ts +0 -34
  51. package/dist/preflight-playwright.js +0 -355
  52. package/dist/preflight-schemas.json +0 -1110
  53. package/dist/preflight.d.ts +0 -1
  54. package/dist/preflight.generated.d.ts +0 -1965
  55. package/dist/preflight.generated.js +0 -556
  56. package/dist/preflight.js +0 -2
  57. package/dist/preparation.d.ts +0 -31
  58. package/dist/preparation.js +0 -164
  59. package/dist/prepared-processor.d.ts +0 -10
  60. package/dist/prepared-processor.js +0 -122
  61. package/dist/recurly.generated.d.ts +0 -1
  62. package/dist/recurly.generated.js +0 -87
  63. package/dist/registry.d.ts +0 -76
  64. package/dist/registry.js +0 -296
  65. package/dist/spreedly.generated.d.ts +0 -10
  66. package/dist/spreedly.generated.js +0 -332
  67. package/dist/stripe-checkout.d.ts +0 -81
  68. package/dist/stripe-checkout.generated.d.ts +0 -82
  69. package/dist/stripe-checkout.generated.js +0 -1004
  70. package/dist/stripe-checkout.js +0 -140
  71. package/dist/substitute.d.ts +0 -38
  72. package/dist/substitute.js +0 -23
  73. package/dist/substitutions.generated.d.ts +0 -10
  74. package/dist/substitutions.generated.js +0 -66
  75. package/examples/existing-browser.mjs +0 -63
  76. package/examples/preflight/classify-direct.mjs +0 -21
  77. package/examples/preflight/classify-kernel.mjs +0 -30
  78. package/examples/preflight/inspect-browser.mjs +0 -44
  79. package/examples/preflight/kernel-native/README.md +0 -112
  80. package/examples/preflight/kernel-native/documented-adapters.json +0 -113
  81. package/examples/preflight/kernel-native/inventory.json +0 -233
  82. package/examples/preflight/kernel-native/qualification.mjs +0 -182
  83. package/examples/preflight/kernel-profile.empty.json +0 -11
  84. package/examples/preflight/mollie-hosted.observations.json +0 -23
  85. package/examples/preflight/mollie-hosted.result.json +0 -103
  86. package/examples/preflight/stripe-script.direct.result.json +0 -92
  87. package/examples/preflight/stripe-script.observations.json +0 -16
  88. package/examples/preflight/stripe-script.result.json +0 -87
package/dist/preflight.js DELETED
@@ -1,2 +0,0 @@
1
- // Read-only discovery is separate from request interception and authorization.
2
- export * from './preflight.generated.js';
@@ -1,31 +0,0 @@
1
- import { type PreparedCheckout } from './client.js';
2
- import type { AttachOptions } from './cdp.js';
3
- import type { CheckoutLifecycle } from './lifecycle.js';
4
- /** A local, one-use rendezvous. It never starts or retries a merchant request. */
5
- export declare class PreparationGate {
6
- private readonly opts;
7
- private readonly lifecycle;
8
- private readonly readDocumentUrl;
9
- private state;
10
- private observedRequest;
11
- private documentUrl;
12
- private prepared?;
13
- private stop;
14
- private expiryTimer?;
15
- constructor(opts: AttachOptions, lifecycle: CheckoutLifecycle, readDocumentUrl: () => Promise<string>);
16
- private prepare;
17
- /** Called for every recognized card mutation, before any await or local retry guard. */
18
- claim(requestUrl: string, requestBody?: string | null, requestHeaders?: Record<string, string>): PreparedCheckout | undefined;
19
- assertDocument(): Promise<void>;
20
- private readDocument;
21
- isEngaged(): boolean;
22
- retireUnboundClaim(): void;
23
- /** A bound native request has its own cancellation/unknown-outcome machinery. */
24
- invalidate(reason: string): void;
25
- /**
26
- * Retire the server handle first, then report the form outcome. Neither
27
- * call waits on the other: a stalled report cannot delay the cancel, and a
28
- * stalled cancel cannot lose the report.
29
- */
30
- private cancelThenObserve;
31
- }
@@ -1,164 +0,0 @@
1
- import { CheckoutPreparationError } from './client.js';
2
- import { validAmountInput } from './client.js';
3
- import { matchesPreparedRequest, validPreparationEnvironment, preparationEndpoint } from './prepared-processor.js';
4
- /** A local, one-use rendezvous. It never starts or retries a merchant request. */
5
- export class PreparationGate {
6
- opts;
7
- lifecycle;
8
- readDocumentUrl;
9
- state = 'unused';
10
- observedRequest = false;
11
- documentUrl = '';
12
- prepared;
13
- stop = new AbortController();
14
- expiryTimer;
15
- constructor(opts, lifecycle, readDocumentUrl) {
16
- this.opts = opts;
17
- this.lifecycle = lifecycle;
18
- this.readDocumentUrl = readDocumentUrl;
19
- lifecycle.setPreparationHandler(options => this.prepare(options));
20
- lifecycle.abort.signal.addEventListener('abort', () => this.invalidate('cancelled'), { once: true });
21
- }
22
- async prepare(options) {
23
- if (this.opts.stripeCheckout)
24
- throw new CheckoutPreparationError(null, 'unsupported_processor');
25
- options = { ...options };
26
- if (this.state !== 'unused' || this.observedRequest || this.lifecycle.getState().status !== 'idle' || this.lifecycle.isBlocked()) {
27
- // A late opt-in cannot turn the next native retry into ordinary approval.
28
- // Preserve any existing authorization's state for reconciliation.
29
- if (this.state === 'unused')
30
- this.state = 'failed';
31
- throw new CheckoutPreparationError(this.prepared?.id ?? null, 'must_prepare_before_first_request');
32
- }
33
- // Reserve synchronously, including while origin discovery/OAuth is pending.
34
- this.state = 'preparing';
35
- this.lifecycle.preparing();
36
- const signal = AbortSignal.any([this.stop.signal, this.lifecycle.abort.signal, ...(options?.signal ? [options.signal] : [])]);
37
- const onAbort = () => this.invalidate('cancelled');
38
- signal.addEventListener('abort', onAbort, { once: true });
39
- try {
40
- if (!options || !validPreparationEnvironment(options.psp, options.environment))
41
- throw new CheckoutPreparationError(null, 'unsupported_processor');
42
- const tokenizer = preparationEndpoint(options.psp, options.environment);
43
- if (!this.opts.vault.isCardRequest(tokenizer, 'POST'))
44
- throw new CheckoutPreparationError(null, 'processor_interception_unavailable');
45
- if (!validAmountInput(this.opts.amount) || this.opts.amount === 0 || !/^[a-z]{3}$/i.test(this.opts.currency ?? ''))
46
- throw new CheckoutPreparationError(null, 'amount_required');
47
- if (signal.aborted)
48
- throw new CheckoutPreparationError(null, 'cancelled');
49
- this.documentUrl = await this.readDocument();
50
- const page = new URL(this.documentUrl);
51
- if (!(page.protocol === 'https:' || (page.protocol === 'http:' && page.hostname === 'localhost')) || page.username || page.password)
52
- throw new CheckoutPreparationError(null, 'merchant_origin_invalid');
53
- const prepared = await this.opts.vault.prepareCheckout({
54
- ...options, user: this.opts.user, merchant: this.opts.merchant,
55
- amount: this.opts.amount, currency: this.opts.currency, cardId: this.opts.cardId,
56
- merchantOrigin: page.origin, checkoutKey: crypto.randomUUID(), timeoutMs: this.opts.timeoutMs, signal,
57
- onPreparationCreated: id => this.lifecycle.preparationCreated(id),
58
- onApprovalUrl: url => {
59
- if (!signal.aborted) {
60
- this.lifecycle.approvalUrl(url);
61
- try {
62
- Promise.resolve(this.opts.onApprovalUrl?.(url)).catch(() => { });
63
- }
64
- catch { /* observer only */ }
65
- }
66
- },
67
- });
68
- this.prepared = prepared;
69
- if (prepared.psp !== options.psp || prepared.environment !== options.environment)
70
- throw new CheckoutPreparationError(prepared.id, 'checkout_changed');
71
- if (signal.aborted || this.state !== 'preparing') {
72
- void this.cancelThenObserve(prepared.id, 'cancelled');
73
- throw new CheckoutPreparationError(prepared.id, 'cancelled');
74
- }
75
- await this.assertDocument();
76
- if (signal.aborted || this.state !== 'preparing')
77
- throw new CheckoutPreparationError(prepared.id, 'cancelled');
78
- const remaining = Date.parse(prepared.expiresAt) - Date.now();
79
- if (!(remaining > 0))
80
- throw new CheckoutPreparationError(prepared.id, 'expired');
81
- this.state = 'ready';
82
- this.expiryTimer = setTimeout(() => this.invalidate('expired'), remaining);
83
- this.expiryTimer.unref?.();
84
- this.lifecycle.prepared(prepared);
85
- return prepared;
86
- }
87
- catch (error) {
88
- const failure = error instanceof CheckoutPreparationError ? error : new CheckoutPreparationError(this.prepared?.id ?? null, 'preparation_unconfirmed');
89
- this.invalidate(failure.reason);
90
- throw failure;
91
- }
92
- // Keep the signal listener after ready: caller cancellation retires the handle too.
93
- }
94
- /** Called for every recognized card mutation, before any await or local retry guard. */
95
- claim(requestUrl, requestBody, requestHeaders) {
96
- this.observedRequest = true;
97
- if (this.state === 'unused')
98
- return undefined;
99
- if (this.state !== 'ready' || !this.prepared) {
100
- this.invalidate(this.state === 'preparing' ? 'submitted_before_ready' : 'already_used_or_unavailable');
101
- throw new CheckoutPreparationError(this.prepared?.id ?? null, 'already_used_or_unavailable');
102
- }
103
- const prepared = this.prepared;
104
- if (Date.parse(prepared.expiresAt) <= Date.now() || !matchesPreparedRequest(prepared.psp, prepared.environment, requestUrl, 'POST', requestBody, requestHeaders)) {
105
- const reason = Date.parse(prepared.expiresAt) <= Date.now() ? 'expired' : 'checkout_changed';
106
- this.invalidate(reason);
107
- throw new CheckoutPreparationError(prepared.id, reason);
108
- }
109
- this.state = 'consumed';
110
- clearTimeout(this.expiryTimer);
111
- return prepared;
112
- }
113
- async assertDocument() {
114
- if (this.documentUrl && await this.readDocument() !== this.documentUrl)
115
- throw new CheckoutPreparationError(this.prepared?.id ?? null, 'merchant_document_changed');
116
- }
117
- async readDocument() {
118
- const signal = AbortSignal.any([this.stop.signal, this.lifecycle.abort.signal, AbortSignal.timeout(5_000)]);
119
- const failure = () => new CheckoutPreparationError(this.prepared?.id ?? null, 'merchant_document_unavailable');
120
- if (signal.aborted)
121
- throw failure();
122
- let aborted;
123
- const stopped = new Promise((_, reject) => {
124
- aborted = () => reject(failure());
125
- signal.addEventListener('abort', aborted, { once: true });
126
- });
127
- try {
128
- return await Promise.race([this.readDocumentUrl(), stopped]);
129
- }
130
- finally {
131
- signal.removeEventListener('abort', aborted);
132
- }
133
- }
134
- isEngaged() { return this.state !== 'unused'; }
135
- retireUnboundClaim() {
136
- if (this.state !== 'consumed' || !this.prepared || this.lifecycle.getState().authorizationId)
137
- return;
138
- void this.cancelThenObserve(this.prepared.id, 'request_not_bound');
139
- // No bound ID means the adapter must not leave a spent handle appearing ready.
140
- if (this.lifecycle.getState().status === 'ready_to_submit')
141
- this.lifecycle.preparationFailed(new CheckoutPreparationError(this.prepared.id, 'request_not_bound'));
142
- }
143
- /** A bound native request has its own cancellation/unknown-outcome machinery. */
144
- invalidate(reason) {
145
- if (this.state === 'unused' || this.state === 'consumed' || this.state === 'failed')
146
- return;
147
- this.state = 'failed';
148
- clearTimeout(this.expiryTimer);
149
- this.stop.abort();
150
- if (this.prepared)
151
- void this.cancelThenObserve(this.prepared.id, reason);
152
- this.lifecycle.preparationFailed(new CheckoutPreparationError(this.prepared?.id ?? null, reason));
153
- }
154
- /**
155
- * Retire the server handle first, then report the form outcome. Neither
156
- * call waits on the other: a stalled report cannot delay the cancel, and a
157
- * stalled cancel cannot lose the report.
158
- */
159
- async cancelThenObserve(id, reason) {
160
- const cancellation = this.opts.vault.cancelPreparation(id).catch(() => { });
161
- const observation = this.opts.vault.observePreparation?.(id, this.observedRequest ? 'presented_not_filled' : 'not_presented', reason);
162
- await Promise.all([cancellation, observation?.catch(() => { })]);
163
- }
164
- }
@@ -1,10 +0,0 @@
1
- export type PreparationProcessor = 'square' | 'braintree' | 'worldpay' | 'bambora' | 'mercado_pago' | 'recurly' | 'spreedly' | 'adyen' | 'checkout_com' | 'paysafe';
2
- export type PreparationEnvironment = 'production' | 'sandbox' | 'shared';
3
- /** How the approved card reaches the processor: the device's own request (token), or ciphertext the device produces for this browser to send (cse, Adyen). */
4
- export type PreparationMode = 'token' | 'cse';
5
- export declare function preparationMode(psp: string): PreparationMode;
6
- /** Shared endpoint processors cannot attest test/live mode from their URL or key prefix. Adyen can: test and live are separate hosts and key prefixes. */
7
- export declare function validPreparationEnvironment(psp: string, environment: string): boolean;
8
- export declare function preparationEndpoint(psp: PreparationProcessor, environment: PreparationEnvironment): string;
9
- /** Processor identity and environment are part of the device's prior consent. */
10
- export declare function matchesPreparedRequest(psp: PreparationProcessor, environment: PreparationEnvironment, requestUrl: string, method: string, body?: string | null, headers?: Record<string, string>): boolean;
@@ -1,122 +0,0 @@
1
- import { paysafePreparationUrl, isPreparedPaysafeRequest } from './paysafe.generated.js';
2
- import { adyenPreparationUrl, isPreparedAdyenRequest } from './adyen.generated.js';
3
- import { braintreeEnvironment, isPreparedBraintreeRequest, readTokenizationJson } from './braintree.js';
4
- import { isPreparedRecurlyRequest } from './recurly.generated.js';
5
- import { isPreparedSpreedlyRequest, isSpreedlyTokenRequest, SPREEDLY_TOKEN_ENDPOINT } from './spreedly.generated.js';
6
- import { checkoutComPreparationUrl, isPreparedCheckoutComRequest } from './checkout-com.generated.js';
7
- export function preparationMode(psp) {
8
- return psp === 'adyen' ? 'cse' : 'token';
9
- }
10
- /** Shared endpoint processors cannot attest test/live mode from their URL or key prefix. Adyen can: test and live are separate hosts and key prefixes. */
11
- export function validPreparationEnvironment(psp, environment) {
12
- if (psp === 'bambora' || psp === 'mercado_pago' || psp === 'recurly' || psp === 'spreedly')
13
- return environment === 'shared';
14
- return ['square', 'braintree', 'worldpay', 'adyen', 'checkout_com', 'paysafe'].includes(psp) && ['production', 'sandbox'].includes(environment);
15
- }
16
- export function preparationEndpoint(psp, environment) {
17
- if (!validPreparationEnvironment(psp, environment))
18
- throw new Error('unsupported_preparation_processor');
19
- if (psp === 'paysafe')
20
- return paysafePreparationUrl(environment);
21
- if (psp === 'checkout_com')
22
- return checkoutComPreparationUrl(environment);
23
- if (psp === 'adyen')
24
- return adyenPreparationUrl(environment);
25
- if (psp === 'bambora')
26
- return 'https://api.bam.shift4api.net/scripts/tokenization/tokens';
27
- if (psp === 'mercado_pago')
28
- return 'https://api.mercadopago.com/v1/card_tokens';
29
- if (psp === 'recurly')
30
- return 'https://api.recurly.com/js/v1/token';
31
- if (psp === 'spreedly')
32
- return SPREEDLY_TOKEN_ENDPOINT;
33
- if (psp === 'worldpay')
34
- return environment === 'production'
35
- ? 'https://access.worldpay.com/sessions/card' : 'https://try.access.worldpay.com/sessions/card';
36
- if (psp === 'braintree')
37
- return environment === 'production'
38
- ? 'https://payments.braintree-api.com/graphql' : 'https://payments.sandbox.braintree-api.com/graphql';
39
- return environment === 'production'
40
- ? 'https://pci-connect.squareup.com/v2/card-nonce' : 'https://pci-connect.squareupsandbox.com/v2/card-nonce';
41
- }
42
- function record(value) {
43
- return value !== null && typeof value === 'object' && !Array.isArray(value);
44
- }
45
- const keys = (value, allowed) => Object.keys(value).every(key => allowed.includes(key));
46
- const string = (value, max = 1024) => typeof value === 'string' && value.length > 0 && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
47
- const digits = (value, min, max) => typeof value === 'string' && value.length >= min && value.length <= max && !/[^0-9]/.test(value);
48
- const month = (value) => (typeof value === 'number' && Number.isInteger(value) || digits(value, 1, 2)) && Number(value) >= 1 && Number(value) <= 12;
49
- const year = (value) => (typeof value === 'number' && Number.isInteger(value) || digits(value, 2, 4)) && (Number(value) >= 0 && Number(value) <= 99 || Number(value) >= 2000 && Number(value) <= 9999);
50
- /** Native fresh-card shapes only. Saved-card, charge and recurring siblings never consume consent. */
51
- function freshCardBody(psp, body) {
52
- const value = readTokenizationJson(body);
53
- if (!value)
54
- return false;
55
- if (psp === 'worldpay')
56
- return keys(value, ['identity', 'cardNumber', 'cardExpiryDate', 'cvc'])
57
- && string(value.identity) && digits(value.cardNumber, 12, 19)
58
- && record(value.cardExpiryDate) && keys(value.cardExpiryDate, ['month', 'year'])
59
- && month(value.cardExpiryDate.month) && year(value.cardExpiryDate.year)
60
- && (value.cvc === undefined || digits(value.cvc, 3, 4));
61
- if (psp === 'bambora')
62
- return keys(value, ['number', 'expiry_month', 'expiry_year', 'cvd'])
63
- && digits(value.number, 12, 19) && month(value.expiry_month) && year(value.expiry_year)
64
- && (value.cvd === undefined || digits(value.cvd, 3, 4));
65
- if (psp !== 'mercado_pago' || !keys(value, ['card_number', 'expiration_month', 'expiration_year', 'security_code', 'cardholder', 'device'])
66
- || !digits(value.card_number, 12, 19) || !month(value.expiration_month) || !year(value.expiration_year)
67
- || (value.security_code !== undefined && value.security_code !== '' && !digits(value.security_code, 3, 4)) || !record(value.cardholder)
68
- || !keys(value.cardholder, ['name', 'identification']))
69
- return false;
70
- const holder = value.cardholder;
71
- if (holder.name !== undefined && holder.name !== '' && !string(holder.name))
72
- return false;
73
- if (holder.identification !== undefined && (!record(holder.identification) || !keys(holder.identification, ['type', 'number'])
74
- || Object.values(holder.identification).some(v => v !== '' && !string(v))))
75
- return false;
76
- if (value.device !== undefined && (!record(value.device) || !keys(value.device, ['meli'])
77
- || !record(value.device.meli) || !keys(value.device.meli, ['session_id']) || !string(value.device.meli.session_id, 4096)))
78
- return false;
79
- return true;
80
- }
81
- /** Processor identity and environment are part of the device's prior consent. */
82
- export function matchesPreparedRequest(psp, environment, requestUrl, method, body, headers) {
83
- if (method.toUpperCase() !== 'POST' || !validPreparationEnvironment(psp, environment))
84
- return false;
85
- if (psp === 'paysafe')
86
- return isPreparedPaysafeRequest(requestUrl, method, body ?? null, environment, headers);
87
- if (psp === 'checkout_com')
88
- return isPreparedCheckoutComRequest(requestUrl, method, body ?? null, environment, headers);
89
- if (psp === 'adyen')
90
- return isPreparedAdyenRequest(requestUrl, method, body ?? null, environment);
91
- if (psp === 'braintree')
92
- return braintreeEnvironment(requestUrl) === environment && isPreparedBraintreeRequest(body ?? null);
93
- if (psp === 'spreedly')
94
- return isSpreedlyTokenRequest(requestUrl, method) && isPreparedSpreedlyRequest(body ?? null);
95
- try {
96
- const request = new URL(requestUrl), endpoint = new URL(preparationEndpoint(psp, environment));
97
- if (request.username || request.password || request.hash)
98
- return false;
99
- if (psp === 'recurly')
100
- return [endpoint.href, 'https://api.eu.recurly.com/js/v1/token'].includes(requestUrl)
101
- && isPreparedRecurlyRequest(body ?? null);
102
- if (psp === 'bambora') {
103
- if (![endpoint.href, 'https://api.na.bambora.com/scripts/tokenization/tokens'].includes(requestUrl))
104
- return false;
105
- }
106
- else if (psp === 'worldpay') {
107
- if (requestUrl !== endpoint.href)
108
- return false;
109
- }
110
- else if (request.origin !== endpoint.origin || request.pathname !== endpoint.pathname)
111
- return false;
112
- if (psp === 'mercado_pago') {
113
- const names = [...request.searchParams.keys()];
114
- if (new Set(names).size !== names.length || names.some(name => !['public_key', 'locale', 'js_version', 'referer'].includes(name)))
115
- return false;
116
- }
117
- return psp === 'square' || freshCardBody(psp, body ?? null);
118
- }
119
- catch {
120
- return false;
121
- }
122
- }
@@ -1 +0,0 @@
1
- export declare function isPreparedRecurlyRequest(body: string | null): boolean;
@@ -1,87 +0,0 @@
1
- // Generated from @agent-cards/payment-core. Do not edit.
2
- // source-sha256: 6a1932841621aca1a7371165e0b54a161c4dd5bb077acc190a0c5399229fa350
3
- /** Normal Recurly.js 4 new-card POST, before it consumes an approval.
4
- * Native fields and indexed form serialization: recurly/recurly-js commit
5
- * 7cd9a2b131bf12d7cebfbc7eea1ba86aa54c60e7, lib/recurly/{token,request,fraud}.js
6
- * and risk/{risk,three-d-secure/three-d-secure}.js. Worldpay and Cybersource
7
- * risk sessions depend on the original card; proactive Braintree also produces
8
- * another token type. All nonempty risk results are refused until collection
9
- * can use the approved card. This function never rewrites the body.
10
- */
11
- const SCALARS = new Set(['number', 'month', 'year', 'cvv', 'type', 'key', 'version', 'deviceId', 'sessionId', 'instanceId',
12
- 'first_name', 'last_name', 'address1', 'address2', 'company', 'country', 'city', 'state', 'postal_code', 'phone',
13
- 'vat_number', 'tax_identifier', 'tax_identifier_type', 'fraud_session_id', 'card_network_preference']);
14
- const BROWSER = new Set(['color_depth', 'java_enabled', 'language', 'referrer_url', 'screen_height', 'screen_width', 'time_zone_offset', 'user_agent']);
15
- const FRAUD_PROCESSORS = new Set(['kount', 'litle_threat_metrix', 'braintree', 'fraudnet']);
16
- const contiguous = (values) => [...values.keys()].sort((a, b) => Number(a) - Number(b)).every((value, index) => value === String(index));
17
- export function isPreparedRecurlyRequest(body) {
18
- if (typeof body !== 'string' || !body || body.length > 65536)
19
- return false;
20
- const chunks = body.split('&');
21
- if (chunks.length > 200)
22
- return false;
23
- const fields = new Map();
24
- const fraud = new Map();
25
- try {
26
- for (const chunk of chunks) {
27
- const separator = chunk.indexOf('=');
28
- if (separator < 1)
29
- return false;
30
- const name = decodeURIComponent(chunk.slice(0, separator).replace(/\+/g, ' '));
31
- const value = decodeURIComponent(chunk.slice(separator + 1).replace(/\+/g, ' '));
32
- if (fields.has(name) || value.length > 4096 || /[\u0000-\u001f\u007f]/.test(value))
33
- return false;
34
- fields.set(name, value);
35
- if (SCALARS.has(name))
36
- continue;
37
- const browser = /^browser\[([a-z_]+)\]$/.exec(name);
38
- if (browser && BROWSER.has(browser[1]))
39
- continue;
40
- const structured = /^fraud\[([0-9]|1[0-9])\]\[(.+)\]$/.exec(name);
41
- if (!structured)
42
- return false;
43
- const [, index, leaf] = structured;
44
- if (!['processor', 'session_id'].includes(leaf) && !/^udf\]\[([0-9]|1[0-9])\]\[(label|value)$/.test(leaf))
45
- return false;
46
- if (!fraud.has(index))
47
- fraud.set(index, new Map());
48
- fraud.get(index).set(leaf, value);
49
- }
50
- }
51
- catch {
52
- return false;
53
- }
54
- const number = fields.get('number') ?? '', month = fields.get('month') ?? '', year = fields.get('year') ?? '';
55
- if (!/^[0-9]{12,19}$/.test(number) || !/^[0-9]{1,2}$/.test(month) || Number(month) < 1 || Number(month) > 12
56
- || !/^(?:[0-9]{2}|20[0-9]{2})$/.test(year))
57
- return false;
58
- if (fields.has('cvv') && fields.get('cvv') !== '' && !/^[0-9]{3,4}$/.test(fields.get('cvv')))
59
- return false;
60
- if (fields.has('type') && fields.get('type') !== 'credit_card')
61
- return false;
62
- // A co-badged network choice belongs to the entered card's supported brands.
63
- if (fields.get('card_network_preference'))
64
- return false;
65
- if (fields.has('key') && !fields.get('key')?.trim())
66
- return false;
67
- if (!contiguous(fraud))
68
- return false;
69
- for (const item of fraud.values()) {
70
- if (!FRAUD_PROCESSORS.has(item.get('processor') ?? '') || !item.get('session_id'))
71
- return false;
72
- const udf = new Map();
73
- for (const key of item.keys()) {
74
- const field = /^udf\]\[([0-9]|1[0-9])\]\[(label|value)$/.exec(key);
75
- if (!field)
76
- continue;
77
- if (item.get('processor') !== 'kount')
78
- return false;
79
- if (!udf.has(field[1]))
80
- udf.set(field[1], new Set());
81
- udf.get(field[1]).add(field[2]);
82
- }
83
- if (!contiguous(udf) || [...udf.values()].some(keys => keys.size !== 2))
84
- return false;
85
- }
86
- return true;
87
- }
@@ -1,76 +0,0 @@
1
- /**
2
- * Which outbound requests carry a raw card, and where the card sits inside them.
3
- *
4
- * This is the only merchant-specific knowledge in the system, and it is keyed by
5
- * PSP rather than by merchant: every Shopify store shares one entry, every Stripe
6
- * site shares another. The list is served from the Agentcard API at runtime so
7
- * integrators pick up new processors without shipping a release.
8
- */
9
- export type Encoding = 'json' | 'form';
10
- /**
11
- * How a paused request gets its card. `token` (the default when absent): the
12
- * cardholder's device calls the processor and the runtime replays the
13
- * processor's response. `cse`: the processor encrypts the card in-page
14
- * (Adyen); the device returns ciphertext and the runtime substitutes it into
15
- * the paused body and lets the request continue. `hosted_form`: the device
16
- * submits the processor's own form and the paused request resolves with no
17
- * processor answer (served only to SDKs that ask for it on sync).
18
- */
19
- export type CheckoutMode = 'token' | 'cse' | 'hosted_form';
20
- export interface Recognizer {
21
- /** Stable id, e.g. "shopify" */
22
- psp: string;
23
- /** Matches the tokenization endpoint. */
24
- match: RegExp;
25
- /**
26
- * Anchored (`^…$`) regex SOURCES for the hostnames this PSP may ever receive
27
- * a card on — the server's own allowlist, served next to `match` and carried
28
- * through syncRegistry() verbatim.
29
- *
30
- * Strings, not RegExps, because that is the wire shape and because nothing
31
- * here matches with them: they exist so a transport that can only pre-filter
32
- * by URL (CDP's `Fetch.enable`, which takes globs) can be armed from the
33
- * registry instead of a hardcoded list. Optional so a hand-built registry
34
- * (tests, pinning) still type-checks.
35
- */
36
- hosts?: string[];
37
- encoding: Encoding;
38
- /**
39
- * Request headers that must be replayed verbatim for the merchant to accept
40
- * the call (signatures, client tokens). Matched case-insensitively.
41
- */
42
- passthroughHeaders: RegExp[];
43
- /**
44
- * Form keys that carry the card number. When present, a request on this
45
- * recognizer's endpoints is a card request only if its body carries one of
46
- * them: a Stripe confirmation that pays with a method the page already
47
- * created (`payment_method=pm_...`) carries none, so there is nothing for
48
- * the cardholder to approve. Served by the API and carried through
49
- * syncRegistry() verbatim.
50
- */
51
- cardFields?: string[];
52
- /**
53
- * With cardFields: the endpoints (regex source over hostname + pathname,
54
- * anchored) where a request without a card is routine and never ours, so
55
- * it continues untouched; a request without a card anywhere else is refused.
56
- */
57
- passWithoutCard?: string;
58
- /**
59
- * True when the card is encrypted inside the page before the request leaves.
60
- * A digit swap is useless here; the vault must re-run the PSP's client-side
61
- * crypto instead. Refused locally unless the entry also carries
62
- * `mode: 'cse'`, which says the vault does exactly that.
63
- */
64
- clientSideEncrypted?: boolean;
65
- /** How the card reaches the processor; absent means 'token'. Carried verbatim by syncRegistry. */
66
- mode?: CheckoutMode;
67
- }
68
- import { BUILTIN_REGISTRY } from './builtin-registry.generated.js';
69
- export { BUILTIN_REGISTRY };
70
- export declare function findRecognizer(url: string, registry?: Recognizer[]): Recognizer | null;
71
- /**
72
- * Glob url patterns covering every host in `registry` — what to hand
73
- * `Fetch.enable` so a card request is ever paused. Deliberately a superset of
74
- * the recognizers; isCardRequest is the exact check.
75
- */
76
- export declare function cardUrlPatterns(registry: Recognizer[]): string[];