@agent-cards/sdk 0.20.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/dist/client.js CHANGED
@@ -12,6 +12,9 @@ import { BuyModule } from './buy.js';
12
12
  import { Languages } from './languages.js';
13
13
  import { RequestRegistry } from './requests.js';
14
14
  import { AgentcardError } from './voice-types.js';
15
+ import { GMO_CAPABILITY, GMO_PSP, gmoTokenAnswer } from './gmo.js';
16
+ import { isCybersourceFlexTokenShape } from './cybersource-flex.generated.js';
17
+ import { CYBERSOURCE_FLEX_CAPABILITY, CYBERSOURCE_FLEX_PSP, validCybersourceFlexSidecar } from './cybersource-flex.js';
15
18
  /**
16
19
  * The modes this SDK can finish. Asked for on syncRegistry (the API serves
17
20
  * only recognizers in these modes, so a request this build cannot complete
@@ -31,14 +34,34 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
31
34
  /**
32
35
  * Dark-launch capabilities this build can finish. Sent on syncRegistry as
33
36
  * ?capabilities= so the API also serves the recognizers gated behind one of
34
- * these. `fiserv_card_capture`: Fiserv Commerce Hub's Secure Data Capture card
37
+ * these, and only to an org Agentcard enabled for the processor.
38
+ * `fiserv_card_capture`: Fiserv Commerce Hub's Secure Data Capture card
35
39
  * capture, which this build pauses and pays only through a prepared checkout
36
40
  * (pre-claim.ts, fiserv.ts). The API serves Fiserv's recognizer only to a build
37
41
  * that lists it and only for a company Agentcard turned Fiserv on for, so an older
38
42
  * SDK never pauses a capture it cannot finish and no SDK pauses one for a company
39
43
  * Fiserv is off for; the API also refuses a Fiserv payment for every such company.
44
+ * `gmo_shared_pk`: GMO token v2's getToken on the shared-key path, which this
45
+ * build judges before it pauses (gmo.ts) and pays only for the amount the
46
+ * agent passes. A gated recognizer never sits in BUILTIN_REGISTRY, so it is
47
+ * paused only after a sync.
48
+ * `capture_context`: CyberSource Flex Microform, whose create carries the capture
49
+ * context this build reads from the card field iframe (cybersource-flex.ts); the API
50
+ * serves Flex's recognizer the same way, only for a company Agentcard turned it on for.
51
+ * A gated recognizer is never in BUILTIN_REGISTRY, so a build reaches it only through a
52
+ * sync that declares its capability.
40
53
  */
41
- export const SUPPORTED_CAPABILITIES = ['fiserv_card_capture'];
54
+ export const SUPPORTED_CAPABILITIES = ['fiserv_card_capture', GMO_CAPABILITY, CYBERSOURCE_FLEX_CAPABILITY];
55
+ /**
56
+ * Processors whose paused request names no amount at all, so the pair the
57
+ * agent passes is the amount the cardholder approves: authorize() refuses one
58
+ * without it (AmountRequiredError), and the API answers 400 amount_required
59
+ * for the same case. GMO's card token request carries none: the merchant's
60
+ * server charges the token later, for the amount it decides.
61
+ */
62
+ const AGENT_AMOUNT_PROCESSORS = [GMO_PSP];
63
+ /** How AmountRequiredError names a processor. */
64
+ const PROCESSOR_NAMES = { [GMO_PSP]: 'GMO' };
42
65
  /** An integer in the smallest unit, or a decimal string in normal units with a point; nothing else. */
43
66
  export function validAmountInput(amount) {
44
67
  if (typeof amount === 'number')
@@ -78,6 +101,50 @@ export class UnsupportedModeError extends Error {
78
101
  this.name = 'UnsupportedModeError';
79
102
  }
80
103
  }
104
+ /**
105
+ * The recognizer is gated behind a dark-launch capability this SDK build does not
106
+ * declare (SUPPORTED_CAPABILITIES), so it cannot send what that processor's create
107
+ * needs. Thrown locally before any create or prompt, and terminal. A registry the
108
+ * API served never holds such an entry (it serves a gated one only to a build that
109
+ * declares it); a hand-built one can.
110
+ */
111
+ export class UnsupportedCapabilityError extends Error {
112
+ capability;
113
+ constructor(capability) {
114
+ super(`the checkout needs the "${capability}" capability, which this version of @agent-cards/checkout cannot complete; upgrade the SDK.`);
115
+ this.capability = capability;
116
+ this.name = 'UnsupportedCapabilityError';
117
+ }
118
+ }
119
+ /**
120
+ * placeholderCard() found no stored card to describe (`card_not_found`), the stored card's
121
+ * network has no documented placeholder number (`brand_unsupported`), or the stored card
122
+ * carries no usable expiry (`expiry_unknown`). Nothing was created and nothing was charged.
123
+ */
124
+ export class PlaceholderCardError extends Error {
125
+ reason;
126
+ constructor(reason) {
127
+ super(`no placeholder card: ${reason}`);
128
+ this.reason = reason;
129
+ this.name = 'PlaceholderCardError';
130
+ }
131
+ }
132
+ /**
133
+ * The placeholder number per network: a documented test card that the pinned Microform
134
+ * card-type table detects as that network and nothing else. Visa, Mastercard, Amex and JCB
135
+ * are CyberSource's own test cards (developer.cybersource.com hello-world testing guide).
136
+ * Discover is not: CyberSource's 6011111111111117 falls in the table's Maestro range, so a
137
+ * merchant that allows Discover but not Maestro refuses it in the card field itself; Stripe's
138
+ * documented Discover test card 6011000990139424 is detected as Discover alone. A network
139
+ * outside this list has no placeholder and placeholderCard() refuses it rather than guess.
140
+ */
141
+ const PLACEHOLDER_NUMBERS = Object.freeze({
142
+ visa: '4111111111111111',
143
+ mastercard: '5555555555554444',
144
+ amex: '378282246310005',
145
+ discover: '6011000990139424',
146
+ jcb: '3566111111111113',
147
+ });
81
148
  /**
82
149
  * The recognizer says this processor is preparation-required, and no preparation
83
150
  * was passed. Its card may be sent only after the cardholder approved a
@@ -94,6 +161,24 @@ export class PreparationRequiredError extends Error {
94
161
  this.name = 'PreparationRequiredError';
95
162
  }
96
163
  }
164
+ /**
165
+ * The paused request names no amount and none was passed. A GMO card token
166
+ * request carries no amount (the merchant's server charges the token later),
167
+ * so the `amount` and `currency` you pass are what the cardholder approves.
168
+ * Thrown locally before any create or prompt, and terminal: the same request
169
+ * without an amount fails the same way. The API answers 400 amount_required
170
+ * for the same case. A page total (`pageAmount`) does not count.
171
+ */
172
+ export class AmountRequiredError extends Error {
173
+ psp;
174
+ code = 'amount_required';
175
+ constructor(psp) {
176
+ const processor = PROCESSOR_NAMES[psp] ?? psp;
177
+ super(`A ${processor} payment request names no amount, so pass amount and currency: that is the amount the cardholder approves. Nothing was created and nothing was charged.`);
178
+ this.psp = psp;
179
+ this.name = 'AmountRequiredError';
180
+ }
181
+ }
97
182
  export class ApprovalTimeoutError extends Error {
98
183
  constructor(ms) { super(`user did not approve within ${ms}ms`); this.name = 'ApprovalTimeoutError'; }
99
184
  }
@@ -688,9 +773,14 @@ export class VaultClient {
688
773
  let raw;
689
774
  // ?capabilities= is the dark-launch half of the same negotiation: a
690
775
  // processor gated behind a capability is served only when this build lists
691
- // it. This build lists fiserv_card_capture, so it arms Fiserv's card capture
692
- // for a company Agentcard turned Fiserv on for; an older build, and every
693
- // other company's, never sees that recognizer.
776
+ // it (SUPPORTED_CAPABILITIES: fiserv_card_capture, gmo_shared_pk,
777
+ // capture_context) and the caller's company is one Agentcard turned that
778
+ // processor on for, so this build arms Fiserv's card capture, GMO's getToken
779
+ // and CyberSource Flex's card fields only for such a company, and an older
780
+ // build never receives a recognizer it cannot judge. Every other org's
781
+ // registry never recognizes a Flex host, so its Microform requests go
782
+ // through untouched; the create checks the org again (403
783
+ // processor_unavailable).
694
784
  const capabilities = SUPPORTED_CAPABILITIES.length ? `&capabilities=${SUPPORTED_CAPABILITIES.join(',')}` : '';
695
785
  // merchant_profiles=1 asks for the reviewed Adyen merchant profiles too, each
696
786
  // as a `{ merchant_profile }` entry after the recognizers; an API that
@@ -786,6 +876,39 @@ export class VaultClient {
786
876
  const registry = this.mergedRegistry();
787
877
  return this.sandbox ? [...deriveCardUrlPatterns(registry), ...LOOPBACK_URL_PATTERNS] : deriveCardUrlPatterns(registry);
788
878
  }
879
+ /**
880
+ * What to type into a checkout so the page's own card facts match the stored card that
881
+ * pays (see PlaceholderCard): read from GET /api/v2/vault_cards, which lists each stored card's
882
+ * network and expiry. `cardId` names the stored card; omitted, the user's most recently added
883
+ * card. Pin the returned `cardId` on the attachment or authorize(), so the approval page
884
+ * preselects that card: without a pin the page preselects its own choice (its recommended
885
+ * card, or the first card the cardholder's device can open), which can be another card. After
886
+ * prepare(), pass the prepared checkout's `cardId`, the card the cardholder already approved.
887
+ * The facts hold only while the cardholder pays with that card. Throws PlaceholderCardError
888
+ * when there is no such card, its network has no placeholder, or it carries no expiry.
889
+ */
890
+ async placeholderCard(input) {
891
+ if (!input || typeof input.user !== 'string' || !input.user)
892
+ throw new Error('placeholderCard needs the user whose stored card will pay.');
893
+ // The card list lives under /api/v2 only: the API aliases /v2/checkout alone without the prefix.
894
+ const list = await this.get(`/api/v2/vault_cards?user_id=${encodeURIComponent(input.user)}`, input.signal);
895
+ const cards = Array.isArray(list?.data) ? list.data : [];
896
+ const card = (input.cardId === undefined ? cards[0] : cards.find((entry) => entry?.id === input.cardId));
897
+ if (!card || typeof card !== 'object' || typeof card.id !== 'string' || !card.id)
898
+ throw new PlaceholderCardError('card_not_found');
899
+ const brand = typeof card.brand === 'string' ? card.brand.toLowerCase() : '';
900
+ const number = Object.prototype.hasOwnProperty.call(PLACEHOLDER_NUMBERS, brand) ? PLACEHOLDER_NUMBERS[brand] : undefined;
901
+ if (!number)
902
+ throw new PlaceholderCardError('brand_unsupported');
903
+ const month = card.expiry_month, year = card.expiry_year;
904
+ if (!Number.isSafeInteger(month) || month < 1 || month > 12
905
+ || !Number.isSafeInteger(year) || year < 2000 || year > 2099)
906
+ throw new PlaceholderCardError('expiry_unknown');
907
+ return Object.freeze({
908
+ cardId: card.id, brand, number, securityCode: brand === 'amex' ? '1234' : '123',
909
+ expiryMonth: String(month).padStart(2, '0'), expiryYear: String(year),
910
+ });
911
+ }
789
912
  /** Wait for real device approval before the caller starts native tokenization. */
790
913
  async prepareCheckout(input) {
791
914
  input = { ...input };
@@ -1069,6 +1192,22 @@ export class VaultClient {
1069
1192
  throw new CardEncryptedError(rec.psp);
1070
1193
  if (!SUPPORTED_MODES.includes(mode))
1071
1194
  throw new UnsupportedModeError(mode);
1195
+ // A dark-launch recognizer names the capability its create needs. A build that does not
1196
+ // declare it cannot send what that create carries, so it refuses here, before any create;
1197
+ // a registry the API served never holds such an entry.
1198
+ if (rec.capability !== undefined && !SUPPORTED_CAPABILITIES.includes(rec.capability))
1199
+ throw new UnsupportedCapabilityError(rec.capability);
1200
+ // CyberSource Flex: the capture context and the Microform fields ride on the request (the
1201
+ // adapters read both from the card field iframe) and the API verifies them before anything
1202
+ // exists. They belong to a Flex request and nothing else, as the API holds them.
1203
+ const flex = rec.psp === CYBERSOURCE_FLEX_PSP;
1204
+ if (!flex && (input.request.capture_context !== undefined || input.request.microform !== undefined)) {
1205
+ throw new Error('request.capture_context and request.microform belong to a CyberSource Flex card request.');
1206
+ }
1207
+ if (flex && !validCybersourceFlexSidecar(input.request.capture_context, input.request.microform)) {
1208
+ throw new Error('A CyberSource Flex card request carries the capture context its card field iframe was loaded with (request.capture_context) '
1209
+ + 'and the Microform fields that loaded (request.microform); attachToCdp and attachToPlaywright read both.');
1210
+ }
1072
1211
  // Say no rather than coerce. isCardRequest only ever pauses a POST, and the
1073
1212
  // service stores the replay template with method 'POST' hardcoded — so a
1074
1213
  // caller handing in a PUT or GET would get it silently rewritten and the
@@ -1090,6 +1229,12 @@ export class VaultClient {
1090
1229
  if (hasAmount && !validAmountInput(input.amount)) {
1091
1230
  throw new Error('amount is an integer in the currency\'s smallest unit (2306 for $23.06), or a decimal string in normal units ("23.06").');
1092
1231
  }
1232
+ // A processor whose request names no amount (AGENT_AMOUNT_PROCESSORS) is
1233
+ // paid only for the pair passed here: it is all the approval can show.
1234
+ // Refused before any create or prompt; the API refuses the same create as
1235
+ // amount_required.
1236
+ if (!hasAmount && AGENT_AMOUNT_PROCESSORS.includes(rec.psp))
1237
+ throw new AmountRequiredError(rec.psp);
1093
1238
  const timeoutMs = input.timeoutMs ?? 15 * 60_000;
1094
1239
  if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2_147_483_647)
1095
1240
  throw new Error('timeoutMs must be a positive integer no larger than 2147483647.');
@@ -1135,6 +1280,11 @@ export class VaultClient {
1135
1280
  ...(checkoutContext ? { [STRIPE_CHECKOUT_CONTEXT_HEADER]: encodeStripeCheckoutContext(checkoutContext) } : {}) },
1136
1281
  body: input.request.body,
1137
1282
  ...(mercadoContext ? { mercado_checkout: mercadoContext } : {}),
1283
+ ...(flex ? {
1284
+ capture_context: input.request.capture_context,
1285
+ microform: { version: input.request.microform.version, fields: [...input.request.microform.fields],
1286
+ ...(input.request.microform.trigger ? { trigger: input.request.microform.trigger } : {}) },
1287
+ } : {}),
1138
1288
  },
1139
1289
  };
1140
1290
  try {
@@ -1334,6 +1484,12 @@ export class VaultClient {
1334
1484
  || typeof response.body !== 'string' || !response.headers || typeof response.headers !== 'object' || Array.isArray(response.headers)) {
1335
1485
  throw new PaymentOutcomeUnknownError(authorizationId, 'approved_response_malformed');
1336
1486
  }
1487
+ // A Flex approval answers the card field with the transient token CyberSource signed for
1488
+ // the device's sealed card, which the device checked before it approved. Anything else
1489
+ // is not an answer to hand the field iframe: the device may already have sent the card.
1490
+ if (flex && (response.status < 200 || response.status > 299 || !isCybersourceFlexTokenShape(response.body))) {
1491
+ throw new PaymentOutcomeUnknownError(authorizationId, 'cybersource_flex_token_unconfirmed');
1492
+ }
1337
1493
  if (shopOrder) {
1338
1494
  let receipt;
1339
1495
  try {
@@ -1358,6 +1514,17 @@ export class VaultClient {
1358
1514
  response.body = JSON.stringify(receipt);
1359
1515
  response.headers = { 'content-type': 'application/json' };
1360
1516
  }
1517
+ if (rec.psp === GMO_PSP) {
1518
+ // GMO's answer enters the page only as one clean single-use token under a JSON
1519
+ // content type (gmo.ts gmoTokenAnswer). Anything else is never handed over; the
1520
+ // card may have left the device, so the outcome is unknown rather than declined.
1521
+ const answer = gmoTokenAnswer(response.status, response.body);
1522
+ if (!answer)
1523
+ throw new PaymentOutcomeUnknownError(authorizationId, 'gmo_token_unconfirmed');
1524
+ response.status = answer.status;
1525
+ response.headers = answer.headers;
1526
+ response.body = answer.body;
1527
+ }
1361
1528
  let processorContext;
1362
1529
  if (mercadoContext) {
1363
1530
  try {
@@ -0,0 +1,183 @@
1
+ /** The processor id, as the registry and the API name it. */
2
+ export declare const CYBERSOURCE_FLEX_PSP = "cybersource_flex";
3
+ /**
4
+ * The dark-launch capability the Flex recognizer names: the capture-context sidecar a create
5
+ * must carry. syncRegistry declares it (SUPPORTED_CAPABILITIES), because this build reads the
6
+ * context and sends it; the API serves the recognizer only to a client that does.
7
+ */
8
+ export declare const CYBERSOURCE_FLEX_CAPABILITY = "capture_context";
9
+ /** The Microform v2 card fields the cardholder's device can rebuild. */
10
+ export type MicroformField = 'number' | 'securityCode';
11
+ /**
12
+ * The Microform fields of one payment, as the create carries them: the field iframe's version,
13
+ * the fields that loaded (the number field always, the security code field when the merchant
14
+ * created it), and the field whose iframe sent this request. The device rebuilds the iframe's
15
+ * exact plaintext from them, and the triggering field's data comes first in it.
16
+ */
17
+ export interface MicroformSidecar {
18
+ version: string;
19
+ fields: MicroformField[];
20
+ trigger: MicroformField;
21
+ }
22
+ /** request.capture_context and request.microform on the create. */
23
+ export interface CybersourceFlexSidecar {
24
+ /** The capture context, byte for byte as the field iframe's URL fragment carries it. */
25
+ capture_context: string;
26
+ microform: MicroformSidecar;
27
+ }
28
+ /** A Flex card request the claim flow may take over. */
29
+ export interface CybersourceFlexPause {
30
+ sidecar: CybersourceFlexSidecar;
31
+ /**
32
+ * The request's shape for the retry rule (cdp.ts sameCheckoutRequest): microform type, fields,
33
+ * the context's key id and the verified capture context itself. Every createToken seals a fresh
34
+ * JWE, so the body itself never repeats; a retry under the same context and fields carries this
35
+ * same shape, and a field reloaded under any other context never reuses the approval, even one
36
+ * whose key id is the same.
37
+ */
38
+ shape: string;
39
+ }
40
+ /**
41
+ * Why a Flex card request is not one this build can pay; reported on the unsupported_checkout
42
+ * event. Each is a fact about the page that its next request repeats: a request that is not a
43
+ * Microform card field's (including one from a frame this attachment read and found to be no
44
+ * Microform field), an unreviewed field version, or fields the device cannot rebuild.
45
+ */
46
+ export type MicroformUnsupportedReason = 'microform_request_unrecognized' | 'microform_version_unreviewed' | 'microform_fields_unsupported';
47
+ /**
48
+ * Why this one request is refused while the next may pass; reported on the blocked event.
49
+ * `microform_frame_unread`: the frame that sent it could not be read (a missed event, a frame
50
+ * tree read that failed or timed out), which the page's next request may not repeat.
51
+ */
52
+ export type CybersourceFlexRefusal = 'capture_context_invalid' | 'capture_context_mismatch' | 'request_body_unreadable' | 'microform_frame_unread';
53
+ export type CybersourceFlexDecision = {
54
+ kind: 'pause';
55
+ flex: CybersourceFlexPause;
56
+ } | {
57
+ kind: 'continue';
58
+ } | {
59
+ kind: 'unsupported';
60
+ reason: MicroformUnsupportedReason;
61
+ } | {
62
+ kind: 'refuse';
63
+ reason: CybersourceFlexRefusal;
64
+ cause?: string;
65
+ };
66
+ /** The paused request, as the pre-claim gates see it. */
67
+ export interface FlexRequest {
68
+ url: string;
69
+ method: string;
70
+ headers: Record<string, string>;
71
+ body: string | null;
72
+ }
73
+ /** An https URL on flex.cybersource.com or testflex.cybersource.com, with no port or credentials. */
74
+ export declare function isCybersourceFlexHost(url: string): boolean;
75
+ /**
76
+ * How an adapter fails a card request it will not let through: a CyberSource Flex request as a
77
+ * network error (Playwright 'failed', CDP 'Failed', net::ERR_FAILED), every other request as an
78
+ * abort (net::ERR_ABORTED). Microform 2.12.1's field reports a failed request to the merchant's page
79
+ * at once (createToken's callback gets CREATE_TOKEN_XHR_ERROR), so the page runs its own error path:
80
+ * a message, a retry under the same capture context, or GetPay's reload under a new one. It never
81
+ * reports an aborted request, so after a refusal or a decline the page's createToken would wait with
82
+ * no answer. Both were observed with the real field on CyberSource's sandbox.
83
+ */
84
+ export declare function refusedRequestFailure(url: string): 'failed' | 'aborted';
85
+ /** A Microform field iframe's URL, read: payment-core's parseMicroformFragment. */
86
+ type MicroformFrame = {
87
+ environment: string;
88
+ version: string;
89
+ reviewed: boolean;
90
+ microformType: string;
91
+ microformId: string | null;
92
+ fieldType: string;
93
+ captureContext: string;
94
+ };
95
+ type VerifiedContext = {
96
+ url: string;
97
+ environment: string;
98
+ exp: number;
99
+ jwk: {
100
+ kid: string;
101
+ };
102
+ };
103
+ type Verification = {
104
+ ok: true;
105
+ context: VerifiedContext;
106
+ } | {
107
+ ok: false;
108
+ cause: string;
109
+ };
110
+ /**
111
+ * Each capture context verified once, offline, against the keys payment-core pins, and
112
+ * remembered for the attachment. The adapters start a context's check when its field iframe
113
+ * loads, so by the time the page sends its card the answer is usually settled and the pause
114
+ * stays synchronous. Bounded: the oldest context is dropped past 32.
115
+ */
116
+ export declare class CaptureContexts {
117
+ private readonly entries;
118
+ verify(jwt: string): Verification | Promise<Verification>;
119
+ /** Start the check of a card field iframe's capture context from the frame's URL, when it is one. */
120
+ warm(url: string | undefined): void;
121
+ }
122
+ /**
123
+ * Judge one POST to a Flex host, given the frame that sent it and every Microform frame this
124
+ * attachment knows. `issuing` is that frame read as a Microform field: `undefined` when its URL
125
+ * could not be read at all, `null` when it was read and is no Microform v2 field (the merchant's
126
+ * own page or frame, which is where Flex API direct traffic comes from). In order:
127
+ * 1. a check (eCheck) Microform: continue, it carries no card;
128
+ * 2. a body the adapter could not read: refuse this request;
129
+ * 3. not a field iframe's sealed card (application/jwt, a Microform JWE, the exact tokens
130
+ * URL): unsupported, Microform v0.x and Flex API direct post here too;
131
+ * 4. a frame that could not be read: refuse this request, since the read can fail for a
132
+ * reason the page's next request does not repeat (a missed event, a slow frame tree read);
133
+ * 5. a frame read and found to be no Microform field, an unreviewed version, or fields the
134
+ * device cannot rebuild: unsupported, as the page's next request repeats each of them;
135
+ * 6. a capture context that does not verify, has expired, names another URL or host, or did
136
+ * not seal this body: refuse this request;
137
+ * 7. otherwise pause, with the sidecar the create carries.
138
+ * Returns synchronously when the context's verification already settled.
139
+ */
140
+ export declare function judgeMicroformRequest(request: FlexRequest, issuing: MicroformFrame | null | undefined, frames: Iterable<MicroformFrame>, contexts: CaptureContexts): CybersourceFlexDecision | Promise<CybersourceFlexDecision>;
141
+ /**
142
+ * The CDP adapter's record of Microform field iframes, keyed by frame id (an out-of-process
143
+ * iframe's target id is its frame id). Fed only from CDP sources that report a document the
144
+ * frame COMMITTED, with its fragment: Page.frameNavigated (the child's own session for an
145
+ * out-of-process iframe, the parent's for an in-process one), the iframe target's url on
146
+ * Target.attachedToTarget when the SDK attaches to a form that already rendered, and the
147
+ * pausing session's Page.getFrameTree as a last read. A document request (Network.
148
+ * requestWillBeSent, Fetch.requestPaused) is only a hint that warms the context's check: a page
149
+ * can start a navigation to a genuine field URL in a frame it scripts and stop it before it
150
+ * commits, and that frame then still runs the page's own document. A frame that navigates
151
+ * elsewhere or detaches is forgotten. Bounded to the 64 most recent frames.
152
+ */
153
+ export declare class MicroformFrames {
154
+ private readonly frames;
155
+ readonly contexts: CaptureContexts;
156
+ /** Start the capture context's check from a navigation that has not committed; records nothing. */
157
+ warm(url: string | undefined): void;
158
+ /** A frame's committed document URL, fragment included. A URL that is not a field iframe's forgets the frame. */
159
+ observe(frameId: unknown, url: unknown): void;
160
+ forget(frameId: unknown): void;
161
+ /**
162
+ * Judge a POST the frame `frameId` sent. When that frame is not on record, `reread` reads the
163
+ * pausing session's frame tree once (recording what it finds) and resolves to the frame's
164
+ * committed URL, or undefined when the frame was not found or the read failed: a frame read and
165
+ * found to be no field is judged as such, one never read is refused for this request alone.
166
+ */
167
+ judge(request: FlexRequest, frameId: string | undefined, reread?: () => Promise<string | undefined>): CybersourceFlexDecision | Promise<CybersourceFlexDecision>;
168
+ }
169
+ /**
170
+ * The Playwright adapter's judge: Playwright keeps a frame's fragment in frame.url() (checked in
171
+ * cybersource-flex-browser.test.mjs) and moves frame.url() only when a navigation commits, so the
172
+ * issuing frame and its siblings are read at pause time. `issuingUrl` is undefined when the
173
+ * request's frame could not be read at all.
174
+ */
175
+ export declare function judgeMicroformFrameUrls(request: FlexRequest, issuingUrl: string | undefined, frameUrls: Iterable<string | undefined>, contexts: CaptureContexts): CybersourceFlexDecision | Promise<CybersourceFlexDecision>;
176
+ /**
177
+ * Whether a create's sidecar has the shape the API admits (routes/api/v2/checkout.ts
178
+ * createSchema): a capture context of 1 to 16384 characters, and a Microform version with a
179
+ * number field, an optional security code field and a trigger among them. authorize() checks
180
+ * it before any create, so a hand-built request fails locally rather than at the API.
181
+ */
182
+ export declare function validCybersourceFlexSidecar(captureContext: unknown, microform: unknown): boolean;
183
+ export {};
@@ -0,0 +1,92 @@
1
+ declare var CYBERSOURCE_FLEX_RULES_SOURCE: Readonly<{
2
+ version: "2.12.1";
3
+ sha256: "8429ebae83e9745cd53ad8ff0d9ba69bee0087c1435ed0695a25444bed418327";
4
+ }>;
5
+ declare var CybersourceFlexError: {
6
+ new (reason: any, message: any): {
7
+ name: string;
8
+ message: string;
9
+ stack?: string;
10
+ cause?: unknown;
11
+ };
12
+ };
13
+ declare function isVerifiedCaptureContext(value: any): boolean;
14
+ declare function flexHostEnvironment(hostname: any): "production" | "sandbox" | null;
15
+ declare var CYBERSOURCE_FLEX_PSP: string;
16
+ declare var CYBERSOURCE_FLEX_CONTENT_TYPE: string;
17
+ declare var CYBERSOURCE_FLEX_TOKENS_URLS: Readonly<{
18
+ production: `https://flex.cybersource.com${string}`;
19
+ sandbox: `https://testflex.cybersource.com${string}`;
20
+ }>;
21
+ declare var CYBERSOURCE_FLEX_CAPTURE_CONTEXT_MAX_LENGTH: number;
22
+ declare var CYBERSOURCE_FLEX_REVIEWED_IFRAME_VERSIONS: readonly string[];
23
+ declare var CYBERSOURCE_FLEX_SIGNING_KEYS: any;
24
+ declare var CYBERSOURCE_FLEX_TEST_PANS: Readonly<Set<string>>;
25
+ declare function verifyCaptureContext(jwt: any, { crypto, now }?: {}): Promise<Readonly<{
26
+ raw: any;
27
+ signingKid: any;
28
+ environment: any;
29
+ origin: string;
30
+ path: string;
31
+ url: string;
32
+ jwk: Readonly<{
33
+ kty: "RSA";
34
+ n: any;
35
+ e: any;
36
+ kid: any;
37
+ }>;
38
+ modulusBytes: number;
39
+ index: any;
40
+ contextType: any;
41
+ allowedCardNetworks: readonly any[] | null;
42
+ targetOrigins: readonly any[];
43
+ iat: any;
44
+ exp: any;
45
+ }>>;
46
+ declare function cybersourceFlexEnvironment(url: any): "production" | "sandbox" | undefined;
47
+ declare function parseMicroformFragment(rawUrl: any): Readonly<{
48
+ environment: "production" | "sandbox";
49
+ version: string;
50
+ reviewed: boolean;
51
+ microformType: any;
52
+ microformId: any;
53
+ fieldType: any;
54
+ keyId: string | null;
55
+ captureContext: any;
56
+ }> | null;
57
+ declare function detectCardTypes(pan: any, allowedCardNetworks: any): {
58
+ codes: any[];
59
+ valid: boolean;
60
+ cards: {
61
+ code: any;
62
+ name: any;
63
+ valid: any;
64
+ }[];
65
+ securityCode: {
66
+ required: boolean;
67
+ length: any;
68
+ };
69
+ };
70
+ declare function cybersourceFlexTemplateKid(body: any): any;
71
+ declare function isCybersourceFlexTemplateBody(body: any): boolean;
72
+ declare function isCybersourceFlexMediaType(value: any): boolean;
73
+ declare function isCybersourceFlexRequest(url: any, method: any, body: any, headers: any): boolean;
74
+ declare function assertCybersourceFlexTemplate(body: any, verified: any): void;
75
+ declare function buildCardPlaintext(verified: any, card: any, { fields, trigger, now }?: {
76
+ trigger?: string | undefined;
77
+ }): string;
78
+ declare function sealForContext(verified: any, plaintext: any, { crypto }?: {}): Promise<string>;
79
+ declare var CYBERSOURCE_FLEX_REFUSAL_REASONS: readonly string[];
80
+ declare function cybersourceFlexRefusalCode(text: any): string | null;
81
+ declare function isCybersourceFlexTokenShape(text: any): boolean;
82
+ declare function transientTokenSigned(status: any, body: any, verified: any, { crypto }?: {}): Promise<{
83
+ confirmed: boolean;
84
+ reason: any;
85
+ }>;
86
+ declare function transientTokenFacts(status: any, body: any, verified: any, card: any, { crypto }?: {}): Promise<{
87
+ confirmed: boolean;
88
+ reason: any;
89
+ }>;
90
+ declare function cybersourceFlexPreparationUrl(environment: any): any;
91
+ declare function isPreparedCybersourceFlexRequest(url: any, method: any, body: any, environment: any, headers: any): boolean;
92
+ export { CYBERSOURCE_FLEX_CAPTURE_CONTEXT_MAX_LENGTH, CYBERSOURCE_FLEX_CONTENT_TYPE, CYBERSOURCE_FLEX_PSP, CYBERSOURCE_FLEX_REFUSAL_REASONS, CYBERSOURCE_FLEX_REVIEWED_IFRAME_VERSIONS, CYBERSOURCE_FLEX_RULES_SOURCE, CYBERSOURCE_FLEX_SIGNING_KEYS, CYBERSOURCE_FLEX_TEST_PANS, CYBERSOURCE_FLEX_TOKENS_URLS, CybersourceFlexError, assertCybersourceFlexTemplate, buildCardPlaintext, cybersourceFlexEnvironment, flexHostEnvironment as cybersourceFlexHostEnvironment, cybersourceFlexPreparationUrl, cybersourceFlexRefusalCode, cybersourceFlexTemplateKid, detectCardTypes, isCybersourceFlexMediaType, isCybersourceFlexRequest, isCybersourceFlexTemplateBody, isCybersourceFlexTokenShape, isPreparedCybersourceFlexRequest, isVerifiedCaptureContext, parseMicroformFragment, sealForContext, transientTokenFacts, transientTokenSigned, verifyCaptureContext };