@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.
Files changed (110) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/PREFLIGHT.md +7 -1
  3. package/README.md +197 -102
  4. package/dist/aci.d.ts +52 -22
  5. package/dist/aci.js +79 -15
  6. package/dist/adyen-merchant-hosted.generated.d.ts +2 -2
  7. package/dist/adyen-merchant-hosted.generated.js +586 -55
  8. package/dist/agent-amount.generated.js +1 -1
  9. package/dist/agentcard.d.ts +4 -5
  10. package/dist/agentcard.js +4 -5
  11. package/dist/amadeus.d.ts +9 -3
  12. package/dist/amadeus.js +33 -4
  13. package/dist/api-module.d.ts +1 -7
  14. package/dist/api-module.js +3 -5
  15. package/dist/{voice-retry.js → api-retry.js} +1 -1
  16. package/dist/api-types.d.ts +53 -0
  17. package/dist/{voice-types.js → api-types.js} +7 -2
  18. package/dist/approvals.d.ts +166 -0
  19. package/dist/approvals.js +213 -0
  20. package/dist/aurus.d.ts +33 -0
  21. package/dist/aurus.js +75 -0
  22. package/dist/builtin-registry.generated.js +1 -1
  23. package/dist/buy.d.ts +20 -101
  24. package/dist/buy.js +19 -74
  25. package/dist/capabilities.generated.js +1 -1
  26. package/dist/card-fields.generated.js +2864 -249
  27. package/dist/cardlink-vpos.d.ts +43 -0
  28. package/dist/cardlink-vpos.js +90 -0
  29. package/dist/cdp.d.ts +3 -13
  30. package/dist/cdp.js +244 -175
  31. package/dist/checkout-com.generated.js +5 -1
  32. package/dist/client.d.ts +170 -22
  33. package/dist/client.js +281 -40
  34. package/dist/creditguard.generated.d.ts +4 -4
  35. package/dist/cybersource-flex.generated.d.ts +39 -1
  36. package/dist/cybersource-flex.generated.js +100 -11
  37. package/dist/datatrans.generated.js +22 -1
  38. package/dist/eghl.d.ts +40 -0
  39. package/dist/eghl.js +72 -0
  40. package/dist/elavon-converge.generated.js +5 -3
  41. package/dist/gmo.generated.js +7 -2
  42. package/dist/hold.d.ts +8 -0
  43. package/dist/hold.js +43 -2
  44. package/dist/hosted-entry.js +4 -0
  45. package/dist/index.d.ts +9 -15
  46. package/dist/index.js +6 -10
  47. package/dist/lifecycle.d.ts +9 -61
  48. package/dist/lifecycle.js +24 -10
  49. package/dist/merchant-card-endpoints.generated.d.ts +6 -1
  50. package/dist/merchant-card-endpoints.generated.js +344 -19
  51. package/dist/merchant-hosted.d.ts +8 -1
  52. package/dist/nike-order.d.ts +51 -0
  53. package/dist/nike-order.js +81 -0
  54. package/dist/oceanpayment.d.ts +38 -0
  55. package/dist/oceanpayment.js +70 -0
  56. package/dist/paddle-tokenex.generated.js +111 -11
  57. package/dist/payplug.d.ts +41 -0
  58. package/dist/payplug.js +71 -0
  59. package/dist/payrails.generated.js +33 -8
  60. package/dist/pre-claim.d.ts +11 -0
  61. package/dist/pre-claim.js +130 -7
  62. package/dist/preflight-capabilities.generated.d.ts +0 -4
  63. package/dist/preflight-capabilities.generated.js +0 -5
  64. package/dist/preflight-catalog.json +200 -6
  65. package/dist/preflight-playwright.d.ts +2 -2
  66. package/dist/preflight-playwright.js +63 -14
  67. package/dist/preflight-schemas.json +18 -0
  68. package/dist/preflight.generated.d.ts +5 -4
  69. package/dist/preflight.generated.js +63 -3
  70. package/dist/preparation.d.ts +7 -0
  71. package/dist/preparation.js +21 -0
  72. package/dist/prepared-processor.d.ts +18 -5
  73. package/dist/prepared-processor.js +48 -8
  74. package/dist/primer.generated.js +2889 -268
  75. package/dist/processout.generated.d.ts +61 -0
  76. package/dist/processout.generated.js +577 -0
  77. package/dist/requests.js +1 -1
  78. package/dist/shiji-sts.generated.js +111 -11
  79. package/dist/stripe-checkout.generated.js +2761 -319
  80. package/dist/substitutions.generated.js +553 -26
  81. package/dist/tap-payments.generated.js +111 -11
  82. package/dist/twoc2p.generated.js +111 -11
  83. package/dist/url-masks/bluesnap.d.ts +2 -0
  84. package/dist/url-masks/bluesnap.js +15 -0
  85. package/dist/url-masks/oceanpayment.d.ts +2 -0
  86. package/dist/url-masks/oceanpayment.js +13 -0
  87. package/dist/url-masks/payplug.d.ts +2 -0
  88. package/dist/url-masks/payplug.js +13 -0
  89. package/dist/url-masks/vtex_payments.d.ts +2 -0
  90. package/dist/url-masks/vtex_payments.js +12 -0
  91. package/dist/url-masks.generated.d.ts +1 -1
  92. package/dist/url-masks.generated.js +6 -2
  93. package/dist/verifone.generated.js +42 -2
  94. package/dist/vtex-payments.generated.d.ts +127 -0
  95. package/dist/vtex-payments.generated.js +544 -0
  96. package/dist/webhooks.d.ts +2 -2
  97. package/dist/webhooks.js +2 -2
  98. package/examples/preflight/kernel-native/inventory.json +1 -1
  99. package/package.json +6 -8
  100. package/dist/languages.d.ts +0 -23
  101. package/dist/languages.js +0 -76
  102. package/dist/voice-pkce.d.ts +0 -49
  103. package/dist/voice-pkce.js +0 -147
  104. package/dist/voice-stream.generated.d.ts +0 -118
  105. package/dist/voice-stream.generated.js +0 -170
  106. package/dist/voice-types.d.ts +0 -735
  107. package/dist/voice.d.ts +0 -373
  108. package/dist/voice.js +0 -815
  109. package/readme.test.mjs +0 -53
  110. /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: c6a711b5dc964dbeb45863780001d99b79090f919b282fed9ae6fc06b4839681
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 url = `${origin}${FLEX_TOKENS_PATH}`;
258
- if (flx.origin !== origin || flx.path !== FLEX_TOKENS_PATH || own(flx, "tokensUrl") && flx.tokensUrl !== url) {
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-/.test(entry.type));
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: FLEX_TOKENS_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 FRAGMENT_MAX_LENGTH = 16384;
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 > FRAGMENT_MAX_LENGTH + 2048)
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 > FRAGMENT_MAX_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: 829167e03a7ffe9fc109ababe9755f1263a840fd5294ebec970e08d41aa9741b
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: 136e1c2b0fd2bdee0cfbf5efbbfaf1a92ace7d1cc52e17a3fd5a4584370e5e53
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;
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: a65199bcbf0b67546130fb4f2c12ba72c8f51a2ff5b4e3054e2bfcc29b06f95d
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
- if (!MODULUS_BYTES.includes(n.length) || !(n[0] & 128) || !(n[n.length - 1] & 1))
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
- this.rules = [BOLT_HOLD_RULE, ...(placeholder ? [placeholder] : [])];
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
- if (held.kind === 'store_card_post' && input.continuePlaceholder && this.sandbox) {
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;
@@ -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) {