@agent-cards/sdk 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,22 +6,27 @@ import { isPreparedSpreedlyRequest, isSpreedlyTokenRequest, SPREEDLY_TOKEN_ENDPO
6
6
  import { checkoutComPreparationUrl, isPreparedCheckoutComRequest } from './checkout-com.generated.js';
7
7
  import { classifyDeclaredMerchantRequest, classifyMerchantRequest } from './merchant-hosted.js';
8
8
  import { fiservCapturePinId, fiservPreparationUrl, isPreparedFiservRequest } from './fiserv.js';
9
+ import { cybersourceFlexPreparationUrl, isPreparedCybersourceFlexRequest } from './cybersource-flex.generated.js';
9
10
  export function preparationMode(psp) {
10
11
  return psp === 'adyen' || psp === 'fiserv' ? 'cse' : 'token';
11
12
  }
12
13
  /**
13
14
  * Shared endpoint processors cannot attest test/live mode from their URL or key prefix.
14
15
  * Adyen can: test and live are separate hosts and key prefixes. Fiserv's are separate
15
- * hosts too: connect (production) and connect-cert (sandbox).
16
+ * hosts too: connect (production) and connect-cert (sandbox). So are CyberSource
17
+ * Flex's: flex.cybersource.com is live, testflex.cybersource.com is test, and each has
18
+ * its own signing keys.
16
19
  */
17
20
  export function validPreparationEnvironment(psp, environment) {
18
21
  if (psp === 'bambora' || psp === 'mercado_pago' || psp === 'recurly' || psp === 'spreedly')
19
22
  return environment === 'shared';
20
- return ['square', 'braintree', 'worldpay', 'adyen', 'checkout_com', 'paysafe', 'fiserv'].includes(psp) && ['production', 'sandbox'].includes(environment);
23
+ return ['square', 'braintree', 'worldpay', 'adyen', 'checkout_com', 'paysafe', 'fiserv', 'cybersource_flex'].includes(psp) && ['production', 'sandbox'].includes(environment);
21
24
  }
22
25
  export function preparationEndpoint(psp, environment) {
23
26
  if (!validPreparationEnvironment(psp, environment))
24
27
  throw new Error('unsupported_preparation_processor');
28
+ if (psp === 'cybersource_flex')
29
+ return cybersourceFlexPreparationUrl(environment);
25
30
  if (psp === 'paysafe')
26
31
  return paysafePreparationUrl(environment);
27
32
  if (psp === 'fiserv')
@@ -90,6 +95,11 @@ function freshCardBody(psp, body) {
90
95
  export function matchesPreparedRequest(psp, environment, requestUrl, method, body, headers) {
91
96
  if (method.toUpperCase() !== 'POST' || !validPreparationEnvironment(psp, environment))
92
97
  return false;
98
+ // CyberSource Flex: this environment's exact tokens URL, application/jwt with only the headers a
99
+ // browser adds beside it, and a Microform field iframe's JWE. The adapters claim only after the
100
+ // Flex gate verified the issuing field iframe's capture context against that JWE's key.
101
+ if (psp === 'cybersource_flex')
102
+ return isPreparedCybersourceFlexRequest(requestUrl, method.toUpperCase(), body ?? null, environment, headers);
93
103
  if (psp === 'paysafe')
94
104
  return isPreparedPaysafeRequest(requestUrl, method, body ?? null, environment, headers);
95
105
  // Fiserv: that environment's card-capture endpoint exactly, the capture rules on the whole
@@ -6,7 +6,12 @@
6
6
  * site shares another. The list is served from the Agentcard API at runtime so
7
7
  * integrators pick up new processors without shipping a release.
8
8
  */
9
- export type Encoding = 'json' | 'form';
9
+ /**
10
+ * The shape of the card-bearing body. `jwe`: a compact JWE the processor's own card field
11
+ * sealed (CyberSource Flex), opaque to everyone but the processor; served only with the
12
+ * capability that reads its capture context.
13
+ */
14
+ export type Encoding = 'json' | 'form' | 'jwe';
10
15
  /**
11
16
  * How a paused request gets its card. `token` (the default when absent): the
12
17
  * cardholder's device calls the processor and the runtime replays the
@@ -76,10 +81,14 @@ export interface Recognizer {
76
81
  mode?: CheckoutMode;
77
82
  /**
78
83
  * Dark launch: a capability name the API attaches to an entry it serves only
79
- * to a client that declared it (?capabilities=). Absent on every entry in
80
- * BUILTIN_REGISTRY, because a gated entry is left out of the built-in
81
- * fallback. Carried verbatim by syncRegistry for a client that does declare
82
- * it, so the SDK never has to act on it.
84
+ * to a client that declared it (?capabilities=), from an org Agentcard
85
+ * enabled for the processor. Absent on every entry in BUILTIN_REGISTRY,
86
+ * because a gated entry is left out of the built-in fallback. Carried
87
+ * verbatim by syncRegistry for a client that does declare it
88
+ * (SUPPORTED_CAPABILITIES). authorize() refuses an entry whose capability
89
+ * this build does not list in SUPPORTED_CAPABILITIES
90
+ * (UnsupportedCapabilityError), which only a hand-built registry can hold.
91
+ * GMO names `gmo_shared_pk` and CyberSource Flex names `capture_context`.
83
92
  */
84
93
  capability?: string;
85
94
  /**
@@ -102,8 +111,9 @@ export interface Recognizer {
102
111
  /**
103
112
  * True when a processor's host environment is enforced: the API refuses a
104
113
  * create whose host environment disagrees with the row's test/live nature, and
105
- * the device sends only documented test cards to a sandbox host. Absent on
106
- * every entry today; carried verbatim by syncRegistry, and the SDK never acts
114
+ * the device sends only documented test cards to a sandbox host. GMO sets it
115
+ * (stg.token.mul-pay.jp is its test host), and as a dark entry it is never in
116
+ * BUILTIN_REGISTRY; carried verbatim by syncRegistry, and the SDK never acts
107
117
  * on it.
108
118
  */
109
119
  hostEnvironmentPinned?: boolean;
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: b51ce95dc43d8ab4a7576f0cd0b2309802bb124e15197d5026a2202524d6ccf0
3
+ // artifact-sha256: 26a574d3944a8fbf0c12ddc9378d2ae3772bd2cdc78f95a5db1b61c037ee5474
4
4
  // src/deep-freeze.js
5
5
  function deepFreeze(value) {
6
6
  if (value && typeof value === "object" && !Object.isFrozen(value)) {
@@ -632,6 +632,32 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
632
632
  hostEnvironmentPinned: true,
633
633
  encoding: "json",
634
634
  passthroughHeaders: ["^x-mp-client-type$"]
635
+ },
636
+ {
637
+ psp: "cybersource_flex",
638
+ // CyberSource Flex Microform v2. ONE card-bearing request: a POST from a Microform field
639
+ // iframe (the loader picks the number or the securityCode iframe at random) to the capture
640
+ // context's flx.origin + flx.path, /flex/v2/tokens, sent as `application/jwt; charset=utf-8`
641
+ // with a 30 s XHR timeout. The body is a compact JWE the iframe seals under the context's own
642
+ // key, so it is opaque: no injector exists (injectCard and prepareDeviceReplay refuse this
643
+ // psp), and the device verifies the context, rebuilds the iframe's plaintext with the real
644
+ // card and seals it itself (cybersource-flex-replay.js). The same URL also carries Microform
645
+ // v0.11 and Flex API v2 direct traffic, so recognition is not coverage: the SDK creates only
646
+ // for a POST from a reviewed v2 field iframe whose fragment record it holds.
647
+ // Excluded on purpose: api/apitest.cybersource.com (server APIs, never a browser card),
648
+ // /flex/v1/tokens, /flex/v2/public-keys/*, /microform/bundle/* and flex.in.cybersource.com
649
+ // (it resolves but serves neither keys nor bundles). No other entry shares these hosts.
650
+ // `capability` names the capture-context sidecar a client must send on create (the reviewed
651
+ // "sidecar" negotiation term): the entry stays out of BUILTIN_REGISTRY, is served only to a
652
+ // client that declares it, and is off for every org until one is opted in. testflex is the
653
+ // sandbox host and the environment is pinned (S7): test kids and testflex only on sandbox rows.
654
+ capability: "capture_context",
655
+ match: "(?:test)?flex\\.cybersource\\.com/flex/v2/tokens(?![\\w/-])",
656
+ hosts: ["^flex\\.cybersource\\.com$", "^testflex\\.cybersource\\.com$"],
657
+ sandboxHosts: ["^testflex\\.cybersource\\.com$"],
658
+ hostEnvironmentPinned: true,
659
+ encoding: "jwe",
660
+ passthroughHeaders: []
635
661
  }
636
662
  ]);
637
663
  var PSP_RECOGNIZERS = deepFreeze(PROCESSOR_DEFINITIONS.map(({ compose, encryptedCardFields, ...definition }) => definition));
@@ -682,10 +708,19 @@ var WITHOUT_CHECKOUT_SESSIONS = Object.freeze({
682
708
  var HEADER_NEVER_FORWARD = /^(cookie|set-cookie|authorization2|proxy-authorization|host|content-length|origin|referer)$/i;
683
709
  var ALLOWED_MEDIA_TYPES = {
684
710
  json: ["application/json", "text/plain"],
685
- form: ["application/x-www-form-urlencoded"]
711
+ form: ["application/x-www-form-urlencoded"],
712
+ jwe: ["application/jwt"]
686
713
  };
714
+ var JWE_CONTENT_TYPE = "application/jwt; charset=utf-8";
687
715
  function contentTypeFor(pspOrEntry, headers) {
688
716
  const rec = trustedClientRecognizer(pspOrEntry);
717
+ if (rec.encoding === "jwe") {
718
+ const captured = Object.entries(headers || {}).find(([k]) => /^content-type$/i.test(k));
719
+ const base2 = captured && typeof captured[1] === "string" ? captured[1].split(";")[0].trim().toLowerCase() : "";
720
+ if (!ALLOWED_MEDIA_TYPES.jwe.includes(base2))
721
+ throw new Error("Refusing to pay: this processor takes its sealed card only as application/jwt.");
722
+ return JWE_CONTENT_TYPE;
723
+ }
689
724
  const encoding = rec && rec.encoding === "form" ? "form" : "json";
690
725
  const allowed = ALLOWED_MEDIA_TYPES[encoding];
691
726
  const fallback = allowed[0];
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "catalog_version": "2026-09-15.1",
4
- "catalog_sha256": "a67f74c2466590d65338b8a909f4978fc4eebf2c71f20a8bda219cbb6989d010",
4
+ "catalog_sha256": "927bdc284703286962911cd6d9c13f6b2117f5a177db39c06eca506924f81db1",
5
5
  "baseline_version": "kernel-native-docs-2026-09-11.1",
6
6
  "baseline_sha256": "d86722f49633d8223fccae54cd3d485df4375aee06aa156a273cc689b7c8507c",
7
7
  "native_build": null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-cards/sdk",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
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",
@@ -27,8 +27,8 @@
27
27
  "_comment_build": "The public SDK keeps zero runtime dependencies. Its build vendors deterministic metadata and substitution artifacts from the internal payment core, and the voice streaming door from packages/voice/src/core (the phone package, @agent-cards/voice, ships it to phones); TypeScript remains pinned.",
28
28
  "build": "node ../payment-core/scripts/build.mjs && node scripts/generate-payment-core.mjs && node scripts/generate-voice-stream.mjs && npx -y -p typescript@5.9.3 tsc && node scripts/generate-preflight-contract.mjs",
29
29
  "prepublishOnly": "pnpm build",
30
- "test": "node test.mjs && node --test lifecycle.test.mjs merchant-abort.test.mjs preparation.test.mjs braintree.test.mjs pre-claim.test.mjs autopilot.test.mjs stripe-checkout.test.mjs payment-core.test.mjs prepared-processor.test.mjs merchant-hosted.test.mjs merchant-total.test.mjs sandbox-declaration.test.mjs minimum-delay.test.mjs paysafe.test.mjs payu.test.mjs attachment.test.mjs worker-targets.test.mjs mercado-checkout.test.mjs mercado-polling.test.mjs voice.test.mjs agentcard.test.mjs buy.test.mjs readme.test.mjs && node --test preflight-package.test.mjs preflight-collector.test.mjs && node --test kernel-native-qualification.test.mjs && node --test ../vault/scripts/recurly-validation/watch-duty-sdk-result.test.mjs",
31
- "test:browser": "node browser.test.mjs && node stripe-browser.test.mjs && node preparation-browser.test.mjs && node checkout-com-browser.test.mjs && node paysafe-preparation-browser.test.mjs && node owned-shop-browser.test.mjs && node spreedly-browser.test.mjs && node worker-browser.test.mjs && node adyen-merchant-hosted-browser.test.mjs && node fiserv-browser.test.mjs",
30
+ "test": "node test.mjs && node --test lifecycle.test.mjs merchant-abort.test.mjs preparation.test.mjs braintree.test.mjs pre-claim.test.mjs autopilot.test.mjs stripe-checkout.test.mjs payment-core.test.mjs prepared-processor.test.mjs merchant-hosted.test.mjs merchant-total.test.mjs sandbox-declaration.test.mjs minimum-delay.test.mjs paysafe.test.mjs payu.test.mjs attachment.test.mjs worker-targets.test.mjs mercado-checkout.test.mjs mercado-polling.test.mjs voice.test.mjs agentcard.test.mjs buy.test.mjs readme.test.mjs gmo.test.mjs cybersource-flex.test.mjs && node --test preflight-package.test.mjs preflight-collector.test.mjs && node --test kernel-native-qualification.test.mjs && node --test ../vault/scripts/recurly-validation/watch-duty-sdk-result.test.mjs",
31
+ "test:browser": "node browser.test.mjs && node stripe-browser.test.mjs && node preparation-browser.test.mjs && node checkout-com-browser.test.mjs && node paysafe-preparation-browser.test.mjs && node owned-shop-browser.test.mjs && node spreedly-browser.test.mjs && node worker-browser.test.mjs && node adyen-merchant-hosted-browser.test.mjs && node fiserv-browser.test.mjs && node gmo-browser.test.mjs && node cybersource-flex-browser.test.mjs",
32
32
  "check:payment-core": "node scripts/generate-payment-core.mjs --check",
33
33
  "check:voice-stream": "node scripts/generate-voice-stream.mjs --check",
34
34
  "test:preflight": "node --test preflight-package.test.mjs preflight-collector.test.mjs kernel-native-qualification.test.mjs",