@agent-cards/sdk 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,10 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Pay a CyberSource Flex Microform checkout (Amtrak, Sonos, Learning Without Tears) behind a dark launch. `syncRegistry()` now declares the `capture_context` capability (`SUPPORTED_CAPABILITIES`), and the API serves the Flex recognizer only to an org Agentcard has enabled for it, so every other org's Microform requests go through untouched, as before. Both adapters read the capture context from the card field iframe's URL, verify it offline against CyberSource's pinned signing keys, and send it with the Microform fields on the create; the cardholder's device seals the real card for CyberSource and the field's paused request is answered with the transient token. Only Microform 2.12.1 card fields pause, and a field counts only once its document has loaded: a check Microform continues, any other request on the tokens URL is aborted as `unsupported_checkout` before an authorization exists, and a field the SDK could not read is refused for that request alone. A retry from either field after the field's 30-second timeout is answered from the same approval, and `prepare({ psp: 'cybersource_flex', environment: 'production' | 'sandbox' })` binds one field request. `vault.placeholderCard({ user, cardId })` returns the test number, security code and real expiry to type so the page's own card facts match the stored card; pin its `cardId` on the attachment so the approval page preselects that card. New exports: `SUPPORTED_CAPABILITIES`, `UnsupportedCapabilityError`, `PlaceholderCardError`, and the types `PlaceholderCard`, `MicroformSidecar` and `MicroformField`. The matching API and Vault releases are required.
6
+
5
7
  - Recognize Gravy (Gr4vy) Secure Fields. The registry and the coverage catalog now name the new-card request that updates a checkout session at `/checkout/sessions/<session id>/fields` on the merchant's own `api.<merchant>.gr4vy.app` host, in sandbox and production, with `payment_method.number`, `payment_method.expiration_date` as one `MM/YY` value and `payment_method.security_code`. Gravy publishes this request as an HTTP PUT and the SDK pauses and replays POST requests only, so the recognizer identifies the destination and fills the body but intercepts nothing yet; a PUT on that host continues to Gravy untouched, and the preflight reports Gravy `unsupported` with `request_method_not_supported` in every card scenario until the SDK can intercept that method. The shape follows Gravy's published contract and is not confirmed by a live capture. Updating the session stores the card with Gravy; the merchant creates the transaction on its own server, so a completed request is not a payment. Gravy Embed on `embed.<merchant>.gr4vy.app` is not covered.
6
8
 
7
9
  - Attach in a browser whose default context runs extensions. A browser extension's service worker or background page, at a `chrome-extension://` URL, no longer counts as a site's worker: `attachToPlaywright` and `attachToCdp` attach beside it, and one that starts later leaves the checkout armed, because an extension's worker never serves the merchant's requests. Cloud browsers such as Anchor run their agent, ad blocker and popup blocker as extensions in the default context, and stock Chrome runs its component extensions there, so that context used to be refused with `Service workers are active` or `checkout_attachment_failed`. A site's own service worker, a shared worker, or a worker with no URL still refuses the context.
8
10
 
11
+ - Pay a GMO Payment Gateway checkout (teamLab's ticket sites) behind a dark launch. `syncRegistry()` now declares the `gmo_shared_pk` capability (`SUPPORTED_CAPABILITIES`, now exported), and the API serves the GMO recognizer only to an org Agentcard has enabled for GMO, so no other org's GMO requests are paused or refused. For an enabled org, both adapters pause `mp-token.js`'s `getToken` for one token on `token.mul-pay.jp` or `stg.token.mul-pay.jp`: the cardholder's device sends the card to GMO, and the page receives only GMO's single-use token, reduced to the fields `mp-token.js` reads; any other answer throws `PaymentOutcomeUnknownError` with reason `gmo_token_unconfirmed`. Any other request to that URL (a merchant-registered key, GMO Elements, several tokens, a shop id that does not match the host) and GMO token v1's script request are aborted before anything is created: the attachment reads `unsupported` with reason `unsupported_gmo_request` or `unsupported_gmo_token_v1`, which the `unsupported_checkout` event also carries. A body the SDK cannot read is aborted alone (`blocked`, `unreadable_gmo_request`). The request names no amount, so pass `amount` and `currency`; without them it throws the new `AmountRequiredError` before anything is created. A retry the page sends after losing its request takes the pending approval only when it names the same shop. The matching API release is required; the Vault's GMO path ships separately.
12
+
9
13
  ## 1.3.2
10
14
 
11
15
  - **`AppVoiceKeyDescription` names no `agreement_public_key`** (WP115, the audit's fourth lane). The key the app copy is sealed for is never a business's to name: the frame mints it and the person's vault proves it at the set-up inside the app. `voice.appKeys.register` sends the signing half alone; the API answers `agreement_key_not_accepted` to a body that still carries one, and `GET /vault/voice/app_copy/:lang` answers `agreement_key_required` until the vault has vouched for the key.
package/PREFLIGHT.md CHANGED
@@ -179,6 +179,10 @@ The catalog includes reviewed discovery rules and direct SDK implementation capa
179
179
 
180
180
  Fiserv Commerce Hub is not in the table. Agentcard keeps Fiserv off until it turns it on for a company, so the bundled profile declares no Fiserv support. Fiserv's assets still identify `fiserv`: `payment-fields.js` and `checkout.js` on `commercehub-checkout.fiservapps.com` and the card frame on `commercehub-secure-data-capture.fiservapps.com`, with their `-cert` hosts. The result stays `unknown`, and a page that loads a Fiserv asset beside another processor's reads as `ambiguous`. A company that Agentcard turned Fiserv on for pays it through `prepare()`, as the [README](./README.md) describes.
181
181
 
182
+ GMO Payment Gateway (`gmo_pg`) is identified from its token v2 script `mp-token.js` on `static.mul-pay.jp` and `stg.static.mul-pay.jp`, 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 GMO checkout stays `unknown`, even when your SDK build can pay it. GMO token v1 (`ext/js/token.js`), GMO Elements card frames and Link-type payment pages name their processor and return `unknown` with `processor_variant_unverified`. Read the [GMO section of the README](./README.md#pay-a-gmo-payment-gateway-checkout) for what the SDK covers.
183
+
184
+ CyberSource Flex Microform (`cybersource_flex`) is identified from its loader script and its card field iframes on `flex.cybersource.com` and `testflex.cybersource.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 and a Microform checkout stays `unknown`, even when your SDK build can pay it. Read the [CyberSource Flex section of the README](./README.md#pay-a-cybersource-flex-microform-checkout) for what the SDK covers.
185
+
182
186
  The bundled direct SDK profile declares processor support for `one_time` purchases, with one exception. Gravy Secure Fields sends its card request as an HTTP PUT, and the SDK pauses and replays POST requests only, so a Gravy checkout returns `unsupported` with the reason `request_method_not_supported` in every card scenario until the SDK can intercept that method. The catalog carries each processor's request limitations and known exclusions. A detection rule does not cover every product or regional variant sold under the processor's name. For example, Moneris Hosted Tokenization and Moneris Checkout use different payment paths; the implemented adapter covers Hosted Tokenization.
183
187
 
184
188
  The catalog also carries reviewed hosted, regional, and versioned variants observed on live checkouts: Adyen secured fields on the regional live hosts and any release, the Square card element frame, dLocal Smart Fields releases, Mercado Pago secure fields and the guest Checkout Pro card form, and the Rapyd hosted checkout page. A variant the implemented adapter does not admit, such as the Tranzila terminal-root page, the Authorize.Net hosted payment form, or a Worldpay hosted payment page, identifies its processor and returns `unknown` with `processor_variant_unverified`. A new fingerprint never declares payment support by itself.
package/README.md CHANGED
@@ -238,6 +238,8 @@ Coverage is specific to the processor request format, merchant setup, browser tr
238
238
  | Tranzila | supported (mode `hosted_form`): the cardholder finishes on Tranzila's own page; the paused form navigation resolves to a synthetic page, and you poll the merchant's order state |
239
239
  | Fiserv Commerce Hub | implemented (mode `cse`, prepared checkout only) and off until Agentcard turns it on for your company: the card frame's card capture on `connect.fiservapis.com` (or `connect-cert.fiservapis.com` in the sandbox), with the approved card encrypted on the cardholder's device under a key Agentcard reviewed for the merchant. A merchant whose frame posts to Fiserv's `payment-link` endpoint is not recognized. No live merchant purchase is verified yet |
240
240
  | Gravy (Gr4vy) Secure Fields | not supported: recognized, not intercepted, and the preflight reports `unsupported` with `request_method_not_supported`. The card request is Secure Fields' update of a checkout session: a JSON body to `/checkout/sessions/<session id>/fields` on the merchant's own host `api.<merchant>.gr4vy.app` (sandbox: `api.sandbox.<merchant>.gr4vy.app`), carrying `payment_method.number`, `payment_method.expiration_date` as one `MM/YY` value and `payment_method.security_code`. Gravy publishes this request as an HTTP PUT and the SDK pauses and replays POST requests only, so today the SDK recognizes the destination but the request continues to Gravy untouched. The shape follows Gravy's published Secure Fields contract and is not confirmed by a capture from a live merchant form. Updating the session stores the card with Gravy; the merchant creates the transaction from the session on its own server, so a completed request is not a payment. Gravy Embed, the hosted frame on `embed.<merchant>.gr4vy.app`, is not covered |
241
+ | GMO Payment Gateway | dark launch (mode `token`): only for an org Agentcard has enabled; see [Pay a GMO Payment Gateway checkout](#pay-a-gmo-payment-gateway-checkout) |
242
+ | CyberSource Flex Microform | dark launch: Microform 2.12.1 card fields only, after `vault.syncRegistry()`, and only for an org Agentcard has enabled. For any other org the API does not serve the recognizer, so nothing pauses there. The cardholder's device seals the card for CyberSource and the card field receives the transient token. Validated in local Chromium; no live merchant purchase yet. See [Pay a CyberSource Flex Microform checkout](#pay-a-cybersource-flex-microform-checkout) |
241
243
 
242
244
  The recognizer list, and the status of each reviewed Adyen merchant profile on
243
245
  your client, are fetched from the API at runtime (`agentcard.checkout.syncRegistry()`),
@@ -338,6 +340,11 @@ three; the difference matters if you drive `authorize()` yourself.
338
340
  `syncRegistry()` asks the API for `SUPPORTED_MODES` only
339
341
  (`token,cse,hosted_form`), so a processor whose flow this build cannot finish
340
342
  is never paused; the API serves `hosted_form` entries only to callers that ask.
343
+ It also declares `SUPPORTED_CAPABILITIES` (`capture_context`), the dark-launch
344
+ half of the same negotiation: a processor gated behind a capability is served
345
+ only to a build that declares it and never sits in the built-in list, so it is
346
+ paused only after a sync. A registry entry naming a capability this build does
347
+ not declare throws `UnsupportedCapabilityError` before creation.
341
348
  A registry mode this SDK cannot finish throws `UnsupportedModeError` before
342
349
  creation. An approval returned in an unexpected mode has an unknown outcome
343
350
  and holds the attachment for reconciliation. The `authorized` event's detail names
@@ -421,6 +428,10 @@ one. `amountAuthority` on every replay is `stripe_payment_intent`,
421
428
  that is not turned on, not offered to your account, or whose key or checkout
422
429
  page changed, throws `CheckoutPreparationError` with the API's code as its
423
430
  `reason`.
431
+ - `AmountRequiredError` (`code: 'amount_required'`): the processor's request
432
+ names no amount (GMO) and you passed none, so there is nothing for the
433
+ cardholder to approve. Thrown before anything is created; terminal for that
434
+ page. Pass `amount` and `currency`.
424
435
  - `CardEncryptedError`: this processor encrypts the card in-page and its
425
436
  registry entry does not (yet) say the vault can produce that ciphertext;
426
437
  route the purchase to an Agentcard-issued card instead.
@@ -432,6 +443,20 @@ one. `amountAuthority` on every replay is `stripe_payment_intent`,
432
443
  was created and nothing was charged; call `prepareCheckout()` first. The
433
444
  adapters refuse such a request before it pauses, with the controller reason
434
445
  `preparation_required`.
446
+ - `UnsupportedCapabilityError`: the registry entry is gated behind a
447
+ dark-launch capability this build does not declare. Only a hand-built
448
+ registry can hold one. Upgrade.
449
+ - CyberSource Flex refusals at create, all terminal for that page:
450
+ `CheckoutApiError` with `code` `processor_unavailable` (403, not enabled for
451
+ your org), `capture_context_required`, `capture_context_invalid` (with a
452
+ `reason`), `capture_context_mismatch`, `cybersource_environment_mismatch` (a
453
+ test capture context from a live client, or the reverse),
454
+ `microform_unsupported` or `invalid_cybersource_request` (400). An approved
455
+ Flex authorization whose answer is not a transient token raises
456
+ `PaymentOutcomeUnknownError` with reason `cybersource_flex_token_unconfirmed`.
457
+ - `PlaceholderCardError`: `placeholderCard()` found no such stored card
458
+ (`card_not_found`), no placeholder for its network (`brand_unsupported`), or
459
+ no expiry on it (`expiry_unknown`). Nothing was created.
435
460
  - `SubstitutionError`: a `cse` approval could not be written into the paused
436
461
  body (the four encrypted fields were not there). The request is failed and
437
462
  the next one is judged afresh.
@@ -668,6 +693,7 @@ await page.getByRole('button', { name: 'Pay', exact: true }).click();
668
693
  | Spreedly | `shared` | Native iframe JSON POST to `/v1/payment_methods/restricted.json` on `core.spreedly.com` |
669
694
  | Adyen | `production` or `sandbox` | The Sessions `/checkoutshopper/v1/sessions/{id}/payments` POST on the matching Adyen host family (`sandbox` is `checkoutshopper-test.adyen.com` with a `test_` client key; `production` is the live hosts with a `live_` key), carrying a fresh card's four encrypted fields |
670
695
  | Fiserv | `production` or `sandbox`, with `merchantProfile` | The card frame's `/ch/payments-vas/v1/card-capture` POST on `connect.fiservapis.com` (`production`) or `connect-cert.fiservapis.com` (`sandbox`), carrying a new card's envelope under the key `merchantProfile` names |
696
+ | CyberSource Flex | `production` or `sandbox` | A Microform 2.12.1 card field's `application/jwt` POST to `/flex/v2/tokens` on `flex.cybersource.com` (`production`) or `testflex.cybersource.com` (`sandbox`), sealed under the capture context the field was loaded with |
671
697
 
672
698
  Use `environment: 'shared'` for Bambora, Mercado Pago, Recurly and Spreedly because the same endpoint serves test and live requests. Agentcard cannot establish the processor's test mode from that URL or a credential prefix. Configure test mode through the merchant's processor account when testing. Agentcard's own `sandbox` flag remains separate. Prepared Worldpay, Bambora, Mercado Pago and Recurly requests reject saved-card and recurring request bodies; a refused request retires the local preparation. Reconcile any existing merchant attempt before creating a new attachment.
673
699
 
@@ -779,6 +805,133 @@ On a client that declares a profile, that profile id names the declaration: `pre
779
805
 
780
806
  A raw CDP runtime arms `vault.merchantProfileUrlPatterns()` after `syncRegistry()`, beside `vault.cardUrlPatterns()`, reads `vault.merchantProfileOf(url)` and `classifyMerchantRequest(profile, url, method, body)` for each paused request, and writes a merchant-hosted approval with `substituteMerchantHostedBody(body, replay)`. Never continue a card body, or a body you could not read, to a profile's endpoint that `authorize()` did not pay.
781
807
 
808
+ ### Pay a GMO Payment Gateway checkout
809
+
810
+ GMO Payment Gateway (PG Multipayment) launches dark: `syncRegistry()` declares
811
+ the `gmo_shared_pk` capability (`SUPPORTED_CAPABILITIES`), and the API serves
812
+ the GMO recognizer only to an org Agentcard has enabled for GMO. For every other
813
+ org nothing changes: GMO's requests are never paused and never refused.
814
+
815
+ For an enabled org, the SDK pauses one request: `mp-token.js`'s `getToken` after
816
+ `Multipayment.init(shopId)` with one argument, as teamLab's ticket sites call
817
+ it. That is a `POST` to `https://token.mul-pay.jp/payment/CreateToken.json` (test
818
+ host `stg.token.mul-pay.jp`) with `X-MP-Client-Type: js-legacy` and one token.
819
+ The card inside it is encrypted in the page, so the SDK sends the paused
820
+ envelope as it is; the cardholder's device builds GMO's envelope with the real
821
+ card and sends it to GMO itself. The page receives only GMO's answer: one
822
+ single-use token, valid 30 minutes and spendable only by the shop's own site.
823
+ The SDK hands the page nothing but that token answer under a JSON content type;
824
+ any other answer on an approved authorization throws `PaymentOutcomeUnknownError`
825
+ with reason `gmo_token_unconfirmed`.
826
+
827
+ ```ts
828
+ await vault.syncRegistry();
829
+ const controller = await attachToPlaywright(page, {
830
+ vault, user: 'usr_123', merchant: 'teamLab Borderless',
831
+ amount: 3800, currency: 'jpy', // required: GMO's token request names no amount
832
+ });
833
+ ```
834
+
835
+ GMO's request names no amount, so pass `amount` and `currency`: they are what
836
+ the cardholder approves. Without them the request throws `AmountRequiredError`
837
+ before anything is created. The merchant's server charges the token after you
838
+ confirm the order, for the amount it decides, and a 3-D Secure challenge that
839
+ follows runs in your browser: call `requestUserAction('3ds')` when you see one.
840
+
841
+ Every other request to that URL is aborted before anything is created, and the
842
+ attachment reads `unsupported` with the reason, also on the
843
+ `unsupported_checkout` event:
844
+
845
+ - `unsupported_gmo_request`: a GMO request the vault cannot pay, such as
846
+ `init(shopId, publicKey)` (a merchant-registered key), GMO Elements, more than
847
+ one token, or a shop id that does not match the host (`tshop` ids belong on
848
+ the test host).
849
+ - `unsupported_gmo_token_v1`: GMO token v1 (`ext/js/token.js`), which sends the
850
+ card in a script request.
851
+
852
+ A request whose body the SDK cannot read is aborted alone with a `blocked`
853
+ event, `unreadable_gmo_request`, and the next one is judged again.
854
+
855
+ ### Pay a CyberSource Flex Microform checkout
856
+
857
+ Amtrak, Sonos and Learning Without Tears take cards in CyberSource Flex
858
+ Microform. Each card field is CyberSource's own iframe, and on createToken the
859
+ field seals what the agent typed into a JWE under a capture context the merchant
860
+ minted for this checkout. Nothing in that body can be swapped, so the
861
+ cardholder's device seals the real card under the same context and sends it to
862
+ CyberSource itself; the SDK answers the field's paused request with the
863
+ transient token CyberSource returned.
864
+
865
+ ```ts
866
+ await vault.syncRegistry(); // the Flex recognizer is never in the built-in list
867
+ const card = await vault.placeholderCard({ user: 'your-user-id' }); // what the agent types
868
+ const checkout = await attachToPlaywright(page, {
869
+ vault, user: 'your-user-id', merchant: 'Example merchant', amount: 4210, currency: 'USD',
870
+ cardId: card.cardId, // the approval page preselects the card the placeholder describes
871
+ });
872
+ // Type card.number and card.securityCode into the Microform fields, and pick
873
+ // card.expiryMonth and card.expiryYear in the merchant's own expiry fields.
874
+ ```
875
+
876
+ - **Dark launch.** `syncRegistry()` declares the `capture_context` capability,
877
+ and the API serves the Flex recognizer only to an org Agentcard has enabled
878
+ for CyberSource Flex. For any other org the SDK never recognizes a Flex
879
+ request, so the card field's request goes through untouched, as it did
880
+ before this release. The create checks the org again and answers
881
+ `403 processor_unavailable` if Agentcard turned the processor off after your
882
+ last sync.
883
+ - **The capture context.** Both adapters read it from the card field iframe's
884
+ URL, verify it offline against CyberSource's pinned signing keys, and send it
885
+ on the create as `request.capture_context` with `request.microform` (the
886
+ field iframe's version, the fields that loaded, and the one that sent the
887
+ request). The API and the cardholder's device verify it again. It never
888
+ appears in events.
889
+ - **What pauses.** Only a POST to `/flex/v2/tokens` from a Microform 2.12.1
890
+ card field whose loaded document this attachment read, sealed under that
891
+ field's capture context. A check (eCheck) Microform continues untouched.
892
+ Microform v0.x, Flex API direct traffic or any request from a frame that is
893
+ no Microform 2 field, another field-iframe version, or fields the
894
+ cardholder's device cannot rebuild is aborted as `unsupported_checkout` with `reason`
895
+ `microform_request_unrecognized`, `microform_version_unreviewed` or
896
+ `microform_fields_unsupported`; the controller reads `unsupported` with
897
+ reason `microform_unsupported` and nothing is created. A capture context that
898
+ does not verify, has expired, or did not seal the request, or a card field
899
+ the SDK could not read in time, is refused for that request alone (`blocked`
900
+ with `capture_context_invalid`, `capture_context_mismatch` or
901
+ `microform_frame_unread`), so the page's next request can pass. Every Flex
902
+ request the SDK refuses, and one whose approval ends in a decline or an API
903
+ refusal, fails in the page as a network error: the field's createToken
904
+ reports `CREATE_TOKEN_XHR_ERROR` at once, and the merchant's own error
905
+ handling runs (a message, a retry, or a reload under a new capture context).
906
+ - **Page facts.** The card field detects the brand from the number the agent
907
+ typed, and the merchant posts that brand and its own expiry fields beside the
908
+ token, while the token carries the real card. `placeholderCard()` reads the
909
+ stored card's network and expiry from `GET /api/v2/vault_cards` and returns a
910
+ documented test number of that network (Visa, Mastercard, Amex, Discover or
911
+ JCB), a placeholder security code of that network's length, and the real
912
+ expiry. Without a `cardId` it describes the user's most recently added card.
913
+ Pass the returned `cardId` to the attachment so the approval page preselects
914
+ that card; without it the page picks its own, which can be another card.
915
+ After `prepare()`, pass the prepared checkout's `cardId` instead: the
916
+ cardholder already approved that card. The facts hold only while the
917
+ cardholder pays with that card. Another network throws
918
+ `PlaceholderCardError`.
919
+ - **30 seconds.** The field iframe gives up on its request after 30 seconds.
920
+ Pass `amount` and `currency`: the page's next createToken for the same
921
+ purchase, from either field under the same capture context, is then answered
922
+ from the same approval. Fields the page reloads under a new capture context
923
+ ask the cardholder again. A page
924
+ that tokenizes on its own once the fields are valid (no Pay click) needs
925
+ `prepare({ psp: 'cybersource_flex', environment: 'production' })` before the
926
+ agent types the last card field; `sandbox` is `testflex.cybersource.com` with
927
+ CyberSource's test keys, and a test capture context pays only from a test
928
+ client.
929
+ - **One createToken per purchase.** A prepared approval binds one request. On
930
+ Amtrak, leave "add to profile" unticked or check out as a guest: that box
931
+ calls createToken a second time before Pay.
932
+ - Autopilot never pays a Flex checkout; the cardholder approves each one on
933
+ their device.
934
+
782
935
  ### Unsupported endpoints
783
936
 
784
937
  `paymentEndpoints` is an explicit list supplied by the integrator after observing
package/dist/cdp.d.ts CHANGED
@@ -127,7 +127,8 @@ export interface AttachOptions extends LifecycleOptions, ExecutionMetadata {
127
127
  * AuthorizeInput.amount: the processor's own amount is the higher authority
128
128
  * and is read right before the card is sent; a hint lets a bad purchase be
129
129
  * refused the moment it opens, and one that disagrees with the processor is
130
- * refused with nothing charged.
130
+ * refused with nothing charged. Required for GMO, whose card token request
131
+ * names no amount: without it a GMO request fails with AmountRequiredError.
131
132
  */
132
133
  amount?: number | string;
133
134
  currency?: string;