@agent-cards/sdk 0.23.6 → 0.23.8
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 +9 -1
- package/PREFLIGHT.md +1 -1
- package/README.md +146 -4
- package/dist/amadeus.d.ts +16 -4
- package/dist/amadeus.js +51 -16
- package/dist/cdp.js +183 -26
- package/dist/client.d.ts +22 -0
- package/dist/client.js +165 -13
- package/dist/creditguard.generated.d.ts +1 -1
- package/dist/cybersource-secure-acceptance.d.ts +142 -34
- package/dist/cybersource-secure-acceptance.generated.d.ts +67 -0
- package/dist/cybersource-secure-acceptance.generated.js +966 -0
- package/dist/cybersource-secure-acceptance.js +244 -3
- package/dist/index.d.ts +1 -0
- package/dist/juspay.generated.d.ts +1 -1
- package/dist/pre-claim.d.ts +3 -1
- package/dist/pre-claim.js +85 -6
- package/dist/preparation.js +15 -2
- package/dist/prepared-processor.d.ts +19 -5
- package/dist/prepared-processor.js +70 -3
- package/dist/registry.js +14 -1
- package/dist/stripe-checkout.generated.js +35 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,9 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Pay Arrow Electronics' card save as its live checkout signs it. Arrow's server adds an id of its own, `merchant_defined_data8` (digits), to the form and to the end of `unsigned_field_names` when it signs the save, and 0.23.7 refused that request with `secure_acceptance_unsigned_fields_unreviewed` on 2026-10-10, after the cardholder approved and before any card was sent. The request now passes with that field or without it; a list that names it anywhere but last, names it twice or posts it unlisted is still refused, and a value other than at most 20 digits is refused as `secure_acceptance_merchant_reference`. Nothing else about Arrow's lists changed. A payment also needs the matching API and Vault releases.
|
|
6
|
+
|
|
5
7
|
- Pause a Worldpay eProtect registration whatever Worldpay key its page names. Worldpay rotates its production eProtect key daily (`26457000011` on 2026-10-08), and 0.23.3 refused every registration whose `publicKeyId` was not `26455500012`, the key it shipped, so an attached checkout at any eProtect merchant read `unsupported`. The SDK now takes any key id in Worldpay's digits; the cardholder's device encrypts the card under the key it reads from Worldpay's own `eProtect-api3.js` and writes that key's id into its copy, so no later rotation needs an SDK release. SmartPak Equine (paypageId `zhs8KzuRAryaq4cs` at `https://www.smartpakequine.com`) and Nordstrom (`fL33BPkRvbRqJ29S` at `https://www.nordstrom.com`) are reviewed eProtect merchants: a live checkout there passes `merchant: 'SmartPak Equine'` or `merchant: 'Nordstrom'` exactly, and `prepare()` refuses another name with `merchant_profile_mismatch` before any network call. eProtect stays dark for every org Agentcard has not enabled, and a payment also needs the matching API and Vault releases.
|
|
6
8
|
|
|
7
|
-
-
|
|
9
|
+
- Pay Arrow Electronics through the CyberSource Secure Acceptance lane, for a company Agentcard turned the lane on for. Arrow's reviewed entry, `arrow_token_create`, now ships enabled and pins the profile id and access key Arrow's checkout at `https://www.arrow.com` sends, read from Arrow's public page model. `prepare({ psp: 'cybersource_secure_acceptance', environment: 'production' })` at that page fills Arrow's profile id and needs `merchant: 'Arrow Electronics'`, or it fails with `merchant_profile_mismatch` before any call. Once CyberSource's page reaches Arrow's receipt, the SDK watches 10 seconds for Arrow's payment method request and emits `secure_acceptance_reply_unused` when none comes. The API still refuses any other profile id, access key or page on the production host (`unsupported_checkout`), and every company Agentcard has not turned the lane on for keeps the stop.
|
|
10
|
+
|
|
11
|
+
- Pay a CyberSource Secure Acceptance checkout through a prepared checkout (dark launch). The sync now also asks for `capabilities=cybersource_sa_token_create`, and the API serves Secure Acceptance's token create only for a company Agentcard turned it on for; every other company keeps the stop below. For such a company the SDK pauses the token create a merchant's form posts into a hidden iframe (`POST /silent/embedded/token/create` on `secureacceptance.cybersource.com`, or `testsecureacceptance.cybersource.com` for `environment: 'sandbox'`), as Arrow Electronics' checkout does when your agent clicks Save, binds it to the preparation, and answers the iframe with CyberSource's own reply page once the cardholder's device sent the card and neither the device nor the API found a card in the reply, so the merchant's receipt page receives CyberSource's signed reply in your browser's session. Call `prepare({ psp: 'cybersource_secure_acceptance', environment, secureAcceptanceProfileId? })` before Save, with the attachment's `cardId` (required, `card_required`); on production the SDK fills the profile id of a merchant Agentcard reviewed for the page's origin, refuses another merchant name (`merchant_profile_mismatch`) and refuses a checkout it can name no profile id for (`secure_acceptance_profile_id_invalid`). The same post as the page's own navigation, from a fetch or an XHR, or with no preparation, ends on the stopped page or fails, and nothing reaches CyberSource from your browser. 20 seconds after the pause with nothing handed over, the SDK retires the attempt (`secure_acceptance_send_window_closed`, or `secure_acceptance_send_window_unconfirmed` while the device may be sending), and a request the cardholder's device stopped at its own earlier boundary reads the same `failed`; a Save while Agentcard has not turned the relay on ends on the stopped page with nothing created, and the controller reads `failed` with reason `cybersource_sa_relay_unavailable`; 45 seconds after it with no reply the outcome is unknown (`PaymentOutcomeUnknownError`, reason `secure_acceptance_reply_late`); a reply that fails the rule is `secure_acceptance_reply_unconfirmed`. After delivery, for a merchant whose reviewed entry names its next request, the SDK watches 10 seconds for it and emits `secure_acceptance_reply_unused` with `{ authorizationId, last4 }` when none comes. The checkout pays with a card the cardholder stored, as every prepared checkout does, and the approval screen says the merchant can keep the card on file; Agentcard has turned no production merchant on yet. The raw CDP adapter also tells a page's own navigation and an iframe target's without reading the frame tree, which Chromium does not answer while that navigation is paused. TypeScript: `PrepareCheckoutOptions` and `PreparedCheckout` gain `secureAcceptanceProfileId`, and `SecureAcceptanceCheckoutRefusal` and `SecureAcceptanceUnknownReason` are exported. Matching API, Vault and relay releases are required.
|
|
12
|
+
|
|
13
|
+
- Read a prepared Amadeus add that the cardholder's device stopped at its own send boundary as the send window closing, not as the cardholder declining. The device sends nothing once 12 seconds have passed since it last saw the checkout unbound, earlier than the SDK's own 20 second window, and declines the request with reason `amadeus_send_window_closed`; the controller read that as `declined` with reason `approval_declined`. It now reads `failed` with reason `amadeus_send_window_closed`, as when the SDK's window closes first, and `authorize()` rejects with `CheckoutPreparationError` rather than `ApprovalDeclinedError`. Nothing was sent either way. A prepared checkout is used once, so attach again and prepare again to retry.
|
|
14
|
+
|
|
15
|
+
- 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, except the token create a company Agentcard turned the Secure Acceptance lane on for pays (above): 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.
|
|
8
16
|
|
|
9
17
|
- 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`.
|
|
10
18
|
|
package/PREFLIGHT.md
CHANGED
|
@@ -209,7 +209,7 @@ 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).
|
|
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. The processor ships dark for its token create, `/silent/embedded/token/create`: only an org Agentcard enabled pays it, and only through a prepared checkout, so the bundled catalog leaves it out and an inspection stays `unknown`. 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
213
|
|
|
214
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.
|
|
215
215
|
|
package/README.md
CHANGED
|
@@ -255,7 +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) |
|
|
258
|
+
| CyberSource Secure Acceptance Silent Order POST (the merchant's own card form posted to CyberSource; Arrow Electronics) | dark launch: prepared checkout only, for an org Agentcard enabled. After `syncRegistry()`, for such an org, the SDK pauses the token create a merchant's form posts into a hidden iframe (`/silent/embedded/token/create` on `secureacceptance.cybersource.com`, or `testsecureacceptance.cybersource.com` in CyberSource's test environment), the cardholder's device sends the card to CyberSource, and the iframe gets CyberSource's own reply page. See [Pay a CyberSource Secure Acceptance checkout](#pay-a-cybersource-secure-acceptance-checkout-dark-launch). For every other company, and for every other card post to a `/silent/` path on those hosts or on `secureacceptance.in.cybersource.com` (India), no Agentcard payment takes the card over, and the SDK stops the post 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 |
|
|
259
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 |
|
|
260
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) |
|
|
261
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 |
|
|
@@ -284,7 +284,7 @@ so new processors work without you shipping a release. `attachToCdp` derives the
|
|
|
284
284
|
`Fetch.enable` url patterns from that same list rather than a constant, which is
|
|
285
285
|
why the sync call belongs before the attach. `agentcard.checkout.cardUrlPatterns()` returns
|
|
286
286
|
those patterns if you arm a CDP connection yourself. Call
|
|
287
|
-
`GET /v2/checkout/recognizers?modes=token,cse,hosted_form,device_encrypt&capabilities=fiserv_card_capture,gmo_shared_pk,capture_context,juspay_txns.v1,datatrans_secure_fields_tokenize,authorize_net_dpm_form,pelecard_transaction,aci_split_card.v1,creditguard_pps_process,primer_payment_instruments,payrails_device_encrypt,converge_hpp_process,worldpay_eprotect_jsonp,wspay_confirm,shiji_sts_pinned_key,amadeus_checkout_actions.v1,worldline_mycheckout_form,verifone_card_capture,ekashu_hosted_payment_page`
|
|
287
|
+
`GET /v2/checkout/recognizers?modes=token,cse,hosted_form,device_encrypt&capabilities=fiserv_card_capture,gmo_shared_pk,capture_context,juspay_txns.v1,datatrans_secure_fields_tokenize,authorize_net_dpm_form,pelecard_transaction,aci_split_card.v1,creditguard_pps_process,primer_payment_instruments,payrails_device_encrypt,converge_hpp_process,worldpay_eprotect_jsonp,wspay_confirm,shiji_sts_pinned_key,amadeus_checkout_actions.v1,worldline_mycheckout_form,verifone_card_capture,ekashu_hosted_payment_page,cybersource_sa_token_create`
|
|
288
288
|
for the list this build syncs. The API serves Fiserv's card capture only to a
|
|
289
289
|
build that lists `fiserv_card_capture`, and only for a company Agentcard turned
|
|
290
290
|
Fiserv on for. An older SDK never pauses a Fiserv request it could not finish,
|
|
@@ -304,7 +304,10 @@ same holds for the Verifone profiles and `verifone_card_capture`. NMI's hosted
|
|
|
304
304
|
payment page (`ekashu_hosted_payment_page`) is served on the same terms: this
|
|
305
305
|
build pauses the merchant's entry form to `live.ekashu.com` for the
|
|
306
306
|
cardholder's device and fails every other post your browser makes to NMI, so
|
|
307
|
-
NMI never sees the card your agent typed.
|
|
307
|
+
NMI never sees the card your agent typed. CyberSource Secure Acceptance's token
|
|
308
|
+
create (`cybersource_sa_token_create`) is served on the same terms too; every
|
|
309
|
+
other company keeps the stop every build applies to a card posted to Secure
|
|
310
|
+
Acceptance.
|
|
308
311
|
|
|
309
312
|
## Modes
|
|
310
313
|
|
|
@@ -1164,7 +1167,7 @@ await controller.prepare({ psp: 'amadeus_checkout', environment: 'production' })
|
|
|
1164
1167
|
|
|
1165
1168
|
Amadeus Checkout ships dark: `syncRegistry()` declares `amadeus_checkout_actions.v1`, and the API serves the recognizer only to an org Agentcard enabled for it, so every other org's Amadeus pages run as before. The Checkout Web SDK sends every action to one URL through its tunnel iframe, starting with `load` as the payment step opens. Only the card `add` pauses. `load`, the BIN lookup and every other action continue untouched in every state, and none of them spends or blocks the preparation, so call `prepare()` once the payment step shows. The add binds when its `Amadeus-Checkout-Sdk-Host` header names the prepared page's host; the cardholder's device fills in the card and sends it, and the page's tunnel receives Amadeus's answer. The add names no amount, so the amount you pass is the one the cardholder approves.
|
|
1166
1169
|
|
|
1167
|
-
The tunnel gives up on the add 30 seconds after sending it, so the device has 20 seconds from the pause. When they pass with no answer on its way, the SDK asks the API to retire the authorization, or, while the create has not answered, the preparation and any request already bound to it. The API confirms a retirement only while the card is still on the device. Once it does, nothing was sent and nothing can be: the SDK fails the page's request, and the controller reads `failed` with reason `amadeus_send_window_closed`. A prepared checkout is used once, so attach again and prepare again to retry. When the API cannot confirm, the card can already be on its way, so the SDK waits for the device's answer, and the tunnel's own 30 seconds still end the request.
|
|
1170
|
+
The tunnel gives up on the add 30 seconds after sending it, so the device has 20 seconds from the pause. When they pass with no answer on its way, the SDK asks the API to retire the authorization, or, while the create has not answered, the preparation and any request already bound to it. The API confirms a retirement only while the card is still on the device. Once it does, nothing was sent and nothing can be: the SDK fails the page's request, and the controller reads `failed` with reason `amadeus_send_window_closed`. The cardholder's device keeps an earlier boundary of its own, 12 seconds after it last saw the checkout unbound: past it the device sends nothing and declines the request with the same reason, and the SDK and the controller read it the same way. A prepared checkout is used once, so attach again and prepare again to retry. When the API cannot confirm, the card can already be on its way, so the SDK waits for the device's answer, and the tunnel's own 30 seconds still end the request.
|
|
1168
1171
|
|
|
1169
1172
|
The SDK refuses some requests itself, before any row or prompt, and the controller reads `failed` with the reason: Air France's and KLM's add, whose expiry their own form keeps (`amadeus_merchant_held_expiry`), a Data Collection Page store until the merchant's field configuration is reviewed (`amadeus_dcp_field_config_unreviewed`), and an add whose Host header names another page (`amadeus_header_binding`). Card saving, split payments, installments, currency conversion and Amadeus 3-D Secure data are refused the same way. The SDK judges an add before a card is chosen, so it cannot see the card's network: an add naming another network reaches the cardholder's device, which refuses it after approval (`vendor_mismatch`), with nothing sent. An add with no preparation is refused with `PreparationRequiredError`. An add that pays with a card the merchant saved carries no card number and continues untouched, and so does Air France's Nuvei payment with a `ccTempToken` after the add. Autopilot never pays an Amadeus payment.
|
|
1170
1173
|
|
|
@@ -2027,6 +2030,145 @@ does not support a merchant timeout of 5 seconds or less. The pre-live host
|
|
|
2027
2030
|
receives only Worldpay's documented test cards. Autopilot never pays eProtect.
|
|
2028
2031
|
The matching API, Vault and relay releases are required.
|
|
2029
2032
|
|
|
2033
|
+
### Pay a CyberSource Secure Acceptance checkout (dark launch)
|
|
2034
|
+
|
|
2035
|
+
CyberSource Secure Acceptance support ships dark. A merchant that takes the
|
|
2036
|
+
card in its own form, such as Arrow Electronics, has its server sign the order
|
|
2037
|
+
fields, then submits them with the four card fields as a hidden form into an
|
|
2038
|
+
iframe: `POST https://secureacceptance.cybersource.com/silent/embedded/token/create`
|
|
2039
|
+
(`testsecureacceptance.cybersource.com` in CyberSource's test environment, the
|
|
2040
|
+
`sandbox` environment), a `create_payment_token` at a signed amount of 0.
|
|
2041
|
+
CyberSource answers inside that iframe with a page whose form posts its signed
|
|
2042
|
+
reply, with a payment token only the merchant's CyberSource account can charge,
|
|
2043
|
+
to the merchant's receipt page; the merchant charges its own cart total against
|
|
2044
|
+
that token later, at place order, server to server. `syncRegistry()` declares
|
|
2045
|
+
the `cybersource_sa_token_create` capability, and the API serves the Secure
|
|
2046
|
+
Acceptance recognizer to a build that declares it only for an org Agentcard has
|
|
2047
|
+
enabled. Every other org keeps the stop described in the processor table.
|
|
2048
|
+
|
|
2049
|
+
Who can pay: an org Agentcard turned the lane on for, with a card the
|
|
2050
|
+
cardholder stored in their Vault, as for every prepared checkout. The merchant
|
|
2051
|
+
keeps a token for that card and charges its own cart total later, at place
|
|
2052
|
+
order, so the cardholder approves the total your agent read, and the approval
|
|
2053
|
+
screen says the merchant can keep the card on file, as it does for Worldpay
|
|
2054
|
+
eProtect. In the sandbox the stored card is one of CyberSource's test cards.
|
|
2055
|
+
|
|
2056
|
+
The merchant's page asks its iframe for the reply 30 times, a second apart,
|
|
2057
|
+
from Save, which is too soon for a prompt that waits on a person. A Secure
|
|
2058
|
+
Acceptance checkout is therefore always prepared, before the agent clicks Save:
|
|
2059
|
+
|
|
2060
|
+
```ts
|
|
2061
|
+
const card = await vault.placeholderCard({ user: 'your-user-id', cardId: 'vc_...' }); // what the agent types
|
|
2062
|
+
const checkout = await attachToPlaywright(page, {
|
|
2063
|
+
vault, user: 'your-user-id', merchant: 'Arrow Electronics',
|
|
2064
|
+
amount: 1234, currency: 'usd', // the cart total the agent read: what the cardholder approves
|
|
2065
|
+
cardId: card.cardId, // required: the stored card that pays, locked to the approval
|
|
2066
|
+
onApprovalUrl: deliverPrivatelyToCardholder,
|
|
2067
|
+
});
|
|
2068
|
+
// The agent types card.number, card.securityCode and the card's real expiry, the card's own billing
|
|
2069
|
+
// name and address, and leaves "save for future use" and "set as default" unticked. Then:
|
|
2070
|
+
await checkout.prepare({ psp: 'cybersource_secure_acceptance', environment: 'production' });
|
|
2071
|
+
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
|
2072
|
+
```
|
|
2073
|
+
|
|
2074
|
+
- What the agent types: the placeholder number of the paying card's network
|
|
2075
|
+
and its placeholder security code (`placeholderCard()`), the card's real
|
|
2076
|
+
expiry, and the card's own billing name and address, which the merchant's
|
|
2077
|
+
server signs and its charge checks. The merchant's "save for future use" and
|
|
2078
|
+
"set as default" stay unticked: a request that keeps the card for later
|
|
2079
|
+
orders, or edits a saved one, is refused.
|
|
2080
|
+
- `secureAcceptanceProfileId` names the merchant's Secure Acceptance profile
|
|
2081
|
+
(a UUID shape): the CyberSource merchant the token is created for. On
|
|
2082
|
+
production, leave it out for a merchant Agentcard reviewed: the SDK fills the
|
|
2083
|
+
profile id the reviewed entry pins for the checkout page's origin, and
|
|
2084
|
+
`merchant` must be that entry's name, the one the approval shows
|
|
2085
|
+
(`merchant_profile_mismatch`). A checkout it can name no profile id for is
|
|
2086
|
+
refused with `secure_acceptance_profile_id_invalid`. In the sandbox, name your
|
|
2087
|
+
own test profile's id. A token request naming another profile id never spends
|
|
2088
|
+
the approval.
|
|
2089
|
+
- `cardId` is required (`card_required`), and the approval is locked to that
|
|
2090
|
+
card.
|
|
2091
|
+
|
|
2092
|
+
After Save, the SDK pauses the iframe's post and binds it to the preparation
|
|
2093
|
+
only when payment-core's rule admits it for the prepared environment, profile
|
|
2094
|
+
id and checkout page, which the post's own `Origin` header names. The
|
|
2095
|
+
cardholder's device, still on the approval page, writes the real card into the
|
|
2096
|
+
four card fields and sends the post to CyberSource through Agentcard's
|
|
2097
|
+
encrypted relay. When CyberSource's reply page carries no card, which the device
|
|
2098
|
+
and the API each check, the SDK answers the paused iframe with that page, byte
|
|
2099
|
+
for byte, status 200 and its one content type, so the browser loads it as
|
|
2100
|
+
CyberSource's own page and its form delivers CyberSource's signed reply to the
|
|
2101
|
+
merchant's receipt page in your browser's session, as it would without
|
|
2102
|
+
Agentcard. The merchant's page then sets its payment method. The SDK reads the
|
|
2103
|
+
authorization every 500 ms for 30 seconds, so the reply lands inside the
|
|
2104
|
+
merchant's poll.
|
|
2105
|
+
|
|
2106
|
+
| Request to Secure Acceptance | What the adapters do |
|
|
2107
|
+
| --- | --- |
|
|
2108
|
+
| The token create a merchant's form posts into an iframe, naming the prepared profile id, from the prepared page, on the prepared environment's host | Paused, bound to the preparation and answered with CyberSource's page |
|
|
2109
|
+
| The same post with no preparation on the attachment | The iframe gets the stopped page (`stopped_before_send`), nothing is created, and the controller reads `failed` with reason `preparation_required` |
|
|
2110
|
+
| The same post when the preparation is not ready or spent, names another profile id or page, carries no `Origin`, or keeps the card | The iframe gets the stopped page, and the preparation is retired with `checkout_changed` or payment-core's reason (`secure_acceptance_origin_header`, `secure_acceptance_keeps_card`, ...) |
|
|
2111
|
+
| The token create sent as the page's own navigation, from a fetch or from an XHR | Stopped as an unsupported checkout (`unsupported_cybersource_secure_acceptance`) |
|
|
2112
|
+
| Every other `/silent/` path, the India host, another spelling of the URL, a `sale` or an `authorization` | Stopped for every company, as the processor table says |
|
|
2113
|
+
| A post with no card number | Continues untouched |
|
|
2114
|
+
|
|
2115
|
+
The clocks, from the pause:
|
|
2116
|
+
|
|
2117
|
+
- 20 seconds with nothing being handed to the iframe: the SDK retires the
|
|
2118
|
+
preparation or cancels the authorization. Confirmed (the card never left the
|
|
2119
|
+
device): the iframe gets the stopped page, and the controller reads `failed`
|
|
2120
|
+
with reason `secure_acceptance_send_window_closed` (a `blocked` event of that
|
|
2121
|
+
name). Not confirmed, since the device may already be sending: it keeps
|
|
2122
|
+
waiting (`secure_acceptance_send_window_unconfirmed`). The cardholder's
|
|
2123
|
+
device keeps an earlier boundary of its own, 12 seconds after it last saw
|
|
2124
|
+
the checkout unbound: past it the device sends nothing and declines the
|
|
2125
|
+
request with the same reason, and the iframe and the controller read the
|
|
2126
|
+
same as when the SDK's window closes.
|
|
2127
|
+
- 45 seconds with no reply: `PaymentOutcomeUnknownError` with reason
|
|
2128
|
+
`secure_acceptance_reply_late`. The iframe gets the stopped page
|
|
2129
|
+
`sent_answer_not_shown`, naming the authorization, unless the API confirmed
|
|
2130
|
+
the card never left (`stopped_before_send`).
|
|
2131
|
+
- A reply that fails payment-core's rule raises `secure_acceptance_reply_unconfirmed`,
|
|
2132
|
+
and one the API no longer serves (60 seconds after approval)
|
|
2133
|
+
`response_expired`; the iframe gets `sent_answer_not_shown`. A card CyberSource
|
|
2134
|
+
refused raises `ProcessorRefusedError`, the controller reads `declined`, and the
|
|
2135
|
+
iframe gets `sent_answer_not_shown`.
|
|
2136
|
+
|
|
2137
|
+
What a late or unread reply means: CyberSource may have created a token the
|
|
2138
|
+
merchant's page never read, and the merchant may keep it. For a merchant whose
|
|
2139
|
+
reviewed entry names the request its page sends once the receipt answered
|
|
2140
|
+
(Arrow's payment method set), the SDK watches 10 seconds for it after delivery,
|
|
2141
|
+
read-only; when none comes it emits `secure_acceptance_reply_unused` with
|
|
2142
|
+
`{ authorizationId, last4 }`. Reload checkout: if the merchant lists a saved
|
|
2143
|
+
card ending in those four digits, pick it; otherwise ask for a new checkout and
|
|
2144
|
+
a new approval. Never type a card into a spent approval.
|
|
2145
|
+
|
|
2146
|
+
3-D Secure: the merchant charges the token at place order and may run 3-D
|
|
2147
|
+
Secure in your browser (Arrow uses Cardinal). Only a frictionless result places
|
|
2148
|
+
the order: a challenge stops it before any authorization, so nothing is
|
|
2149
|
+
charged. Report the order as not placed. The merchant keeps the token, as the
|
|
2150
|
+
approval screen told the cardholder, until the cardholder removes the saved card
|
|
2151
|
+
in their account with the merchant.
|
|
2152
|
+
|
|
2153
|
+
Arrow Electronics is the one merchant Agentcard reviewed on the production
|
|
2154
|
+
host. Its entry, `arrow_token_create`, pins the profile id and access key
|
|
2155
|
+
Arrow's checkout at `https://www.arrow.com` sends, so `prepare()` there fills
|
|
2156
|
+
Arrow's profile id, and `merchant` must be `Arrow Electronics`. The API refuses
|
|
2157
|
+
any other profile id, access key or page on the production host
|
|
2158
|
+
(`unsupported_checkout`, with `secure_acceptance_merchant_unreviewed` or
|
|
2159
|
+
`secure_acceptance_access_key_unreviewed`). The sandbox takes your own test
|
|
2160
|
+
profile and CyberSource's test cards. `prepare()` reports the API's refusals by name,
|
|
2161
|
+
with the API's own reason as the error's `detail`: `unsupported_checkout`,
|
|
2162
|
+
`processor_unavailable`, `merchant_profile_mismatch`, the environment refusals,
|
|
2163
|
+
and, until Agentcard turns the relay on,
|
|
2164
|
+
`cybersource_sa_relay_unavailable`. A Save while the relay is off ends the same
|
|
2165
|
+
way: the API refuses the create before any authorization exists, the iframe
|
|
2166
|
+
gets the stopped page (`stopped_before_send`), the preparation is retired, and
|
|
2167
|
+
the controller reads `failed` with reason `cybersource_sa_relay_unavailable`.
|
|
2168
|
+
The SDK writes no CORS header on the iframe's answer; Playwright adds its own to
|
|
2169
|
+
any cross-origin fulfill, which a navigation never reads. Autopilot never pays
|
|
2170
|
+
this lane. The matching API, Vault and relay releases are required.
|
|
2171
|
+
|
|
2030
2172
|
### Pay a WSPay Components checkout
|
|
2031
2173
|
|
|
2032
2174
|
Hotel booking pages such as Valamar's take cards in WSPay Components, WSPay's
|
package/dist/amadeus.d.ts
CHANGED
|
@@ -8,7 +8,18 @@ export declare const AMADEUS_SEND_WINDOW_MS = 20000;
|
|
|
8
8
|
* limit. As a network error, the tunnel reports it at once as its own "Error".
|
|
9
9
|
*/
|
|
10
10
|
export declare function failsAsNetworkError(url: string): boolean;
|
|
11
|
-
/** The
|
|
11
|
+
/** The processor a send window's events and stop cause name. */
|
|
12
|
+
export type SendWindowProcessor = 'amadeus' | 'secure_acceptance';
|
|
13
|
+
/** Why a send window stopped its attempt: the API confirmed the card never left the device and never can. */
|
|
14
|
+
export type SendWindowClosed = `${SendWindowProcessor}_send_window_closed`;
|
|
15
|
+
/**
|
|
16
|
+
* The send window for a request paused under a preparation for `psp`, or null when it has none: a
|
|
17
|
+
* prepared Amadeus SDK add, and a prepared CyberSource Secure Acceptance token create, each 20 seconds.
|
|
18
|
+
*/
|
|
19
|
+
export declare function preparedSendWindowMs(psp: string | undefined, url: string): number | null;
|
|
20
|
+
/** The processor a prepared request's send window names in its events, or null when it has none. */
|
|
21
|
+
export declare function sendWindowProcessor(psp: string | undefined): SendWindowProcessor | null;
|
|
22
|
+
/** The send window for a prepared Amadeus SDK add, or null: preparedSendWindowMs for Amadeus alone. */
|
|
12
23
|
export declare function amadeusSendWindowMs(psp: string | undefined, url: string): number | null;
|
|
13
24
|
/** What the window reads from the adapter, re-read when it closes. */
|
|
14
25
|
export interface SendWindowHooks {
|
|
@@ -32,10 +43,11 @@ export interface SendWindowHooks {
|
|
|
32
43
|
/** The part of a merchant attempt the window acts on. */
|
|
33
44
|
export interface SendWindowAttempt {
|
|
34
45
|
readonly signal: AbortSignal;
|
|
35
|
-
stop(cause:
|
|
46
|
+
stop(cause: SendWindowClosed): void;
|
|
36
47
|
}
|
|
37
48
|
/**
|
|
38
49
|
* Arm the window on one paused request. Returns the function that disarms it, which the adapter
|
|
39
|
-
* calls once the attempt settles, whatever the outcome.
|
|
50
|
+
* calls once the attempt settles, whatever the outcome. `processor` names the window's events and
|
|
51
|
+
* the attempt's stop cause (Amadeus by default).
|
|
40
52
|
*/
|
|
41
|
-
export declare function armSendWindow(ms: number, attempt: SendWindowAttempt, hooks: SendWindowHooks): () => void;
|
|
53
|
+
export declare function armSendWindow(ms: number, attempt: SendWindowAttempt, hooks: SendWindowHooks, processor?: SendWindowProcessor): () => void;
|
package/dist/amadeus.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Two Amadeus Checkout rules both browser adapters share: how a refused card request fails
|
|
2
|
-
// (failsAsNetworkError), and the send window of a prepared SDK add, below
|
|
2
|
+
// (failsAsNetworkError), and the send window of a prepared SDK add, below, which a prepared
|
|
3
|
+
// CyberSource Secure Acceptance token create shares.
|
|
3
4
|
//
|
|
4
5
|
// The Checkout Web SDK's tunnel iframe gives its add XHR 30 seconds (xhr.timeout = 30000 in
|
|
5
6
|
// tunnel.50fc56c91f295b5bfef0.js), then reports a timeout and Air France's page gives the payment
|
|
@@ -8,17 +9,26 @@
|
|
|
8
9
|
// SDK cannot see the device start sending, and the API can: a plain runtime cancel is confirmed only
|
|
9
10
|
// while the card has not moved, and once it is, the device's send is refused.
|
|
10
11
|
//
|
|
12
|
+
// A merchant's page that posts CyberSource Secure Acceptance's token create into a hidden frame asks
|
|
13
|
+
// that frame for the reply 30 times, a second apart, from Save (Arrow Electronics), then gives up
|
|
14
|
+
// without a word. A token CyberSource creates after that point is one the page never reads, so a
|
|
15
|
+
// paused token create gets the same 20 second window (cybersource-secure-acceptance.ts).
|
|
16
|
+
//
|
|
11
17
|
// So when the window closes and no answer is being handed to the page yet, the adapter asks for that
|
|
12
18
|
// cancel. Confirmed: nothing was sent and nothing can be, so the attempt stops, the page's request
|
|
13
|
-
// fails, and the checkout reads failed. Not
|
|
14
|
-
// attempt keeps waiting for its answer, which can
|
|
15
|
-
// deadline still ends it. A create that has not answered yet
|
|
16
|
-
// bound the preparation the cardholder approved, and that
|
|
17
|
-
// first retires the preparation: once the API confirms no
|
|
18
|
-
// when a request already holds it, that authorization gets
|
|
19
|
-
// when the API confirms neither, the attempt keeps waiting. The
|
|
20
|
-
// client timeout and gets no window.
|
|
19
|
+
// fails (a Secure Acceptance frame gets the stopped page), and the checkout reads failed. Not
|
|
20
|
+
// confirmed: the device may already be sending, so the attempt keeps waiting for its answer, which can
|
|
21
|
+
// still reach the page, and the page's own deadline still ends it. A create that has not answered yet
|
|
22
|
+
// may already have committed its row and bound the preparation the cardholder approved, and that
|
|
23
|
+
// device may be sending too. So the window first retires the preparation: once the API confirms no
|
|
24
|
+
// request can bind it, nothing can be sent; when a request already holds it, that authorization gets
|
|
25
|
+
// the same cancel and the same rule, and when the API confirms neither, the attempt keeps waiting. The
|
|
26
|
+
// Data Collection Page's store has no client timeout and gets no window. The window's events name the
|
|
27
|
+
// processor: `amadeus_send_window_closed` and `amadeus_send_window_unconfirmed`, or
|
|
28
|
+
// `secure_acceptance_send_window_closed` and `secure_acceptance_send_window_unconfirmed`.
|
|
21
29
|
import { amadeusCardUrl } from './amadeus-checkout.generated.js';
|
|
30
|
+
import { isSecureAcceptanceTokenRequest } from './cybersource-secure-acceptance.generated.js';
|
|
31
|
+
import { SECURE_ACCEPTANCE_PSP, SECURE_ACCEPTANCE_SEND_WINDOW_MS } from './cybersource-secure-acceptance.js';
|
|
22
32
|
/** How long after the pause a paused Amadeus SDK add may still be sent. */
|
|
23
33
|
export const AMADEUS_SEND_WINDOW_MS = 20_000;
|
|
24
34
|
/**
|
|
@@ -31,15 +41,39 @@ export const AMADEUS_SEND_WINDOW_MS = 20_000;
|
|
|
31
41
|
export function failsAsNetworkError(url) {
|
|
32
42
|
return amadeusCardUrl(url) !== null;
|
|
33
43
|
}
|
|
34
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* The send window for a request paused under a preparation for `psp`, or null when it has none: a
|
|
46
|
+
* prepared Amadeus SDK add, and a prepared CyberSource Secure Acceptance token create, each 20 seconds.
|
|
47
|
+
*/
|
|
48
|
+
export function preparedSendWindowMs(psp, url) {
|
|
49
|
+
if (psp === 'amadeus_checkout')
|
|
50
|
+
return amadeusCardUrl(url)?.path === 'sdk' ? AMADEUS_SEND_WINDOW_MS : null;
|
|
51
|
+
if (psp === SECURE_ACCEPTANCE_PSP) {
|
|
52
|
+
let tokenCreate = false;
|
|
53
|
+
try {
|
|
54
|
+
tokenCreate = isSecureAcceptanceTokenRequest(url, 'POST') === true;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
tokenCreate = false;
|
|
58
|
+
}
|
|
59
|
+
return tokenCreate ? SECURE_ACCEPTANCE_SEND_WINDOW_MS : null;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/** The processor a prepared request's send window names in its events, or null when it has none. */
|
|
64
|
+
export function sendWindowProcessor(psp) {
|
|
65
|
+
return psp === 'amadeus_checkout' ? 'amadeus' : psp === SECURE_ACCEPTANCE_PSP ? 'secure_acceptance' : null;
|
|
66
|
+
}
|
|
67
|
+
/** The send window for a prepared Amadeus SDK add, or null: preparedSendWindowMs for Amadeus alone. */
|
|
35
68
|
export function amadeusSendWindowMs(psp, url) {
|
|
36
|
-
return psp === 'amadeus_checkout'
|
|
69
|
+
return psp === 'amadeus_checkout' ? preparedSendWindowMs(psp, url) : null;
|
|
37
70
|
}
|
|
38
71
|
/**
|
|
39
72
|
* Arm the window on one paused request. Returns the function that disarms it, which the adapter
|
|
40
|
-
* calls once the attempt settles, whatever the outcome.
|
|
73
|
+
* calls once the attempt settles, whatever the outcome. `processor` names the window's events and
|
|
74
|
+
* the attempt's stop cause (Amadeus by default).
|
|
41
75
|
*/
|
|
42
|
-
export function armSendWindow(ms, attempt, hooks) {
|
|
76
|
+
export function armSendWindow(ms, attempt, hooks, processor = 'amadeus') {
|
|
43
77
|
let disarmed = false;
|
|
44
78
|
const settled = () => disarmed || attempt.signal.aborted || hooks.delivering();
|
|
45
79
|
// Resolves only once the API confirms the card never left the device and never can; see the top
|
|
@@ -69,11 +103,12 @@ export function armSendWindow(ms, attempt, hooks) {
|
|
|
69
103
|
if (settled())
|
|
70
104
|
return;
|
|
71
105
|
if (!confirmed) {
|
|
72
|
-
hooks.onEvent?.({ type: 'blocked', detail:
|
|
106
|
+
hooks.onEvent?.({ type: 'blocked', detail: `${processor}_send_window_unconfirmed` });
|
|
73
107
|
return;
|
|
74
108
|
}
|
|
75
|
-
|
|
76
|
-
|
|
109
|
+
const closed = `${processor}_send_window_closed`;
|
|
110
|
+
hooks.onEvent?.({ type: 'blocked', detail: closed });
|
|
111
|
+
attempt.stop(closed);
|
|
77
112
|
};
|
|
78
113
|
const timer = setTimeout(() => { void close(); }, ms);
|
|
79
114
|
timer.unref?.();
|