@agent-cards/sdk 0.23.17 → 0.23.19

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,8 +2,12 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - 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
+
5
7
  - 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.
6
8
 
9
+ - Keep a SumUp checkout's id out of your events and error messages. SumUp's Payment Widget sends the card to `https://api.sumup.com/v0.2/checkouts/<checkout id>` with no cookie and no auth header, so that id opens the checkout, its amount and its merchant, to anyone who holds it. `redactUrl`, every event and every error message now show it as `{checkout}` on SumUp's API host, as in `https://api.sumup.com/v0.2/checkouts/{checkout}`, and drop the query as before. The SDK does not pause a SumUp payment yet. No API or Vault release is needed.
10
+
7
11
  - Refuse a sandbox declaration whose endpoint is on agentcard.com, Agentcard's own domain, as one on agentcard.sh is already refused. A `sandboxMerchants` entry on `VaultClient` whose endpoint is agentcard.com or any host under it makes the constructor throw:
8
12
 
9
13
  ```text
@@ -16,7 +20,7 @@
16
20
 
17
21
  - Pause Redsys's hosted payment page (the TPV Virtual redirect most Spanish banks give their merchants) for the cardholder's device. The sync now also asks for `capabilities=redsys_hosted_payment_page`, and the API serves Redsys's recognizer only for a company Agentcard turned Redsys on for. The SDK pauses the merchant's signed entry form to `sis.redsys.es/sis/realizarPago` (or `/sis/realizarPago/utf-8`), 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 Redsys, its card page's own post, its older page's 3-D Secure card lookup, InSite's card post and any post to Redsys's test platform, carries the card your agent typed or opens a page that asks for one: the SDK fails it and reads `unsupported` with reason `processor_card_page`. Your events and error messages name Redsys's session id, the `;jsessionid=` part of its paths, as `{session}`. Matching API and Vault releases are required. Redsys stays off for every company Agentcard has not turned it on for.
18
22
 
19
- - Keep a PayMongo checkout's session id out of your events and error messages. PayMongo's hosted checkout sends the card to its own page URL, `https://checkout.paymongo.com/<session>`, with no auth header and no cookie, so the session id is the only credential that request needs: anyone who holds it can open the checkout, its amount and its merchant, and pay it. `redactUrl`, every event and every error message now show it as `{session}` on that host, as in `https://checkout.paymongo.com/{session}`. The SDK does not pause a PayMongo payment yet. No API or Vault release is needed.
23
+ - Keep a PayMongo checkout's session id out of your events and error messages. PayMongo's hosted checkout sends the card to its own page URL, `https://checkout.paymongo.com/<session>`, with no auth header and no cookie, so the session id is the only credential that request needs: anyone who holds it can open the checkout, its amount and its merchant, and pay it. `redactUrl`, every event and every error message now show it as `{session}` on that host, as in `https://checkout.paymongo.com/{session}`.
20
24
 
21
25
  - 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.
22
26
  - 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.
@@ -139,12 +143,26 @@
139
143
 
140
144
  - Keep a Payfast payment's id out of your events and error messages. Payfast's hosted payment page sends the card to `/eng/method/CreditCard/<uuid>/pay` on its own host (`payment.payfast.io`, `www.payfast.co.za`, `sandbox.payfast.co.za`) with no auth header, so the payment's uuid is the only credential that request needs: anyone who holds it can read the payment page, its amount and its merchant. `redactUrl`, every event and every error message now show it as `{uuid}` on every host Payfast owns, on every route of the page, as in `https://payment.payfast.io/eng/method/CreditCard/{uuid}/pay`. The SDK does not pause a Payfast payment yet. No API or Vault release is needed.
141
145
 
146
+ - Keep a processor's id out of your events and error messages however the URL spells the slashes in front of it. A path keeps a percent escape as it was written, so a slash can reach `redactUrl` as `%2F` or `%2f`, escaped twice as `%252F`, or doubled. Earlier releases showed the last digits of a Payfast payment, a CreditGuard transaction or a Gravy session written without its hyphens after an escaped slash, as in `https://payment.payfast.io/eng/method/CreditCard%{uuid}7f/pay`, and all of one written with its hyphens escaped, as of a Przelewy24 transaction's token. They showed a Stripe Checkout Session, a PayPal order, a GoPay card form's token, a Paddle checkout and a WSPay or Monri client secret whole behind a slash escaped twice or doubled, or behind a route whose own slash was escaped, a Lyra session behind an escaped `;` or `=`, and a Redsys session behind one escaped twice. `redactUrl`, every event and every error message now show each id as its label in every one of these spellings, as in `https://payment.payfast.io/eng/method/CreditCard%2F{uuid}/pay`. No API or Vault release is needed.
147
+
148
+ - Keep a processor's id out of `redactUrl`, your events and your error messages when text before the URL reads as a scheme. Earlier releases read `see:https://api.stripe.com/v1/payment_pages/<session id>/confirm` as a URL with no host, and `http:https://api.stripe.com/...` as a URL whose host is `https`, and showed the whole id in both: a Stripe Checkout Session, a Revolut order, a WSPay client secret and every other id `redactUrl` hides. A processor's URL inside another URL's path showed its id the same way, as did a second ACI checkout id after the first, and a KOMOJU URL showed the first characters of any longer id inside it. `redactUrl`, every event and every error message now hide a processor's id wherever the text names that processor's host, as in `see:https://api.stripe.com/v1/payment_pages/{session}/confirm`. Any other URL with a host reads as before. A URL with no host, such as `about:blank`, now reads as written up to its query, where earlier releases showed `nullblank`. No API or Vault release is needed.
149
+
142
150
  - Pay a live Pelecard checkout, for a company Agentcard turned Pelecard on for. Pelecard's test and live terminals share `gateway21.pelecard.biz` and `gateway20.pelecard.biz`, and neither the payment page nor its request says which kind a page is on, so the API refused every live Pelecard create with `400 pelecard_live_unsupported`, before any row or prompt. On 2026-10-11 Agentcard accepted that a live payment page could sit on one of Pelecard's public test terminals, and a live create now parks like a test one. The cardholder approves under Pelecard's note, which says the merchant may have Pelecard keep the card, since a merchant can set `CreateToken` when it creates the payment page, and only a card the cardholder stored pays: the API refuses a card a company added as `card_not_stored`. A test-mode checkout still takes only a documented test card. No SDK upgrade is needed. A payment also needs the matching API, Vault and checkout relay releases, with `CHECKOUT_PELECARD_RELAY_ENABLED` on and the company listed for Pelecard.
143
151
 
144
152
  - 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.
145
153
 
146
154
  - 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. The SDK does not pause a GoPay payment yet. No API or Vault release is needed.
147
155
 
156
+ - 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
+
158
+ - Keep a KOMOJU session's id out of your events and error messages. KOMOJU's Hosted Fields frame pays a session with `/api/v1/sessions/<id>/pay` on `komoju.com` under the merchant's publishable key, which is public, so the session id is what lets anyone read that session and pay it. `redactUrl`, every event and every error message show it as `{session}`, as in `https://komoju.com/api/v1/sessions/{session}/pay`. KOMOJU stays dark for every company Agentcard has not turned it on for, and no API or Vault release is needed.
159
+
160
+ - 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
+
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`. The SDK does not pause a Revolut payment yet. No API or Vault release is needed.
163
+
164
+ - 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
+
148
166
  ## 0.23.0
149
167
 
150
168
  ### Upgrading from 0.22.0
package/README.md CHANGED
@@ -289,7 +289,7 @@ so new processors work without you shipping a release. `attachToCdp` derives the
289
289
  `Fetch.enable` url patterns from that same list rather than a constant, which is
290
290
  why the sync call belongs before the attach. `agentcard.checkout.cardUrlPatterns()` returns
291
291
  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`
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`
293
293
  for the list this build syncs. The API serves Fiserv's card capture only to a
294
294
  build that lists `fiserv_card_capture`, and only for a company Agentcard turned
295
295
  Fiserv on for. An older SDK never pauses a Fiserv request it could not finish,
@@ -313,6 +313,10 @@ NMI never sees the card your agent typed. Redsys's hosted payment page
313
313
  (`redsys_hosted_payment_page`) is served the same way: this build pauses the
314
314
  merchant's signed entry form to `sis.redsys.es/sis/realizarPago` for the
315
315
  cardholder's device and fails every other post your browser makes to Redsys.
316
+ PayMongo's hosted checkout (`paymongo_checkout_pay`) is served on the same terms:
317
+ this build pauses the checkout page's own card attach on `checkout.paymongo.com`,
318
+ the cardholder's device sends it through the checkout relay, and the page gets
319
+ PayMongo's answer.
316
320
  CyberSource Secure Acceptance's token
317
321
  create (`cybersource_sa_token_create`) is served on the same terms too; every
318
322
  other company keeps the stop every build applies to a card posted to Secure
@@ -1596,8 +1600,9 @@ number and CVV are Datatrans's own iframes on `pay.datatrans.com`
1596
1600
  whole card in one request: `POST /upp/payment/SecureFields/paymentField`,
1597
1601
  form encoded, in TOKENIZE mode. The SDK pauses that request, the cardholder's
1598
1602
  device sends the real card to Datatrans, and the iframe receives Datatrans's
1599
- token (`transactionId`) and its `cardInfo`: brand, credit or debit, consumer
1600
- or corporate, issuing country and issuer, never a digit of the card.
1603
+ token (`transactionId`) and, when Datatrans sends it, the card's `cardInfo`:
1604
+ brand, credit or debit, consumer or corporate, issuing country, issuer,
1605
+ account type and BIN length, never a digit of the card.
1601
1606
 
1602
1607
  ```ts
1603
1608
  await vault.syncRegistry(); // the Datatrans recognizer is never in the built-in list
@@ -1637,7 +1642,8 @@ const checkout = await attachToPlaywright(page, {
1637
1642
  whenever a field loses focus or the expiry changes. Once a post was
1638
1643
  approved, a later post from the same iframe that differs from it only in
1639
1644
  the expiry (`expm`, `expy`) and the browser details is answered with the
1640
- same token and `cardInfo`: no second prompt, no second card sent. The
1645
+ same answer, the token and any `cardInfo` it came with: no second prompt,
1646
+ no second card sent. The
1641
1647
  adapters emit `repeat_answered` with the `authorizationId`; it is not a
1642
1648
  second payment and must not be counted as one. Any other change (a new
1643
1649
  `formId` from a reloaded iframe, another placeholder card or CVV, another
@@ -1763,6 +1769,13 @@ const checkout = await attachToPlaywright(page, {
1763
1769
  session is expected to last about as long from page load, so the approval
1764
1770
  has to land inside it. A 3-D Secure answer is never read as approved:
1765
1771
  reconcile that payment with the merchant.
1772
+ - Pelecard's code 302, "Debit was successful but merchant is not responding",
1773
+ is a payment that went through. The approval is recorded and the SDK reads
1774
+ `awaiting_merchant`, as for any approved Pelecard payment, until your
1775
+ `resolveMerchantResult` confirms the order with the merchant. When Pelecard
1776
+ names no landing page for a 302, Pelecard's page stays where it is and
1777
+ shows that text in its red error row with Pay enabled again. Go by the
1778
+ SDK's state, not that row, and never pay again on a new payment page.
1766
1779
 
1767
1780
  ### Pay a CreditGuard checkout
1768
1781
 
package/dist/aci.d.ts CHANGED
@@ -70,13 +70,6 @@ export declare function aciCardLegAnswer(response: {
70
70
  status: number;
71
71
  body: string;
72
72
  }): AciAnswer | null;
73
- /**
74
- * The URL with an ACI checkout id taken out of its path, or null for a URL that is not on one of
75
- * ACI's hosts under /v1/checkouts/. The id is the checkout's only capability: once the card-number
76
- * request carried the real card, anyone holding it can post /payment against that card for the
77
- * rest of the checkout's 30 minutes, so it never rides on an event.
78
- */
79
- export declare function redactAciCheckoutId(raw: string): string | null;
80
73
  /** How long the persistent-context engine probe (a CDP session opening) may take before it counts as no. */
81
74
  export declare const ACI_ENGINE_PROBE_MS = 5000;
82
75
  /**
package/dist/aci.js CHANGED
@@ -84,25 +84,6 @@ export function aciCardLegAnswer(response) {
84
84
  return null;
85
85
  return { status: response.status, headers: { 'content-type': 'application/json' }, body: rebuilt };
86
86
  }
87
- /**
88
- * The URL with an ACI checkout id taken out of its path, or null for a URL that is not on one of
89
- * ACI's hosts under /v1/checkouts/. The id is the checkout's only capability: once the card-number
90
- * request carried the real card, anyone holding it can post /payment against that card for the
91
- * rest of the checkout's 30 minutes, so it never rides on an event.
92
- */
93
- export function redactAciCheckoutId(raw) {
94
- let url;
95
- try {
96
- url = new URL(raw);
97
- }
98
- catch {
99
- return null;
100
- }
101
- if (!['eu-prod.oppwa.com', 'oppwa.com', 'eu-test.oppwa.com', 'test.oppwa.com'].includes(url.hostname))
102
- return null;
103
- const path = /^\/v1\/checkouts\/[^/]+(\/.*)?$/.exec(url.pathname);
104
- return path ? `${url.origin}/v1/checkouts/{checkoutId}${path[1] ?? ''}` : null;
105
- }
106
87
  /** How long the persistent-context engine probe (a CDP session opening) may take before it counts as no. */
107
88
  export const ACI_ENGINE_PROBE_MS = 5_000;
108
89
  /**