@agent-cards/sdk 0.23.6 → 0.23.7

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.
@@ -12,6 +12,7 @@ import { CREDITGUARD_SANDBOX_HOST, creditguardPageProcessUrl, creditguardPrepara
12
12
  import { eprotectPreparationUrl, isPreparedEprotectRequest } from './eprotect.generated.js';
13
13
  import { amadeusEnvironment, amadeusPreparationUrls, amadeusRequestRefusal, isPreparedAmadeusRequest } from './amadeus-checkout.generated.js';
14
14
  import { isPreparedVerifoneRequest } from './verifone.js';
15
+ import { isPreparedSecureAcceptanceRequest, secureAcceptanceEnvironment, secureAcceptancePreparationUrl, secureAcceptanceRequestRefusal, } from './cybersource-secure-acceptance.generated.js';
15
16
  export function preparationMode(psp) {
16
17
  return psp === 'adyen' || psp === 'fiserv' || psp === 'verifone' ? 'cse' : 'token';
17
18
  }
@@ -25,12 +26,14 @@ export function preparationRequestMethod(psp) {
25
26
  * hosts too: connect (production) and connect-cert (sandbox). So are CyberSource
26
27
  * Flex's: flex.cybersource.com is live, testflex.cybersource.com is test, and each has
27
28
  * its own signing keys. A Verifone preparation pays in the environment of the key its reviewed
28
- * profile pins.
29
+ * profile pins. CyberSource Secure Acceptance's are separate hosts too: secureacceptance is
30
+ * live, testsecureacceptance is test.
29
31
  */
30
32
  export function validPreparationEnvironment(psp, environment) {
31
33
  if (psp === 'bambora' || psp === 'mercado_pago' || psp === 'recurly' || psp === 'spreedly')
32
34
  return environment === 'shared';
33
- return ['square', 'braintree', 'worldpay', 'adyen', 'checkout_com', 'paysafe', 'fiserv', 'cybersource_flex', 'aci', 'creditguard', 'worldpay_eprotect', 'amadeus_checkout', 'verifone'].includes(psp) && ['production', 'sandbox'].includes(environment);
35
+ return ['square', 'braintree', 'worldpay', 'adyen', 'checkout_com', 'paysafe', 'fiserv', 'cybersource_flex', 'aci', 'creditguard', 'worldpay_eprotect', 'amadeus_checkout', 'verifone',
36
+ 'cybersource_secure_acceptance'].includes(psp) && ['production', 'sandbox'].includes(environment);
34
37
  }
35
38
  /**
36
39
  * ACI's prepared endpoint, the literal the API names (backend checkout-prepared-processor.ts): its
@@ -69,6 +72,9 @@ export function preparationEndpoint(psp, environment) {
69
72
  return creditguardPreparationUrl(environment);
70
73
  if (psp === 'worldpay_eprotect')
71
74
  return eprotectPreparationUrl(environment);
75
+ // The token-create URL on the environment's host, as the API names it: the one request the claim binds.
76
+ if (psp === 'cybersource_secure_acceptance')
77
+ return secureAcceptancePreparationUrl(environment);
72
78
  // A Verifone endpoint is its reviewed profile's leg, never one per environment (verifone.ts).
73
79
  if (psp === 'verifone')
74
80
  throw new Error('unsupported_preparation_processor');
@@ -195,6 +201,20 @@ export function matchesPreparedRequest(psp, environment, requestUrl, method, bod
195
201
  }
196
202
  if (method.toUpperCase() !== 'POST' || !validPreparationEnvironment(psp, environment))
197
203
  return false;
204
+ // CyberSource Secure Acceptance: the token create on the prepared environment's host, admitted whole by
205
+ // payment-core (isPreparedSecureAcceptanceRequest, the rule the API and the Vault bind with too), naming
206
+ // the profile id the preparation carries, with its Origin, its Referer and its receipt page on the page
207
+ // the cardholder approved. A preparation that carries no profile id or no page never matches.
208
+ if (psp === 'cybersource_secure_acceptance') {
209
+ if (typeof binding.secureAcceptanceProfileId !== 'string' || typeof binding.merchantOrigin !== 'string')
210
+ return false;
211
+ try {
212
+ return isPreparedSecureAcceptanceRequest(requestUrl, 'POST', body ?? '', headers ?? {}, environment, { profileId: binding.secureAcceptanceProfileId, merchantOrigin: binding.merchantOrigin }) === true;
213
+ }
214
+ catch {
215
+ return false;
216
+ }
217
+ }
198
218
  // CyberSource Flex: this environment's exact tokens URL, application/jwt with only the headers a
199
219
  // browser adds beside it, and a Microform field iframe's JWE. The adapters claim only after the
200
220
  // Flex gate verified the issuing field iframe's capture context against that JWE's key.
@@ -285,8 +305,16 @@ export function matchesPreparedMerchantRequest(profileId, requestUrl, method, bo
285
305
  * A Worldpay eProtect preparation names the paypageId its registration must carry.
286
306
  * An Amadeus preparation binds its SDK's Host header to the page the cardholder approved
287
307
  * (merchantOrigin), as the API does.
308
+ * A CyberSource Secure Acceptance preparation names the profile id its token create must carry and
309
+ * the page it must come from, and its signed currency must be the approved one, as the API's create
310
+ * holds it.
288
311
  */
289
312
  export function matchesPreparation(preparation, requestUrl, method, body, headers) {
313
+ if (preparation.psp === 'cybersource_secure_acceptance') {
314
+ if (!matchesPreparedRequest(preparation.psp, preparation.environment, requestUrl, method, body, headers, { merchantOrigin: preparation.merchantOrigin, secureAcceptanceProfileId: preparation.secureAcceptanceProfileId }))
315
+ return false;
316
+ return secureAcceptanceClaimRefusal(requestUrl, body, headers, preparation.merchantOrigin, preparation.currency) === null;
317
+ }
290
318
  // A Verifone preparation pays only its profile's reviewed leg, as payment-core pauses it.
291
319
  if (preparation.psp === 'verifone') {
292
320
  return typeof preparation.merchantProfile === 'string' && method.toUpperCase() === 'POST'
@@ -303,6 +331,28 @@ export function matchesPreparation(preparation, requestUrl, method, body, header
303
331
  }
304
332
  return matchesPreparedRequest(preparation.psp, preparation.environment, requestUrl, method, body, headers, { merchantOrigin: preparation.merchantOrigin, paymentPageUrl: preparation.paymentPageUrl, paypageId: preparation.paypageId });
305
333
  }
334
+ /**
335
+ * What payment-core's request rule (secureAcceptanceRequestRefusal) refuses in a Secure Acceptance token
336
+ * create sent from the approved page, for the approved currency when one is given, or null when it
337
+ * refuses nothing. A rule that cannot judge the request refuses it as unreadable.
338
+ */
339
+ function secureAcceptanceClaimRefusal(requestUrl, body, headers, merchantOrigin, currency) {
340
+ if (typeof merchantOrigin !== 'string')
341
+ return 'origin_header';
342
+ let verdict;
343
+ try {
344
+ verdict = secureAcceptanceRequestRefusal({ url: requestUrl, method: 'POST', headers: { ...(headers ?? {}) }, body: body ?? '' }, { merchantOrigin, ...(typeof currency === 'string' ? { currency } : {}) });
345
+ }
346
+ catch {
347
+ return 'body_unreadable';
348
+ }
349
+ if (verdict === null || verdict === undefined)
350
+ return null;
351
+ if (typeof verdict === 'string')
352
+ return verdict;
353
+ const reason = verdict.reason;
354
+ return typeof reason === 'string' ? reason : 'body_unreadable';
355
+ }
306
356
  /**
307
357
  * The reason a claim reports for a request its preparation does not match. An Amadeus preparation
308
358
  * names what payment-core's Amadeus check refused on its own environment's card URL, as
@@ -312,7 +362,24 @@ export function matchesPreparation(preparation, requestUrl, method, body, header
312
362
  * header that does not name the approved page is `amadeus_header_binding`. Everything else, another
313
363
  * processor's request or the other environment's host included, is `checkout_changed`.
314
364
  */
315
- export function preparedRequestRefusal(psp, environment, requestUrl, body, headers, merchantOrigin) {
365
+ export function preparedRequestRefusal(psp, environment, requestUrl, body, headers, merchantOrigin, currency) {
366
+ // A Secure Acceptance token create on the prepared environment's host names what payment-core's request
367
+ // rule refused, as `secure_acceptance_<reason>`: a frame post with no Origin header is
368
+ // `secure_acceptance_origin_header`, a receipt page off the approved page `secure_acceptance_receipt_page`.
369
+ // Another profile id, another host or another request is `checkout_changed`.
370
+ if (psp === 'cybersource_secure_acceptance') {
371
+ let hostEnvironment = null;
372
+ try {
373
+ hostEnvironment = secureAcceptanceEnvironment(new URL(requestUrl).hostname);
374
+ }
375
+ catch {
376
+ hostEnvironment = null;
377
+ }
378
+ if (hostEnvironment !== environment)
379
+ return 'checkout_changed';
380
+ const reason = secureAcceptanceClaimRefusal(requestUrl, body, headers, merchantOrigin, currency);
381
+ return reason === null ? 'checkout_changed' : `secure_acceptance_${reason}`;
382
+ }
316
383
  if (psp !== 'amadeus_checkout' || amadeusEnvironment(requestUrl) !== environment)
317
384
  return 'checkout_changed';
318
385
  const merchantHost = merchantHostOf(merchantOrigin);
package/dist/registry.js CHANGED
@@ -10,6 +10,8 @@
10
10
  import { BUILTIN_REGISTRY } from './builtin-registry.generated.js';
11
11
  import { isEprotectTokenRequest } from './eprotect.generated.js';
12
12
  import { EPROTECT_PSP } from './eprotect.js';
13
+ import { isSecureAcceptanceTokenRequest } from './cybersource-secure-acceptance.generated.js';
14
+ import { SECURE_ACCEPTANCE_PSP } from './cybersource-secure-acceptance.js';
13
15
  export { BUILTIN_REGISTRY };
14
16
  /** True for a plain-http loopback address (`127.0.0.1` or `localhost`, any port); the only http a sandbox recogniser admits. */
15
17
  export function isLoopbackUrl(parsed) {
@@ -80,6 +82,10 @@ export function findRecognizer(url, registry = BUILTIN_REGISTRY, opts = {}) {
80
82
  return (registry.find((r) => {
81
83
  if (r.bareEndpoint === true && /[?#]/.test(parsed.href))
82
84
  return false;
85
+ // CyberSource Secure Acceptance's token create is its exact URL on its own two hosts and nowhere else:
86
+ // a fixture store pays it on testsecureacceptance.cybersource.com, never on a loopback path.
87
+ if (r.psp === SECURE_ACCEPTANCE_PSP)
88
+ return false;
83
89
  const anchored = new RegExp(`^(?:${r.match.source})$`, 'i');
84
90
  return literalHosts(r.hosts).some((host) => anchored.test(`${host}${parsed.pathname}`));
85
91
  }) ?? null);
@@ -110,7 +116,14 @@ export function findRecognizer(url, registry = BUILTIN_REGISTRY, opts = {}) {
110
116
  // the request: as on the server (payment-core resolveRecognizerForUrl),
111
117
  // only the vendor's exact registration is recognized, never another
112
118
  // /eProtect/paypage call.
113
- return r.psp !== EPROTECT_PSP || isEprotectTokenRequest(url, 'GET');
119
+ if (r.psp === EPROTECT_PSP)
120
+ return isEprotectTokenRequest(url, 'GET');
121
+ // CyberSource Secure Acceptance's token create is recognized as the server
122
+ // recognizes it: its exact URL, with no query, fragment, trailing dot or
123
+ // doubled slash. Any other spelling stays the stop's (pre-claim.ts).
124
+ if (r.psp === SECURE_ACCEPTANCE_PSP)
125
+ return isSecureAcceptanceTokenRequest(url, 'POST') === true;
126
+ return true;
114
127
  }) ?? null);
115
128
  }
116
129
  /**
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: 2a58cd64ab81d468d022c21c6f4d6be6e5be4cd29acd1ad8576b549981433afa
3
+ // artifact-sha256: ae602a804a759165b26cb804547bdc99792036a22cc355a6dba49cfb11e92897
4
4
  // src/deep-freeze.js
5
5
  function deepFreeze(value) {
6
6
  if (value && typeof value === "object" && !Object.isFrozen(value)) {
@@ -931,6 +931,37 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
931
931
  encoding: "jwe",
932
932
  passthroughHeaders: []
933
933
  },
934
+ {
935
+ psp: "cybersource_secure_acceptance",
936
+ // CyberSource Secure Acceptance's Checkout API (Silent Order POST), Arrow Electronics' card save:
937
+ // the merchant's page posts its server-signed fields and the unsigned card fields as one hidden
938
+ // form navigation into a frame, POST /silent/embedded/token/create, application/x-www-form-
939
+ // urlencoded, a token create at a signed amount of 0. PAN@ card_number (form), expiry @
940
+ // card_expiry_date (MM-YYYY), CVN @ card_cvn, network @ card_type. cybersource-secure-acceptance.js
941
+ // holds the request grammar, the reviewed merchants, the device's four-field fill and the reply
942
+ // rule; recognition also requires isSecureAcceptanceTokenRequest, so only the exact URL on the two
943
+ // hosts is this request. NOT here: /silent/pay, /silent/token/create, the token update paths and
944
+ // secureacceptance.in.cybersource.com, which the checkout SDK stops for every company, and Hosted
945
+ // Checkout (the same hosts without /silent/), where the agent types the card on CyberSource's page.
946
+ // The test host takes only CyberSource's test cards (S7).
947
+ // Ships dark: served only to an SDK that declares this capability, gated off for every org until
948
+ // an operator allowlists one, prepared before Save (the merchant's page asks its frame for the
949
+ // answer for 30 s), and never replayed by autopilot (EXCLUDED_OPERATIONS).
950
+ match: "(?:secureacceptance\\.cybersource\\.com|testsecureacceptance\\.cybersource\\.com)/silent/embedded/token/create(?![\\w/.-])",
951
+ hosts: ["^secureacceptance\\.cybersource\\.com$", "^testsecureacceptance\\.cybersource\\.com$"],
952
+ sandboxHosts: ["^testsecureacceptance\\.cybersource\\.com$"],
953
+ hostEnvironmentPinned: true,
954
+ capability: "cybersource_sa_token_create",
955
+ preparationRequired: true,
956
+ encoding: "form",
957
+ // The paused request's own Origin and Referer: the device sends them with the card, and the
958
+ // request rule holds both to the preparation's merchant origin. Nothing else rides.
959
+ passthroughHeaders: ["^origin$", "^referer$"],
960
+ cardFields: ["card_number"],
961
+ // A post with no card number on the same URL carries nothing of the agent's and continues untouched.
962
+ passWithoutCard: "(?:secureacceptance\\.cybersource\\.com|testsecureacceptance\\.cybersource\\.com)/silent/embedded/token/create",
963
+ request: { method: "POST", cardIn: "body", response: "html_page" }
964
+ },
934
965
  {
935
966
  psp: "juspay",
936
967
  // Juspay Express Checkout, the merchant-page card flow Swiggy web ships (reviewed bundle
@@ -1381,6 +1412,9 @@ var DATATRANS_TEST_PANS = Object.freeze([
1381
1412
  "36000010000022",
1382
1413
  "3550998599961037"
1383
1414
  ]);
1415
+ // src/cybersource-secure-acceptance.js
1416
+ var SA_BODY_LIMIT = 32 * 1024;
1417
+ var SA_REPLY_LIMIT = 64 * 1024;
1384
1418
  // src/amadeus-checkout.js
1385
1419
  var AMADEUS_CARD_URLS = Object.freeze({
1386
1420
  production: Object.freeze({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-cards/sdk",
3
- "version": "0.23.6",
3
+ "version": "0.23.7",
4
4
  "description": "Agentcard's SDK: one client, Agentcard, with two modules. checkout pauses a browser purchase and pays it with the person's own card; voice connects a person to your product once and approves a purchase by voice inside it. Card data never enters your process.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",