@agent-cards/sdk 0.23.28 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +84 -0
- package/PREFLIGHT.md +7 -1
- package/README.md +197 -102
- package/dist/aci.d.ts +52 -22
- package/dist/aci.js +79 -15
- package/dist/adyen-merchant-hosted.generated.d.ts +2 -2
- package/dist/adyen-merchant-hosted.generated.js +586 -55
- package/dist/agent-amount.generated.js +1 -1
- package/dist/agentcard.d.ts +4 -5
- package/dist/agentcard.js +4 -5
- package/dist/amadeus.d.ts +9 -3
- package/dist/amadeus.js +33 -4
- package/dist/api-module.d.ts +1 -7
- package/dist/api-module.js +3 -5
- package/dist/{voice-retry.js → api-retry.js} +1 -1
- package/dist/api-types.d.ts +53 -0
- package/dist/{voice-types.js → api-types.js} +7 -2
- package/dist/approvals.d.ts +166 -0
- package/dist/approvals.js +213 -0
- package/dist/aurus.d.ts +33 -0
- package/dist/aurus.js +75 -0
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/buy.d.ts +20 -101
- package/dist/buy.js +19 -74
- package/dist/capabilities.generated.js +1 -1
- package/dist/card-fields.generated.js +2864 -249
- package/dist/cardlink-vpos.d.ts +43 -0
- package/dist/cardlink-vpos.js +90 -0
- package/dist/cdp.d.ts +3 -13
- package/dist/cdp.js +244 -175
- package/dist/checkout-com.generated.js +5 -1
- package/dist/client.d.ts +170 -22
- package/dist/client.js +281 -40
- package/dist/creditguard.generated.d.ts +4 -4
- package/dist/cybersource-flex.generated.d.ts +39 -1
- package/dist/cybersource-flex.generated.js +100 -11
- package/dist/datatrans.generated.js +22 -1
- package/dist/eghl.d.ts +40 -0
- package/dist/eghl.js +72 -0
- package/dist/elavon-converge.generated.js +5 -3
- package/dist/gmo.generated.js +7 -2
- package/dist/hold.d.ts +8 -0
- package/dist/hold.js +43 -2
- package/dist/hosted-entry.js +4 -0
- package/dist/index.d.ts +9 -15
- package/dist/index.js +6 -10
- package/dist/lifecycle.d.ts +9 -61
- package/dist/lifecycle.js +24 -10
- package/dist/merchant-card-endpoints.generated.d.ts +6 -1
- package/dist/merchant-card-endpoints.generated.js +344 -19
- package/dist/merchant-hosted.d.ts +8 -1
- package/dist/nike-order.d.ts +51 -0
- package/dist/nike-order.js +81 -0
- package/dist/oceanpayment.d.ts +38 -0
- package/dist/oceanpayment.js +70 -0
- package/dist/paddle-tokenex.generated.js +111 -11
- package/dist/payplug.d.ts +41 -0
- package/dist/payplug.js +71 -0
- package/dist/payrails.generated.js +33 -8
- package/dist/pre-claim.d.ts +11 -0
- package/dist/pre-claim.js +130 -7
- package/dist/preflight-capabilities.generated.d.ts +0 -4
- package/dist/preflight-capabilities.generated.js +0 -5
- package/dist/preflight-catalog.json +200 -6
- package/dist/preflight-playwright.d.ts +2 -2
- package/dist/preflight-playwright.js +63 -14
- package/dist/preflight-schemas.json +18 -0
- package/dist/preflight.generated.d.ts +5 -4
- package/dist/preflight.generated.js +63 -3
- package/dist/preparation.d.ts +7 -0
- package/dist/preparation.js +21 -0
- package/dist/prepared-processor.d.ts +18 -5
- package/dist/prepared-processor.js +48 -8
- package/dist/primer.generated.js +2889 -268
- package/dist/processout.generated.d.ts +61 -0
- package/dist/processout.generated.js +577 -0
- package/dist/requests.js +1 -1
- package/dist/shiji-sts.generated.js +111 -11
- package/dist/stripe-checkout.generated.js +2761 -319
- package/dist/substitutions.generated.js +553 -26
- package/dist/tap-payments.generated.js +111 -11
- package/dist/twoc2p.generated.js +111 -11
- package/dist/url-masks/bluesnap.d.ts +2 -0
- package/dist/url-masks/bluesnap.js +15 -0
- package/dist/url-masks/oceanpayment.d.ts +2 -0
- package/dist/url-masks/oceanpayment.js +13 -0
- package/dist/url-masks/payplug.d.ts +2 -0
- package/dist/url-masks/payplug.js +13 -0
- package/dist/url-masks/vtex_payments.d.ts +2 -0
- package/dist/url-masks/vtex_payments.js +12 -0
- package/dist/url-masks.generated.d.ts +1 -1
- package/dist/url-masks.generated.js +6 -2
- package/dist/verifone.generated.js +42 -2
- package/dist/vtex-payments.generated.d.ts +127 -0
- package/dist/vtex-payments.generated.js +544 -0
- package/dist/webhooks.d.ts +2 -2
- package/dist/webhooks.js +2 -2
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +6 -8
- package/dist/languages.d.ts +0 -23
- package/dist/languages.js +0 -76
- package/dist/voice-pkce.d.ts +0 -49
- package/dist/voice-pkce.js +0 -147
- package/dist/voice-stream.generated.d.ts +0 -118
- package/dist/voice-stream.generated.js +0 -170
- package/dist/voice-types.d.ts +0 -735
- package/dist/voice.d.ts +0 -373
- package/dist/voice.js +0 -815
- package/readme.test.mjs +0 -53
- /package/dist/{voice-retry.d.ts → api-retry.d.ts} +0 -0
|
@@ -12,6 +12,19 @@ declare var CybersourceFlexError: {
|
|
|
12
12
|
};
|
|
13
13
|
declare function isVerifiedCaptureContext(value: any): boolean;
|
|
14
14
|
declare function flexHostEnvironment(hostname: any): "production" | "sandbox" | null;
|
|
15
|
+
declare var UNIFIED_CHECKOUT_HOSTS: Readonly<{
|
|
16
|
+
production: "up.cybersource.com";
|
|
17
|
+
sandbox: "testup.cybersource.com";
|
|
18
|
+
}>;
|
|
19
|
+
declare var UNIFIED_CHECKOUT_REVIEWED_CLIENT_VERSIONS: readonly string[];
|
|
20
|
+
declare var UNIFIED_CHECKOUT_FRAMES: readonly string[];
|
|
21
|
+
declare function unifiedCheckoutHostEnvironment(hostname: any): "production" | "sandbox" | null;
|
|
22
|
+
declare function parseUnifiedCheckoutFrame(rawUrl: any): Readonly<{
|
|
23
|
+
environment: "production" | "sandbox";
|
|
24
|
+
frame: string;
|
|
25
|
+
asset: string;
|
|
26
|
+
captureContext: string;
|
|
27
|
+
}> | null;
|
|
15
28
|
declare var CYBERSOURCE_FLEX_PSP: string;
|
|
16
29
|
declare var CYBERSOURCE_FLEX_CONTENT_TYPE: string;
|
|
17
30
|
declare var CYBERSOURCE_FLEX_TOKENS_URLS: Readonly<{
|
|
@@ -38,6 +51,7 @@ declare function verifyCaptureContext(jwt: any, { crypto, now }?: {}): Promise<R
|
|
|
38
51
|
modulusBytes: number;
|
|
39
52
|
index: any;
|
|
40
53
|
contextType: any;
|
|
54
|
+
clientVersion: any;
|
|
41
55
|
allowedCardNetworks: readonly any[] | null;
|
|
42
56
|
targetOrigins: readonly any[];
|
|
43
57
|
iat: any;
|
|
@@ -76,6 +90,29 @@ declare function buildCardPlaintext(verified: any, card: any, { fields, trigger,
|
|
|
76
90
|
trigger?: string | undefined;
|
|
77
91
|
}): string;
|
|
78
92
|
declare function sealForContext(verified: any, plaintext: any, { crypto }?: {}): Promise<string>;
|
|
93
|
+
declare function verifyUnifiedCheckoutContext(jwt: any, { crypto, now }?: {}): Promise<Readonly<{
|
|
94
|
+
raw: any;
|
|
95
|
+
signingKid: any;
|
|
96
|
+
environment: any;
|
|
97
|
+
origin: string;
|
|
98
|
+
path: string;
|
|
99
|
+
url: string;
|
|
100
|
+
jwk: Readonly<{
|
|
101
|
+
kty: "RSA";
|
|
102
|
+
n: any;
|
|
103
|
+
e: any;
|
|
104
|
+
kid: any;
|
|
105
|
+
}>;
|
|
106
|
+
modulusBytes: number;
|
|
107
|
+
index: any;
|
|
108
|
+
contextType: any;
|
|
109
|
+
clientVersion: any;
|
|
110
|
+
allowedCardNetworks: readonly any[] | null;
|
|
111
|
+
targetOrigins: readonly any[];
|
|
112
|
+
iat: any;
|
|
113
|
+
exp: any;
|
|
114
|
+
}>>;
|
|
115
|
+
declare function buildUnifiedCheckoutPlaintext(verified: any, card: any, { billTo, now }?: {}): string;
|
|
79
116
|
declare var CYBERSOURCE_FLEX_REFUSAL_REASONS: readonly string[];
|
|
80
117
|
declare function cybersourceFlexRefusalCode(text: any): string | null;
|
|
81
118
|
declare function isCybersourceFlexTokenShape(text: any): boolean;
|
|
@@ -107,6 +144,7 @@ declare function admitCybersourceFlexPreparation(prepared: any, { crypto, now }?
|
|
|
107
144
|
modulusBytes: number;
|
|
108
145
|
index: any;
|
|
109
146
|
contextType: any;
|
|
147
|
+
clientVersion: any;
|
|
110
148
|
allowedCardNetworks: readonly any[] | null;
|
|
111
149
|
targetOrigins: readonly any[];
|
|
112
150
|
iat: any;
|
|
@@ -115,4 +153,4 @@ declare function admitCybersourceFlexPreparation(prepared: any, { crypto, now }?
|
|
|
115
153
|
fields: readonly string[];
|
|
116
154
|
}>>;
|
|
117
155
|
declare function sameCybersourceFlexPreparation(prepared: any, request: any): boolean;
|
|
118
|
-
export { CYBERSOURCE_FLEX_CAPTURE_CONTEXT_MAX_LENGTH, CYBERSOURCE_FLEX_CONTENT_TYPE, CYBERSOURCE_FLEX_PRESEAL_TRIGGER, 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, admitCybersourceFlexPreparation, assertCybersourceFlexTemplate, buildCardPlaintext, cybersourceFlexEnvironment, flexHostEnvironment as cybersourceFlexHostEnvironment, cybersourceFlexPreparationUrl, cybersourceFlexRefusalCode, cybersourceFlexTemplateKid, detectCardTypes, isCybersourceFlexMediaType, isCybersourceFlexRequest, isCybersourceFlexTemplateBody, isCybersourceFlexTokenShape, isPreparedCybersourceFlexRequest, isVerifiedCaptureContext, parseMicroformFragment, sameCybersourceFlexPreparation, sealForContext, transientTokenFacts, transientTokenSigned, verifyCaptureContext };
|
|
156
|
+
export { CYBERSOURCE_FLEX_CAPTURE_CONTEXT_MAX_LENGTH, CYBERSOURCE_FLEX_CONTENT_TYPE, CYBERSOURCE_FLEX_PRESEAL_TRIGGER, 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, UNIFIED_CHECKOUT_FRAMES, UNIFIED_CHECKOUT_HOSTS, UNIFIED_CHECKOUT_REVIEWED_CLIENT_VERSIONS, admitCybersourceFlexPreparation, assertCybersourceFlexTemplate, buildCardPlaintext, buildUnifiedCheckoutPlaintext, cybersourceFlexEnvironment, flexHostEnvironment as cybersourceFlexHostEnvironment, cybersourceFlexPreparationUrl, cybersourceFlexRefusalCode, cybersourceFlexTemplateKid, detectCardTypes, isCybersourceFlexMediaType, isCybersourceFlexRequest, isCybersourceFlexTemplateBody, isCybersourceFlexTokenShape, isPreparedCybersourceFlexRequest, isVerifiedCaptureContext, parseMicroformFragment, parseUnifiedCheckoutFrame, sameCybersourceFlexPreparation, sealForContext, transientTokenFacts, transientTokenSigned, unifiedCheckoutHostEnvironment, verifyCaptureContext, verifyUnifiedCheckoutContext };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256:
|
|
3
|
+
// artifact-sha256: a9a624dbdd5a025d710f99e5ce83a985e4561b4db6d3e60dc427b5e406e4b653
|
|
4
4
|
// src/cybersource-flex-rules.js
|
|
5
5
|
var CYBERSOURCE_FLEX_RULES_SOURCE = Object.freeze({ version: "2.12.1", sha256: "8429ebae83e9745cd53ad8ff0d9ba69bee0087c1435ed0695a25444bed418327" });
|
|
6
6
|
var CYBERSOURCE_FLEX_RULES = [
|
|
@@ -201,6 +201,7 @@ async function sealCompactJwe(jwk, plaintext, { crypto }) {
|
|
|
201
201
|
}
|
|
202
202
|
// src/cybersource-flex-context.js
|
|
203
203
|
var FLEX_TOKENS_PATH = "/flex/v2/tokens";
|
|
204
|
+
var UNIFIED_CHECKOUT_TOKENS_PATH = "/flex/v2/tokens/uc";
|
|
204
205
|
var FLEX_HOSTS = Object.freeze({ production: "flex.cybersource.com", sandbox: "testflex.cybersource.com" });
|
|
205
206
|
var CAPTURE_CONTEXT_MAX_LENGTH = 16384;
|
|
206
207
|
var CLOCK_SKEW_MS = 6e4;
|
|
@@ -233,7 +234,8 @@ function origins(value) {
|
|
|
233
234
|
return Object.freeze([]);
|
|
234
235
|
return Object.freeze(value.filter((origin) => typeof origin === "string" && origin.length <= 2048).slice(0, 16));
|
|
235
236
|
}
|
|
236
|
-
async function verifyContextWithKeys(jwt, keys, { crypto, now } = {}) {
|
|
237
|
+
async function verifyContextWithKeys(jwt, keys, { crypto, now, kind = "microform" } = {}) {
|
|
238
|
+
const unified = kind === "unified_checkout";
|
|
237
239
|
const [encodedHeader] = compactParts(jwt, 3, CAPTURE_CONTEXT_MAX_LENGTH, "format");
|
|
238
240
|
const peek = jsonFromBytes(base64UrlToBytes(encodedHeader), "format");
|
|
239
241
|
const kid = peek.kid;
|
|
@@ -254,8 +256,9 @@ async function verifyContextWithKeys(jwt, keys, { crypto, now } = {}) {
|
|
|
254
256
|
if (!record(flx))
|
|
255
257
|
refuse("format", "the capture context has no Flex section.");
|
|
256
258
|
const origin = `https://${host}`;
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
+
const path = unified ? UNIFIED_CHECKOUT_TOKENS_PATH : FLEX_TOKENS_PATH;
|
|
260
|
+
const url = `${origin}${path}`;
|
|
261
|
+
if (unified ? flx.origin !== origin || flx.tokensUrl !== url || own(flx, "path") : flx.origin !== origin || flx.path !== FLEX_TOKENS_PATH || own(flx, "tokensUrl") && flx.tokensUrl !== url) {
|
|
259
262
|
refuse("url_mismatch", `the capture context does not send the card to ${url}.`);
|
|
260
263
|
}
|
|
261
264
|
const jwk = flx.jwk;
|
|
@@ -271,21 +274,22 @@ async function verifyContextWithKeys(jwt, keys, { crypto, now } = {}) {
|
|
|
271
274
|
refuse("expired", "the capture context has expired.");
|
|
272
275
|
}
|
|
273
276
|
const ctx = Array.isArray(payload.ctx) ? payload.ctx : [];
|
|
274
|
-
const index = ctx.findIndex((entry) => record(entry) && typeof entry.type === "string" && /^mf
|
|
277
|
+
const index = ctx.findIndex((entry) => record(entry) && typeof entry.type === "string" && (unified ? /^uc-/ : /^mf-/).test(entry.type));
|
|
275
278
|
if (index < 0)
|
|
276
|
-
refuse("microform", "the capture context is not a Microform context.");
|
|
279
|
+
refuse("microform", unified ? "the capture context is not a Unified Checkout context." : "the capture context is not a Microform context.");
|
|
277
280
|
const data = record(ctx[index].data) ? ctx[index].data : {};
|
|
278
281
|
const context = Object.freeze({
|
|
279
282
|
raw: jwt,
|
|
280
283
|
signingKid: kid,
|
|
281
284
|
environment,
|
|
282
285
|
origin,
|
|
283
|
-
path
|
|
286
|
+
path,
|
|
284
287
|
url,
|
|
285
288
|
jwk: Object.freeze({ kty: "RSA", n: jwk.n, e: jwk.e, kid: jwk.kid }),
|
|
286
289
|
modulusBytes: Math.ceil(bits / 8),
|
|
287
290
|
index,
|
|
288
291
|
contextType: ctx[index].type,
|
|
292
|
+
clientVersion: unified && typeof data.clientVersion === "string" && data.clientVersion.length <= 32 ? data.clientVersion : null,
|
|
289
293
|
allowedCardNetworks: networks(data.allowedCardNetworks),
|
|
290
294
|
targetOrigins: origins(data.targetOrigins),
|
|
291
295
|
iat: Number.isFinite(payload.iat) ? payload.iat : null,
|
|
@@ -294,6 +298,51 @@ async function verifyContextWithKeys(jwt, keys, { crypto, now } = {}) {
|
|
|
294
298
|
VERIFIED.add(context);
|
|
295
299
|
return context;
|
|
296
300
|
}
|
|
301
|
+
// src/cybersource-unified-checkout.js
|
|
302
|
+
var UNIFIED_CHECKOUT_HOSTS = Object.freeze({ production: "up.cybersource.com", sandbox: "testup.cybersource.com" });
|
|
303
|
+
var UNIFIED_CHECKOUT_REVIEWED_CLIENT_VERSIONS = Object.freeze(["1.8.2"]);
|
|
304
|
+
var UNIFIED_CHECKOUT_FRAMES = Object.freeze(["mce", "orc", "ga"]);
|
|
305
|
+
var FRAME_PATH = /^\/uc\/v1\/assets\/([A-Za-z0-9_-]{16,1024})\/(mce|orc|ga)\/(mce|orc|ga)\.html$/;
|
|
306
|
+
var FRAGMENT_MAX_LENGTH = CAPTURE_CONTEXT_MAX_LENGTH + 4096;
|
|
307
|
+
function unifiedCheckoutHostEnvironment(hostname) {
|
|
308
|
+
const host = String(hostname ?? "").toLowerCase();
|
|
309
|
+
if (host === UNIFIED_CHECKOUT_HOSTS.production)
|
|
310
|
+
return "production";
|
|
311
|
+
if (host === UNIFIED_CHECKOUT_HOSTS.sandbox)
|
|
312
|
+
return "sandbox";
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
function parseUnifiedCheckoutFrame(rawUrl) {
|
|
316
|
+
if (typeof rawUrl !== "string" || rawUrl.length > FRAGMENT_MAX_LENGTH + 2048)
|
|
317
|
+
return null;
|
|
318
|
+
let url;
|
|
319
|
+
try {
|
|
320
|
+
url = new URL(rawUrl);
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
const environment = unifiedCheckoutHostEnvironment(url.hostname);
|
|
326
|
+
const path = FRAME_PATH.exec(url.pathname);
|
|
327
|
+
if (url.protocol !== "https:" || url.port || url.username || url.password || !environment || !path || path[2] !== path[3])
|
|
328
|
+
return null;
|
|
329
|
+
const fragment = url.hash.slice(1);
|
|
330
|
+
if (!fragment || fragment.length > FRAGMENT_MAX_LENGTH)
|
|
331
|
+
return null;
|
|
332
|
+
let value;
|
|
333
|
+
try {
|
|
334
|
+
value = JSON.parse(decodeURIComponent(fragment));
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
340
|
+
return null;
|
|
341
|
+
const ctx = value.ctx;
|
|
342
|
+
if (typeof ctx !== "string" || ctx.length < 1 || ctx.length > CAPTURE_CONTEXT_MAX_LENGTH)
|
|
343
|
+
return null;
|
|
344
|
+
return Object.freeze({ environment, frame: path[2], asset: path[1], captureContext: ctx });
|
|
345
|
+
}
|
|
297
346
|
// src/cybersource-flex.js
|
|
298
347
|
var CYBERSOURCE_FLEX_PSP = "cybersource_flex";
|
|
299
348
|
var CYBERSOURCE_FLEX_CONTENT_TYPE = "application/jwt; charset=utf-8";
|
|
@@ -343,11 +392,11 @@ function cybersourceFlexEnvironment(url) {
|
|
|
343
392
|
return "sandbox";
|
|
344
393
|
return void 0;
|
|
345
394
|
}
|
|
346
|
-
var
|
|
395
|
+
var FRAGMENT_MAX_LENGTH2 = 16384;
|
|
347
396
|
var IFRAME_PATH = /^\/microform\/bundle\/v(\d+\.\d+\.\d+)\/iframe\.html$/;
|
|
348
397
|
var FIELD_TYPES = Object.freeze(["number", "securityCode"]);
|
|
349
398
|
function parseMicroformFragment(rawUrl) {
|
|
350
|
-
if (typeof rawUrl !== "string" || rawUrl.length >
|
|
399
|
+
if (typeof rawUrl !== "string" || rawUrl.length > FRAGMENT_MAX_LENGTH2 + 2048)
|
|
351
400
|
return null;
|
|
352
401
|
let url;
|
|
353
402
|
try {
|
|
@@ -361,7 +410,7 @@ function parseMicroformFragment(rawUrl) {
|
|
|
361
410
|
if (url.protocol !== "https:" || url.port || url.username || url.password || !environment || !path)
|
|
362
411
|
return null;
|
|
363
412
|
const fragment = url.hash.slice(1);
|
|
364
|
-
if (!fragment || fragment.length >
|
|
413
|
+
if (!fragment || fragment.length > FRAGMENT_MAX_LENGTH2)
|
|
365
414
|
return null;
|
|
366
415
|
let value;
|
|
367
416
|
try {
|
|
@@ -556,6 +605,46 @@ async function sealForContext(verified, plaintext, { crypto } = {}) {
|
|
|
556
605
|
refuse("context", "the capture context was not verified on this device.");
|
|
557
606
|
return sealCompactJwe(verified.jwk, plaintext, { crypto });
|
|
558
607
|
}
|
|
608
|
+
function verifyUnifiedCheckoutContext(jwt, { crypto, now } = {}) {
|
|
609
|
+
return verifyContextWithKeys(jwt, CYBERSOURCE_FLEX_SIGNING_KEYS, { crypto, now, kind: "unified_checkout" });
|
|
610
|
+
}
|
|
611
|
+
var BILL_TO_NAME = /^[^\u0000-\u001f\u007f<>]{1,60}$/;
|
|
612
|
+
var BILL_TO_EMAIL = /^[^\s@<>]{1,64}@[^\s@<>]{1,190}$/;
|
|
613
|
+
function buildUnifiedCheckoutPlaintext(verified, card, { billTo, now } = {}) {
|
|
614
|
+
if (!isVerifiedCaptureContext(verified) || !/^uc-/.test(verified.contextType))
|
|
615
|
+
refuse("context", "the capture context was not verified as a Unified Checkout context on this device.");
|
|
616
|
+
if (!UNIFIED_CHECKOUT_REVIEWED_CLIENT_VERSIONS.includes(verified.clientVersion))
|
|
617
|
+
refuse("microform_version", "this Unified Checkout client version was not reviewed.");
|
|
618
|
+
const first = billTo?.firstName;
|
|
619
|
+
const last = billTo?.lastName;
|
|
620
|
+
const email = billTo?.email;
|
|
621
|
+
if (typeof first !== "string" || !BILL_TO_NAME.test(first) || typeof last !== "string" || !BILL_TO_NAME.test(last) || email !== void 0 && (typeof email !== "string" || !BILL_TO_EMAIL.test(email)))
|
|
622
|
+
refuse("fields", "the billing name or email is not one the card entry takes.");
|
|
623
|
+
const normalized = flexCard(card, typeof now === "function" ? now() : Date.now());
|
|
624
|
+
const detected = detectCardTypes(normalized.number, verified.allowedCardNetworks ?? void 0);
|
|
625
|
+
if (detected.codes.length === 0)
|
|
626
|
+
refuse("card_network", "this merchant's CyberSource form does not accept this card network.");
|
|
627
|
+
if (!detected.valid)
|
|
628
|
+
refuse("card_invalid", "the card number fails its network's length or check digit.");
|
|
629
|
+
if (detected.securityCode.required && !new RegExp(`^[0-9]{${detected.securityCode.length}}$`).test(normalized.cvc)) {
|
|
630
|
+
refuse("card_security_code", "the security code has the wrong length for this card.");
|
|
631
|
+
}
|
|
632
|
+
const data = {
|
|
633
|
+
PANENTRY: {
|
|
634
|
+
orderInformation: { billTo: { firstName: first, lastName: last, ...email !== void 0 ? { email } : {} } },
|
|
635
|
+
paymentInformation: { card: {
|
|
636
|
+
number: normalized.number,
|
|
637
|
+
expirationMonth: normalized.expirationMonth,
|
|
638
|
+
expirationYear: normalized.expirationYear,
|
|
639
|
+
securityCode: normalized.cvc,
|
|
640
|
+
type: detected.codes[0]
|
|
641
|
+
} }
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
const plaintext = JSON.stringify({ data, context: verified.raw, index: 0 }).replace(/[\u0080-\uffff]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
645
|
+
asciiBytes(plaintext, "card_ascii");
|
|
646
|
+
return plaintext;
|
|
647
|
+
}
|
|
559
648
|
var TOKEN_MAX_LENGTH = 8192;
|
|
560
649
|
var CYBERSOURCE_FLEX_REFUSAL_REASONS = Object.freeze([
|
|
561
650
|
"VALIDATION_ERROR",
|
|
@@ -728,4 +817,4 @@ function sameCybersourceFlexPreparation(prepared, request) {
|
|
|
728
817
|
const theirs = new Set(two.fields);
|
|
729
818
|
return mine.size === one.fields.length && theirs.size === two.fields.length && mine.size === theirs.size && [...mine].every((field) => theirs.has(field));
|
|
730
819
|
}
|
|
731
|
-
export { CYBERSOURCE_FLEX_CAPTURE_CONTEXT_MAX_LENGTH, CYBERSOURCE_FLEX_CONTENT_TYPE, CYBERSOURCE_FLEX_PRESEAL_TRIGGER, 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, admitCybersourceFlexPreparation, assertCybersourceFlexTemplate, buildCardPlaintext, cybersourceFlexEnvironment, flexHostEnvironment as cybersourceFlexHostEnvironment, cybersourceFlexPreparationUrl, cybersourceFlexRefusalCode, cybersourceFlexTemplateKid, detectCardTypes, isCybersourceFlexMediaType, isCybersourceFlexRequest, isCybersourceFlexTemplateBody, isCybersourceFlexTokenShape, isPreparedCybersourceFlexRequest, isVerifiedCaptureContext, parseMicroformFragment, sameCybersourceFlexPreparation, sealForContext, transientTokenFacts, transientTokenSigned, verifyCaptureContext };
|
|
820
|
+
export { CYBERSOURCE_FLEX_CAPTURE_CONTEXT_MAX_LENGTH, CYBERSOURCE_FLEX_CONTENT_TYPE, CYBERSOURCE_FLEX_PRESEAL_TRIGGER, 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, UNIFIED_CHECKOUT_FRAMES, UNIFIED_CHECKOUT_HOSTS, UNIFIED_CHECKOUT_REVIEWED_CLIENT_VERSIONS, admitCybersourceFlexPreparation, assertCybersourceFlexTemplate, buildCardPlaintext, buildUnifiedCheckoutPlaintext, cybersourceFlexEnvironment, flexHostEnvironment as cybersourceFlexHostEnvironment, cybersourceFlexPreparationUrl, cybersourceFlexRefusalCode, cybersourceFlexTemplateKid, detectCardTypes, isCybersourceFlexMediaType, isCybersourceFlexRequest, isCybersourceFlexTemplateBody, isCybersourceFlexTokenShape, isPreparedCybersourceFlexRequest, isVerifiedCaptureContext, parseMicroformFragment, parseUnifiedCheckoutFrame, sameCybersourceFlexPreparation, sealForContext, transientTokenFacts, transientTokenSigned, unifiedCheckoutHostEnvironment, verifyCaptureContext, verifyUnifiedCheckoutContext };
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256:
|
|
3
|
+
// artifact-sha256: 169752c8d6a6c9f131c83502766f0026b2ffa45cf9f27c450735eef5ad2fa931
|
|
4
|
+
// src/answer-card-leak.js
|
|
5
|
+
var DECIMAL = /\p{Nd}/u;
|
|
6
|
+
function digitValue(char) {
|
|
7
|
+
const point = char.codePointAt(0);
|
|
8
|
+
let first = point;
|
|
9
|
+
while (DECIMAL.test(String.fromCodePoint(first - 1)))
|
|
10
|
+
first -= 1;
|
|
11
|
+
return String((point - first) % 10);
|
|
12
|
+
}
|
|
13
|
+
var ENCLOSED = /* @__PURE__ */ Object.freeze([[9451, 10, 11], [9461, 10, 1], [9471, 1, 0], [10102, 30, 1], [127243, 2, 0]]);
|
|
14
|
+
var ENCLOSED_CHAR = /[\u24eb-\u24ff\u2776-\u2793\u{1f10b}\u{1f10c}]/gu;
|
|
15
|
+
function enclosedValue(char) {
|
|
16
|
+
const point = char.codePointAt(0);
|
|
17
|
+
const [first, , start] = ENCLOSED.find(([from, size]) => point >= from && point < from + size);
|
|
18
|
+
return String(first === 127243 ? 0 : first === 10102 ? (point - first) % 10 + 1 : start + point - first);
|
|
19
|
+
}
|
|
20
|
+
var asciiDigits = (text2) => (typeof text2.normalize === "function" ? text2.normalize("NFKC") : text2).replace(ENCLOSED_CHAR, enclosedValue).replace(/\p{Nd}/gu, (char) => char >= "0" && char <= "9" ? char : digitValue(char));
|
|
21
|
+
var digitCount = (text2) => (asciiDigits(String(text2)).match(/\p{N}/gu) ?? []).length;
|
|
22
|
+
var NAME_DIGITS = 3;
|
|
4
23
|
// src/datatrans.js
|
|
5
24
|
var DATATRANS_HOSTS = Object.freeze(["pay.datatrans.com", "pay.sandbox.datatrans.com"]);
|
|
6
25
|
var DATATRANS_TOKENIZE_PATH = "/upp/payment/SecureFields/paymentField";
|
|
@@ -374,6 +393,8 @@ function rebuildCardInfo(info) {
|
|
|
374
393
|
return null;
|
|
375
394
|
if (owns(info, "brand") && !optionalText(info.brand, CARD_WORD) || owns(info, "type") && !optionalText(info.type, CARD_WORD) || owns(info, "usage") && !optionalText(info.usage, CARD_WORD) || owns(info, "country") && !optionalText(info.country, COUNTRY) || owns(info, "issuer") && info.issuer !== null && !plain(info.issuer, 128) || owns(info, "coBrand") && info.coBrand !== null && !plain(info.coBrand, 64) || owns(info, "accountType") && !optionalText(info.accountType, CARD_WORD) || owns(info, "binLength") && !optionalBinLength(info.binLength))
|
|
376
395
|
return null;
|
|
396
|
+
if (digitCount(`${info.issuer ?? ""} ${info.coBrand ?? ""}`) > NAME_DIGITS)
|
|
397
|
+
return null;
|
|
377
398
|
const safe = {};
|
|
378
399
|
for (const key of CARD_INFO_KEYS)
|
|
379
400
|
if (owns(info, key))
|
package/dist/eghl.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* eGHL's hosted payment page (securepay.e-ghl.com, the Malaysian gateway now sold as ADAPTIS, behind its own Shopify app
|
|
3
|
+
* among others), as this SDK reads it.
|
|
4
|
+
*
|
|
5
|
+
* The merchant's checkout sends the buyer's browser to https://securepay.e-ghl.com/IPG/payment.aspx with the payment in
|
|
6
|
+
* the query: a GET that carries no card, and the one request a build that declares eGHL's capability pauses
|
|
7
|
+
* (hosted-entry.ts). The cardholder's device repeats it over the end-to-end TLS relay and pays on the card page eGHL
|
|
8
|
+
* returns to it (payment-core processors/eghl.js). Every step toward paying on that page is a POST to eGHL's hosts, the
|
|
9
|
+
* card page's card post to the entry's own address among them, so this SDK aborts every POST there once the lane is
|
|
10
|
+
* served, and no placeholder card reaches eGHL.
|
|
11
|
+
*
|
|
12
|
+
* EGHL_HOST is payment-core's EGHL_HOST, and isEghlEntryUrl is payment-core's isEghlEntryUrl; eghl.test.mjs pins both
|
|
13
|
+
* to payment-core.
|
|
14
|
+
*/
|
|
15
|
+
export declare const EGHL_PSP = "eghl";
|
|
16
|
+
/** The dark-launch capability the API serves eGHL's recognizer behind (payment-core processors/eghl.js). */
|
|
17
|
+
export declare const EGHL_CAPABILITY = "eghl_hosted_payment_page";
|
|
18
|
+
/** eGHL's live host, and its test platform's. */
|
|
19
|
+
export declare const EGHL_HOST = "securepay.e-ghl.com";
|
|
20
|
+
export declare const EGHL_SANDBOX_HOST = "test2pay.ghl.com";
|
|
21
|
+
/**
|
|
22
|
+
* Whether a URL is an entry as a browser sends it: https, eGHL's live host with its entry path or its test host with its
|
|
23
|
+
* test path, the path in any letter case as IIS reads it, the default port, no credentials, no fragment, and a query
|
|
24
|
+
* that names the payment. Any other request to eGHL's hosts is not the entry.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isEghlEntryUrl(raw: unknown): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Whether a request on eGHL's hosts is a step toward paying there: any POST, the card page's card post and its Click to
|
|
29
|
+
* Pay post among them. The entry, the page's scripts and its images are GETs and are not one.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isEghlPayStep(url: string, method: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Whether an adapter must hand this request to the pre-claim gates: it is on eGHL's hosts and this build's registry
|
|
34
|
+
* serves eGHL's entry. The page's posts are not the entry, so the registry's card-request test does not name them; the
|
|
35
|
+
* CDP adapter still pauses them through the recognizer's host patterns, and attachToPlaywright's route predicate asks
|
|
36
|
+
* this, so both adapters show the gates every request to eGHL's hosts once the lane is armed.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isArmedEghlUrl(vault: {
|
|
39
|
+
isCardRequest(url: string, method?: string): boolean;
|
|
40
|
+
} | undefined, url: string): boolean;
|
package/dist/eghl.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* eGHL's hosted payment page (securepay.e-ghl.com, the Malaysian gateway now sold as ADAPTIS, behind its own Shopify app
|
|
3
|
+
* among others), as this SDK reads it.
|
|
4
|
+
*
|
|
5
|
+
* The merchant's checkout sends the buyer's browser to https://securepay.e-ghl.com/IPG/payment.aspx with the payment in
|
|
6
|
+
* the query: a GET that carries no card, and the one request a build that declares eGHL's capability pauses
|
|
7
|
+
* (hosted-entry.ts). The cardholder's device repeats it over the end-to-end TLS relay and pays on the card page eGHL
|
|
8
|
+
* returns to it (payment-core processors/eghl.js). Every step toward paying on that page is a POST to eGHL's hosts, the
|
|
9
|
+
* card page's card post to the entry's own address among them, so this SDK aborts every POST there once the lane is
|
|
10
|
+
* served, and no placeholder card reaches eGHL.
|
|
11
|
+
*
|
|
12
|
+
* EGHL_HOST is payment-core's EGHL_HOST, and isEghlEntryUrl is payment-core's isEghlEntryUrl; eghl.test.mjs pins both
|
|
13
|
+
* to payment-core.
|
|
14
|
+
*/
|
|
15
|
+
export const EGHL_PSP = 'eghl';
|
|
16
|
+
/** The dark-launch capability the API serves eGHL's recognizer behind (payment-core processors/eghl.js). */
|
|
17
|
+
export const EGHL_CAPABILITY = 'eghl_hosted_payment_page';
|
|
18
|
+
/** eGHL's live host, and its test platform's. */
|
|
19
|
+
export const EGHL_HOST = 'securepay.e-ghl.com';
|
|
20
|
+
export const EGHL_SANDBOX_HOST = 'test2pay.ghl.com';
|
|
21
|
+
const LIVE_PATH = '/ipg/payment.aspx';
|
|
22
|
+
const SANDBOX_PATH = '/ipgsg/payment.aspx';
|
|
23
|
+
// An entry of that shape, for asking whether this build's registry serves the entry.
|
|
24
|
+
const PROBE = 'https://securepay.e-ghl.com/IPG/payment.aspx?TransactionType=SALE';
|
|
25
|
+
/**
|
|
26
|
+
* Whether a URL is an entry as a browser sends it: https, eGHL's live host with its entry path or its test host with its
|
|
27
|
+
* test path, the path in any letter case as IIS reads it, the default port, no credentials, no fragment, and a query
|
|
28
|
+
* that names the payment. Any other request to eGHL's hosts is not the entry.
|
|
29
|
+
*/
|
|
30
|
+
export function isEghlEntryUrl(raw) {
|
|
31
|
+
if (typeof raw !== 'string' || raw.length > 8192 || raw.includes('#'))
|
|
32
|
+
return false;
|
|
33
|
+
let url;
|
|
34
|
+
try {
|
|
35
|
+
url = new URL(raw);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (url.href !== raw || url.protocol !== 'https:' || url.port || url.username || url.password || url.search.length < 2)
|
|
41
|
+
return false;
|
|
42
|
+
const path = url.pathname.toLowerCase();
|
|
43
|
+
return (url.hostname === EGHL_HOST && path === LIVE_PATH) || (url.hostname === EGHL_SANDBOX_HOST && path === SANDBOX_PATH);
|
|
44
|
+
}
|
|
45
|
+
/** Whether a URL is on one of eGHL's hosts, live or test: https, no credentials, any port, any case, a trailing dot or not. */
|
|
46
|
+
function onEghlHost(raw) {
|
|
47
|
+
let url;
|
|
48
|
+
try {
|
|
49
|
+
url = new URL(raw);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const host = url.hostname.toLowerCase().replace(/\.$/, '');
|
|
55
|
+
return url.protocol === 'https:' && !url.username && !url.password && (host === EGHL_HOST || host === EGHL_SANDBOX_HOST);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Whether a request on eGHL's hosts is a step toward paying there: any POST, the card page's card post and its Click to
|
|
59
|
+
* Pay post among them. The entry, the page's scripts and its images are GETs and are not one.
|
|
60
|
+
*/
|
|
61
|
+
export function isEghlPayStep(url, method) {
|
|
62
|
+
return onEghlHost(url) && String(method).toUpperCase() === 'POST';
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Whether an adapter must hand this request to the pre-claim gates: it is on eGHL's hosts and this build's registry
|
|
66
|
+
* serves eGHL's entry. The page's posts are not the entry, so the registry's card-request test does not name them; the
|
|
67
|
+
* CDP adapter still pauses them through the recognizer's host patterns, and attachToPlaywright's route predicate asks
|
|
68
|
+
* this, so both adapters show the gates every request to eGHL's hosts once the lane is armed.
|
|
69
|
+
*/
|
|
70
|
+
export function isArmedEghlUrl(vault, url) {
|
|
71
|
+
return onEghlHost(url) && typeof vault?.isCardRequest === 'function' && vault.isCardRequest(PROBE, 'GET');
|
|
72
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256:
|
|
3
|
+
// artifact-sha256: 7f75fa0deecb8db3b33aa07bdaf4a70e5e548c9bd528b45348022c22138599b3
|
|
4
4
|
// src/elavon-converge.js
|
|
5
5
|
var CONVERGE_HOSTS = /* @__PURE__ */ Object.freeze(["api.convergepay.com", "api.demo.convergepay.com", "www.convergepay.com"]);
|
|
6
6
|
var CONVERGE_DEMO_HOST = "api.demo.convergepay.com";
|
|
@@ -339,6 +339,8 @@ var RESPONSE_FIELDS = /* @__PURE__ */ Object.freeze([
|
|
|
339
339
|
"ssl_customer_code"
|
|
340
340
|
]);
|
|
341
341
|
var MASKED_CARD = /^(?=.{12,19}$)\d{0,6}[*Xx#]+\d{0,4}$/;
|
|
342
|
+
var CARD_WORDS = /* @__PURE__ */ Object.freeze(["ssl_card_short_description", "ssl_card_type"]);
|
|
343
|
+
var hasDigit = (value) => /\p{N}/u.test(String(value).normalize("NFKC"));
|
|
342
344
|
var FIELD_NAME = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
343
345
|
function luhn(digits) {
|
|
344
346
|
let sum = 0;
|
|
@@ -391,7 +393,7 @@ function keepReturnField(name, value) {
|
|
|
391
393
|
return false;
|
|
392
394
|
if (lower === "ssl_card_number")
|
|
393
395
|
return typeof value === "string" && MASKED_CARD.test(value);
|
|
394
|
-
return safeScalar(value);
|
|
396
|
+
return safeScalar(value) && !(CARD_WORDS.includes(lower) && hasDigit(value));
|
|
395
397
|
}
|
|
396
398
|
function sanitizeReturnList(list) {
|
|
397
399
|
if (Array.isArray(list)) {
|
|
@@ -423,7 +425,7 @@ function sanitizeConvergeResponse(text) {
|
|
|
423
425
|
if (!owns(answer.responseFields, name))
|
|
424
426
|
continue;
|
|
425
427
|
const value = answer.responseFields[name];
|
|
426
|
-
if (name === "ssl_card_number" ? typeof value === "string" && MASKED_CARD.test(value) : safeScalar(value))
|
|
428
|
+
if (name === "ssl_card_number" ? typeof value === "string" && MASKED_CARD.test(value) : safeScalar(value) && !(CARD_WORDS.includes(name) && hasDigit(value)))
|
|
427
429
|
fields[name] = value;
|
|
428
430
|
}
|
|
429
431
|
out.responseFields = fields;
|
package/dist/gmo.generated.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256:
|
|
3
|
+
// artifact-sha256: 192df19d53f4c68d74db3d692397b94f4a1b45ce8bee44a2326e01b56a329adf
|
|
4
4
|
// src/sha256.js
|
|
5
5
|
var K = /* @__PURE__ */ new Uint32Array([
|
|
6
6
|
1116352408,
|
|
@@ -146,6 +146,7 @@ var BASE64 = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
|
146
146
|
var SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
147
147
|
var RSA_ALGORITHM = [48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0];
|
|
148
148
|
var MODULUS_BYTES = [128, 256, 384, 512];
|
|
149
|
+
var SMALL_MODULUS_BYTES = 64;
|
|
149
150
|
var MIN_PADDING = 8;
|
|
150
151
|
var MAX_DRAWS = 16;
|
|
151
152
|
var TYPED_ARRAY = Object.getPrototypeOf(Uint8Array.prototype);
|
|
@@ -156,6 +157,9 @@ function parseRsaPublicKey(key) {
|
|
|
156
157
|
const named = key && typeof key === "object" && Object.prototype.hasOwnProperty.call(key, "exponent") ? key.exponent : void 0;
|
|
157
158
|
if (named !== void 0 && named !== 17)
|
|
158
159
|
throw refuse("key");
|
|
160
|
+
const sized = key && typeof key === "object" && Object.prototype.hasOwnProperty.call(key, "bits") ? key.bits : void 0;
|
|
161
|
+
if (sized !== void 0 && sized !== 512)
|
|
162
|
+
throw refuse("key");
|
|
159
163
|
if (typeof spki !== "string" || spki.length > 1024 || spki.length % 4 !== 0 || !BASE64.test(spki))
|
|
160
164
|
throw refuse("key");
|
|
161
165
|
if (typeof pin !== "string" || !SHA256_HEX.test(pin))
|
|
@@ -188,7 +192,8 @@ function parseRsaPublicKey(key) {
|
|
|
188
192
|
if (exponent.end !== rsa.end)
|
|
189
193
|
throw refuse("key");
|
|
190
194
|
const n = modulus.bytes;
|
|
191
|
-
|
|
195
|
+
const sizes = sized === 512 ? [SMALL_MODULUS_BYTES] : MODULUS_BYTES;
|
|
196
|
+
if (!sizes.includes(n.length) || !(n[0] & 128) || !(n[n.length - 1] & 1))
|
|
192
197
|
throw refuse("key");
|
|
193
198
|
const e = exponent.bytes;
|
|
194
199
|
const expected = named === 17 ? [17] : [1, 0, 1];
|
package/dist/hold.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export declare const HOLD_DEADLINE_MARGIN_MS = 2000;
|
|
|
32
32
|
* processor's frame encrypts first (Bolt, Adyen): those need their own rule. Null without a usable number.
|
|
33
33
|
*/
|
|
34
34
|
export declare function placeholderHoldRule(numbers: readonly string[], deadlineMs?: number): HoldOnlyRule | null;
|
|
35
|
+
export declare function encryptedCardHoldRule(deadlineMs?: number): HoldOnlyRule;
|
|
35
36
|
/** The hold-only rule a request is a card request under, or null. */
|
|
36
37
|
export declare function matchHoldOnlyRule(request: {
|
|
37
38
|
url: string;
|
|
@@ -99,6 +100,12 @@ export interface CardRequestHold {
|
|
|
99
100
|
readonly held: HeldCardRequest | null;
|
|
100
101
|
/** Card requests failed without being held: a second one, a retry, or one after a decision. */
|
|
101
102
|
readonly refused: number;
|
|
103
|
+
/**
|
|
104
|
+
* The hold-only rules armed beside the registry's recognisers (`bolt`, `placeholder_card`, `encrypted_card`). A
|
|
105
|
+
* processor whose card is encrypted in the page and may be sent inside the merchant's own request (Adyen's
|
|
106
|
+
* Advanced flow) is covered only with `encrypted_card`.
|
|
107
|
+
*/
|
|
108
|
+
readonly covers: readonly string[];
|
|
102
109
|
/** Resolves with the held request once one is held, or null after `timeoutMs` or once the hold is over. */
|
|
103
110
|
whenHeld(timeoutMs?: number): Promise<HeldCardRequest | null>;
|
|
104
111
|
release(input: HoldReleaseInput): Promise<HoldReleaseOutcome>;
|
|
@@ -149,6 +156,7 @@ export declare class HoldGate implements CardRequestHold {
|
|
|
149
156
|
get state(): HoldState;
|
|
150
157
|
get held(): HeldCardRequest | null;
|
|
151
158
|
get refused(): number;
|
|
159
|
+
get covers(): readonly string[];
|
|
152
160
|
/** Fetch patterns the hold-only rules need beside the registry's. */
|
|
153
161
|
patterns(): Array<{
|
|
154
162
|
urlPattern: string;
|
package/dist/hold.js
CHANGED
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
// - Any request whose body carries the placeholder card number (`placeholderNumbers`): a store's own card post or
|
|
18
18
|
// an unknown processor. The browser only ever holds the placeholder, so a body with its digits is a card request
|
|
19
19
|
// wherever it goes. In sandbox it may be released unchanged (it carries the documented test number only).
|
|
20
|
+
// - With `placeholderNumbers`, any request whose body carries a card a processor's frame encrypted in the page
|
|
21
|
+
// (Adyen's encryptedCardNumber, Braintree's legacy CSE, Worldline Connect's encryptedCustomerInput) sent to the
|
|
22
|
+
// merchant's own server instead of the processor (encryptedCardHoldRule). Held, then only cancelled.
|
|
20
23
|
// The hold records the processor and the endpoint (origin and path, any path credential masked), never a body or a
|
|
21
24
|
// header.
|
|
22
25
|
import { redactUrl } from './client.js';
|
|
@@ -54,6 +57,36 @@ export function placeholderHoldRule(numbers, deadlineMs = HOLD_DEFAULT_DEADLINE_
|
|
|
54
57
|
patterns: PLACEHOLDER_RESOURCE_TYPES.map((resourceType) => ({ urlPattern: '*', resourceType })),
|
|
55
58
|
};
|
|
56
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* A card a processor's own frame encrypted in the page, sent on inside the merchant's own request (its order or payment
|
|
62
|
+
* step) instead of to the processor: the placeholder's digits never appear, so placeholderHoldRule cannot see it, and
|
|
63
|
+
* no recogniser matches the merchant's URL. Adyen's card component on Salesforce Commerce, Magento and other Advanced
|
|
64
|
+
* (non-Sessions) integrations posts `encryptedCardNumber` (a JWE, `eyJ...`, or the legacy `adyenjs_0_1_...`) to the
|
|
65
|
+
* store's CheckoutServices-SubmitPayment / PlaceOrder: ca.coach.com's PlaceOrder answered 200 with no hold on
|
|
66
|
+
* 2026-10-23. Also caught: Braintree's legacy client-side encryption (`$bt4|javascript_...`) and Worldline/Ingenico
|
|
67
|
+
* Connect's `encryptedCustomerInput`. Matched in JSON, escaped JSON, URL-encoded and form bodies. Held, then only
|
|
68
|
+
* cancelled: no release can answer a request the vault never encrypted for.
|
|
69
|
+
*/
|
|
70
|
+
const Q = '(?:\\\\?"|%22|%5C%22|\\\\u0022)?';
|
|
71
|
+
const SEP = '\\s*(?::|%3A|=)\\s*';
|
|
72
|
+
// A multipart field: the name's closing quote, the part's remaining header lines, a blank line, then the value.
|
|
73
|
+
// The value is read on its own line only: never across a line break, and never a boundary line (`--...`), so an empty
|
|
74
|
+
// field followed by a long boundary is not a card.
|
|
75
|
+
const PART = '"?[^\\r\\n]{0,80}\\r?\\n(?:[^\\r\\n]{1,200}\\r?\\n){0,4}\\r?\\n[ \\t]*(?!--)';
|
|
76
|
+
const ENCRYPTED_CARD_BODY = new RegExp([
|
|
77
|
+
`encryptedCardNumber${Q}${SEP}${Q}\\s*(?:eyJ|adyenjs_|[A-Za-z0-9+/_$|.-]{40})`,
|
|
78
|
+
`encryptedCardNumber${PART}(?:eyJ|adyenjs_|[A-Za-z0-9+/_$|.-]{40})`,
|
|
79
|
+
`encryptedCustomerInput${PART}[A-Za-z0-9+/_.-]{40}`,
|
|
80
|
+
'adyenjs_0_[0-9]+_[0-9]+(?:\\$|%24)',
|
|
81
|
+
'\\$bt[0-9]\\|javascript_|%24bt[0-9](?:\\||%7C)javascript_',
|
|
82
|
+
`encryptedCustomerInput${Q}${SEP}${Q}\\s*[A-Za-z0-9+/_.-]{40}`,
|
|
83
|
+
].join('|'), 'i');
|
|
84
|
+
export function encryptedCardHoldRule(deadlineMs = HOLD_DEFAULT_DEADLINE_MS) {
|
|
85
|
+
return {
|
|
86
|
+
psp: 'encrypted_card', match: /^[a-z]+:\/\//i, body: ENCRYPTED_CARD_BODY, kind: 'store_card_post', deadlineMs, methods: ['POST', 'PUT', 'PATCH'],
|
|
87
|
+
patterns: PLACEHOLDER_RESOURCE_TYPES.map((resourceType) => ({ urlPattern: '*', resourceType })),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
57
90
|
/** The hold-only rule a request is a card request under, or null. */
|
|
58
91
|
export function matchHoldOnlyRule(request, rules) {
|
|
59
92
|
for (const rule of rules) {
|
|
@@ -127,11 +160,15 @@ export class HoldGate {
|
|
|
127
160
|
this.sandbox = sandbox;
|
|
128
161
|
this.emit = emit;
|
|
129
162
|
const placeholder = options.placeholderNumbers?.length ? placeholderHoldRule(options.placeholderNumbers) : null;
|
|
130
|
-
|
|
163
|
+
// A page the vault types its placeholder into is watched for every card leaving it, encrypted or not; the
|
|
164
|
+
// encrypted-card rule shares the placeholder rule's patterns, so it pauses nothing more. It comes first: a body
|
|
165
|
+
// carrying both the placeholder and an encrypted card is the encrypted card's, and is never continued.
|
|
166
|
+
this.rules = [BOLT_HOLD_RULE, ...(placeholder ? [encryptedCardHoldRule(), placeholder] : [])];
|
|
131
167
|
}
|
|
132
168
|
get state() { return this._state; }
|
|
133
169
|
get held() { return this._held ? { ...this._held } : null; }
|
|
134
170
|
get refused() { return this._refused; }
|
|
171
|
+
get covers() { return this.rules.map((r) => r.psp); }
|
|
135
172
|
/** Fetch patterns the hold-only rules need beside the registry's. */
|
|
136
173
|
patterns() { return this.rules.flatMap((r) => r.patterns); }
|
|
137
174
|
deadlineFor(psp, rule) {
|
|
@@ -245,7 +282,11 @@ export class HoldGate {
|
|
|
245
282
|
let settle;
|
|
246
283
|
const settled = new Promise((resolve) => { settle = resolve; });
|
|
247
284
|
if (!held.replayable) {
|
|
248
|
-
|
|
285
|
+
// Only the plain placeholder post may be sent on in sandbox: an encrypted card inside a merchant's own order or
|
|
286
|
+
// payment request (encrypted_card) is the order itself, and is never continued in any mode.
|
|
287
|
+
if (held.psp === 'encrypted_card')
|
|
288
|
+
return fail('encrypted_card_post');
|
|
289
|
+
if (held.kind === 'store_card_post' && held.psp === 'placeholder_card' && input.continuePlaceholder && this.sandbox) {
|
|
249
290
|
this.decide({ kind: 'continue', settle });
|
|
250
291
|
this.emit('card_request_released', { psp: held.psp, continued: true, amount: input.amount, currency: input.currency.toLowerCase(), upTo: !!input.upTo });
|
|
251
292
|
return settled;
|
package/dist/hosted-entry.js
CHANGED
|
@@ -18,11 +18,15 @@ import { PRZELEWY24_PSP, isPrzelewy24EntryUrl } from './przelewy24.js';
|
|
|
18
18
|
import { COMGATE_PSP, isComgateEntryUrl } from './comgate.js';
|
|
19
19
|
import { TRANSBANK_PSP, isTransbankEntryUrl } from './transbank.js';
|
|
20
20
|
import { BARION_PSP, isBarionEntryUrl } from './barion.js';
|
|
21
|
+
import { PAYPLUG_PSP, isPayplugEntryUrl } from './payplug.js';
|
|
22
|
+
import { EGHL_PSP, isEghlEntryUrl } from './eghl.js';
|
|
21
23
|
const HOSTED_GET_ENTRIES = new Map([
|
|
22
24
|
[PRZELEWY24_PSP, isPrzelewy24EntryUrl],
|
|
23
25
|
[COMGATE_PSP, isComgateEntryUrl],
|
|
24
26
|
[TRANSBANK_PSP, isTransbankEntryUrl],
|
|
25
27
|
[BARION_PSP, isBarionEntryUrl],
|
|
28
|
+
[PAYPLUG_PSP, isPayplugEntryUrl],
|
|
29
|
+
[EGHL_PSP, isEghlEntryUrl],
|
|
26
30
|
]);
|
|
27
31
|
/** Whether this processor's hosted page is entered with a GET this build reads exactly. */
|
|
28
32
|
export function hasHostedGetEntry(psp) {
|