@agent-cards/sdk 0.23.19 → 0.23.21
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 +61 -2
- package/PREFLIGHT.md +4 -0
- package/README.md +175 -7
- package/dist/amadeus.d.ts +4 -2
- package/dist/amadeus.js +16 -3
- package/dist/buy.d.ts +84 -2
- package/dist/buy.js +16 -1
- package/dist/card-endpoint-facts.d.ts +2 -1
- package/dist/card-endpoint-facts.js +18 -9
- package/dist/card-endpoint.d.ts +20 -4
- package/dist/card-endpoint.js +38 -4
- package/dist/card-fields.generated.js +1444 -51
- package/dist/cdp.d.ts +29 -0
- package/dist/cdp.js +444 -24
- package/dist/client.d.ts +134 -7
- package/dist/client.js +350 -25
- package/dist/comgate.d.ts +50 -0
- package/dist/comgate.js +80 -0
- package/dist/cybersource-secure-acceptance.generated.d.ts +1 -1
- package/dist/eprotect.generated.d.ts +1 -1
- package/dist/gmo.generated.js +7 -3
- package/dist/hipay.generated.d.ts +56 -0
- package/dist/hipay.generated.js +392 -0
- package/dist/hold.d.ts +180 -0
- package/dist/hold.js +270 -0
- package/dist/hosted-entry.d.ts +4 -0
- package/dist/hosted-entry.js +32 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -0
- package/dist/lifecycle.d.ts +10 -0
- package/dist/lifecycle.js +21 -0
- package/dist/merchant-card-endpoints.generated.js +102 -11
- package/dist/paddle-tokenex.generated.js +7 -3
- package/dist/paddle-vgs.generated.d.ts +3 -2
- package/dist/paddle-vgs.generated.js +9 -5
- package/dist/payrails.generated.js +7 -3
- package/dist/pre-claim.d.ts +20 -0
- package/dist/pre-claim.js +202 -22
- package/dist/preflight-catalog.json +58 -0
- package/dist/preflight-schemas.json +4 -0
- package/dist/preflight.generated.js +26 -2
- package/dist/prepared-processor.d.ts +6 -2
- package/dist/prepared-processor.js +18 -2
- package/dist/primer.generated.js +1487 -94
- package/dist/przelewy24.d.ts +47 -0
- package/dist/przelewy24.js +88 -0
- package/dist/quickpay.d.ts +44 -0
- package/dist/quickpay.js +65 -0
- package/dist/registry.d.ts +6 -5
- package/dist/registry.js +27 -7
- package/dist/shiji-sts.generated.js +7 -3
- package/dist/stripe-checkout.generated.js +744 -13
- package/dist/tokenex-iframe.d.ts +93 -0
- package/dist/tokenex-iframe.generated.d.ts +81 -0
- package/dist/tokenex-iframe.generated.js +403 -0
- package/dist/tokenex-iframe.js +195 -0
- package/dist/transbank.d.ts +45 -0
- package/dist/transbank.js +73 -0
- package/dist/twoc2p.d.ts +54 -0
- package/dist/twoc2p.generated.d.ts +33 -0
- package/dist/twoc2p.generated.js +1149 -0
- package/dist/twoc2p.js +85 -0
- package/dist/voice.d.ts +16 -13
- package/dist/voice.js +13 -10
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.23.21
|
|
6
|
+
|
|
7
|
+
- Pause ComGate's payment gateway, where most Czech and Slovak e-shops send the buyer, for the cardholder's device. The sync now also asks for `capabilities=comgate_hosted_payment_page`, and the API serves ComGate's recognizer only for a company Agentcard turned ComGate on for. The merchant's server creates the payment at ComGate and sends the buyer to `https://payN.comgate.cz/init?id=<transaction id>`. The SDK pauses that link, in the page, in a frame of it or in a window it opened, as a `hosted_form` authorization that carries the link alone, and answers the paused navigation with the `submitted_on_device` page once the cardholder's device has paid it. The link names no amount, so `authorize()` refuses one without `amount` and `currency`, above zero, with `AmountRequiredError`. Every request but a GET that your browser makes to ComGate's API (`payments.comgate.cz`, `payments-gcp.comgate.cz`) or its card frame hosts (`sec.comgate.cz`, `sec-gcp.comgate.cz`) carries the card your agent typed, part of it or its ciphertext, and the SDK fails each one and reads `unsupported` with reason `processor_card_page`. Your events and error messages name the link without its id. Matching API and Vault releases are required. ComGate stays off for every company Agentcard has not turned it on for.
|
|
8
|
+
|
|
9
|
+
- Pause card requests in a window the merchant's page opens. `attachToCdp` now attaches each window the attached page opens (`window.open`, or a form or link aimed at a new window) in the page's own browser context, and each window such a window opens, and arms it as it arms the page, so a request there reaches the same pause and the same gates. A hosted payment page the merchant opens in its own window, on the merchant's page first and then sent on to the processor, pauses there, and the window gets the `submitted_on_device` page. Earlier releases never saw a request made in such a window. The attach starts when Chromium reports the window, so a window opened straight onto a processor's page can still load it before it is armed. A window another page opens, or one in another browser context, is left alone. `attachToPlaywright` routes the page's own requests, as before.
|
|
10
|
+
|
|
11
|
+
- Name the checkout page when the request `attachToCdp` pauses is the page's own navigation away from it. A hosted payment page entry that the merchant's page sends its window to, by script, by a form or through the merchant's redirect, pauses the page's top-level navigation, and Chromium does not answer a read of that page's frame tree while it is paused. Earlier releases waited one second for that read and then created the authorization with no `checkout_origin`, so a processor whose rules hold the payment to a reviewed merchant's page refused it. The adapter now keeps the URL the page last committed, from its target when it attaches and from each document the page commits after, and names its origin at once. A request from a frame, a worker or another document is read as before.
|
|
12
|
+
|
|
13
|
+
- Pay a MakeCommerce card payment through the checkout relay, for a company Agentcard turns MakeCommerce on for (dark launch). MakeCommerce (Maksekeskus, in Estonia, Latvia, Lithuania and Finland) takes the card in its own card frame, which posts it to `https://cc.maksekeskus.ee/card-payment` under the shop's publishable key. This build declares `makecommerce_card_frame` on every sync, and the API serves the MakeCommerce entry only to a company Agentcard turned MakeCommerce on for, so every other company's MakeCommerce payments go through untouched, as for an unsupported processor. For an enabled company the SDK pauses only the frame's own one-time card payment; a post with "Remember my card" or the subscription box ticked is refused before anything is sent. The post names no price, so `authorize()` refuses one without `amount` above zero and `currency` (`AmountRequiredError`): send the total the shop's order page shows, in euros, the one currency MakeCommerce charges. The cardholder's device sends the card through the checkout relay, and the frame gets MakeCommerce's answer, the URL of its 3-D Secure page, which the frame opens in the agent's browser, where a challenge needs a person at that browser. A MakeCommerce payment also needs the matching API, Vault and checkout relay releases.
|
|
14
|
+
|
|
15
|
+
- Pay a JetBlue booking's card form, TokenEx's hosted iframe v3, for a company Agentcard turns the TokenEx iframe on for (dark launch). JetBlue takes the card number and CVV in TokenEx's cross-origin card-number frame on `htp.tokenex.com` (`test-htp.tokenex.com` in test); the cardholder name and the expiry stay on JetBlue's page. The frame posts the whole card as JSON to its own URL, query included. This build declares `tokenex_iframe_tokenize` on every sync, and the API serves the TokenEx entry only to a company Agentcard turned it on for, so every other company's TokenEx requests go through untouched. For an enabled company both adapters pause only that frame's own card post (on CDP, on the out-of-process frame's own session) and refuse every other post on the frame's path that carries a card, a stored token's CVV re-entry or a query this build has not read among them, with a `blocked` event, detail `invalid_tokenex_iframe_request`, before anything is created and without holding the page. The post names no price, so `authorize()` refuses one without `amount` above zero and `currency` (`AmountRequiredError`): send the total JetBlue's page shows. Once the cardholder approves, the frame gets TokenEx's token as the API recorded it, rebuilt from payment-core's allow-list under a JSON content type, and adds its own `cardType`; an approval whose recorded answer is not a token ends `PaymentOutcomeUnknownError`, reason `tokenex_iframe_token_unconfirmed`, and the frame gets nothing. The frame answers a repeat with nothing changed from its own cache; when it sends the same bytes again (its five-minute timer, a reset, a reload at the same URL), the SDK answers that post with the same token, a `repeat_answered` event and no second prompt, while that approval is the checkout's current one, for at most the iframe key's lifetime, and once the token reached the page only while `resolveMerchantResult` reports the order `pending`. Another placeholder or CVV, a re-created frame or a new key asks the cardholder again. `placeholderCard({ user, psp: 'tokenex_iframe' })` gives a Visa, Mastercard or American Express number the frame reads as the stored card's own brand, and refuses every other network (`brand_unsupported`), since the cardholder's device refuses a placeholder of another brand. Autopilot never pays it. Today the API refuses every TokenEx create as `unsupported_checkout` (`tokenex_iframe_account_unreviewed`) until Agentcard lists JetBlue's account, and a TokenEx payment also needs the matching API, Vault and checkout relay releases.
|
|
16
|
+
|
|
17
|
+
- Pause Przelewy24's pay wall (where most Polish shops send a buyer who pays by card) for the cardholder's device. The sync now also asks for `capabilities=przelewy24_hosted_payment_page`, and the API serves Przelewy24's recognizer only for a company Agentcard turned Przelewy24 on for. The SDK pauses the pay wall link, `https://secure.przelewy24.pl/trnRequest/<token>` (or `sandbox.przelewy24.pl` for a test payment), which carries no card, as a `hosted_form` authorization, and answers the paused navigation with the `submitted_on_device` page once the cardholder's device has paid it. The link names no amount, so send the amount Przelewy24's pay wall shows, with its currency: the API refuses a create without one (`amount_required`). The merchant's page sends the buyer there by a script, by its server's redirect or in a window it opens. `attachToCdp` pauses all three; `attachToPlaywright` pauses only the script's navigation in the page, because Playwright continues a redirect itself and does not route a window the page opens, so attach to a Przelewy24 checkout with `attachToCdp`. Every post a page the SDK watches makes to the pay wall, such as its card method's post and its card form's post, and the request to its Saferpay resolver, which names the typed card's type, leads to a card: the SDK fails each one and reads `unsupported` with reason `processor_card_page`. The SDK watches the attached page and its frames, and under `attachToCdp` each window the page opens. A window `attachToPlaywright` does not watch reaches the pay wall untouched, with the placeholder card your agent types. Matching API and Vault releases are required. Przelewy24 stays off for every company Agentcard has not turned it on for.
|
|
18
|
+
|
|
19
|
+
- Pay a Weeztix ticket shop's card form, which sends the card to Pay. (pay.nl), for a company Agentcard turned Pay. on for (dark launch): the SDK declares `pay_nl_card_token`, pauses the form's card token request and hands the form Pay.'s token answer. Pay.'s request names no price, so pass `amount` and `currency`, above zero: that is the price the cardholder approves. Without them, `authorize()` throws before anything is created:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
AmountRequiredError: A Pay. payment request names no amount, so pass amount and currency: that is the amount the cardholder approves. Nothing was created and nothing was charged.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- Pay a PayPal Card Fields checkout, such as a WooCommerce PayPal Payments store's "Debit & Credit Cards" option, for a company Agentcard turned PayPal Card Fields on for (dark launch). The store's card fields are PayPal's frames, which post the card to `https://www.paypal.com/v2/checkout/orders/{order}/confirm-payment-source` (Card Fields v5) or the same path on `api-m.paypal.com` (v6). The sync now also asks for `capabilities=paypal_card_fields`, and the API serves PayPal's recognizer only to an enabled company. There both adapters pause the frames' own confirm, `authorize()` creates the authorization with it, the cardholder's device sends the card (a v5 confirm through the checkout relay, a v6 one directly), and the frame receives the order's id and status, after which the store's server captures the payment. The confirm names no amount, so pass `amount` and `currency` above zero: `authorize()` refuses one without them with `AmountRequiredError`. A confirm that asks PayPal to keep the card or asks for 3-D Secure on every payment fails in the browser with a `blocked` event. The SDK arms only the confirm's path on PayPal's hosts, never the rest of `www.paypal.com`. Every other company keeps the refusal described below, `unsupported_paypal_card_fields`. A payment also needs the matching API, relay and Vault releases.
|
|
26
|
+
|
|
27
|
+
- Name PayPal Card Fields as PayPal in a preflight inspection. PayPal's JS SDK draws each card field as a frame on `www.paypal.com`, at `/smart/card-field` for Card Fields v5 and at `/web-sdk/v6/card-fields-content` for v6, and an inspection that saw only those frames named no processor (`psp_not_detected`). Both frames now name `paypal`, and the result stays `unknown` while PayPal Card Fields ships dark. The catalog's `catalog_sha256` changes; `catalog_version` stays `2026-09-15.1`.
|
|
28
|
+
|
|
29
|
+
- Pay GoPay's card form through the checkout relay, from Agentcard's own account only for now (dark launch): Agentcard turns it on for another company once a reviewed change binds GoPay's payee and amount to the approval. A Czech or Slovak shop opens GoPay's gateway as a page or as an overlay frame on its own page, and the gateway's card frame posts the card as JSON to its own URL, `https://secure.gopay.com/gp-card-comm/h/<token>` (`secure.sandbox.gopay.com` in test). This build declares `gopay_card_form` on every sync, and the API serves the GoPay entry only to a company Agentcard turned GoPay on for, so every other company's GoPay payments go through untouched, as for an unsupported processor. For an enabled company the SDK pauses only the card frame's own card post. One card frame serves every GoPay shop and its post names none, so the API takes a live post only on the checkout page of a shop Agentcard reviewed (Caramilla.cz today), refuses any other with `unsupported_checkout`, and names the reviewed shop, whatever `merchant` you send. A shop that sends the buyer away to GoPay's gateway leaves the gateway as the checkout page, which names no shop, so it is refused the same way. The post names no price, so `authorize()` refuses one without `amount` above zero and `currency` (`AmountRequiredError`): send the total GoPay's gateway shows. The cardholder's device sends the card through the checkout relay, and the frame gets GoPay's answer, an empty JSON object. GoPay's gateway then reads how the payment ended from GoPay and runs 3-D Secure in the agent's browser when the bank asks for it, where a challenge needs a person at that browser. A GoPay shop takes a card issued outside Europe only when it asked GoPay for that, and GoPay refuses such a card at authorization, with nothing charged. Your events and error messages also hide a GoPay card form's token behind any path on GoPay's card frame hosts, not only after `/h/`, and with any of its digits percent-escaped. A GoPay payment also needs the matching API, Vault and checkout relay releases.
|
|
30
|
+
|
|
31
|
+
- Keep a processor's path credential out of hold mode. `onHeld`, `controller.hold.held`, the `card_request_held`, `card_request_refused`, `card_request_expired` and `card_request_lost` events, and the `card_request_unwatched` event a shared worker's card request raises named the request's origin and path as written, so a credential a processor writes into the path, such as a GoPay card form's token or a PayMongo session, showed there. They now name it as `redactUrl` does, as every other event already did. No API or Vault release is needed.
|
|
32
|
+
|
|
33
|
+
- Pay a Revolut Business card field, from Agentcard's own company only for now (dark launch): Agentcard turns it on for another company once the API reads the order a payment names and holds its merchant, amount and currency to what the cardholder approves. Revolut's card field and card popup, on a merchant's own page (Revolut's WooCommerce plugin among them) or on Revolut's hosted checkout, post the card from their frame on `merchant.revolut.com` to `https://merchant.revolut.com/api/public/orders/<order token>/payments` as one JSON body. This build declares `revolut_card_field` on every sync, and the API serves the Revolut entry only to a company Agentcard turned Revolut on for, so every other company's Revolut payments go through untouched, as for an unsupported processor. For an enabled company the SDK pauses only the frame's card payment: a payment by Apple Pay, Google Pay or Revolut Pay on the same URL, and the frame's poll of the payment, continue untouched, and a card payment that asks Revolut to keep the card on file, or any other card body that is not the frame's own, fails in the browser before anything is created. The card payment names the order's amount in minor units but not its currency, so `authorize()` refuses one without `amount` above zero and `currency` (`AmountRequiredError`): send the order's total and its currency. The API refuses an amount that is not the card payment's, or a currency Revolut and Agentcard do not both count in hundredths (`amount_mismatch`), and any request that is not the card field's own (`invalid_revolut_request`). The cardholder's device sends the card to Revolut itself, since Revolut's host answers the Vault's origin, and the frame gets Revolut's own answer: the payment Revolut created, which the frame then follows through any 3-D Secure step in the agent's browser, where a challenge needs a person at that browser. No live payment has gone through this route yet. A payment also needs the matching API and Vault releases.
|
|
34
|
+
|
|
35
|
+
- Pay SumUp's Payment Widget for a company Agentcard turns SumUp on for (dark launch). SumUp's WooCommerce plugin, payment links, Online Store and invoices all mount one widget, whose hidden form frame on `gateway.sumup.com` sends the card in one `PUT` to `https://api.sumup.com/v0.2/checkouts/<checkout id>`. This build declares `sumup_checkout_process` on every sync, and the API serves the SumUp entry only to a company Agentcard turned SumUp on for, so every other company's SumUp payments go through untouched. For an enabled company the SDK pauses that `PUT` and only that method, through `attachToCdp` or `attachToPlaywright`: the form frame is on another origin than the page, so an attachment inside the page never sees it. For that company every other body in that `PUT` fails in the frame before the SDK creates anything: a method other than a card, a card body that saves the card or splits it into installments, and the card body of SumUp's own Hosted Checkout page (`checkout.sumup.com`), which also names the product and the page's session. The request names no price, so `authorize()` refuses one without `amount` above zero and `currency` (`AmountRequiredError`): send the total SumUp's payment page shows. The API reads the checkout from SumUp, refuses a total that is not the checkout's exactly (`amount_mismatch`), a test-mode create (`live_host_on_sandbox_row`), a checkout that is paid, failed, expired, sets up recurring payments or belongs to a SumUp test merchant (`sumup_checkout_unpayable`), and a merchant Agentcard has not reviewed (`unsupported_checkout`), and it names the reviewed merchant, whatever `merchant` you send. The cardholder's device sends the card to SumUp itself, and the frame gets SumUp's own answer: the paid checkout, or the 3-D Secure step the widget then runs in the agent's browser, where a challenge needs a person at that browser. A SumUp payment also needs the matching API and Vault releases.
|
|
36
|
+
|
|
37
|
+
- Pay a GoDaddy Payments checkout, a GoDaddy Online Store's card step, for a company Agentcard turned GoDaddy Payments on for (dark launch). The store's card form is Poynt's frame on `cdn.poynt.net`, which posts the card to `https://services.poynt.net/v1/commerce/stores/<store id>/payment-tokens/generate-nonce`. The sync now also asks for `capabilities=poynt_collect_nonce`, and the API serves Poynt's recognizer only to an enabled company. There both adapters pause that post, `authorize()` creates the authorization with it, the cardholder's device sends the card through the checkout relay, and the frame receives the nonce Poynt minted, which the store's page sends to its own server to charge. The request names no amount, so pass `amount` and `currency` above zero: `authorize()` refuses one without them with `AmountRequiredError`. A live payment needs a store Agentcard reviewed at its own checkout page, or the create answers `unsupported_checkout`, and a test key is refused, since Poynt has no test host. A form that asks Poynt to keep the card fails in the browser with a `blocked` event. The store's pay button runs an invisible reCAPTCHA, so a person presses it. Every other company keeps today's path, where the request continues to Poynt untouched. A payment also needs the matching API, relay and Vault releases.
|
|
38
|
+
|
|
39
|
+
- Pause Transbank's Webpay Plus, the card payment form most Chilean shops send the buyer to, for the cardholder's device. The sync now also asks for `capabilities=transbank_webpay_hosted_payment_page`, and the API serves Webpay's recognizer only for a company Agentcard turned Webpay on for. The merchant's server creates the transaction at Transbank and sends the buyer to `https://webpay3g.transbank.cl/webpayserver/initTransaction?token_ws=<token>` with a GET, as the official WooCommerce plugin does. The SDK pauses that entry in the page or a frame of it, and with `attachToCdp` also in a window the page opened, as a `hosted_form` authorization that carries the entry alone, and answers the paused navigation with the `submitted_on_device` page once the cardholder's device has paid it. With `attachToPlaywright`, a popup that opens Webpay needs its own attachment, and the entry pauses only when the page navigates to it directly or by script. A merchant whose server redirects the checkout to Webpay needs `attachToCdp`, because Playwright's `page.route` never sees a redirect hop; with `attachToPlaywright` that checkout reads `unsupported` with reason `processor_card_page` at Webpay's token submit, and nothing is paid. The entry names no amount, so `authorize()` refuses one without `amount` and `currency`, above zero, with `AmountRequiredError`; the API takes only Chilean pesos (`clp`). Every request but a GET that your browser makes to Webpay's hosts (`webpay3g.transbank.cl`, `webpay3gint.transbank.cl`) either spends the token (`init_transaction.cgi`) or carries the card your agent typed (Webpay's card lookup, card check and card post), and the SDK fails each one and reads `unsupported` with reason `processor_card_page`. Your events and error messages name the entry without its token. Matching API, relay and Vault releases are required. Webpay stays off for every company Agentcard has not turned it on for.
|
|
40
|
+
|
|
41
|
+
## 0.23.1 to 0.23.20
|
|
42
|
+
|
|
43
|
+
- Continue a browser checkout from typed fields. `buy.ask` and `buy.confirm` now return `checkout_handoff`, the step your own agent takes when the purchase agent hands it a matched offer's checkout: the `buy_url` to open in the browser that checks out, the store and product, the listed price, the checkout authorization to create and the time to create it by. It is null on every other turn, and on a kind of step this version does not know. `buy.conversation` now returns `open_offers`, each offer handed to your agent and how far it got, and `browser_checkouts`, each checkout Agentcard ran in its own browser with its status, total, cart hash, approval link and order id. Before, the SDK dropped all three, so a caller read the step from `reply` and could not recover a browser checkout's approval link or order id from the conversation. Both lists are empty when the API sends none. TypeScript: `BuyTurn` gains `checkout_handoff` and `BuyConversation` gains `open_offers` and `browser_checkouts`; the new types `BuyCheckoutHandoff`, `BuyOpenOffer`, `BuyBrowserCheckout` and `BuyApiAmount` are exported. The listed price and the authorization hint arrive as the API writes them (`BuyApiAmount`: their `currency` typed, their amount fields passed through). An API that sends none of them reads as null and empty lists, so this release works before and after the API release that adds the browser path.
|
|
44
|
+
|
|
45
|
+
- Hold a card request with nothing created until the person confirms the total. `attachToCdp(…, { hold: true })` holds the first card request; `controller.release({ amount, currency, pageTotal })` creates the authorization at that total only when the page still shows it (`upTo: true` for a ceiling), and `controller.hold.cancel()` fails it. A held request is cancelled before its processor's own wait, and every other card request fails at once. Hold mode also holds Bolt's tokenizer (cancel only) and, with `placeholderNumbers`, any request carrying the placeholder card. New `serviceWorkers: 'bypass'` sends every armed frame's requests past site service workers instead of refusing the page, and `sharedWorkers: 'accept'` accepts a shared worker unwatched instead of refusing the page (both the default in hold mode; `'observe'` attaches and watches it where the browser allows). Exported: `CardRequestHold`, `HeldCardRequest`, `HoldOptions`, `HoldReleaseInput`, `HoldReleaseOutcome`, `HoldState`, `HoldEndedError`, `HoldAnsweredError`, `BOLT_HOLD_RULE`, `placeholderHoldRule`, `matchHoldOnlyRule`, `HOLD_DEADLINES_MS`.
|
|
46
|
+
|
|
47
|
+
- Hold a repeat confirm for the person's decision with `buy.confirm({ ..., wait: true })`. On a browser cart already waiting for the person's approval, the API holds the confirm up to 25 seconds and answers the moment they decide (`approved_not_charged` in sandbox, `order_placed` live, or `declined`), so your agent no longer polls. The first confirm never holds: it answers with the `approval_url` at once. Without `wait`, a confirm behaves as before. Needs an API release that reads `wait`; an older API ignores it and answers at once.
|
|
48
|
+
|
|
49
|
+
- Read `approved_not_charged` as a turn's status. A sandbox browser checkout the person approved now answers `status: 'approved_not_charged'` instead of `declined`: the card was never sent to the store, so nothing was ordered or charged, and `decline_code` still reads `sandbox_mode`. Earlier releases read the new status as `needs_input`. Every other sandbox answer, such as a sandbox order through a store integration, still answers `declined` with `sandbox_mode`. TypeScript: `BuyStatus` gains `approved_not_charged`; code that switches exhaustively over it needs the new case.
|
|
50
|
+
|
|
51
|
+
- Approve on Agentcard's page when a client names no approval posture. A client built without `voice`, or with `voice` and no `approval`, now runs every `voice.*` verb as `approval: 'sheet'` (the person approves on Agentcard's page) instead of throwing `missing_approval_posture`, so a checkout client that pauses a card request and asks for approval works without extra options. `approval: 'in_app'` is unchanged, and a value that is neither `'sheet'` nor `'in_app'` still throws `missing_approval_posture` at construction. A behaviour change for clients that relied on the error: release it as a minor version.
|
|
52
|
+
|
|
53
|
+
- Continue 2C2P's hosted payment page (`pgw-ui.2c2p.com`, builds 4.1 to 4.5) with the card field the cardholder's device writes, for Agentcard's proof company only (dark launch). 2C2P's page encrypts the card into one field, `payment.data.securePayToken`, and posts it to `pgwv4.2c2p.com/payment/<build>/Payment` (`sandbox-pgwv4.2c2p.com` for test payments). This build declares `twoc2p_secure_pay` on every sync, and the API serves the 2C2P entry only to Agentcard's proof company, whatever its processor lists name, so every other company's 2C2P payments go through untouched, as for an unsupported processor. The Payment names no price, so `authorize()` refuses one without `amount` above zero and `currency` (`AmountRequiredError`): send the total 2C2P's page shows. Before it creates anything, the API reads the merchant and the total from 2C2P itself and refuses another total (`amount_mismatch`), a saved card, an instalment plan, a currency conversion or a points redemption (`invalid_twoc2p_request`), and a live merchant Agentcard has not reviewed (`unsupported_checkout`). After the approval the SDK writes the device's field over the one your agent's page wrote, in that Payment alone, moves no other byte, and continues the page's own request; 3-D Secure then runs in the agent's browser, where a challenge needs a person at that browser. A request that is not byte for byte the one the cardholder approved is refused, and the SDK retires that approval as `merchant_never_retried`. The API serves the device's field only in the 10 minutes after the approval and then deletes it. Your events and error messages show nothing after the page's build folder on 2C2P's page hosts: `https://pgw-ui.2c2p.com/payment/4.1/{token}`. A 2C2P payment also needs the matching API and Vault releases.
|
|
54
|
+
|
|
5
55
|
- Answer a Datatrans form's second card post from a token answer that carries no `cardInfo`, or a `cardInfo` with the `accountType` and `binLength` fields Datatrans documents. Datatrans's card frame reads both answers as a success, and the API records them once it runs this release's checkout rules. An earlier SDK version still hands the first answer to the page, because it passes on whatever the API stored, but it sends the page's next post of the same form to the cardholder as a new approval.
|
|
6
56
|
|
|
7
57
|
- Finish a Worldpay eProtect checkout that your code resumes itself. With `attachToCdp(…, { pauseOnly: true })`, you create the authorization, read it, and call `controller.resume(read)`. The read carries the card registration as Agentcard stored it, bare JSON, and earlier releases handed exactly that to the merchant's paused registration script. Worldpay's script reads its answer only through the callback the request named, so it never received the registration: the page waited until the merchant's timeout, and the checkout read `outcome_unknown` with reason `merchant_request_aborted` after the cardholder had approved. The SDK now answers the resumed registration as it answers one it created itself, with the registration inside the request's own callback under Worldpay's script type. The SDK never hands the page a read that is not this request's registration, such as one already inside a callback or another request's answer: `resume` rejects with `PaymentOutcomeUnknownError`, reason `eprotect_registration_unconfirmed`, and the paused request fails. A checkout whose authorization the SDK creates was already answered this way and does not change. No API or Vault release is needed.
|
|
@@ -24,6 +74,7 @@
|
|
|
24
74
|
|
|
25
75
|
- Pay a Paddle checkout whose card method names TokenEx, from Agentcard's own account only for now (dark launch): Agentcard turns it on for another company once it has confirmed that a card encrypted for one Paddle checkout cannot pay another. On such a checkout, Paddle's frame RSA-encrypts the card number and the CVV in the page itself, each on its own, under the account's TokenEx key, and posts them to `https://checkout-service.paddle.com/<checkout>/pay` (`https://sandbox-checkout-service.paddle.com/<checkout>/pay` in test) with the placeholder's first six and last four digits, expiry and brand. The SDK now pauses that `/pay`, and once the cardholder approves, the cardholder's device encrypts the real card under the key the SDK pins for that checkout's environment and the SDK writes the two ciphertexts and those five facts over the placeholder's, changing nothing else in the request. The full card number never reaches the agent's page. The sync now also asks for `capabilities=paddle_tokenex_pay`. The ciphertext goes only into the `/pay` the approval was created from, byte for byte, because the account's TokenEx key would decrypt it in any later `/pay` of that seller. A `/pay` that names no TokenEx card (PayPal, a wallet, a saved card) carries none of the cardholder's cards and continues untouched, and the SDK refuses a `/pay` that is not the frame's shape with `paddle_tokenex_pay_unsupported` before anything is sent. No live payment has gone through this route yet. A payment also needs the matching API and Vault releases.
|
|
26
76
|
- Pay a Lyra payment form (PayZen, Systempay or Sogecommerce) for a company Agentcard turned Lyra on for (dark launch): the SDK declares `lyra_krypton_payment`, pauses the form's card payment and hands the form Lyra's answer with the payment id its 3-D Secure step needs.
|
|
77
|
+
|
|
27
78
|
- Keep a Lyra payment's session id out of your events and error messages. Lyra's Krypton payment form (PayZen, Systempay, Sogecommerce, Lyra Collect) sends its payment to `/api-payment/V4/Charge/Public/ProcessPayment/2;jsessionid=<id>` on the brand's API host, and that id, with the form token the page holds, continues the payment on Lyra's side. `redactUrl`, every event and every error message show it as `{session}`, as in `https://api.systempay.fr/api-payment/V4/Charge/Public/ProcessPayment/2;jsessionid={session}`. Lyra stays dark for every company Agentcard has not turned it on for, and no API or Vault release is needed.
|
|
28
79
|
|
|
29
80
|
- Stop PayPal's Card Fields and PayPal's guest card form from sending the card your agent typed to PayPal. A checkout that loads PayPal's JS SDK with `components=card-fields`, such as a WooCommerce PayPal Payments store's "Debit & Credit Cards" option, takes the card in frames on `www.paypal.com/smart/card-field` and sends it to `https://www.paypal.com/v2/checkout/orders/{id}/confirm-payment-source`; PayPal's v6 SDK sends the same request to `api-m.paypal.com`, and PayPal's sandbox uses `www.sandbox.paypal.com` and `api-m.sandbox.paypal.com`. PayPal's black "Debit or Credit Card" button takes the card on PayPal's own guest form, which sends it to `https://www.paypal.com/graphql?OnboardGuestMutation`, and Card Fields saves a card with no purchase through `graphql?UpdateVaultSetupToken`. No Agentcard payment took any of these over, and earlier releases let them through, so PayPal received the dummy card and declined it. Both adapters now fail each of them in the browser for every company, and the controller reads `unsupported` with reason `unsupported_paypal_card_fields`, or `unsupported_paypal_guest_checkout` for the guest form, and holds: to pay another way on the same page, attach again. A request with no card in it, such as a PayPal wallet confirm or the buttons' other GraphQL calls, 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 GraphQL post it cannot read is failed the same way only when its URL names one of the two card operations. A guest form that PayPal opens in a popup window runs in another page, which the SDK covers only when you attach it to that page as well. Events and error messages now name a PayPal order as `{order}`, as in `https://www.paypal.com/v2/checkout/orders/{order}/confirm-payment-source`, on every PayPal host, legacy Hosted Fields' `cors.api.paypal.com` included: an order id opens PayPal's own checkout page for that order to anyone who holds it. TypeScript: the controller's `unsupported()` reasons gain `unsupported_paypal_card_fields` and `unsupported_paypal_guest_checkout`; code that switches exhaustively over them needs the new cases.
|
|
@@ -32,6 +83,8 @@
|
|
|
32
83
|
|
|
33
84
|
- Pay a store on Paysafe Checkout's current build, 1.16.1. The loader Paysafe tells merchants to include, `checkout/v1/latest`, now runs version 1.16.1, and that build sends the card to `api.paysafe.com/checkout/api/v1/tokenize` (`api.test.paysafe.com` in its test environment), where 1.8.0 sent it to `hosted.paysafe.com`. Earlier releases did not recognize that request, so it was never paused and the agent's placeholder card reached Paysafe. The SDK now pauses it and keeps the three headers the build sends beside its credential: `X-Internal-Correlation-Id`, `X-App-Version` and `X-TransactionSource`. Paysafe.js latest sends the same three, and its replay keeps them too. `prepare({ psp: 'paysafe' })` binds a request only to a Checkout build Agentcard read, by the version the request names: 1.8.0 on `hosted.paysafe.com`, or 1.16.1 on `api.paysafe.com`, each with its own headers. A request from any other build stops with `checkout_changed`, and nothing is sent. An SDK that syncs its registry pauses the 1.16.1 request once the API release deploys; a prepared 1.16.1 checkout needs this release. Both need a Vault release after 0.6.2.
|
|
34
85
|
|
|
86
|
+
- Read an Authorize.Net refusal as a refusal. Authorize.Net's Accept.js endpoint answers HTTP 200 whether or not it took the card and names the outcome in the answer's `resultCode`, and the cardholder's device recorded an `Error` answer as approved: `authorize()` resolved and the paused request continued with Authorize.Net's refusal in its body. The device now records it as Authorize.Net's refusal, so `authorize()` throws `ProcessorRefusedError` with Authorize.Net's code, such as `E00007`, and the authorization reads `declined` with reason `processor_refused`. Only an answer that carries Accept.js's nonce under `resultCode` `Ok` is approved; any other answer leaves the outcome unknown. No SDK upgrade is needed: the device changes with the next Vault release, and the API judges the answer again at approve once it deploys.
|
|
87
|
+
|
|
35
88
|
- Keep a Gravy checkout session out of your events and error messages. Gravy's Secure Fields send the card to `/checkout/sessions/<session id>/fields` on the merchant's Gravy host with no auth header, so the session id is the only credential that request needs: anyone who holds it can write card fields into the session until it expires. Earlier releases named the full URL in the `card_request_paused` event and in the error `authorize()` throws for a request in the wrong method, so the session id reached your `onEvent` handler and every log that recorded the event or the error. `redactUrl`, every event and every error message now show it as `{session}`, as in `https://api.<merchant>.gr4vy.app/checkout/sessions/{session}/fields`. No API or Vault release is needed.
|
|
36
89
|
|
|
37
90
|
- Pay FlixBus's checkout on Payrails web SDK 6.7.0. FlixBus's page now loads web SDK 6.7.0 in place of 5.50.0, and 6.7.0's Pay now button sends the card-form payment without the `enrollInstrumentToNetworkOffers` member that 5.50.0 sent as `false`. 0.23.11 required that member and aborted every such payment as `unsupported_payrails_card_payment` before creating anything, so no FlixBus payment reached an approval. A payment that names no `enrollInstrumentToNetworkOffers` now pauses as 5.50.0's does: Payrails reads a missing flag as `false`, and 6.7.0 sends no other member in its place. A payment that sets the flag `true` or keeps the card (`storeInstrument: true`, or `meta.order.processingType` `CardOnFile` or `UnscheduledCardOnFile`) still aborts as `payrails_card_on_file_consent_required`, and the flag in another case, in another place or as anything but a boolean aborts as `unsupported_payrails_card_payment`. A payment also needs the matching API and Vault releases.
|
|
@@ -127,6 +180,8 @@
|
|
|
127
180
|
|
|
128
181
|
- Pay Cinemark and Dick's Sporting Goods' own storefront, two reviewed Adyen merchants that take the card on their own server. Both profiles, `cinemark_initiate` and `dsg_paygate_v2`, ship `enabled`, and the API serves them `enabled` only to a client of a company Agentcard turned them on for. For every other client the adapters keep pausing and aborting their card requests as `merchant_profile_observed`, as before. A payment there needs a preparation for the profile and the cardholder's approval in the Vault, and Autopilot never pays one. The create carries the merchant's own checkout total as your agent's browser read it, and the API refuses a payment whose amount is not that total. Dick's paygate at Golf Galaxy, Public Lands and Going Going Gone moves to its own profile, `dsg_other_stores_paygate_v2`, which ships in `observe`, so the adapters abort those card requests as before. Matching API and Vault releases are required.
|
|
129
182
|
|
|
183
|
+
- Read the booking D-EDGE's payment hub holds for a card post from D-EDGE's card frame, where HUMA Kotor Bay and other D-EDGE hotels take the card. The adapters record the hub's own `POST /load/<session>` answer, read-only, and send it with the create, cut to what the card pays now, the room's total and the part of it left for the stay, the rate's deposit and scheduled-payment marks and the booking's status. A deposit other than your integer `amount` throws `AmountMismatchError` with `amountSource: 'merchant_total'`. A rate that only guarantees the booking with the card, a charge on a later date, or part of the room left to pay at the stay throws `CardEndpointRefusedError` with reason `facts_refused`, because the hotel would charge the card after a single-use card may have closed; a stay tax the hotel collects at the desk is not part of the room. A voucher, points or a bank chosen in the hub after the booking loaded throws it with reason `facts_unavailable`. D-EDGE stays in `observe`, so its card post still ends on the stopped page with reason `not_enabled`. The API answers a D-EDGE create that carries no hub record with `409 merchant_facts_required`, and one at a rate the hotel charges later with `409 merchant_facts_refused` and reason `deferred`, before it checks whether D-EDGE takes payments, so an earlier SDK cannot pay D-EDGE once Agentcard enables it. A matching API release is required.
|
|
184
|
+
|
|
130
185
|
- Stop the card fields adyen-web 5 encrypts at a reviewed Adyen merchant profile. adyen-web 5 seals each field as an RSA-OAEP / A256CBC-HS512 JWE, where adyen-web 6 writes RSA-OAEP-256 / A256GCM, and the SDK read only adyen-web 6's form. So the card requests of Cathay Pacific's payment page and Dunelm's legacy checkout, and Cathay's charge request after its signing step, reached the merchant with the agent's dummy card. The adapters now read both forms as a card. Both profiles ship in `observe`, so the adapters abort each of these requests before it leaves the browser and report it as `merchant_profile_observed`. The Vault writes only adyen-web 6's form, so the API refuses a create for a card request in adyen-web 5's form with `cse_template_unsupported`, and nothing is charged.
|
|
131
186
|
|
|
132
187
|
- Abort the requests in which IHG's pay page sends the typed card outside its reviewed card request, in every status, as `card_endpoint_refused` with reason `card_resend`. The page posts the same booking to IHG's enrollment service while its pre-ticked "Enroll for free" box stays ticked, posts it on other paths for more than one room, a travel agent or Business Rewards, and at a hotel that asks for 3-D Secure first posts the number to two 3-D Secure calls. The SDK let each of these reach IHG with the agent's dummy card. A booking with the box unticked reaches IHG's reviewed card request, which the adapters refuse as before while IHG is in `observe`.
|
|
@@ -151,7 +206,9 @@
|
|
|
151
206
|
|
|
152
207
|
- Pay a Wix checkout whose card goes to Wix's own tokenizer, from Agentcard's own account only for now (dark launch): Agentcard turns it on for another company once it checks that the site instance Wix's card frame sends belongs to the reviewed store. Every Wix checkout mounts Wix Cashier's card frame, and for Wix Payments and classic Stripe on Wix that frame posts the card to `https://cashier-pci.wixapps.net/_api/payment-gateway-web/tokenize/v2/tokens`. Wix's payment widget fails the payment 30 seconds after it asks the frame for a token, too soon for an approval after Pay, so the SDK pays a Wix checkout only through `prepare({ psp: 'wix_payments', environment: 'production' })`, at a store Agentcard reviewed (ParaHouse Magazine, Psalms & Potions and Alexandra Hagel today), with `amount` and `currency`, the total the cardholder approves: the request names none. Wix has no test environment, so a test client's `prepare()` fails with `live_host_on_sandbox_row`. The sync now also asks for `capabilities=wix_payments_tokenize`. Once the cardholder approves, the frame's request pauses, the cardholder's device sends the card through the checkout relay, and the frame receives Wix's token. The frame sends its card request once more under a fresh site instance after Wix answers 401 or 403: the SDK refuses that second request in the browser and never answers it from the first approval. The SDK fails a paused request the cardholder's device has not sent 20 seconds after the pause, of the 30 seconds Wix waits, with reason `wix_payments_send_window_closed`, as it fails a prepared Secure Acceptance token create, and the device sends nothing past its own earlier boundary.
|
|
153
208
|
|
|
154
|
-
-
|
|
209
|
+
- Pay a HiPay checkout whose card goes to HiPay's own vault, for a company Agentcard turned HiPay on for (dark launch). HiPay's hosted fields, which HiPay's WooCommerce, PrestaShop and Magento modules use by default, send the card from a hidden frame on `libs.hipay.com` to `https://secure2-vault.hipay-tpp.com/rest/v2/token` under the merchant's public credential, and the merchant's page gives that request 20 seconds after Pay, too soon for an approval after Pay. So the SDK pays a HiPay checkout only through `prepare({ psp: 'hipay', environment: 'production' })` (`'sandbox'` for HiPay's stage vault, with a test client), with `amount` and `currency` above zero, the total the cardholder approves: the request names none. On the live vault only a merchant Agentcard reviewed is paid, at its own checkout page and under its own public credential; none is reviewed yet, so a live `prepare()` fails with `unsupported_checkout`. The sync now also asks for `capabilities=hipay_vault_token`. The frame posts the card number alone while it is typed, and posts `/rest/v2/token/update` once it holds a token: the SDK fails both in the browser, and any post to the token URL that carries no security code with them, with a `blocked` event (`hipay_number_only`, `hipay_update_refused`) and spends no ready preparation, so the frame keeps the number and Pay sends one complete request. Once the cardholder approves, that request pauses, the cardholder's device sends the card to HiPay itself, and the frame receives HiPay's token: the SDK reads the approval every 500 ms for the 20 seconds the page waits. The SDK fails a paused request the device has not sent 12 seconds after the pause, of the 20 seconds the page waits, with reason `hipay_send_window_closed`. A payment also needs the matching API and Vault releases, and a company listed for HiPay.
|
|
210
|
+
|
|
211
|
+
- Keep a GoPay card form's token out of your events and error messages. GoPay's card frame sends the card to its own URL, `https://secure.gopay.com/gp-card-comm/h/<token>?lang=..&ver=..&SG=<signature>`, and that token, with the signature in the query, opens that payment's card form to anyone who holds it. `redactUrl`, every event and every error message now show it as `{token}` on GoPay's card frame hosts, as in `https://secure.gopay.com/gp-card-comm/h/{token}`, and drop the query as before. No API or Vault release is needed.
|
|
155
212
|
|
|
156
213
|
- Pay PayMongo's hosted checkout through the checkout relay, for a company Agentcard turns PayMongo on for (dark launch). Every PayMongo payment link, Storefront page and Checkout Session opens one page, `https://checkout.paymongo.com/<session>`, whose Pay button posts the card to that page's own URL as a Next.js Server Action. This build declares `paymongo_checkout_pay` on every sync, and the API serves the PayMongo entry only to a company Agentcard turned PayMongo on for, so every other company's PayMongo payments go through untouched, as for an unsupported processor. For an enabled company the SDK pauses only the checkout page's own card attach. The attach names no price, so `authorize()` refuses one without `amount` above zero and `currency` (`AmountRequiredError`): send the total PayMongo's checkout shows, in Philippine pesos. The API reads the checkout session from PayMongo and refuses a total that is not the session's to the centavo (`amount_mismatch`), a test-mode session on live credentials or the reverse (`paymongo_environment_mismatch`), and a session that has expired, is already being paid, takes no cards or adds PayMongo's fee on top (`paymongo_checkout_unpayable`), and it names PayMongo's merchant, whatever `merchant` you send. The cardholder's device sends the card through the checkout relay, and the page gets PayMongo's own answer: the paid payment, or the step toward PayMongo's 3-D Secure page, which the page then runs in the agent's browser, where a challenge needs a person at that browser. A PayMongo payment also needs the matching API, Vault and checkout relay releases.
|
|
157
214
|
|
|
@@ -159,10 +216,12 @@
|
|
|
159
216
|
|
|
160
217
|
- Keep a Przelewy24 transaction's token out of your events and error messages. Przelewy24's pay wall names its transaction by one token in every path (`/trnRequest/<token>`, its method links, `/card/p24form/<token>`, `/trnResult/<token>`), with no auth header, so the token is all a browser needs to open that transaction's pay wall, with its merchant, its amount and its card form. `redactUrl`, every event and every error message now show it as `{token}` on Przelewy24's hosts, on every route, as in `https://secure.przelewy24.pl/trnRequest/{token}`. The SDK does not pause a Przelewy24 payment yet. No API or Vault release is needed.
|
|
161
218
|
|
|
162
|
-
- Keep a Revolut order's token out of your events and error messages. Revolut's card field and card popup send the card to `https://merchant.revolut.com/api/public/orders/<order token>/payments` (`sandbox-merchant.revolut.com` in test) with no auth header that binds it, so the order token is the only credential that request needs: anyone who holds it can read the order, its amount and its merchant, and pay it. The frame then reads the payment at `/api/public/payments/<payment token>`, and Revolut's hosted checkout page names the order token in its own paths. `redactUrl`, every event and every error message now show each of those tokens as `{token}` on those hosts, as in `https://merchant.revolut.com/api/public/orders/{token}/payments`.
|
|
219
|
+
- Keep a Revolut order's token out of your events and error messages. Revolut's card field and card popup send the card to `https://merchant.revolut.com/api/public/orders/<order token>/payments` (`sandbox-merchant.revolut.com` in test) with no auth header that binds it, so the order token is the only credential that request needs: anyone who holds it can read the order, its amount and its merchant, and pay it. The frame then reads the payment at `/api/public/payments/<payment token>`, and Revolut's hosted checkout page names the order token in its own paths. `redactUrl`, every event and every error message now show each of those tokens as `{token}` on those hosts, as in `https://merchant.revolut.com/api/public/orders/{token}/payments`. No API or Vault release is needed.
|
|
163
220
|
|
|
164
221
|
- Keep a Paytrail payment's id and a Payment Highway card page's session out of your events and error messages. Paytrail's card method posts to `/payments/<transaction>/click-to-pay/loading-and-redirect` on `services.paytrail.com`, and that id opens Paytrail's own payment wall for the payment (`pay.paytrail.com/pay/<transaction>`), its amount, its merchant and every method's form, to anyone who holds it. Payment Highway, Paytrail's card platform, names its card page's session in the same way (`/form/<session>/pay_with_click_to_pay` on `v1.api.paymenthighway.io`). `redactUrl`, every event and every error message now show them as `{transaction}` and `{session}`, as in `https://services.paytrail.com/payments/{transaction}/click-to-pay/loading-and-redirect`. The SDK does not pause a Paytrail payment yet. No API or Vault release is needed.
|
|
165
222
|
|
|
223
|
+
- Pause QuickPay's hosted payment window (where most Danish and Nordic webshops send the buyer) for the cardholder's device. The sync now also asks for `capabilities=quickpay_hosted_payment_page`, and the API serves QuickPay's recognizer only for a company Agentcard turned QuickPay on for. The SDK pauses the merchant's signed entry form to `https://payment.quickpay.net/`, which carries no card, as a `hosted_form` authorization, and answers the paused navigation with the `submitted_on_device` page once the cardholder's device has paid it. Every other post your browser makes to QuickPay, the window's card post, its fee lookup, its wallet token post and its 3-D Secure step, carries the card your agent typed or follows it, and so does the window's lookup of the card's first digits: the SDK fails each one and reads `unsupported` with reason `processor_card_page`. Your events and error messages name the window's session, the 32-hex first segment of its paths, as `{session}`. Matching API and Vault releases are required. QuickPay stays off for every company Agentcard has not turned it on for.
|
|
224
|
+
|
|
166
225
|
## 0.23.0
|
|
167
226
|
|
|
168
227
|
### Upgrading from 0.22.0
|
package/PREFLIGHT.md
CHANGED
|
@@ -191,6 +191,8 @@ Juspay (`juspay`) is not in this table. Juspay launches dark: the direct SDK pay
|
|
|
191
191
|
|
|
192
192
|
Datatrans Secure Fields (`datatrans`, PCI Proxy) has reviewed discovery rules for its `secure-fields-2.0.0` script and its card iframe on `pay.datatrans.com` and `pay.sandbox.datatrans.com`, but it is not in this table. It launches dark: the direct SDK pays it only after `syncRegistry()` and only for an org Agentcard has enabled, so the bundled catalog leaves it out, its assets are not evidence, and a Datatrans checkout stays `unknown`, even when your SDK build can pay it. A page that loads Secure Fields beside another processor's SDK reads exactly as it did before.
|
|
193
193
|
|
|
194
|
+
The TokenEx hosted iframe v3 (`tokenex_iframe`, JetBlue's card form) has reviewed discovery rules for its `Iframe-v3` parent library and its `/iframe/v3` card iframe on `htp.tokenex.com` and `test-htp.tokenex.com`, and waits the same way: it launches dark, so its assets are not evidence and a TokenEx checkout stays `unknown`, even when your SDK build can pay it for an org Agentcard has enabled. Read the [TokenEx iframe section of the README](./README.md#pay-a-tokenex-iframe-checkout-dark-launch) for what the SDK covers.
|
|
195
|
+
|
|
194
196
|
ACI Worldwide's COPYandPAY widget (`aci`) is identified from its widget script and its card iframe on `eu-prod.oppwa.com`, `oppwa.com`, `eu-test.oppwa.com` and `test.oppwa.com`, but it is not in this table. It launches dark: the direct SDK pays it only after `syncRegistry()`, only through a prepared checkout, and only for an org Agentcard has enabled, so the bundled catalog leaves it out and an ACI checkout stays `unknown`, even when your SDK build can pay it. Read the [ACI section of the README](./README.md#pay-an-aci-copyandpay-checkout) for what the SDK covers.
|
|
195
197
|
|
|
196
198
|
Primer (`primer`) is identified from its Web SDK v2 script `PrimerCoreSDK.min.js` and its `hosted-input.html` and `api-controller.html` card frames on `sdk.primer.io`, but it is not in this table. It launches dark: the direct SDK pays it only after `syncRegistry()` and only for an org Agentcard has enabled, so the bundled catalog leaves it out and a Primer checkout stays `unknown`, even when your SDK build can pay it. Primer Checkout (`@primer-io/primer-js`), whose hosted api-controller lives under `/web/hosted-pages/`, names its processor and returns `unknown` with `processor_variant_unverified`. Read the [Primer section of the README](./README.md#pay-a-primer-checkout) for what the SDK covers.
|
|
@@ -205,6 +207,8 @@ Shiji STS (`shiji_sts`), the card leg of MyCheck Wallet V3 at La Quinta by Wyndh
|
|
|
205
207
|
|
|
206
208
|
Wix Payments (`wix_payments`) has no discovery rule in this table. Wix Cashier's card frame on `cashier-pci.wixapps.net` is the same for every provider a Wix site connects, and for Square and Wix's Stripe Gateway it sends the card to the processor's own SDK rather than to Wix's tokenizer, so the frame on a page is no evidence of this lane, and a Wix checkout stays `unknown`. The direct SDK pauses the card request the frame sends to Wix's tokenizer only after `syncRegistry()`, only through a prepared checkout, and only for an org Agentcard has enabled. Read the Wix row of the [README](./README.md) processor table.
|
|
207
209
|
|
|
210
|
+
HiPay (`hipay`) has no discovery rule in this table. HiPay's JavaScript SDK (`libs.hipay.com/js/sdkjs.js`) loads on every HiPay checkout, hosted fields or HiPay's own hosted page, so the script on a page is no evidence that a payment can be approved, and a HiPay checkout stays `unknown`. The direct SDK pauses the token create HiPay's hosted fields send only after `syncRegistry()`, only through a prepared checkout, and only for an org Agentcard has enabled. Read the HiPay row of the [README](./README.md) processor table.
|
|
211
|
+
|
|
208
212
|
Gravy Secure Fields (`gravy`) is identified from the loader its merchants load, `/secure-fields/latest/secure-fields.js` on `cdn.<merchant>.gr4vy.app` (or `cdn.sandbox.<merchant>.gr4vy.app`), but it is not in this table. It launches dark: the direct SDK pauses Gravy's card request, a `PUT` from Gravy's controller frame, only after `syncRegistry()` and only for an org Agentcard has enabled, so the bundled catalog leaves it out and a Gravy checkout stays `unknown`.
|
|
209
213
|
|
|
210
214
|
Paddle (`paddle_vgs`) has no discovery rule in this table. Paddle's checkout frame on `buy.paddle.com` names the tokenizer it sends the card through only after the buyer's first step, and Paddle can name another one for each checkout, so nothing on the page before that step says which one a checkout uses, and a Paddle checkout stays `unknown`. The direct SDK pauses the card request a VGS checkout sends to `vault.paddle.com/cards` only after `syncRegistry()`, only through a prepared checkout, and only for an org Agentcard has enabled. Read the Paddle row of the [README](./README.md) processor table for what the SDK covers.
|
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ const bundle = await agentcard.voice.challengeForHost(purchase.id, { lang: 'es'
|
|
|
57
57
|
const reg = await agentcard.voice.appKeys.register(key, { user_id: person.user_id }); // the key the app's frame minted; reg.credential goes back to the phone
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
One choice per business, not per purchase
|
|
60
|
+
One choice per business, not per purchase. Leave it out and the client approves on Agentcard's page, as with `approval: 'sheet'`; a value that is neither `'sheet'` nor `'in_app'` throws `missing_approval_posture` at construction.
|
|
61
61
|
|
|
62
62
|
### The card that pays
|
|
63
63
|
|
|
@@ -180,6 +180,12 @@ Merchant timeouts still apply to the request itself: Square's observed tokenizat
|
|
|
180
180
|
|
|
181
181
|
`attachToCdp(…, { pauseOnly: true, onPaused })` pauses the card request without creating the purchase and hands you `{ request, recognizer, page_origin, controller }`. Create it with `voice.authorizations.create` as on any other purchase, read the result, and `controller.resume(result)` replays the processor's answer into the page. `recognizer` names the processor that claimed the request (`stripe`), and so does the `card_request_paused` event's detail, so nothing has to re-match the URL.
|
|
182
182
|
|
|
183
|
+
### Hold the card request until the person confirms the total
|
|
184
|
+
|
|
185
|
+
`attachToCdp(…, { hold: true })` pauses the first card request and holds it with nothing created. `controller.hold.whenHeld()` resolves with what was held (the processor and the endpoint, never a body). Once the person confirmed the total and the page still shows it, `controller.release({ amount, currency, pageTotal })` creates the authorization at that amount: a live client answers the page with the device's replay, a sandbox client never sends the request on and resolves `approved_not_charged`. A page total that moved, or one you could not read (`pageTotal: null`), cancels the request instead. `upTo: true` releases at a ceiling while the page total stays at or under it. `controller.hold.cancel()` fails the request. A held request is cancelled before its processor's own wait runs out (Braintree 60 s, Bolt 20 s, 30 s for the rest), and every other card request, and every one after a decision, fails at once.
|
|
186
|
+
|
|
187
|
+
Hold mode also catches Bolt's tokenizer, which no device replay can answer (held, then only cancelled), and, with `hold: { placeholderNumbers }`, any request whose body carries the placeholder card, wherever it goes. In sandbox, `release({ …, continuePlaceholder: true })` sends a store's own card post on unchanged, since it carries the documented test number only. Site service workers are bypassed (`serviceWorkers: 'bypass'`) instead of refusing the page, and a shared worker is accepted unwatched (`sharedWorkers: 'accept'`): Fetch reaches no shared worker, so nothing it sends can be approved, and the browser only ever holds the placeholder card. `sharedWorkers: 'observe'` attaches to it and ends the checkout on a card request it sends, on a browser that allows that attach (Kernel does not).
|
|
188
|
+
|
|
183
189
|
### Recognisers and request builders
|
|
184
190
|
|
|
185
191
|
Both are registries the SDK ships and you extend at runtime, so a processor or a store Agentcard does not know yet needs no release.
|
|
@@ -255,7 +261,7 @@ Coverage is specific to the processor request format, merchant setup, browser tr
|
|
|
255
261
|
| 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
262
|
| 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
263
|
| 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
|
-
| PayPal Card Fields (PayPal JS SDK `components=card-fields`, v5 and v6) and PayPal's guest card form (the "Debit or Credit Card" button) |
|
|
264
|
+
| PayPal Card Fields (PayPal JS SDK `components=card-fields`, v5 and v6) and PayPal's guest card form (the "Debit or Credit Card" button) | Card Fields: dark launch. After `vault.syncRegistry()`, and only for an org Agentcard has enabled, the SDK pauses the frames' confirm to `/v2/checkout/orders/{order}/confirm-payment-source` on `www.paypal.com` or `api-m.paypal.com`, always with the cardholder's approval, and the page gets the order's id and status; pass `amount` and `currency`, since the confirm names none. The guest card form is not supported. For every other org, and for the guest form always, the SDK fails each card request, so PayPal never receives the dummy card, and the controller reads `unsupported` with reason `unsupported_paypal_card_fields` (or `unsupported_paypal_guest_checkout` for the guest form) and holds the attachment until you attach again; a PayPal wallet payment continues untouched |
|
|
259
265
|
| 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 |
|
|
260
266
|
| EBANX (EBANX.js and EBANX's hosted payment page; Nuuvem Colombia) | supported for the token request (`POST /ws/token`): the SDK pauses it, and the cardholder's device sends the card there. EBANX's page then sends the card again. 3-D Secure's `/ws/threedsecureserver-generateToken`, `-authentications` and `-authentication-results` carry the card. The hosted page's confirm, `/ws/checkout-v2/confirm`, carries the card code. For a company Agentcard turned it on for, the approval's read names until when it covers those sends (`followUpsUntil`). While the Vault page stays open, the cardholder's device sends each covered one, bound to the approved request. The cardholder sees no second prompt. The page receives EBANX's answer. The confirm is covered only once 3-D Secure succeeded for the invoice it pays. It pays in one instalment and keeps no card for one-click payments. A card checkout that runs no 3-D Secure, picks instalments or saves the card fails in the browser. Every other such send fails in the browser too, for every company. EBANX never receives the dummy card or code. The page shows its own payment error. The controller reads `unsupported` with reason `unsupported_ebanx_card_resend` and holds the attachment until you attach again. After one such send fails or is refused, the SDK sends nothing more on that attachment. It also sends nothing once the checkout no longer names that approval: a final merchant result such as `completed` or `failed`, a new token request, or a new attempt after a merchant failure, and nothing while a reconciliation runs. While a send is out, `reconcile()` and `retryAfterMerchantFailure()` throw, and a new card request fails in the browser. A confirm with no card code, such as a PSE bank transfer, continues untouched |
|
|
261
267
|
| 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 |
|
|
@@ -270,6 +276,7 @@ Coverage is specific to the processor request format, merchant setup, browser tr
|
|
|
270
276
|
| Juspay Express Checkout | dark launch: the new-card flow Swiggy's website sends from its own page, for an org Agentcard has enabled, after `vault.syncRegistry()`. Validated in local Chromium with no live merchant purchase yet; see [Pay a Juspay checkout](#pay-a-juspay-checkout) |
|
|
271
277
|
| Reviewed merchant card endpoints: AT&T, Italo, Universal Orlando, Target Optical, Opus Virtual Offices, Delta, Hilton, IHG, American Airlines, EconomyBookings, The Verb Hotel, RunSignUp, Mindbody, LookFantastic, Lexicon (FastSpring's popup checkout), and Hyatt, LifeMiles, Southwest Airlines and United Airlines, whose page encrypts the card under the merchant's own key, and the card forms Athens-Clarke County's MyRec page, Los Angeles and San Francisco on Conduent eTIMS, the D-EDGE card frame and NIC Common Checkout (NJMCdirect and other state agencies) submit as a page load | recognized in mode `card_endpoint`, every one in observe: the adapters pause each merchant's own card request and refuse it as `unsupported_checkout` before any authorization, so the dummy card never reaches the merchant, and a request to the same URL with no card in it continues untouched. A profile takes payments only after a reviewed partner HAR, a reviewed release of this SDK and the API, and per-org enablement |
|
|
272
278
|
| Datatrans Secure Fields (PCI Proxy; RaiseNow donations, British Airways) | dark launch: the card-number iframe's TOKENIZE card post only, after `vault.syncRegistry()`, and only for an org Agentcard has enabled (see [Pay a Datatrans Secure Fields checkout](#pay-a-datatrans-secure-fields-checkout)). The cardholder's device sends the card to Datatrans and the iframe receives the token and its `cardInfo`. The one live merchant account Agentcard has reviewed is RaiseNow's shared donation account, so a RaiseNow charity's one-time donation pays and every other live Datatrans checkout is refused as `datatrans_merchant_unregistered`. A post that asks PCI Proxy to keep the card on file, which British Airways's Nexus checkout sends for every new card, is refused before any prompt while Agentcard's card-on-file policy refuses it, so a Nexus checkout does not complete yet |
|
|
279
|
+
| TokenEx hosted iframe v3 (JetBlue's card form) | dark launch: the PCI card-number frame's own card post only, after `vault.syncRegistry()`, and only for an org Agentcard has enabled (see [Pay a TokenEx iframe checkout](#pay-a-tokenex-iframe-checkout-dark-launch)). Pass `amount` and `currency`. The cardholder's device sends the card to TokenEx and the frame receives TokenEx's token. No TokenEx account is reviewed yet, so the API refuses every create as `unsupported_checkout` until Agentcard lists JetBlue's |
|
|
273
280
|
| Authorize.Net Direct Post Method and SIM | dark launch (mode `hosted_form`, capability `authorize_net_dpm_form`): served only to an org Agentcard enabled for it. The card form posts one form navigation to `transact.dll` on `secure2`, `secure` or `test.authorize.net`; the cardholder's device submits it, and you poll the merchant's order state. Not eligible for auto-approval |
|
|
274
281
|
| Pelecard (Iframe/Redirect 2.0) | dark: paused only for an org Agentcard enables for Pelecard, in test mode and live. One-time card payments on `gateway21` and `gateway20.pelecard.biz`; pass `amount`, `currency` and the page's total as `pageAmount`. A live checkout pays only with a card the cardholder stored. See [Pay a Pelecard checkout](#pay-a-pelecard-checkout) |
|
|
275
282
|
| CreditGuard (Hyp PPS hosted payment page) | dark launch, prepared checkout only (see "Pay a CreditGuard checkout"): served to this build (`capabilities=creditguard_pps_process`) only for a company Agentcard enabled for it, and prepared or paid only for that company. The cardholder's device sends the card and the page reads CreditGuard's answer. 3-D Secure, OTP, installments, save-my-card and currency conversion are refused. No UAT or live capture yet |
|
|
@@ -277,11 +284,13 @@ Coverage is specific to the processor request format, merchant setup, browser tr
|
|
|
277
284
|
| Payrails web SDK (Blacklane, FlixBus) | dark launch (mode `device_encrypt`): only for an org Agentcard has enabled, and only the card form's single-use save and its single-use payment until card-on-file consent exists; see [Pay a Payrails checkout](#pay-a-payrails-checkout) |
|
|
278
285
|
| Elavon Converge (Hosted Payment Page) | dark: paused only for an org Agentcard enables for Converge, always with the cardholder's approval, never autopilot. One credit card sale in US dollars per payment page on `api.convergepay.com` or `www.convergepay.com` (`api.demo.convergepay.com` in Converge's demo environment); no amount needed. See [Pay an Elavon Converge payment page](#pay-an-elavon-converge-payment-page) |
|
|
279
286
|
| Moneris Hosted Tokenization (Moneris's card frame; Raisin donation pages) | dark launch: after `vault.syncRegistry()`, and only for an org Agentcard has enabled, the SDK pauses the frame's post to `/HPPtoken/request.php` on `www3.moneris.com` or `gateway.moneris.com` (`esqa.moneris.com` for a test row) when it carries the card number, expiry and CVD, always with the cardholder's approval. A live payment needs a merchant Agentcard reviewed: the profile id the frame names must be one pinned for the checkout page's origin (Jack.org's Raisin page today), or the create answers `unsupported_checkout`. A frame that leaves the expiry or CVD to the merchant's page, or a post to any other `request.php` URL, fails in the browser with a `blocked` event, `request_without_card`. The cardholder's device sends the card through the checkout relay, and the page gets Moneris's temporary token, which the merchant charges from its own server: pass `amount` and `currency`, the total the cardholder approves. Moneris Checkout (`/chkt/`, `/chktv2/`) is not supported |
|
|
287
|
+
| GoDaddy Payments (Poynt's card frame on a GoDaddy Online Store checkout) | dark launch: after `vault.syncRegistry()`, and only for an org Agentcard has enabled, the SDK pauses the frame's post to `/v1/commerce/stores/<store id>/payment-tokens/generate-nonce` on `services.poynt.net`, always with the cardholder's approval. A live payment needs a store Agentcard reviewed: the store id must be one pinned for the checkout page's origin, or the create answers `unsupported_checkout`; a test key is refused, since Poynt has no test host. A form that asks Poynt to keep the card fails in the browser with a `blocked` event. The cardholder's device sends the card through the checkout relay, and the page gets Poynt's nonce, which the store's server charges: pass `amount` and `currency`, the total the cardholder approves. The store's pay button runs an invisible reCAPTCHA, so a person presses it |
|
|
280
288
|
| WSPay Components (WSPay's build of Monri Components) | dark launch: the card frame's confirm of a new card, after `vault.syncRegistry()`, with `amount` and `currency`, and only for an org Agentcard has enabled; for any other org the SDK leaves WSPay's requests alone. The cardholder's device sends the card to WSPay and the frame receives WSPay's answer. A confirm that asks WSPay to keep the card on file, as Valamar's does on every booking, is paid only as a hotel's EUR 1.00 card guarantee with a card the cardholder stored. Validated in local Chromium against a model of the card frame; no WSPay sandbox run or live merchant purchase yet. See [Pay a WSPay Components checkout](#pay-a-wspay-components-checkout) |
|
|
281
289
|
| Shiji STS (MyCheck Wallet V3; Wyndham's booking pages) | dark launch, mode `cse` under a pinned key, and only for an org Agentcard has enabled. The card token a hotel booking mints guarantees the stay, so the cardholder approves with a card they stored, under a line that says the hotel can keep it on file. See [Shiji STS](#shiji-sts-mycheck-wallet-v3) |
|
|
282
290
|
| Wix checkout (Wix Cashier's card frame; Wix Payments and classic Stripe on Wix) | dark launch: only in Agentcard's own account for now, after `vault.syncRegistry()`, and only through `prepare({ psp: 'wix_payments', environment: 'production' })` at a store Agentcard reviewed, with `amount` and `currency`. The frame's post to `cashier-pci.wixapps.net/_api/payment-gateway-web/tokenize/v2/tokens` pauses, the cardholder's device sends the card through the checkout relay, and the frame receives Wix's token. Wix has no test environment. The SDK refuses the frame's second card request after a 401 or 403 in the browser, and fails a request still unsent 20 seconds after the pause, before the card leaves (`wix_payments_send_window_closed`) |
|
|
291
|
+
| HiPay Enterprise (HiPay's hosted fields; France, Portugal, Italy, Spain) | dark launch: after `vault.syncRegistry()`, only for an org Agentcard has enabled, and only through `prepare({ psp: 'hipay', environment: 'production' })` (`'sandbox'` for HiPay's stage vault) at a merchant Agentcard reviewed, with `amount` and `currency` above zero. The hosted fields' post to `secure2-vault.hipay-tpp.com/rest/v2/token` pauses, the cardholder's device sends the card to HiPay directly, and the frame receives HiPay's token. The frame's number-only post and its `/update` fail in the browser (`hipay_number_only`, `hipay_update_refused`), and a request still unsent 12 seconds after the pause fails before the card leaves (`hipay_send_window_closed`). Agentcard has reviewed one merchant so far, HappyBulle: a live checkout prepares only at `https://www.happybulle.com`, with `merchant: 'HappyBulle'`; `prepare()` at any other page fails with `unsupported_checkout`, and so does the create for a card request under any public username but HappyBulle's own |
|
|
283
292
|
| Amadeus Checkout (Air France, KLM) | dark: only for an org Agentcard enabled, and only through `prepare({ psp: 'amadeus_checkout', cardId })`. The Checkout Web SDK's card add pauses; every other action on its URL continues. Air France and KLM keep the expiry in their own form, so the agent types the stored card's real expiry there and the checkout pays only with that card. Every Data Collection Page store is refused as `amadeus_dcp_field_config_unreviewed`. No live purchase yet. See "Prepare an Amadeus Checkout card step" |
|
|
284
|
-
| Paddle checkout (Paddle's card vault;
|
|
293
|
+
| Paddle checkout (Paddle's card vault or TokenEx; any Paddle seller) | dark launch: after `vault.syncRegistry()`, and only for an org Agentcard has enabled. On a Paddle checkout whose card method names VGS, pay only through `prepare({ psp: 'paddle_vgs', environment: 'production' })` (`'sandbox'` for Paddle's sandbox) on an attachment whose `cardId` names the paying card; without one, `prepare()` fails with `card_required`. The frame's post to `vault.paddle.com/cards` pauses, the cardholder's device sends the card itself, and the frame receives the stored card's id; its second request after its 10-second timeout gets the same answer. Type `placeholderCard()`'s facts for that card into Paddle's form, and pass `amount` and `currency`, the total the cardholder approves. Only Visa, Mastercard and American Express cards are paid (`paddle_vgs_card_unsupported` otherwise), and a typed expiry or placeholder that is not the card's fails the create with `paddle_vgs_card_mismatch`. On the live vault, Agentcard pays a checkout at any Paddle seller's own https page; from any other page, such as `http://localhost:4000`, `prepare()` fails with `unsupported_checkout`. Paddle's vault keeps the card, so Paddle can charge it again without another approval, at a subscription's renewal for example. Paddle's `/pay` that follows names the approved card's first six and last four digits, expiry and brand: the SDK writes them over the ones Paddle's form read from the placeholder, and aborts a `/pay` that names another stored card or comes more than a minute later. On a Paddle checkout whose card method names TokenEx, call no `prepare()`: the SDK pauses Paddle's `/pay`, and once the cardholder approves it, writes the card the cardholder's device encrypted over the placeholder's. With a `paddle_vgs` preparation on the attachment, the SDK refuses that `/pay` with `checkout_changed`. A Paddle checkout that sends the card through Spreedly is not supported |
|
|
285
294
|
|
|
286
295
|
The recognizer list, and the status of each reviewed Adyen merchant profile on
|
|
287
296
|
your client, are fetched from the API at runtime (`agentcard.checkout.syncRegistry()`),
|
|
@@ -289,13 +298,14 @@ so new processors work without you shipping a release. `attachToCdp` derives the
|
|
|
289
298
|
`Fetch.enable` url patterns from that same list rather than a constant, which is
|
|
290
299
|
why the sync call belongs before the attach. `agentcard.checkout.cardUrlPatterns()` returns
|
|
291
300
|
those patterns if you arm a CDP connection yourself. Call
|
|
292
|
-
`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,gravy_session_fields_put,moneris_hosted_tokenization,paddle_vgs_cards,redsys_hosted_payment_page,paymongo_checkout_pay`
|
|
301
|
+
`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,gravy_session_fields_put,moneris_hosted_tokenization,paddle_vgs_cards,paddle_tokenex_pay,redsys_hosted_payment_page,lyra_krypton_payment,payfast_card_pay,wix_payments_tokenize,paymongo_checkout_pay,pay_nl_card_token,hipay_vault_token,twoc2p_secure_pay,quickpay_hosted_payment_page,revolut_card_field,paypal_card_fields,gopay_card_form,comgate_hosted_payment_page,przelewy24_hosted_payment_page,makecommerce_card_frame,tokenex_iframe_tokenize,transbank_webpay_hosted_payment_page,sumup_checkout_process`
|
|
293
302
|
for the list this build syncs. The API serves Fiserv's card capture only to a
|
|
294
303
|
build that lists `fiserv_card_capture`, and only for a company Agentcard turned
|
|
295
304
|
Fiserv on for. An older SDK never pauses a Fiserv request it could not finish,
|
|
296
305
|
and no SDK pauses one for a company Fiserv is off for. The API serves Juspay's
|
|
297
306
|
card flow the same way, behind `juspay_txns.v1`, Datatrans's Secure Fields
|
|
298
|
-
card post behind `datatrans_secure_fields_tokenize`, the
|
|
307
|
+
card post behind `datatrans_secure_fields_tokenize`, the TokenEx iframe's card
|
|
308
|
+
post behind `tokenex_iframe_tokenize`, the Authorize.Net DPM
|
|
299
309
|
or SIM form behind `authorize_net_dpm_form`, Pelecard's card submission
|
|
300
310
|
behind `pelecard_transaction`, ACI's split card behind `aci_split_card.v1`,
|
|
301
311
|
CreditGuard's hosted page submit behind `creditguard_pps_process`,
|
|
@@ -313,10 +323,58 @@ NMI never sees the card your agent typed. Redsys's hosted payment page
|
|
|
313
323
|
(`redsys_hosted_payment_page`) is served the same way: this build pauses the
|
|
314
324
|
merchant's signed entry form to `sis.redsys.es/sis/realizarPago` for the
|
|
315
325
|
cardholder's device and fails every other post your browser makes to Redsys.
|
|
326
|
+
Transbank's Webpay Plus (`transbank_webpay_hosted_payment_page`) is served the
|
|
327
|
+
same way: this build pauses the merchant's redirect to
|
|
328
|
+
`webpay3g.transbank.cl/webpayserver/initTransaction?token_ws=<token>` in the
|
|
329
|
+
page or a frame of it, and with `attachToCdp` also in a window the page opened,
|
|
330
|
+
for the cardholder's device, and fails every request but a GET your browser
|
|
331
|
+
makes to Webpay, so the token is never spent in your browser and Webpay never
|
|
332
|
+
sees the card your agent typed. `attachToPlaywright` covers the page it is
|
|
333
|
+
attached to and that page's frames, and pauses the entry when one of them
|
|
334
|
+
navigates to it directly or by script. Playwright's `page.route` never sees the
|
|
335
|
+
hop of an HTTP redirect, so a merchant whose server answers its checkout with a
|
|
336
|
+
redirect to Webpay needs `attachToCdp`: with `attachToPlaywright`, Webpay's
|
|
337
|
+
page loads unpaused and the SDK fails its token submit as
|
|
338
|
+
`processor_card_page`, so nothing is paid and no approval starts. A checkout
|
|
339
|
+
that opens Webpay in a popup needs that popup attached on its own, and a popup's
|
|
340
|
+
first navigation needs the integration
|
|
341
|
+
[Browser integration and merchant outcomes](#browser-integration-and-merchant-outcomes)
|
|
342
|
+
describes.
|
|
316
343
|
PayMongo's hosted checkout (`paymongo_checkout_pay`) is served on the same terms:
|
|
317
344
|
this build pauses the checkout page's own card attach on `checkout.paymongo.com`,
|
|
318
345
|
the cardholder's device sends it through the checkout relay, and the page gets
|
|
319
346
|
PayMongo's answer.
|
|
347
|
+
Przelewy24's pay wall (`przelewy24_hosted_payment_page`) is served the same way:
|
|
348
|
+
this build pauses the pay wall link `secure.przelewy24.pl/trnRequest/<token>`
|
|
349
|
+
for the cardholder's device and fails every post a page it watches makes to the
|
|
350
|
+
pay wall, and its Saferpay resolver. A merchant's server often sends the buyer
|
|
351
|
+
to that link with a redirect or in a new window, and only `attachToCdp` pauses
|
|
352
|
+
a redirect and watches a window the page opens.
|
|
353
|
+
QuickPay's hosted payment window (`quickpay_hosted_payment_page`) is served the
|
|
354
|
+
same way: this build pauses the merchant's signed entry form to
|
|
355
|
+
`payment.quickpay.net` for the cardholder's device and fails every other post
|
|
356
|
+
your browser makes to QuickPay, and the window's lookup of the typed card's
|
|
357
|
+
first digits.
|
|
358
|
+
Revolut Business's card field (`revolut_card_field`) is served on the same
|
|
359
|
+
terms: this build pauses the frame's card payment on `merchant.revolut.com`,
|
|
360
|
+
the cardholder's device sends it to Revolut itself, and the frame gets
|
|
361
|
+
Revolut's answer.
|
|
362
|
+
GoPay's card form (`gopay_card_form`) is served only in Agentcard's own account
|
|
363
|
+
for now (dark launch), until a reviewed change binds GoPay's payee and amount
|
|
364
|
+
to the approval: this build pauses the card frame's own post on
|
|
365
|
+
`secure.gopay.com`, at a shop Agentcard reviewed, the cardholder's device sends
|
|
366
|
+
it through the checkout relay, and the frame gets GoPay's answer.
|
|
367
|
+
ComGate's payment gateway (`comgate_hosted_payment_page`) is served only to a
|
|
368
|
+
build that lists it, for a company Agentcard turned ComGate on for: this build
|
|
369
|
+
pauses the gateway link the merchant sends the buyer to,
|
|
370
|
+
`payN.comgate.cz/init?id=<transaction id>`, in the page, in a frame of it or in
|
|
371
|
+
a window it opened, for the cardholder's device, and fails every request but a
|
|
372
|
+
GET your browser makes to ComGate's API and card frame hosts.
|
|
373
|
+
SumUp's Payment Widget (`sumup_checkout_process`) is served on the same terms:
|
|
374
|
+
this build pauses the widget's card `PUT` to `api.sumup.com/v0.2/checkouts/<id>`,
|
|
375
|
+
the API takes it only for a reviewed SumUp merchant and at the `amount` the
|
|
376
|
+
payment page shows, the cardholder's device sends it to SumUp, and the widget
|
|
377
|
+
gets SumUp's answer.
|
|
320
378
|
CyberSource Secure Acceptance's token
|
|
321
379
|
create (`cybersource_sa_token_create`) is served on the same terms too; every
|
|
322
380
|
other company keeps the stop every build applies to a card posted to Secure
|
|
@@ -565,6 +623,23 @@ mode; the difference matters if you drive `authorize()` yourself.
|
|
|
565
623
|
reads `stopped_before_send`: the `PaymentOutcomeUnknownError` then carries
|
|
566
624
|
`processorRequestStarted: false`.
|
|
567
625
|
|
|
626
|
+
D-EDGE's card frame, where HUMA Kotor Bay and other D-EDGE hotels take the
|
|
627
|
+
card, posts it as a page load of the frame, and the payment hub around the
|
|
628
|
+
frame loads the booking from its own host (`POST /load/<session>`). The SDK
|
|
629
|
+
records that answer, read-only, and sends it with the create, cut to what
|
|
630
|
+
the card pays now, the room's total and the part of it left for the stay,
|
|
631
|
+
the rate's deposit and scheduled-payment marks and the booking's status. A
|
|
632
|
+
deposit other than your integer `amount` is an `AmountMismatchError` against
|
|
633
|
+
the merchant's total. A rate that only guarantees the booking with the
|
|
634
|
+
card, a charge on a later date, or part of the room left to pay at the stay
|
|
635
|
+
is refused as `facts_refused` before any authorization: the hotel would
|
|
636
|
+
charge the card after a single-use card may have closed. A stay tax the
|
|
637
|
+
hotel collects at the desk is not part of the room, so a room paid in full
|
|
638
|
+
goes on. A voucher, points or a bank chosen in the hub after the booking
|
|
639
|
+
loaded leaves the booking unknown, and the payment is refused as
|
|
640
|
+
`facts_unavailable`. D-EDGE is in observe, so its card post ends on the
|
|
641
|
+
stopped page with reason `not_enabled` until Agentcard enables it.
|
|
642
|
+
|
|
568
643
|
`syncRegistry()` asks the API for `SUPPORTED_MODES` only
|
|
569
644
|
(`token,cse,hosted_form,device_encrypt`), so a processor whose flow this build
|
|
570
645
|
cannot finish is never paused; the API serves `hosted_form` and `device_encrypt`
|
|
@@ -766,6 +841,15 @@ is gone resolves as `PaymentOutcomeUnknownError` with reason
|
|
|
766
841
|
card-on-file policy refuses card on file,
|
|
767
842
|
`datatrans_card_on_file_refused` themselves, before any create, and those
|
|
768
843
|
two do not end interception for the page.
|
|
844
|
+
- TokenEx iframe refusals at create, all terminal for that page:
|
|
845
|
+
`CheckoutApiError` with `code` `processor_unavailable` (403, the TokenEx
|
|
846
|
+
iframe is not enabled for your org), `invalid_tokenex_iframe_request` (the
|
|
847
|
+
post is not the card-number frame's own card post), `unsupported_checkout`
|
|
848
|
+
(reason `tokenex_iframe_account_unreviewed`: the post names a TokenEx account
|
|
849
|
+
and origin Agentcard has not reviewed, which is every account today), or
|
|
850
|
+
`amount_required`. The adapters abort the posts behind
|
|
851
|
+
`invalid_tokenex_iframe_request` themselves, before any create, and that
|
|
852
|
+
does not end interception for the page.
|
|
769
853
|
- `CardEncryptedError`: this processor encrypts the card in-page and its
|
|
770
854
|
registry entry does not (yet) say the vault can produce that ciphertext;
|
|
771
855
|
route the purchase to an Agentcard-issued card instead.
|
|
@@ -887,8 +971,9 @@ is gone resolves as `PaymentOutcomeUnknownError` with reason
|
|
|
887
971
|
paused request is not the card request Agentcard reviewed; `detail` names
|
|
888
972
|
fields, never values), `facts_unavailable` (the merchant keeps its own
|
|
889
973
|
record of the order and this runtime read none for this checkout) or
|
|
890
|
-
`facts_refused` (that record says the order renews,
|
|
891
|
-
|
|
974
|
+
`facts_refused` (that record says the order renews, that the merchant
|
|
975
|
+
charges the card later, as a hotel rate that only guarantees the booking
|
|
976
|
+
does, or that the checkout is in test mode). Every reason but `request_unsupported`, `facts_unavailable`
|
|
892
977
|
and `facts_refused` holds the attachment. The adapters also emit `card_endpoint_refused` with the
|
|
893
978
|
profile, the endpoint origin and the reason.
|
|
894
979
|
- `PageAmountRequiredError` (`code: 'page_amount_required'`, `psp`): the paused
|
|
@@ -1696,6 +1781,89 @@ const checkout = await attachToPlaywright(page, {
|
|
|
1696
1781
|
on their device. `pay.sandbox.datatrans.com` takes only a test-mode client
|
|
1697
1782
|
and `pay.datatrans.com` only a live one.
|
|
1698
1783
|
|
|
1784
|
+
### Pay a TokenEx iframe checkout (dark launch)
|
|
1785
|
+
|
|
1786
|
+
JetBlue's booking checkout ("Review & Pay") takes the card number and CVV in
|
|
1787
|
+
TokenEx's hosted iframe v3: cross-origin frames on `htp.tokenex.com`
|
|
1788
|
+
(`test-htp.tokenex.com` in test) that JetBlue's page never reads. The
|
|
1789
|
+
cardholder name and the expiry are JetBlue's own fields. When JetBlue asks the
|
|
1790
|
+
frame to tokenize, the card-number frame reads the CVV from its CVV frame and
|
|
1791
|
+
sends the whole card in one request: `POST` to its own document URL, query
|
|
1792
|
+
included, as JSON. The SDK pauses that request, the cardholder's device sends
|
|
1793
|
+
the real card to TokenEx, and the frame receives TokenEx's token, to which it
|
|
1794
|
+
adds its own `cardType` before JetBlue's page reads it.
|
|
1795
|
+
|
|
1796
|
+
```ts
|
|
1797
|
+
await vault.syncRegistry(); // the TokenEx recognizer is never in the built-in list
|
|
1798
|
+
const placeholder = await vault.placeholderCard({ user: 'your-user-id', psp: 'tokenex_iframe' });
|
|
1799
|
+
// Type placeholder.number and placeholder.securityCode into the TokenEx fields, and the
|
|
1800
|
+
// card's real expiry (placeholder.expiryMonth, placeholder.expiryYear) and name into JetBlue's own.
|
|
1801
|
+
const checkout = await attachToPlaywright(page, {
|
|
1802
|
+
vault, user: 'your-user-id', cardId: placeholder.cardId, merchant: 'JetBlue', amount: '452.10', currency: 'USD',
|
|
1803
|
+
resolveMerchantResult, // reads JetBlue's own order state; see "The same card, sent again" below
|
|
1804
|
+
});
|
|
1805
|
+
```
|
|
1806
|
+
|
|
1807
|
+
- **Dark launch.** `syncRegistry()` declares the `tokenex_iframe_tokenize`
|
|
1808
|
+
capability, and the API serves the TokenEx recognizer only to an org
|
|
1809
|
+
Agentcard has enabled for it. Every other org's SDK leaves TokenEx requests
|
|
1810
|
+
alone, as before.
|
|
1811
|
+
- **Not live yet.** A token minted into a TokenEx account can be read back as
|
|
1812
|
+
the card number by that account's holder, so the API takes a post only for a
|
|
1813
|
+
TokenEx account and merchant origin Agentcard reviewed, and names that
|
|
1814
|
+
account on the approval. None is reviewed yet: every create answers
|
|
1815
|
+
`unsupported_checkout` (reason `tokenex_iframe_account_unreviewed`) until
|
|
1816
|
+
Agentcard lists JetBlue's. A TokenEx payment also needs the matching API,
|
|
1817
|
+
Vault and checkout relay releases.
|
|
1818
|
+
- **Pass the amount.** The card post names no amount: JetBlue's server charges
|
|
1819
|
+
the token later. `authorize()` refuses one without `amount` above zero and
|
|
1820
|
+
`currency` (`AmountRequiredError`), before anything is created; a page total
|
|
1821
|
+
alone is not enough. Send the total JetBlue's page shows.
|
|
1822
|
+
- **What pauses.** Only the PCI card-number frame's own card post with its CVV
|
|
1823
|
+
frame, exactly as TokenEx's frame writes it. Any other post on the frame's
|
|
1824
|
+
`/iframe/v3` path that carries a card (a stored token's CVV re-entry, a frame
|
|
1825
|
+
without a CVV frame, 3-D Secure or fraud services turned on, a key or query
|
|
1826
|
+
this build has not read, a content type other than JSON) is aborted with a
|
|
1827
|
+
`blocked` event, detail `invalid_tokenex_iframe_request`. Nothing is created,
|
|
1828
|
+
nobody is prompted, the controller's state does not change, and the
|
|
1829
|
+
placeholder never reaches TokenEx. TokenEx's frame waits for that request
|
|
1830
|
+
until it is reloaded, so the page has to load the card form again. A post
|
|
1831
|
+
with nothing typed continues untouched, and so do the frame documents and
|
|
1832
|
+
TokenEx's BinLookup and Forter calls.
|
|
1833
|
+
- **What the frame receives.** TokenEx's token as the API recorded it, rebuilt
|
|
1834
|
+
from Agentcard's allow-list (`token`, `firstSix`, `lastFour`,
|
|
1835
|
+
`referenceNumber`, `tokenHMAC`, `cvvIncluded`) under a JSON content type. An
|
|
1836
|
+
approval whose recorded answer is not a token, TokenEx's own refusal
|
|
1837
|
+
included, ends `PaymentOutcomeUnknownError` with reason
|
|
1838
|
+
`tokenex_iframe_token_unconfirmed`, and the frame gets nothing: the card may
|
|
1839
|
+
already have left the cardholder's device.
|
|
1840
|
+
- **The same card, sent again.** TokenEx's frame answers a repeat with nothing
|
|
1841
|
+
changed from its own cache, with no request. It sends the same bytes again
|
|
1842
|
+
only when its five-minute timer has run, or after a reset or a reload at the
|
|
1843
|
+
same URL. Once a post was approved, the SDK answers that post from the same
|
|
1844
|
+
frame, byte for byte, with the same token: no second prompt, no second card
|
|
1845
|
+
sent. The adapters emit `repeat_answered` with the `authorizationId`; it is
|
|
1846
|
+
not a second payment and must not be counted as one. Another placeholder or
|
|
1847
|
+
CVV is a new approval, because the frame names the brand from the digits
|
|
1848
|
+
typed; so is a re-created frame or one JetBlue minted a new key for. The
|
|
1849
|
+
answer lasts while that approval is the checkout's current one, and at most
|
|
1850
|
+
the iframe key's lifetime (TokenEx's `ExpiresInSeconds`, at most 20
|
|
1851
|
+
minutes), after which TokenEx would refuse the same post. Once the token
|
|
1852
|
+
reached the page, before each re-send the SDK calls your
|
|
1853
|
+
`resolveMerchantResult` and answers only while it reports `pending`; an
|
|
1854
|
+
attachment with no `resolveMerchantResult` answers no re-send after the
|
|
1855
|
+
token reached the page, and each one asks the cardholder again.
|
|
1856
|
+
- **Type a placeholder of the card's brand.** The frame names the brand to
|
|
1857
|
+
JetBlue's page from the digits the agent types, so the cardholder's device
|
|
1858
|
+
refuses a card whose brand differs, before anything is sent. Use
|
|
1859
|
+
`placeholderCard({ user, psp: 'tokenex_iframe' })`: it gives a Visa,
|
|
1860
|
+
Mastercard or American Express number the frame reads as the stored card's
|
|
1861
|
+
own brand, and throws `PlaceholderCardError` (`brand_unsupported`) for any
|
|
1862
|
+
other network. Agentcard-issued cards are Mastercard.
|
|
1863
|
+
- Autopilot never pays a TokenEx checkout; the cardholder approves each one on
|
|
1864
|
+
their device. `test-htp.tokenex.com` takes only a test-mode client and
|
|
1865
|
+
`htp.tokenex.com` only a live one.
|
|
1866
|
+
|
|
1699
1867
|
### Pay a Pelecard checkout
|
|
1700
1868
|
|
|
1701
1869
|
Pelecard's hosted payment page (Iframe/Redirect 2.0) runs at
|
package/dist/amadeus.d.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
export declare const AMADEUS_SEND_WINDOW_MS = 20000;
|
|
3
3
|
/** How long after the pause a paused Wix tokenize request may still be sent, of the 30 seconds Wix's widget waits. */
|
|
4
4
|
export declare const WIX_PAYMENTS_SEND_WINDOW_MS = 20000;
|
|
5
|
+
/** How long after the pause a paused HiPay token create may still be sent, of the 20 seconds the merchant's page waits. */
|
|
6
|
+
export declare const HIPAY_SEND_WINDOW_MS = 12000;
|
|
5
7
|
/**
|
|
6
8
|
* True when a request the adapters refuse must fail as a network error (ERR_FAILED) rather than as
|
|
7
9
|
* the cancellation (ERR_ABORTED) they use elsewhere. An XMLHttpRequest reports a cancellation only
|
|
@@ -11,13 +13,13 @@ export declare const WIX_PAYMENTS_SEND_WINDOW_MS = 20000;
|
|
|
11
13
|
*/
|
|
12
14
|
export declare function failsAsNetworkError(url: string): boolean;
|
|
13
15
|
/** The processor a send window's events and stop cause name. */
|
|
14
|
-
export type SendWindowProcessor = 'amadeus' | 'secure_acceptance' | 'wix_payments';
|
|
16
|
+
export type SendWindowProcessor = 'amadeus' | 'secure_acceptance' | 'wix_payments' | 'hipay';
|
|
15
17
|
/** Why a send window stopped its attempt: the API confirmed the card never left the device and never can. */
|
|
16
18
|
export type SendWindowClosed = `${SendWindowProcessor}_send_window_closed`;
|
|
17
19
|
/**
|
|
18
20
|
* The send window for a request paused under a preparation for `psp`, or null when it has none: a
|
|
19
21
|
* prepared Amadeus SDK add, a prepared CyberSource Secure Acceptance token create and a prepared Wix
|
|
20
|
-
* tokenize request, each 20 seconds.
|
|
22
|
+
* tokenize request, each 20 seconds, and a prepared HiPay token create, 12 seconds.
|
|
21
23
|
*/
|
|
22
24
|
export declare function preparedSendWindowMs(psp: string | undefined, url: string): number | null;
|
|
23
25
|
/** The processor a prepared request's send window names in its events, or null when it has none. */
|