@agent-cards/sdk 0.23.4 → 0.23.5
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 +4 -0
- package/PREFLIGHT.md +2 -0
- package/README.md +1 -0
- package/dist/card-endpoint-page.d.ts +5 -3
- package/dist/cdp.js +7 -2
- package/dist/cybersource-secure-acceptance.d.ts +78 -0
- package/dist/cybersource-secure-acceptance.js +214 -0
- package/dist/lifecycle.d.ts +6 -3
- package/dist/lifecycle.js +5 -2
- package/dist/pre-claim.d.ts +1 -0
- package/dist/pre-claim.js +76 -9
- package/dist/preflight-catalog.json +81 -0
- package/dist/preflight-schemas.json +6 -0
- package/dist/preflight.generated.js +21 -1
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Stop the card your agent typed from reaching CyberSource through Secure Acceptance's Silent Order POST. A merchant that takes the card in its own form, such as Arrow Electronics, has its server sign the order fields, then posts them with the card fields (`card_number`, `card_expiry_date`, `card_cvn`) to a `/silent/` path on `secureacceptance.cybersource.com` (`testsecureacceptance.cybersource.com` in CyberSource's test environment, `secureacceptance.in.cybersource.com` in India); Arrow submits that form into a hidden iframe. No Agentcard payment takes that post over, and earlier releases let it through, so CyberSource received the dummy card. Both adapters now stop it for every company, whatever `syncRegistry()` served: a form the page submits as a page load, in the page or in a frame, gets the page that says Agentcard stopped the payment before the card reached CyberSource, and a fetch or an XHR fails. A `card_number` in the URL's query, as a form with method GET sends it, stops the request the same way. The controller reads `unsupported` with reason `unsupported_cybersource_secure_acceptance` and holds, like every `unsupported` reason: to pay another way on the same page, attach again. A post with no `card_number`, or an empty one, such as a payment with a stored `payment_token`, continues untouched, and so does Hosted Checkout, the same hosts without `/silent/`, where your agent types the card on CyberSource's own page. The SDK stops a post whose body it cannot read with a `blocked` event, `unreadable_cybersource_secure_acceptance_request`, and does not hold the checkout. TypeScript: the controller's `unsupported()` reasons gain `unsupported_cybersource_secure_acceptance`; code that switches exhaustively over them needs the new case.
|
|
6
|
+
|
|
7
|
+
- Name CyberSource Secure Acceptance's Silent Order POST in a preflight inspection. A form, a frame or a document on a `/silent/` path of `secureacceptance.cybersource.com`, `testsecureacceptance.cybersource.com` or `secureacceptance.in.cybersource.com` now names `cybersource_secure_acceptance`, and the result stays `unknown`: `processor_variant_unverified` for a visible form, and `payment_signal_inactive` for a hidden one such as Arrow's. The catalog's `catalog_sha256` changes; `catalog_version` stays `2026-09-15.1`.
|
|
8
|
+
|
|
5
9
|
- Stop PayPal's legacy Hosted Fields from sending the card your agent typed to PayPal's payment confirm. A checkout that loads PayPal's JS SDK with `components=hosted-fields`, such as Planet Golf UK's PayPal Checkout, takes the card in frames on `assets.braintreegateway.com` and sends it to `https://cors.api.paypal.com/v2/checkout/orders/{id}/confirm-payment-source` (`cors.api.sandbox.paypal.com` in PayPal's sandbox). No Agentcard payment takes that request over, and earlier releases let it through, so PayPal received the dummy card and declined it. Both adapters now fail that request in the browser for every company, whatever `syncRegistry()` served, and the controller reads `unsupported` with reason `unsupported_paypal_hosted_fields` and holds, like every `unsupported` reason: to pay another way on the same page, attach again. A confirm with no card number, such as a PayPal wallet payment, continues untouched. A confirm whose body the SDK cannot read is failed with a `blocked` event, `unreadable_paypal_request`, and the checkout is not held. A merchant that turns on PayPal's card installments also has the card number field send each typed number to PayPal's installment lookup, `/v1/credit/calculated-financing-options`; the SDK leaves that lookup alone, and it pays nothing. TypeScript: the controller's `unsupported()` reasons gain `unsupported_paypal_hosted_fields`; code that switches exhaustively over them needs the new case.
|
|
6
10
|
|
|
7
11
|
- Name PayPal's legacy Hosted Fields as PayPal in a preflight inspection, not as Braintree. Their card fields are braintree-web builds whose version carries `-payments-sdk` (`3.32.0-payments-sdk-dev` on Planet Golf UK), and inspection read the page as `braintree` with status `supported`. The frames now name `paypal`, and the result stays `unknown` with `processor_variant_unverified`. A braintree-web script from a `-payments-sdk` build is no longer Braintree evidence either, and a variant seen in several frames lists its limitation once. The catalog's `catalog_sha256` changes; `catalog_version` stays `2026-09-15.1`.
|
package/PREFLIGHT.md
CHANGED
|
@@ -209,6 +209,8 @@ The catalog also carries reviewed hosted, regional, and versioned variants obser
|
|
|
209
209
|
|
|
210
210
|
PayPal's legacy Hosted Fields (`paypal`) are identified from the card fields PayPal's JS SDK renders with `components=hosted-fields`: braintree-web builds whose version carries `-payments-sdk`, such as `3.32.0-payments-sdk-dev`, served from `assets.braintreegateway.com` as `hosted-fields-input-frame.min.html` and `hosted-fields-payments-sdk-tokenization-frame.min.html`. PayPal's frames send the card to PayPal, not to Braintree, so a page that shows them names `paypal` and returns `unknown` with `processor_variant_unverified`, and no braintree-web file from a `-payments-sdk` build counts as Braintree evidence. The SDK fails the card request those frames send; see the PayPal Hosted Fields row in the [README's processor table](./README.md#supported-processors).
|
|
211
211
|
|
|
212
|
+
An inspection identifies CyberSource Secure Acceptance's Silent Order POST (`cybersource_secure_acceptance`) from the form a merchant's own page posts the card from, and from a frame or a document on the same path: a `/silent/` path, such as `/silent/pay` or `/silent/embedded/token/create`, on `secureacceptance.cybersource.com`, `testsecureacceptance.cybersource.com` or `secureacceptance.in.cybersource.com`. No Vault mode takes that post over, so a page that shows the form names `cybersource_secure_acceptance` and returns `unknown`: with `processor_variant_unverified` when the form is visible, and with `payment_signal_inactive` when it is hidden, as on Arrow Electronics' checkout. An inspection does not name Hosted Checkout, the same hosts without `/silent/`. The SDK stops the card post; see the CyberSource Secure Acceptance row in the [README's processor table](./README.md#supported-processors).
|
|
213
|
+
|
|
212
214
|
Adyen's `supported` covers the Sessions payment request on Adyen's own hosts. Some Adyen merchants post the four encrypted fields to their own server instead, and their pages load the same secured fields, so an inspection identifies Adyen on both and cannot tell the two flows apart. The SDK pays a merchant's own endpoint only through a merchant profile Agentcard reviewed for it; the reviewed profiles (Dunelm and Cinemark) are in `observe`, so the SDK aborts their card requests and nothing is charged. The `actual_payment_request_validation` requirement is where that difference shows: check the paused request's host before treating an Adyen result as payable.
|
|
213
215
|
|
|
214
216
|
Authorize.Net Direct Post Method and SIM ship dark. The SDK pauses their `transact.dll` card form only for an org Agentcard enabled for them (capability `authorize_net_dpm_form`), and the bundled catalog leaves them out, so the table above does not list them and inspection answers for their forms exactly as it did before: the SIM hosted form on `secure.authorize.net` identifies Authorize.Net and returns `unknown` with `processor_variant_unverified`.
|
package/README.md
CHANGED
|
@@ -255,6 +255,7 @@ Coverage is specific to the processor request format, merchant setup, browser tr
|
|
|
255
255
|
| Mercado Pago | Card tokenization and prepared checkout are implemented. Guest Checkout Pro in Mexico also corrects the issuer for one native card association when the selected card has the same brand and type. One live MXN 40 Lotería Chida purchase with published SDK 0.10.0 completed automatically through Pay; the merchant confirmed paid status and PDF fulfillment. The historical SDK result remains unknown because the private receipt adapter rejected a relative download URL; a separate read with the corrected adapter confirms that same paid receipt. Independent processor capture/settlement and other country or integration paths remain unverified. |
|
|
256
256
|
| VGS Collect (Very Good Security; Wolt) | not supported: VGS's proxy aliases only submissions from its own iframe, so a replay from the cardholder's device is refused by the merchant (verified on Wolt, 2026-09-03). Not recognized, so the agent's browser is not paused there |
|
|
257
257
|
| PayPal Hosted Fields (the legacy card fields of PayPal's JS SDK; Planet Golf UK) | not supported: no Agentcard payment takes over the card these fields send to `cors.api.paypal.com`. The SDK fails that request for every company, so PayPal's payment confirm never receives the dummy card, and the controller reads `unsupported` with reason `unsupported_paypal_hosted_fields` and holds the attachment until you attach again; a PayPal wallet payment continues untouched |
|
|
258
|
+
| CyberSource Secure Acceptance Silent Order POST (the merchant's own card form posted to CyberSource; Arrow Electronics) | not supported: no Agentcard payment takes over the card a merchant's page posts to a `/silent/` path on `secureacceptance.cybersource.com` (`testsecureacceptance.cybersource.com` in CyberSource's test environment, `secureacceptance.in.cybersource.com` in India). The SDK stops that post for every company, so CyberSource never receives the dummy card: a form the page submits as a page load, in the page or in a frame, ends on the page that says Agentcard stopped the payment, and a fetch or an XHR fails. The controller reads `unsupported` with reason `unsupported_cybersource_secure_acceptance` and holds the attachment until you attach again; a post with no `card_number`, such as a payment with a stored `payment_token`, continues untouched. The SDK does not stop Hosted Checkout, where your agent types the card on CyberSource's own page |
|
|
258
259
|
| Adyen | supported (mode `cse`): the vault encrypts the card for Adyen on the cardholder's device and your browser sends it. Sessions flow: the paused request is `/checkoutshopper/v1/sessions/{id}/payments` on Adyen's own hosts. A merchant that posts the encrypted fields to its own server pauses only when Agentcard reviewed it as a merchant profile. Cinemark and Dick's Sporting Goods' own storefront take payments for a company Agentcard turned them on for; every other reviewed merchant is in `observe`, so its card requests are reported and aborted and nothing pays there yet (see "Adyen merchants that take the card on their own server"). Without a preparation, approval starts at Pay and must land before Adyen Web's own request timeout (observed at 60 seconds on Adyen Web 6.41 and 6.44; not enforced by the SDK); `prepare({ psp: 'adyen' })` moves the approval before Pay |
|
|
259
260
|
| ACI Worldwide COPYandPAY (Halfords) | dark launch: served only after `syncRegistry()` and only for an org Agentcard enabled; prepared checkout only. The card-number request is replayed from the cardholder's device, the page's own CVC and expiry requests are answered locally, and `/payment` continues once. See [Pay an ACI COPYandPAY checkout](#pay-an-aci-copyandpay-checkout) |
|
|
260
261
|
| Tranzila | supported (mode `hosted_form`): the cardholder finishes on Tranzila's own page; the paused form navigation resolves to a synthetic page, and you poll the merchant's order state |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The page a paused card form navigation is answered with when Agentcard stops it
|
|
3
|
-
* (card-endpoint.ts, a profile whose `navigation` is 'top' or 'frame'
|
|
3
|
+
* (card-endpoint.ts, a profile whose `navigation` is 'top' or 'frame', and a form a
|
|
4
|
+
* merchant's page posts to CyberSource Secure Acceptance, cybersource-secure-acceptance.ts).
|
|
4
5
|
*
|
|
5
6
|
* A merchant's form that the page submits as a page load leaves the agent's browser
|
|
6
7
|
* waiting for a page, so an aborted request is the wrong answer: it renders an error
|
|
@@ -24,7 +25,8 @@
|
|
|
24
25
|
* Self-contained HTML: no external resource, no script that runs (the one `<script>` is
|
|
25
26
|
* `application/json`), readable by a person in one paragraph and by an agent through the
|
|
26
27
|
* meta tag, the header and the JSON block. It never carries the endpoint URL, the paused
|
|
27
|
-
* body or the merchant's answer: only the profile's own merchant name,
|
|
28
|
+
* body or the merchant's answer: only the profile's own merchant name, or CyberSource for a
|
|
29
|
+
* Secure Acceptance post, escaped.
|
|
28
30
|
*/
|
|
29
31
|
import type { SyntheticPage } from './hosted-form.js';
|
|
30
32
|
import type { CheckoutState } from './lifecycle.js';
|
|
@@ -45,7 +47,7 @@ export declare function refusedNavigationOutcome(state: {
|
|
|
45
47
|
export interface CardEndpointStoppedPageInput {
|
|
46
48
|
outcome: CardEndpointStoppedOutcome;
|
|
47
49
|
reason: CardEndpointStoppedReason;
|
|
48
|
-
/** The profile's own merchant name (never the integrator's or the page's). */
|
|
50
|
+
/** The profile's own merchant name, or CyberSource for a Secure Acceptance post (never the integrator's or the page's). */
|
|
49
51
|
merchant: string;
|
|
50
52
|
/** The authorization, once one exists. */
|
|
51
53
|
authorizationId?: string | null;
|
package/dist/cdp.js
CHANGED
|
@@ -13,6 +13,7 @@ import { DatatransRepeats } from './datatrans.js';
|
|
|
13
13
|
import { SHIJI_STS_COMPANION_WINDOW_MS, ShijiStsCompanions, ShijiStsError, isShijiStsHost, shijiStsTokenContinuation, } from './shiji-sts.js';
|
|
14
14
|
import { isArmedEkashuUrl } from './ekashu.js';
|
|
15
15
|
import { PAYPAL_HOSTED_FIELDS_PATTERNS, isPaypalHostedFieldsConfirm } from './paypal-hosted-fields.js';
|
|
16
|
+
import { CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS, isCybersourceSecureAcceptanceUrl } from './cybersource-secure-acceptance.js';
|
|
16
17
|
import { CheckoutAttachmentError, attachmentDeadline, attachmentFailure, withinAttachmentDeadline } from './attachment.js';
|
|
17
18
|
import { cseBody } from './cse-body.js';
|
|
18
19
|
import { STRIPE_SETUP_INTENT_CONFIRM, setupConfirmPayingWith } from './stripe-setup.js';
|
|
@@ -1913,10 +1914,12 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1913
1914
|
// PayPal's legacy Hosted Fields confirm is armed for every org, whatever the
|
|
1914
1915
|
// registry serves: one that carries the typed card is paused only to be
|
|
1915
1916
|
// refused, and one with no card continues (see paypal-hosted-fields.ts).
|
|
1917
|
+
// CyberSource Secure Acceptance's /silent/ paths are armed the same way, form
|
|
1918
|
+
// navigations included (see cybersource-secure-acceptance.ts).
|
|
1916
1919
|
const urlPatterns = [...new Set([...(derived.length > 0 ? derived : FALLBACK_CARD_PATTERNS), ...guards.patterns,
|
|
1917
1920
|
...(stripeCheckout.isEnabled() ? ['https://api.stripe.com/*'] : []), MERCADO_CHECKOUT_PATTERN, ...merchantPatterns,
|
|
1918
1921
|
...(recognizesGmo(opts.vault) ? GMO_TOKEN_V1_PATTERNS : []), ...juspayCheckout.urlPatterns(), ...cardEndpointPatterns(),
|
|
1919
|
-
...(payrails ? PAYRAILS_GUARD_PATTERNS : []), ...PAYPAL_HOSTED_FIELDS_PATTERNS])];
|
|
1922
|
+
...(payrails ? PAYRAILS_GUARD_PATTERNS : []), ...PAYPAL_HOSTED_FIELDS_PATTERNS, ...CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS])];
|
|
1920
1923
|
const arm = async (sessionId, resume = false, worker = false) => {
|
|
1921
1924
|
const key = sessionId ?? '__root__';
|
|
1922
1925
|
if (armed.has(key))
|
|
@@ -3189,7 +3192,9 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3189
3192
|
// host: the card page's post and its 3-D Secure calls reach the NMI gate and fail.
|
|
3190
3193
|
|| isArmedEkashuUrl(opts.vault, url.toString())
|
|
3191
3194
|
// PayPal's legacy Hosted Fields confirm, for every org, as attachToCdp arms it (paypal-hosted-fields.ts).
|
|
3192
|
-
|| isPaypalHostedFieldsConfirm(url.toString())
|
|
3195
|
+
|| isPaypalHostedFieldsConfirm(url.toString())
|
|
3196
|
+
// CyberSource Secure Acceptance's /silent/ paths, for every org, as attachToCdp arms them (cybersource-secure-acceptance.ts).
|
|
3197
|
+
|| isCybersourceSecureAcceptanceUrl(url.toString()), async (route) => {
|
|
3193
3198
|
const request = route.request();
|
|
3194
3199
|
// When this request paused, and the page it came from: the merchant's total is read as of now.
|
|
3195
3200
|
const pausedAt = Date.now();
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CyberSource Secure Acceptance's Silent Order POST, which this SDK refuses for every company.
|
|
3
|
+
*
|
|
4
|
+
* CyberSource's Secure Acceptance Checkout API, formerly Silent Order POST, takes the card from a form
|
|
5
|
+
* on the merchant's own page. The merchant's server signs the order fields, and the shopper's browser
|
|
6
|
+
* posts them, with the card fields left unsigned, to a /silent/ path on one of CyberSource's Secure
|
|
7
|
+
* Acceptance hosts: /silent/pay, /silent/token/create, /silent/token/update, and the iframe form of
|
|
8
|
+
* each under /silent/embedded/ (CyberSource's endpoint table, read 2026-10-08). Arrow Electronics'
|
|
9
|
+
* checkout (its public bundles, read 2026-10-08; no card was sent) builds that form once its server
|
|
10
|
+
* has signed the fields and submits it into a hidden iframe:
|
|
11
|
+
*
|
|
12
|
+
* <iframe name="secureAcceptanceiFrame" style="display:none">
|
|
13
|
+
* <form action="https://secureacceptance.cybersource.com/silent/embedded/token/create" method="post"
|
|
14
|
+
* target="secureAcceptanceiFrame">: access_key, profile_id, transaction_type=create_payment_token,
|
|
15
|
+
* amount=0 and the other signed fields with their signature, then card_type, card_number,
|
|
16
|
+
* card_expiry_date (MM-YYYY) and card_cvn
|
|
17
|
+
*
|
|
18
|
+
* No Vault mode takes this post over, so no company can pay it, and an SDK without this guard let the
|
|
19
|
+
* card the agent typed reach CyberSource. Both adapters arm /silent/ on the three hosts whatever the
|
|
20
|
+
* synced registry serves, over https and http, each host also spelled with the trailing dot of a fully
|
|
21
|
+
* qualified name, and the pre-claim gate (cybersourceSecureAcceptancePreClaim, pre-claim.ts) judges
|
|
22
|
+
* each request there, whether a fetch, an XHR or a form navigation of the page or of a frame sent it,
|
|
23
|
+
* by its body and by its URL's query (cybersourceSecureAcceptanceRequest):
|
|
24
|
+
* - a card_number that holds a value: stop it, so CyberSource never receives the card. A form
|
|
25
|
+
* navigation is answered with the stopped page (card-endpoint-page.ts), which names CyberSource,
|
|
26
|
+
* since an aborted page load renders nothing and invites another click on Pay; any other request
|
|
27
|
+
* fails. The checkout reads `unsupported` with reason `unsupported_cybersource_secure_acceptance`
|
|
28
|
+
* and holds, as every unsupported refusal does;
|
|
29
|
+
* - no card_number, or an empty one, as in a payment with a stored payment_token: continue untouched;
|
|
30
|
+
* - a body the adapter could not read: stop that request alone, since nothing proves it carries no card.
|
|
31
|
+
* A request the synced registry recognizes as a card request belongs to that recognizer instead; no
|
|
32
|
+
* build serves one today. Hosted Checkout, the same hosts without /silent/, takes the card on
|
|
33
|
+
* CyberSource's own page, and neither adapter arms it.
|
|
34
|
+
*/
|
|
35
|
+
export declare const CYBERSOURCE_SECURE_ACCEPTANCE_REASON: "unsupported_cybersource_secure_acceptance";
|
|
36
|
+
/** Where the stopped page says the card was going. */
|
|
37
|
+
export declare const CYBERSOURCE_SECURE_ACCEPTANCE_RECIPIENT = "CyberSource";
|
|
38
|
+
/** CyberSource's Secure Acceptance hosts: production, test, and production in India. */
|
|
39
|
+
export declare const CYBERSOURCE_SECURE_ACCEPTANCE_HOSTS: readonly string[];
|
|
40
|
+
/**
|
|
41
|
+
* CDP Fetch patterns for every /silent/ path on the three hosts, armed on every attachment, over https
|
|
42
|
+
* and over http, each host also with its trailing dot: Chrome sends a URL spelled either way unchanged,
|
|
43
|
+
* and DNS reads both spellings as the same host, so a page's form posted to either reaches CyberSource.
|
|
44
|
+
* The * before silent/ also pauses a path that doubles its slash, as a base URL that ends in a slash
|
|
45
|
+
* joined to /silent/pay writes it, which a server that merges slashes reads as /silent/pay. The gate
|
|
46
|
+
* judges each path with its slashes merged (isCybersourceSecureAcceptanceUrl), and anything else the *
|
|
47
|
+
* pauses, such as /billing/silent/pay, continues untouched. A pattern matches the canonical URL Chrome
|
|
48
|
+
* sends, whose host is lower case and whose standard port is dropped; no URL without silent/ is armed.
|
|
49
|
+
*/
|
|
50
|
+
export declare const CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS: readonly string[];
|
|
51
|
+
/**
|
|
52
|
+
* Whether this URL is a /silent/ path on one of the three hosts: https or http, the host with or without
|
|
53
|
+
* its trailing dot, on the scheme's standard port, with no credentials, and its path read with each run
|
|
54
|
+
* of slashes merged into one, as a server that merges slashes reads it.
|
|
55
|
+
*/
|
|
56
|
+
export declare function isCybersourceSecureAcceptanceUrl(url: string): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* What a request to a /silent/ path carries: 'card' when card_number holds a value in the URL's query,
|
|
59
|
+
* on any method, since a form whose method is GET puts its fields there; otherwise the body's verdict
|
|
60
|
+
* (cybersourceSecureAcceptanceBody) for a method that sends one, and 'no_card' for a GET, a HEAD or a
|
|
61
|
+
* CORS preflight.
|
|
62
|
+
*/
|
|
63
|
+
export declare function cybersourceSecureAcceptanceRequest(url: string, method: string, body: string | null, contentType?: string): 'card' | 'no_card' | 'unreadable';
|
|
64
|
+
/**
|
|
65
|
+
* What a Silent Order POST's body carries: 'card' when any reading of it finds a field named card_number
|
|
66
|
+
* that holds a value, 'no_card' when none does, and 'unreadable' for a body the adapter could not read
|
|
67
|
+
* (null), or a multipart body whose parts cannot be read and in which no reading finds a card, a
|
|
68
|
+
* multipart content type with no boundary included.
|
|
69
|
+
*
|
|
70
|
+
* The page writes both the bytes and their content type, and a server reads the bytes by that type, so
|
|
71
|
+
* the gate trusts no one reading. It reads every body as URL-encoded pairs (split on & and line by line,
|
|
72
|
+
* which also reads a plain-text form's name=value lines), as JSON at any depth, and as a multipart form:
|
|
73
|
+
* by its parts, when its content type names a boundary or it opens with a delimiter line and a part
|
|
74
|
+
* header, and by every part header that names card_number, wherever it sits. A card in any reading stops
|
|
75
|
+
* the post, so a body whose content type names one encoding and whose bytes hold another, a URL-encoded
|
|
76
|
+
* body that also parses as JSON, say, cannot carry the card past the gate.
|
|
77
|
+
*/
|
|
78
|
+
export declare function cybersourceSecureAcceptanceBody(body: string | null, contentType?: string): 'card' | 'no_card' | 'unreadable';
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CyberSource Secure Acceptance's Silent Order POST, which this SDK refuses for every company.
|
|
3
|
+
*
|
|
4
|
+
* CyberSource's Secure Acceptance Checkout API, formerly Silent Order POST, takes the card from a form
|
|
5
|
+
* on the merchant's own page. The merchant's server signs the order fields, and the shopper's browser
|
|
6
|
+
* posts them, with the card fields left unsigned, to a /silent/ path on one of CyberSource's Secure
|
|
7
|
+
* Acceptance hosts: /silent/pay, /silent/token/create, /silent/token/update, and the iframe form of
|
|
8
|
+
* each under /silent/embedded/ (CyberSource's endpoint table, read 2026-10-08). Arrow Electronics'
|
|
9
|
+
* checkout (its public bundles, read 2026-10-08; no card was sent) builds that form once its server
|
|
10
|
+
* has signed the fields and submits it into a hidden iframe:
|
|
11
|
+
*
|
|
12
|
+
* <iframe name="secureAcceptanceiFrame" style="display:none">
|
|
13
|
+
* <form action="https://secureacceptance.cybersource.com/silent/embedded/token/create" method="post"
|
|
14
|
+
* target="secureAcceptanceiFrame">: access_key, profile_id, transaction_type=create_payment_token,
|
|
15
|
+
* amount=0 and the other signed fields with their signature, then card_type, card_number,
|
|
16
|
+
* card_expiry_date (MM-YYYY) and card_cvn
|
|
17
|
+
*
|
|
18
|
+
* No Vault mode takes this post over, so no company can pay it, and an SDK without this guard let the
|
|
19
|
+
* card the agent typed reach CyberSource. Both adapters arm /silent/ on the three hosts whatever the
|
|
20
|
+
* synced registry serves, over https and http, each host also spelled with the trailing dot of a fully
|
|
21
|
+
* qualified name, and the pre-claim gate (cybersourceSecureAcceptancePreClaim, pre-claim.ts) judges
|
|
22
|
+
* each request there, whether a fetch, an XHR or a form navigation of the page or of a frame sent it,
|
|
23
|
+
* by its body and by its URL's query (cybersourceSecureAcceptanceRequest):
|
|
24
|
+
* - a card_number that holds a value: stop it, so CyberSource never receives the card. A form
|
|
25
|
+
* navigation is answered with the stopped page (card-endpoint-page.ts), which names CyberSource,
|
|
26
|
+
* since an aborted page load renders nothing and invites another click on Pay; any other request
|
|
27
|
+
* fails. The checkout reads `unsupported` with reason `unsupported_cybersource_secure_acceptance`
|
|
28
|
+
* and holds, as every unsupported refusal does;
|
|
29
|
+
* - no card_number, or an empty one, as in a payment with a stored payment_token: continue untouched;
|
|
30
|
+
* - a body the adapter could not read: stop that request alone, since nothing proves it carries no card.
|
|
31
|
+
* A request the synced registry recognizes as a card request belongs to that recognizer instead; no
|
|
32
|
+
* build serves one today. Hosted Checkout, the same hosts without /silent/, takes the card on
|
|
33
|
+
* CyberSource's own page, and neither adapter arms it.
|
|
34
|
+
*/
|
|
35
|
+
export const CYBERSOURCE_SECURE_ACCEPTANCE_REASON = 'unsupported_cybersource_secure_acceptance';
|
|
36
|
+
/** Where the stopped page says the card was going. */
|
|
37
|
+
export const CYBERSOURCE_SECURE_ACCEPTANCE_RECIPIENT = 'CyberSource';
|
|
38
|
+
/** CyberSource's Secure Acceptance hosts: production, test, and production in India. */
|
|
39
|
+
export const CYBERSOURCE_SECURE_ACCEPTANCE_HOSTS = Object.freeze([
|
|
40
|
+
'secureacceptance.cybersource.com', 'testsecureacceptance.cybersource.com', 'secureacceptance.in.cybersource.com',
|
|
41
|
+
]);
|
|
42
|
+
/**
|
|
43
|
+
* CDP Fetch patterns for every /silent/ path on the three hosts, armed on every attachment, over https
|
|
44
|
+
* and over http, each host also with its trailing dot: Chrome sends a URL spelled either way unchanged,
|
|
45
|
+
* and DNS reads both spellings as the same host, so a page's form posted to either reaches CyberSource.
|
|
46
|
+
* The * before silent/ also pauses a path that doubles its slash, as a base URL that ends in a slash
|
|
47
|
+
* joined to /silent/pay writes it, which a server that merges slashes reads as /silent/pay. The gate
|
|
48
|
+
* judges each path with its slashes merged (isCybersourceSecureAcceptanceUrl), and anything else the *
|
|
49
|
+
* pauses, such as /billing/silent/pay, continues untouched. A pattern matches the canonical URL Chrome
|
|
50
|
+
* sends, whose host is lower case and whose standard port is dropped; no URL without silent/ is armed.
|
|
51
|
+
*/
|
|
52
|
+
export const CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS = Object.freeze(CYBERSOURCE_SECURE_ACCEPTANCE_HOSTS.flatMap((host) => ['https', 'http'].flatMap((scheme) => [`${scheme}://${host}/*silent/*`, `${scheme}://${host}./*silent/*`])));
|
|
53
|
+
/**
|
|
54
|
+
* Whether this URL is a /silent/ path on one of the three hosts: https or http, the host with or without
|
|
55
|
+
* its trailing dot, on the scheme's standard port, with no credentials, and its path read with each run
|
|
56
|
+
* of slashes merged into one, as a server that merges slashes reads it.
|
|
57
|
+
*/
|
|
58
|
+
export function isCybersourceSecureAcceptanceUrl(url) {
|
|
59
|
+
let parsed;
|
|
60
|
+
try {
|
|
61
|
+
parsed = new URL(url);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return (parsed.protocol === 'https:' || parsed.protocol === 'http:') && !parsed.port && !parsed.username && !parsed.password
|
|
67
|
+
&& CYBERSOURCE_SECURE_ACCEPTANCE_HOSTS.includes(parsed.hostname.toLowerCase().replace(/\.$/, ''))
|
|
68
|
+
&& parsed.pathname.replace(/\/{2,}/g, '/').startsWith('/silent/');
|
|
69
|
+
}
|
|
70
|
+
/** Methods that send no body: only the URL's query can carry a card on them. */
|
|
71
|
+
const BODILESS = new Set(['GET', 'HEAD', 'OPTIONS']);
|
|
72
|
+
/**
|
|
73
|
+
* What a request to a /silent/ path carries: 'card' when card_number holds a value in the URL's query,
|
|
74
|
+
* on any method, since a form whose method is GET puts its fields there; otherwise the body's verdict
|
|
75
|
+
* (cybersourceSecureAcceptanceBody) for a method that sends one, and 'no_card' for a GET, a HEAD or a
|
|
76
|
+
* CORS preflight.
|
|
77
|
+
*/
|
|
78
|
+
export function cybersourceSecureAcceptanceRequest(url, method, body, contentType) {
|
|
79
|
+
let query = '';
|
|
80
|
+
try {
|
|
81
|
+
query = new URL(url).search;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
query = '';
|
|
85
|
+
}
|
|
86
|
+
if (carriesCardNumber(formFields(query)))
|
|
87
|
+
return 'card';
|
|
88
|
+
return BODILESS.has(String(method).toUpperCase()) ? 'no_card' : cybersourceSecureAcceptanceBody(body, contentType);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* What a Silent Order POST's body carries: 'card' when any reading of it finds a field named card_number
|
|
92
|
+
* that holds a value, 'no_card' when none does, and 'unreadable' for a body the adapter could not read
|
|
93
|
+
* (null), or a multipart body whose parts cannot be read and in which no reading finds a card, a
|
|
94
|
+
* multipart content type with no boundary included.
|
|
95
|
+
*
|
|
96
|
+
* The page writes both the bytes and their content type, and a server reads the bytes by that type, so
|
|
97
|
+
* the gate trusts no one reading. It reads every body as URL-encoded pairs (split on & and line by line,
|
|
98
|
+
* which also reads a plain-text form's name=value lines), as JSON at any depth, and as a multipart form:
|
|
99
|
+
* by its parts, when its content type names a boundary or it opens with a delimiter line and a part
|
|
100
|
+
* header, and by every part header that names card_number, wherever it sits. A card in any reading stops
|
|
101
|
+
* the post, so a body whose content type names one encoding and whose bytes hold another, a URL-encoded
|
|
102
|
+
* body that also parses as JSON, say, cannot carry the card past the gate.
|
|
103
|
+
*/
|
|
104
|
+
export function cybersourceSecureAcceptanceBody(body, contentType) {
|
|
105
|
+
if (typeof body !== 'string')
|
|
106
|
+
return 'unreadable';
|
|
107
|
+
const delimiter = multipartDelimiter(body, typeof contentType === 'string' ? contentType : '');
|
|
108
|
+
const parts = delimiter === undefined ? [] : delimiter === null ? null : multipartFields(body, delimiter);
|
|
109
|
+
if (carriesCardNumber(formFields(body)) || carriesCardNumber(jsonFields(body)) || carriesCardNumber(parts ?? [])
|
|
110
|
+
|| multipartNamesCardNumber(body, delimiter ?? undefined))
|
|
111
|
+
return 'card';
|
|
112
|
+
return parts === null ? 'unreadable' : 'no_card';
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Whether a field named card_number holds a value. The name matches in any case and every occurrence
|
|
116
|
+
* counts; an empty value, or one of whitespace only, holds no card.
|
|
117
|
+
*/
|
|
118
|
+
function carriesCardNumber(fields) {
|
|
119
|
+
return fields.some(([name, value]) => name.trim().toLowerCase() === 'card_number'
|
|
120
|
+
&& value !== undefined && value !== null && !(typeof value === 'string' && value.trim() === ''));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The delimiter line of a multipart body: the one its content type names, or, whatever its content type
|
|
124
|
+
* says, the one it opens with when a part header follows. Undefined for a body that is not multipart,
|
|
125
|
+
* and null for a multipart content type that names no boundary.
|
|
126
|
+
*/
|
|
127
|
+
function multipartDelimiter(body, contentType) {
|
|
128
|
+
if (/^\s*multipart\//i.test(contentType)) {
|
|
129
|
+
const boundary = /;\s*boundary=(?:"([^"]+)"|([^;\s]+))/i.exec(contentType);
|
|
130
|
+
return boundary ? `--${boundary[1] ?? boundary[2]}` : null;
|
|
131
|
+
}
|
|
132
|
+
return /^(--[^\r\n]+)\r?\n[^\s:]+:/.exec(body)?.[1];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A multipart body's named parts, or null when a body with content never names its delimiter or one
|
|
136
|
+
* of its parts has no blank line after its headers. A part takes every name its headers give it.
|
|
137
|
+
*/
|
|
138
|
+
function multipartFields(body, delimiter) {
|
|
139
|
+
if (!body.includes(delimiter))
|
|
140
|
+
return body.trim() === '' ? [] : null;
|
|
141
|
+
const fields = [];
|
|
142
|
+
for (const part of body.split(delimiter).slice(1)) {
|
|
143
|
+
if (part.startsWith('--'))
|
|
144
|
+
break; // the closing delimiter
|
|
145
|
+
const blank = /\r?\n\r?\n/.exec(part);
|
|
146
|
+
if (!blank)
|
|
147
|
+
return null;
|
|
148
|
+
const value = part.slice(blank.index + blank[0].length).replace(/\r?\n$/, '');
|
|
149
|
+
for (const name of headerNames(part.slice(0, blank.index)))
|
|
150
|
+
fields.push([name, value]);
|
|
151
|
+
}
|
|
152
|
+
return fields;
|
|
153
|
+
}
|
|
154
|
+
/** Every name a part's headers give it, quoted or bare, whichever header carries it. */
|
|
155
|
+
function headerNames(headers) {
|
|
156
|
+
return [...headers.matchAll(/;\s*name\s*=\s*(?:"([^"]*)"|([^;\s"]*))/gi)].map((match) => match[1] ?? match[2]);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Whether a part named card_number holds a value, read without trusting where the body splits into
|
|
160
|
+
* parts: each header line that names card_number, wherever it sits, opens a part whose value runs from
|
|
161
|
+
* the blank line after its headers to the next delimiter line (the body's own, or any line that opens
|
|
162
|
+
* with -- when it names none). A server that splits the parts elsewhere, at a delimiter written inside
|
|
163
|
+
* another part's value, say, finds no card this reading misses.
|
|
164
|
+
*/
|
|
165
|
+
function multipartNamesCardNumber(body, delimiter) {
|
|
166
|
+
const lines = body.split(/\r?\n/);
|
|
167
|
+
const ends = (line) => (delimiter === undefined ? line.startsWith('--') : [delimiter, `${delimiter}--`].includes(line.trimEnd()));
|
|
168
|
+
for (let i = 0; i < lines.length; i++) {
|
|
169
|
+
if (!/^[^\s:]+:/.test(lines[i]) || !headerNames(lines[i]).some((name) => name.trim().toLowerCase() === 'card_number'))
|
|
170
|
+
continue;
|
|
171
|
+
let j = i + 1;
|
|
172
|
+
while (j < lines.length && lines[j].trim() !== '')
|
|
173
|
+
j++;
|
|
174
|
+
for (j++; j < lines.length && !ends(lines[j]); j++)
|
|
175
|
+
if (lines[j].trim() !== '')
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
/** Every key of a JSON body, at any depth, with its value; none for a body that is not JSON. */
|
|
181
|
+
function jsonFields(body) {
|
|
182
|
+
if (!/^\s*[[{]/.test(body))
|
|
183
|
+
return [];
|
|
184
|
+
let parsed;
|
|
185
|
+
try {
|
|
186
|
+
parsed = JSON.parse(body);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
const fields = [];
|
|
192
|
+
const walk = (node, depth) => {
|
|
193
|
+
if (node === null || typeof node !== 'object' || depth > 64)
|
|
194
|
+
return;
|
|
195
|
+
for (const [name, value] of Object.entries(node)) {
|
|
196
|
+
fields.push([name, value]);
|
|
197
|
+
walk(value, depth + 1);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
walk(parsed, 0);
|
|
201
|
+
return fields;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* URL-encoded pairs, split on & as a server splits a form, and again line by line: a plain-text form
|
|
205
|
+
* sends name=value lines, and no raw line break survives URL encoding, so a line break in a URL-encoded
|
|
206
|
+
* body only comes from a page that wrote one there.
|
|
207
|
+
*/
|
|
208
|
+
function formFields(body) {
|
|
209
|
+
const fields = [...new URLSearchParams(body)];
|
|
210
|
+
for (const line of body.split(/[\r\n]+/))
|
|
211
|
+
for (const pair of new URLSearchParams(line))
|
|
212
|
+
fields.push(pair);
|
|
213
|
+
return fields;
|
|
214
|
+
}
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -226,11 +226,14 @@ export declare class CheckoutLifecycle implements CheckoutController {
|
|
|
226
226
|
* (`unsupported_payrails_card_payment` and the card-save refusal codes), a
|
|
227
227
|
* Shiji STS request its gate refuses (shiji_sts_keep_on_file and the other
|
|
228
228
|
* shiji-sts.ts reasons), a post to a processor's own card page that only
|
|
229
|
-
* the cardholder's device can fill (processor_card_page: NMI's),
|
|
229
|
+
* the cardholder's device can fill (processor_card_page: NMI's), a card
|
|
230
230
|
* sent to PayPal's legacy Hosted Fields confirm, which no Vault mode takes
|
|
231
|
-
* over (unsupported_paypal_hosted_fields, paypal-hosted-fields.ts)
|
|
231
|
+
* over (unsupported_paypal_hosted_fields, paypal-hosted-fields.ts), or a card
|
|
232
|
+
* a merchant's page posts to CyberSource Secure Acceptance's Silent Order
|
|
233
|
+
* POST, which no Vault mode takes over either
|
|
234
|
+
* (unsupported_cybersource_secure_acceptance, cybersource-secure-acceptance.ts).
|
|
232
235
|
*/
|
|
233
|
-
unsupported(reason?: 'unrecognized_payment_endpoint' | 'merchant_profile_observe' | 'merchant_request_unsupported' | GmoUnsupportedReason | 'microform_unsupported' | 'juspay_request_unsupported' | 'merchant_card_endpoint_not_enabled' | 'merchant_card_endpoint_resend_refused' | 'creditguard_three_d_secure' | 'unsupported_primer_sdk' | 'unsupported_primer_request' | PayrailsUnsupportedReason | ShijiStsUnsupportedReason | 'processor_card_page' | 'unsupported_paypal_hosted_fields'): void;
|
|
236
|
+
unsupported(reason?: 'unrecognized_payment_endpoint' | 'merchant_profile_observe' | 'merchant_request_unsupported' | GmoUnsupportedReason | 'microform_unsupported' | 'juspay_request_unsupported' | 'merchant_card_endpoint_not_enabled' | 'merchant_card_endpoint_resend_refused' | 'creditguard_three_d_secure' | 'unsupported_primer_sdk' | 'unsupported_primer_request' | PayrailsUnsupportedReason | ShijiStsUnsupportedReason | 'processor_card_page' | 'unsupported_paypal_hosted_fields' | 'unsupported_cybersource_secure_acceptance'): void;
|
|
234
237
|
/**
|
|
235
238
|
* A card request that only a prepared checkout pays (an Adyen merchant
|
|
236
239
|
* profile's, or a Fiserv card capture) arrived with no preparation on this
|
package/dist/lifecycle.js
CHANGED
|
@@ -202,9 +202,12 @@ export class CheckoutLifecycle {
|
|
|
202
202
|
* (`unsupported_payrails_card_payment` and the card-save refusal codes), a
|
|
203
203
|
* Shiji STS request its gate refuses (shiji_sts_keep_on_file and the other
|
|
204
204
|
* shiji-sts.ts reasons), a post to a processor's own card page that only
|
|
205
|
-
* the cardholder's device can fill (processor_card_page: NMI's),
|
|
205
|
+
* the cardholder's device can fill (processor_card_page: NMI's), a card
|
|
206
206
|
* sent to PayPal's legacy Hosted Fields confirm, which no Vault mode takes
|
|
207
|
-
* over (unsupported_paypal_hosted_fields, paypal-hosted-fields.ts)
|
|
207
|
+
* over (unsupported_paypal_hosted_fields, paypal-hosted-fields.ts), or a card
|
|
208
|
+
* a merchant's page posts to CyberSource Secure Acceptance's Silent Order
|
|
209
|
+
* POST, which no Vault mode takes over either
|
|
210
|
+
* (unsupported_cybersource_secure_acceptance, cybersource-secure-acceptance.ts).
|
|
208
211
|
*/
|
|
209
212
|
unsupported(reason = 'unrecognized_payment_endpoint') {
|
|
210
213
|
this.held = true;
|
package/dist/pre-claim.d.ts
CHANGED
|
@@ -201,6 +201,7 @@ export declare const verifonePreClaim: PreClaimGate;
|
|
|
201
201
|
export declare const fiservPreClaim: PreClaimGate;
|
|
202
202
|
export declare const ekashuPreClaim: PreClaimGate;
|
|
203
203
|
export declare const paypalHostedFieldsPreClaim: PreClaimGate;
|
|
204
|
+
export declare const cybersourceSecureAcceptancePreClaim: PreClaimGate;
|
|
204
205
|
/** The processor gates, in order. A new family adds one entry here; the terminal
|
|
205
206
|
* arbiter runs after all of them and is not part of this list. A gate appended
|
|
206
207
|
* after Stripe is never offered a step Stripe claimed: Stripe answers, aborts or
|
package/dist/pre-claim.js
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
// card-saving bodies, WSPay's confirm URL, which also carries bodies with
|
|
17
17
|
// no new card, Shiji STS's getTokenId and getCvvId, which also carry
|
|
18
18
|
// requests the vault must never pay, and PayPal's legacy Hosted Fields
|
|
19
|
-
// confirm
|
|
19
|
+
// confirm and CyberSource Secure Acceptance's Silent Order POST, whose card
|
|
20
|
+
// no Vault mode takes over.
|
|
20
21
|
// The first three used to be
|
|
21
22
|
// written inline in Fetch.requestPaused and again in the page.route handler, so
|
|
22
23
|
// a decision could drift between the two adapters and a new family had to be
|
|
@@ -75,6 +76,7 @@ import { PAYRAILS_HOLDING_REASONS, PAYRAILS_SESSION_MESSAGE, isPayrailsHost, isP
|
|
|
75
76
|
import { classifyVerifoneProfileRequest, verifoneTemplatedUrl } from './verifone.js';
|
|
76
77
|
import { ekashuEntryUrl, isEkashuEntry, isEkashuHost } from './ekashu.js';
|
|
77
78
|
import { PAYPAL_HOSTED_FIELDS_REASON, isPaypalHostedFieldsConfirm, paypalHostedFieldsBody } from './paypal-hosted-fields.js';
|
|
79
|
+
import { CYBERSOURCE_SECURE_ACCEPTANCE_REASON, CYBERSOURCE_SECURE_ACCEPTANCE_RECIPIENT, cybersourceSecureAcceptanceRequest, isCybersourceSecureAcceptanceUrl, } from './cybersource-secure-acceptance.js';
|
|
78
80
|
/**
|
|
79
81
|
* The URL an event may name for a request. For an armed merchant profile's
|
|
80
82
|
* endpoint or a sibling of it: the endpoint origin, the path TEMPLATE and the
|
|
@@ -169,14 +171,16 @@ function cardEndpointPausedBody(ctx) {
|
|
|
169
171
|
}
|
|
170
172
|
/**
|
|
171
173
|
* Answer a paused card form navigation with the page that says Agentcard stopped it before
|
|
172
|
-
* its card left
|
|
173
|
-
*
|
|
174
|
-
*
|
|
174
|
+
* its card left, naming where the card was going: a card endpoint profile's own merchant, or
|
|
175
|
+
* CyberSource for a Secure Acceptance post. While an approval is outstanding, or once a payment
|
|
176
|
+
* for this checkout may already exist, the page says a payment is in progress instead of that
|
|
177
|
+
* nothing was sent (card-endpoint-page.ts refusedNavigationOutcome), and names that payment's
|
|
178
|
+
* authorization.
|
|
175
179
|
*/
|
|
176
|
-
async function stopNavigationBeforeSend(ctx,
|
|
180
|
+
async function stopNavigationBeforeSend(ctx, merchant, reason) {
|
|
177
181
|
const state = ctx.lifecycle.getState();
|
|
178
182
|
const outcome = refusedNavigationOutcome({ awaitingApproval: ctx.awaitingApproval(), status: state.status });
|
|
179
|
-
await ctx.effects.answer(cardEndpointStoppedPage({ outcome, reason, merchant
|
|
183
|
+
await ctx.effects.answer(cardEndpointStoppedPage({ outcome, reason, merchant,
|
|
180
184
|
authorizationId: outcome === 'payment_in_progress' ? state.authorizationId : null }));
|
|
181
185
|
return { kind: 'answer' };
|
|
182
186
|
}
|
|
@@ -216,13 +220,13 @@ async function cardEndpointNavigationPreClaim(ctx, profile) {
|
|
|
216
220
|
}
|
|
217
221
|
if (navigation !== profile.navigation) {
|
|
218
222
|
ctx.onEvent?.({ type: 'card_endpoint_refused', detail: { ...cardEndpointDetail(profile), reason: 'request_unsupported', refusal: `the card post navigates ${navigation === 'top' ? 'the page' : 'an iframe'}` } });
|
|
219
|
-
return stopNavigationBeforeSend(ctx, profile, 'request_unsupported');
|
|
223
|
+
return stopNavigationBeforeSend(ctx, profile.merchant, 'request_unsupported');
|
|
220
224
|
}
|
|
221
225
|
if (!cardEndpointLive(profile)) {
|
|
222
226
|
ctx.preparationGate.invalidate('unsupported_checkout');
|
|
223
227
|
ctx.lifecycle.unsupported('merchant_card_endpoint_not_enabled');
|
|
224
228
|
ctx.onEvent?.({ type: 'unsupported_checkout', detail: { ...cardEndpointDetail(profile), reason: 'merchant_card_endpoint_not_enabled' } });
|
|
225
|
-
return stopNavigationBeforeSend(ctx, profile, 'not_enabled');
|
|
229
|
+
return stopNavigationBeforeSend(ctx, profile.merchant, 'not_enabled');
|
|
226
230
|
}
|
|
227
231
|
return { kind: 'pause', stripeStep: null, cardEndpoint: profile, navigation };
|
|
228
232
|
}
|
|
@@ -1530,6 +1534,68 @@ async function paypalHostedFieldsUnreadable(ctx) {
|
|
|
1530
1534
|
await ctx.effects.abort();
|
|
1531
1535
|
return { kind: 'abort' };
|
|
1532
1536
|
}
|
|
1537
|
+
// CyberSource Secure Acceptance's Silent Order POST (cybersource-secure-acceptance.ts). The merchant's
|
|
1538
|
+
// page posts the card the agent typed, beside the fields its server signed, to a /silent/ path on
|
|
1539
|
+
// CyberSource's Secure Acceptance hosts, which no Vault mode takes over, so every company's adapters
|
|
1540
|
+
// hand each request there to this gate, whether a fetch, an XHR or a form navigation sent it:
|
|
1541
|
+
// - a card_number that holds a value, in the body or in the URL's query, stops the request as an
|
|
1542
|
+
// unsupported checkout, reason `unsupported_cybersource_secure_acceptance`, with no claim, create or
|
|
1543
|
+
// prompt, so CyberSource never receives the card. A form navigation of the page or of a frame (Arrow
|
|
1544
|
+
// submits its form into a hidden iframe) is answered with the stopped page, as a card endpoint's form
|
|
1545
|
+
// is, since an aborted page load renders nothing and invites another click on Pay; any other request
|
|
1546
|
+
// fails. The attachment then reads `unsupported` and holds: the page's next post would carry the
|
|
1547
|
+
// card again;
|
|
1548
|
+
// - a body the adapter could not read stops that request alone, the same way, with a `blocked` event,
|
|
1549
|
+
// `unreadable_cybersource_secure_acceptance_request`, and does not hold the attachment: nothing
|
|
1550
|
+
// proves it carries no card;
|
|
1551
|
+
// - any other request (a payment with a stored payment_token and no card, a GET, a CORS preflight)
|
|
1552
|
+
// defers, and the arbiter continues it untouched.
|
|
1553
|
+
// A request the synced registry recognizes as a card request defers too, so the arbiter pauses it for
|
|
1554
|
+
// that recognizer; no build serves one today. A path outside /silent/ is not this gate's. Synchronous
|
|
1555
|
+
// until it must stop a request, like every gate. Exported for the gate registry's tests.
|
|
1556
|
+
export const cybersourceSecureAcceptancePreClaim = (ctx) => {
|
|
1557
|
+
const { url, method } = ctx.request;
|
|
1558
|
+
if (!isCybersourceSecureAcceptanceUrl(url) || ctx.vault.isCardRequest(url, method))
|
|
1559
|
+
return undefined;
|
|
1560
|
+
// A reader that throws (Playwright's postData() can) reads as a body the adapter could not read.
|
|
1561
|
+
const { body, contentType } = cardEndpointPausedBody(ctx);
|
|
1562
|
+
const carried = cybersourceSecureAcceptanceRequest(url, method, body, contentType);
|
|
1563
|
+
if (carried === 'no_card')
|
|
1564
|
+
return undefined;
|
|
1565
|
+
return carried === 'card' ? cybersourceSecureAcceptanceUnsupported(ctx) : cybersourceSecureAcceptanceUnreadable(ctx);
|
|
1566
|
+
};
|
|
1567
|
+
async function cybersourceSecureAcceptanceUnsupported(ctx) {
|
|
1568
|
+
const { url, method } = ctx.request;
|
|
1569
|
+
ctx.preparationGate.invalidate('unsupported_checkout');
|
|
1570
|
+
ctx.lifecycle.unsupported(CYBERSOURCE_SECURE_ACCEPTANCE_REASON);
|
|
1571
|
+
ctx.onEvent?.({ type: 'unsupported_checkout', detail: { url: eventUrl(ctx.vault, url), method, reason: CYBERSOURCE_SECURE_ACCEPTANCE_REASON } });
|
|
1572
|
+
return cybersourceSecureAcceptanceStop(ctx);
|
|
1573
|
+
}
|
|
1574
|
+
async function cybersourceSecureAcceptanceUnreadable(ctx) {
|
|
1575
|
+
ctx.onEvent?.({ type: 'blocked', detail: 'unreadable_cybersource_secure_acceptance_request' });
|
|
1576
|
+
return cybersourceSecureAcceptanceStop(ctx);
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Stop a Secure Acceptance post: a form navigation gets the stopped page, and if the page cannot be
|
|
1580
|
+
* answered, or the request is not a navigation, it fails. Either way the card never leaves.
|
|
1581
|
+
*/
|
|
1582
|
+
async function cybersourceSecureAcceptanceStop(ctx) {
|
|
1583
|
+
let navigation = false;
|
|
1584
|
+
try {
|
|
1585
|
+
navigation = ctx.request.navigation === true;
|
|
1586
|
+
}
|
|
1587
|
+
catch {
|
|
1588
|
+
navigation = false;
|
|
1589
|
+
}
|
|
1590
|
+
if (navigation) {
|
|
1591
|
+
try {
|
|
1592
|
+
return await stopNavigationBeforeSend(ctx, CYBERSOURCE_SECURE_ACCEPTANCE_RECIPIENT, 'request_unsupported');
|
|
1593
|
+
}
|
|
1594
|
+
catch { /* the page could not be answered: fail the request below */ }
|
|
1595
|
+
}
|
|
1596
|
+
await ctx.effects.abort().catch(() => { });
|
|
1597
|
+
return { kind: 'abort' };
|
|
1598
|
+
}
|
|
1533
1599
|
// The terminal arbiter, always last. A request that no gate claimed and that is
|
|
1534
1600
|
// neither a native Stripe step nor a recognized card request continues, unless
|
|
1535
1601
|
// it matches a declared payment endpoint we do not support (then it aborts as an
|
|
@@ -1563,7 +1629,8 @@ async function unsupportedContinue(ctx) {
|
|
|
1563
1629
|
* arbiter runs after all of them and is not part of this list. A gate appended
|
|
1564
1630
|
* after Stripe is never offered a step Stripe claimed: Stripe answers, aborts or
|
|
1565
1631
|
* pauses each one itself. */
|
|
1566
|
-
export const PSP_PRE_CLAIM_GATES = [cardEndpointResendPreClaim, cardEndpointPreClaim, mercadoPreClaim, juspayPreClaim, braintreePreClaim, stripePreClaim, merchantHostedPreClaim, verifonePreClaim, fiservPreClaim, gmoPreClaim, cybersourceFlexPreClaim, datatransPreClaim, pelecardPreClaim, aciPreClaim, creditguardPreClaim, primerPreClaim, payrailsPreClaim, convergePreClaim, eprotectPreClaim, wspayPreClaim, shijiStsPreClaim, ekashuPreClaim, paypalHostedFieldsPreClaim
|
|
1632
|
+
export const PSP_PRE_CLAIM_GATES = [cardEndpointResendPreClaim, cardEndpointPreClaim, mercadoPreClaim, juspayPreClaim, braintreePreClaim, stripePreClaim, merchantHostedPreClaim, verifonePreClaim, fiservPreClaim, gmoPreClaim, cybersourceFlexPreClaim, datatransPreClaim, pelecardPreClaim, aciPreClaim, creditguardPreClaim, primerPreClaim, payrailsPreClaim, convergePreClaim, eprotectPreClaim, wspayPreClaim, shijiStsPreClaim, ekashuPreClaim, paypalHostedFieldsPreClaim,
|
|
1633
|
+
cybersourceSecureAcceptancePreClaim];
|
|
1567
1634
|
/** Run each gate in order; the first to return a decision wins. If none does,
|
|
1568
1635
|
* the terminal arbiter decides between continue, abort (unsupported) and pause.
|
|
1569
1636
|
* Returns synchronously while every gate defers synchronously (an ordinary card
|
|
@@ -2172,6 +2172,33 @@
|
|
|
2172
2172
|
"hostname_pattern": "^assets\\.braintreegateway\\.com$",
|
|
2173
2173
|
"pathname_pattern": "^\\/web\\/[0-9]+\\.[0-9]+\\.[0-9]+-payments-sdk(?:-[a-z]+)?\\/html\\/hosted-fields-(?:input-frame|payments-sdk-tokenization-frame)(?:\\.min)?\\.html$"
|
|
2174
2174
|
},
|
|
2175
|
+
{
|
|
2176
|
+
"id": "cybersource-secure-acceptance-silent-order-post",
|
|
2177
|
+
"psp": "cybersource_secure_acceptance",
|
|
2178
|
+
"kinds": [
|
|
2179
|
+
"form",
|
|
2180
|
+
"document",
|
|
2181
|
+
"frame",
|
|
2182
|
+
"iframe"
|
|
2183
|
+
],
|
|
2184
|
+
"origin": "https://secureacceptance.cybersource.com",
|
|
2185
|
+
"path_pattern": "/silent/*",
|
|
2186
|
+
"sources": [
|
|
2187
|
+
{
|
|
2188
|
+
"kind": "official_documentation",
|
|
2189
|
+
"reference": "https://developer.cybersource.com/docs/cybs/en-us/sa/developer/all/sa-checkout/secure-acceptance.html",
|
|
2190
|
+
"reviewed_on": "2026-10-08"
|
|
2191
|
+
},
|
|
2192
|
+
{
|
|
2193
|
+
"kind": "saved_observation",
|
|
2194
|
+
"reference": "arrow-2026-10-08:secure-acceptance-silent-embedded-token-create-hidden-form",
|
|
2195
|
+
"reviewed_on": "2026-10-08"
|
|
2196
|
+
}
|
|
2197
|
+
],
|
|
2198
|
+
"variant_unverified": "CyberSource Secure Acceptance Silent Order POST sends the card from the merchant page to /silent/ on CyberSource's Secure Acceptance hosts; no Vault mode takes that request over.",
|
|
2199
|
+
"hostname_pattern": "^secureacceptance\\.cybersource\\.com$",
|
|
2200
|
+
"pathname_pattern": "^\\/silent\\/(?:[A-Za-z0-9_-]+\\/)*[A-Za-z0-9_-]+\\/?$"
|
|
2201
|
+
},
|
|
2175
2202
|
{
|
|
2176
2203
|
"id": "fiserv-commercehub-payment-fields",
|
|
2177
2204
|
"psp": "fiserv",
|
|
@@ -3432,6 +3459,60 @@
|
|
|
3432
3459
|
"hostname_pattern": "^testflex\\.cybersource\\.com$",
|
|
3433
3460
|
"pathname_pattern": "^\\/microform\\/bundle\\/v2\\.[0-9]+\\.[0-9]+\\/iframe\\.html$"
|
|
3434
3461
|
},
|
|
3462
|
+
{
|
|
3463
|
+
"id": "cybersource-secure-acceptance-silent-order-post-test",
|
|
3464
|
+
"psp": "cybersource_secure_acceptance",
|
|
3465
|
+
"kinds": [
|
|
3466
|
+
"form",
|
|
3467
|
+
"document",
|
|
3468
|
+
"frame",
|
|
3469
|
+
"iframe"
|
|
3470
|
+
],
|
|
3471
|
+
"origin": "https://testsecureacceptance.cybersource.com",
|
|
3472
|
+
"path_pattern": "/silent/*",
|
|
3473
|
+
"sources": [
|
|
3474
|
+
{
|
|
3475
|
+
"kind": "official_documentation",
|
|
3476
|
+
"reference": "https://developer.cybersource.com/docs/cybs/en-us/sa/developer/all/sa-checkout/secure-acceptance.html",
|
|
3477
|
+
"reviewed_on": "2026-10-08"
|
|
3478
|
+
},
|
|
3479
|
+
{
|
|
3480
|
+
"kind": "saved_observation",
|
|
3481
|
+
"reference": "arrow-2026-10-08:secure-acceptance-silent-embedded-token-create-hidden-form",
|
|
3482
|
+
"reviewed_on": "2026-10-08"
|
|
3483
|
+
}
|
|
3484
|
+
],
|
|
3485
|
+
"variant_unverified": "CyberSource Secure Acceptance Silent Order POST sends the card from the merchant page to /silent/ on CyberSource's Secure Acceptance hosts; no Vault mode takes that request over.",
|
|
3486
|
+
"hostname_pattern": "^testsecureacceptance\\.cybersource\\.com$",
|
|
3487
|
+
"pathname_pattern": "^\\/silent\\/(?:[A-Za-z0-9_-]+\\/)*[A-Za-z0-9_-]+\\/?$"
|
|
3488
|
+
},
|
|
3489
|
+
{
|
|
3490
|
+
"id": "cybersource-secure-acceptance-silent-order-post-in",
|
|
3491
|
+
"psp": "cybersource_secure_acceptance",
|
|
3492
|
+
"kinds": [
|
|
3493
|
+
"form",
|
|
3494
|
+
"document",
|
|
3495
|
+
"frame",
|
|
3496
|
+
"iframe"
|
|
3497
|
+
],
|
|
3498
|
+
"origin": "https://secureacceptance.in.cybersource.com",
|
|
3499
|
+
"path_pattern": "/silent/*",
|
|
3500
|
+
"sources": [
|
|
3501
|
+
{
|
|
3502
|
+
"kind": "official_documentation",
|
|
3503
|
+
"reference": "https://developer.cybersource.com/docs/cybs/en-us/sa/developer/all/sa-checkout/secure-acceptance.html",
|
|
3504
|
+
"reviewed_on": "2026-10-08"
|
|
3505
|
+
},
|
|
3506
|
+
{
|
|
3507
|
+
"kind": "saved_observation",
|
|
3508
|
+
"reference": "arrow-2026-10-08:secure-acceptance-silent-embedded-token-create-hidden-form",
|
|
3509
|
+
"reviewed_on": "2026-10-08"
|
|
3510
|
+
}
|
|
3511
|
+
],
|
|
3512
|
+
"variant_unverified": "CyberSource Secure Acceptance Silent Order POST sends the card from the merchant page to /silent/ on CyberSource's Secure Acceptance hosts; no Vault mode takes that request over.",
|
|
3513
|
+
"hostname_pattern": "^secureacceptance\\.in\\.cybersource\\.com$",
|
|
3514
|
+
"pathname_pattern": "^\\/silent\\/(?:[A-Za-z0-9_-]+\\/)*[A-Za-z0-9_-]+\\/?$"
|
|
3515
|
+
},
|
|
3435
3516
|
{
|
|
3436
3517
|
"id": "worldpay-eprotect-js-api-prelive",
|
|
3437
3518
|
"psp": "worldpay_eprotect",
|
|
@@ -469,6 +469,7 @@
|
|
|
469
469
|
"gravy-secure-fields-script",
|
|
470
470
|
"gravy-secure-fields-script-sandbox",
|
|
471
471
|
"paypal-hosted-fields-payments-sdk",
|
|
472
|
+
"cybersource-secure-acceptance-silent-order-post",
|
|
472
473
|
"fiserv-commercehub-payment-fields",
|
|
473
474
|
"fiserv-commercehub-checkout-script",
|
|
474
475
|
"fiserv-secure-data-capture-frame",
|
|
@@ -523,6 +524,8 @@
|
|
|
523
524
|
"gmo-link-hosted-page-test",
|
|
524
525
|
"cybersource-flex-microform-script-test",
|
|
525
526
|
"cybersource-flex-microform-field-test",
|
|
527
|
+
"cybersource-secure-acceptance-silent-order-post-test",
|
|
528
|
+
"cybersource-secure-acceptance-silent-order-post-in",
|
|
526
529
|
"worldpay-eprotect-js-api-prelive",
|
|
527
530
|
"worldpay-eprotect-iframe-client-prelive",
|
|
528
531
|
"worldpay-eprotect-payframe-prelive",
|
|
@@ -1100,6 +1103,7 @@
|
|
|
1100
1103
|
"gravy-secure-fields-script",
|
|
1101
1104
|
"gravy-secure-fields-script-sandbox",
|
|
1102
1105
|
"paypal-hosted-fields-payments-sdk",
|
|
1106
|
+
"cybersource-secure-acceptance-silent-order-post",
|
|
1103
1107
|
"fiserv-commercehub-payment-fields",
|
|
1104
1108
|
"fiserv-commercehub-checkout-script",
|
|
1105
1109
|
"fiserv-secure-data-capture-frame",
|
|
@@ -1154,6 +1158,8 @@
|
|
|
1154
1158
|
"gmo-link-hosted-page-test",
|
|
1155
1159
|
"cybersource-flex-microform-script-test",
|
|
1156
1160
|
"cybersource-flex-microform-field-test",
|
|
1161
|
+
"cybersource-secure-acceptance-silent-order-post-test",
|
|
1162
|
+
"cybersource-secure-acceptance-silent-order-post-in",
|
|
1157
1163
|
"worldpay-eprotect-js-api-prelive",
|
|
1158
1164
|
"worldpay-eprotect-iframe-client-prelive",
|
|
1159
1165
|
"worldpay-eprotect-payframe-prelive",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
2
|
-
// source-sha256:
|
|
2
|
+
// source-sha256: fc24e6cb50ab112c3a15983c461185a9189bd0cf8c5e1ceb73e8432e6eebc37f
|
|
3
3
|
import { CHECKOUT_PREFLIGHT_CAPABILITIES } from './preflight-capabilities.generated.js';
|
|
4
4
|
/** Advisory page discovery. These rules never admit a payment destination. */
|
|
5
5
|
export const CHECKOUT_PREFLIGHT_VERSION = '1';
|
|
@@ -79,6 +79,23 @@ RULES.push({ id: 'paypal-hosted-fields-payments-sdk', psp: 'paypal', kinds: ['fr
|
|
|
79
79
|
paypalHostedFieldsSource('https://www.paypal.com/sdk/js?components=hosted-fields'),
|
|
80
80
|
{ kind: 'saved_observation', reference: 'planet-golf-uk-2026-10-06:paypal-hosted-fields-payments-sdk-frames', reviewed_on: '2026-10-07' }],
|
|
81
81
|
variant_unverified: 'PayPal legacy Hosted Fields frames send the card to the Orders v2 confirm-payment-source on cors.api.paypal.com; no Vault mode takes that request over.' });
|
|
82
|
+
// CyberSource Secure Acceptance's Silent Order POST (the Secure Acceptance Checkout API), reviewed 2026-10-08 from
|
|
83
|
+
// CyberSource's endpoint table and Arrow Electronics' public bundles (no card was sent). The merchant's own page
|
|
84
|
+
// posts the card fields, unsigned, beside the fields its server signed, to a /silent/ path on CyberSource's Secure
|
|
85
|
+
// Acceptance hosts: production, test (testsecureacceptance) and production in India (secureacceptance.in), below.
|
|
86
|
+
// Arrow submits that form into a hidden iframe; a merchant can also post it as the page's own form. No Vault mode
|
|
87
|
+
// takes that post over, and the checkout SDK stops it (checkout/src/cybersource-secure-acceptance.ts). So the form,
|
|
88
|
+
// and a frame or document on a /silent/ path, name cybersource_secure_acceptance and the result stays unknown
|
|
89
|
+
// through `variant_unverified`. Hosted Checkout, the same hosts without /silent/, takes the card on CyberSource's
|
|
90
|
+
// own page and is not named here.
|
|
91
|
+
const secureAcceptanceSources = [
|
|
92
|
+
{ kind: 'official_documentation', reference: 'https://developer.cybersource.com/docs/cybs/en-us/sa/developer/all/sa-checkout/secure-acceptance.html', reviewed_on: '2026-10-08' },
|
|
93
|
+
{ kind: 'saved_observation', reference: 'arrow-2026-10-08:secure-acceptance-silent-embedded-token-create-hidden-form', reviewed_on: '2026-10-08' },
|
|
94
|
+
];
|
|
95
|
+
RULES.push({ id: 'cybersource-secure-acceptance-silent-order-post', psp: 'cybersource_secure_acceptance', kinds: ['form', 'document', 'frame', 'iframe'],
|
|
96
|
+
hostname: /^secureacceptance\.cybersource\.com$/, pathname: /^\/silent\/(?:[A-Za-z0-9_-]+\/)*[A-Za-z0-9_-]+\/?$/,
|
|
97
|
+
origin: 'https://secureacceptance.cybersource.com', path_pattern: '/silent/*', sources: secureAcceptanceSources,
|
|
98
|
+
variant_unverified: 'CyberSource Secure Acceptance Silent Order POST sends the card from the merchant page to /silent/ on CyberSource\'s Secure Acceptance hosts; no Vault mode takes that request over.' });
|
|
82
99
|
// Fiserv Commerce Hub, read 2026-09-23 from Home Depot's OrangePay frame and Fiserv's
|
|
83
100
|
// docs: payment-fields.js renders one Secure Data Capture iframe per card field, and
|
|
84
101
|
// the Checkout JS SDK is Commerce Hub's other browser entry. Fiserv ships dark (its
|
|
@@ -310,6 +327,9 @@ for (const [baseId, suffix, hostname, origin] of [
|
|
|
310
327
|
['gmo-link-hosted-page', 'test', /^pt01\.mul-pay\.jp$/, 'https://pt01.mul-pay.jp'],
|
|
311
328
|
['cybersource-flex-microform-script', 'test', /^testflex\.cybersource\.com$/, 'https://testflex.cybersource.com'],
|
|
312
329
|
['cybersource-flex-microform-field', 'test', /^testflex\.cybersource\.com$/, 'https://testflex.cybersource.com'],
|
|
330
|
+
// Secure Acceptance's test host and its production host in India serve the same /silent/ paths.
|
|
331
|
+
['cybersource-secure-acceptance-silent-order-post', 'test', /^testsecureacceptance\.cybersource\.com$/, 'https://testsecureacceptance.cybersource.com'],
|
|
332
|
+
['cybersource-secure-acceptance-silent-order-post', 'in', /^secureacceptance\.in\.cybersource\.com$/, 'https://secureacceptance.in.cybersource.com'],
|
|
313
333
|
['worldpay-eprotect-js-api', 'prelive', /^request\.eprotect\.vantivprelive\.com$/, 'https://request.eprotect.vantivprelive.com'],
|
|
314
334
|
['worldpay-eprotect-iframe-client', 'prelive', /^request\.eprotect\.vantivprelive\.com$/, 'https://request.eprotect.vantivprelive.com'],
|
|
315
335
|
['worldpay-eprotect-payframe', 'prelive', /^request\.eprotect\.vantivprelive\.com$/, 'https://request.eprotect.vantivprelive.com'],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"catalog_version": "2026-09-15.1",
|
|
4
|
-
"catalog_sha256": "
|
|
4
|
+
"catalog_sha256": "a1bfa3f9da2942711afca14cfc7158a49274869c66e685abc0d36839d2e96454",
|
|
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.23.
|
|
3
|
+
"version": "0.23.5",
|
|
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 ekashu.test.mjs paypal-hosted-fields.test.mjs autopilot.test.mjs stripe-checkout.test.mjs stripe-setup.test.mjs stripe-legacy-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 amadeus.test.mjs pelecard.test.mjs aci.test.mjs primer.test.mjs payrails.test.mjs elavon-converge.test.mjs voice.test.mjs agentcard.test.mjs buy.test.mjs readme.test.mjs gmo.test.mjs cybersource-flex.test.mjs juspay-gate.test.mjs card-endpoint.test.mjs card-endpoint-fastspring.test.mjs card-endpoint-navigation.test.mjs merchant-session-probe.test.mjs datatrans.test.mjs authorize-net-dpm.test.mjs creditguard.test.mjs eprotect.test.mjs wspay.test.mjs shiji-sts.test.mjs worldline-mycheckout.test.mjs verifone.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 amadeus-browser.test.mjs && node adyen-merchant-hosted-browser.test.mjs && node adyen-merchant-frames-browser.test.mjs && node fiserv-browser.test.mjs && node gmo-browser.test.mjs && node cybersource-flex-browser.test.mjs && node juspay-browser.test.mjs && node card-endpoint-browser.test.mjs && node datatrans-browser.test.mjs && node authorize-net-dpm-browser.test.mjs && node pelecard-browser.test.mjs && node aci-browser.test.mjs && node creditguard-browser.test.mjs && node primer-browser.test.mjs && node payrails-browser.test.mjs && node elavon-converge-browser.test.mjs && node eprotect-browser.test.mjs && node wspay-browser.test.mjs && node shiji-sts-browser.test.mjs && node worldline-mycheckout-browser.test.mjs && node paypal-hosted-fields-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 ekashu.test.mjs paypal-hosted-fields.test.mjs cybersource-secure-acceptance.test.mjs autopilot.test.mjs stripe-checkout.test.mjs stripe-setup.test.mjs stripe-legacy-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 amadeus.test.mjs pelecard.test.mjs aci.test.mjs primer.test.mjs payrails.test.mjs elavon-converge.test.mjs voice.test.mjs agentcard.test.mjs buy.test.mjs readme.test.mjs gmo.test.mjs cybersource-flex.test.mjs juspay-gate.test.mjs card-endpoint.test.mjs card-endpoint-fastspring.test.mjs card-endpoint-navigation.test.mjs merchant-session-probe.test.mjs datatrans.test.mjs authorize-net-dpm.test.mjs creditguard.test.mjs eprotect.test.mjs wspay.test.mjs shiji-sts.test.mjs worldline-mycheckout.test.mjs verifone.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 amadeus-browser.test.mjs && node adyen-merchant-hosted-browser.test.mjs && node adyen-merchant-frames-browser.test.mjs && node fiserv-browser.test.mjs && node gmo-browser.test.mjs && node cybersource-flex-browser.test.mjs && node juspay-browser.test.mjs && node card-endpoint-browser.test.mjs && node datatrans-browser.test.mjs && node authorize-net-dpm-browser.test.mjs && node pelecard-browser.test.mjs && node aci-browser.test.mjs && node creditguard-browser.test.mjs && node primer-browser.test.mjs && node payrails-browser.test.mjs && node elavon-converge-browser.test.mjs && node eprotect-browser.test.mjs && node wspay-browser.test.mjs && node shiji-sts-browser.test.mjs && node worldline-mycheckout-browser.test.mjs && node paypal-hosted-fields-browser.test.mjs && node cybersource-secure-acceptance-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",
|