@agent-cards/sdk 0.23.14 → 0.23.16
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 +6 -0
- package/LICENSE +1 -1
- package/PREFLIGHT.md +2 -0
- package/README.md +2 -0
- package/dist/amadeus.d.ts +5 -2
- package/dist/amadeus.js +17 -3
- package/dist/card-fields.generated.js +237 -17
- package/dist/cdp.js +16 -8
- package/dist/client.d.ts +11 -0
- package/dist/client.js +58 -8
- package/dist/lifecycle.d.ts +5 -1
- package/dist/lifecycle.js +3 -0
- package/dist/paypal-card-forms.d.ts +73 -0
- package/dist/paypal-card-forms.js +126 -0
- package/dist/pre-claim.d.ts +1 -0
- package/dist/pre-claim.js +59 -3
- package/dist/prepared-processor.d.ts +5 -1
- package/dist/prepared-processor.js +11 -0
- package/dist/primer.generated.js +248 -28
- package/dist/stripe-checkout.generated.js +79 -1
- package/dist/wix-payments.generated.d.ts +42 -0
- package/dist/wix-payments.generated.js +241 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -14,10 +14,14 @@
|
|
|
14
14
|
|
|
15
15
|
- 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.
|
|
16
16
|
|
|
17
|
+
- 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.
|
|
18
|
+
|
|
17
19
|
- 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.
|
|
18
20
|
- 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.
|
|
19
21
|
- 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.
|
|
20
22
|
|
|
23
|
+
- 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.
|
|
24
|
+
|
|
21
25
|
- Keep a Rapyd hosted page's id and a Stripe Checkout Session's id out of your events and error messages. Rapyd's checkout page sends the card to `/v1/checkout/checkout_<id>` on `api.rapyd.net` (a card page to `/v1/hosted/collect/card/hp_card_<id>/payment_method`) with no auth header, so the page's id is the only credential that request needs: anyone who holds it can read the page and post a card to it until it expires. Stripe Checkout confirms a session at `/v1/payment_pages/<session id>/confirm` with the merchant's publishable key, which is public, so the session id is what lets anyone confirm a payment on it. Earlier releases named both ids in the `card_request_paused` event and in the errors `authorize()` throws, so they reached your `onEvent` handler and every log that recorded the event or the error. `redactUrl`, every event and every error message now show them as `{token}` and `{session}`, as in `https://api.rapyd.net/v1/checkout/{token}` and `https://api.stripe.com/v1/payment_pages/{session}/confirm`. `redactUrl` also masks an ACI checkout id in the shapes it used to keep: a host with a trailing dot, an escaped or doubled slash, and a string that is not a URL. A PaymentIntent or SetupIntent id stays, since confirming one also needs its client_secret. No API or Vault release is needed.
|
|
22
26
|
|
|
23
27
|
- 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.
|
|
@@ -135,6 +139,8 @@
|
|
|
135
139
|
|
|
136
140
|
- 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.
|
|
137
141
|
|
|
142
|
+
- 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.
|
|
143
|
+
|
|
138
144
|
## 0.23.0
|
|
139
145
|
|
|
140
146
|
### Upgrading from 0.22.0
|
package/LICENSE
CHANGED
|
@@ -35,4 +35,4 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
35
35
|
IMPLIED. IN NO EVENT SHALL AGENTCARD CORPORATION BE LIABLE FOR ANY CLAIM,
|
|
36
36
|
DAMAGES OR OTHER LIABILITY ARISING FROM THE SOFTWARE OR ITS USE.
|
|
37
37
|
|
|
38
|
-
Questions: legal@agentcard.
|
|
38
|
+
Questions: legal@agentcard.com
|
package/PREFLIGHT.md
CHANGED
|
@@ -203,6 +203,8 @@ WSPay Components (`wspay`) is identified from its component scripts and its card
|
|
|
203
203
|
|
|
204
204
|
Shiji STS (`shiji_sts`), the card leg of MyCheck Wallet V3 at La Quinta by Wyndham, is identified from the in-page wallet script `wallet-sdk-v3.mycheckapp.com/{version}/static/js/main.js` (and its `-sandbox` and `-test` hosts) and from Shiji's proxy `cdn.shijipayment.com/sts/sts.proxy.v2.1.js` (and its `cdn.shijipayment.dev` copy), but it is not in this table. It launches dark: the direct SDK acts on it only after `syncRegistry()` and only for an org Agentcard has enabled, and even then it refuses every card request as card on file (`shiji_sts_keep_on_file`) until Agentcard can ask the cardholder to let a hotel keep the card, so the bundled catalog leaves it out and a Shiji checkout stays `unknown`. MyCheck's iframe wallet (`sdk.js`, `wallet-iframe.html`) and Shiji's `shijipayments.latest.min.js` name their processor and return `unknown` with `processor_variant_unverified`. Read the [Shiji STS section of the README](./README.md#shiji-sts-mycheck-wallet-v3).
|
|
205
205
|
|
|
206
|
+
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
|
+
|
|
206
208
|
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`.
|
|
207
209
|
|
|
208
210
|
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
|
@@ -255,6 +255,7 @@ Coverage is specific to the processor request format, merchant setup, browser tr
|
|
|
255
255
|
| Mercado Pago | Card tokenization and prepared checkout are implemented. Guest Checkout Pro in Mexico also corrects the issuer for one native card association when the selected card has the same brand and type. One live MXN 40 Lotería Chida purchase with published SDK 0.10.0 completed automatically through Pay; the merchant confirmed paid status and PDF fulfillment. The historical SDK result remains unknown because the private receipt adapter rejected a relative download URL; a separate read with the corrected adapter confirms that same paid receipt. Independent processor capture/settlement and other country or integration paths remain unverified. |
|
|
256
256
|
| VGS Collect (Very Good Security; Wolt) | not supported: VGS's proxy aliases only submissions from its own iframe, so a replay from the cardholder's device is refused by the merchant (verified on Wolt, 2026-09-03). Not recognized, so the agent's browser is not paused there |
|
|
257
257
|
| PayPal Hosted Fields (the legacy card fields of PayPal's JS SDK; Planet Golf UK) | not supported: no Agentcard payment takes over the card these fields send to `cors.api.paypal.com`. The SDK fails that request for every company, so PayPal's payment confirm never receives the dummy card, and the controller reads `unsupported` with reason `unsupported_paypal_hosted_fields` and holds the attachment until you attach again; a PayPal wallet payment continues untouched |
|
|
258
|
+
| PayPal Card Fields (PayPal JS SDK `components=card-fields`, v5 and v6) and PayPal's guest card form (the "Debit or Credit Card" button) | not supported: no Agentcard payment takes over the card these send to `www.paypal.com` or `api-m.paypal.com`. The SDK fails each card request for every company, 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 |
|
|
258
259
|
| CyberSource Secure Acceptance Silent Order POST (the merchant's own card form posted to CyberSource; Arrow Electronics) | dark launch: prepared checkout only, for an org Agentcard enabled. After `syncRegistry()`, for such an org, the SDK pauses the token create a merchant's form posts into a hidden iframe (`/silent/embedded/token/create` on `secureacceptance.cybersource.com`, or `testsecureacceptance.cybersource.com` in CyberSource's test environment), the cardholder's device sends the card to CyberSource, and the iframe gets CyberSource's own reply page. See [Pay a CyberSource Secure Acceptance checkout](#pay-a-cybersource-secure-acceptance-checkout-dark-launch). For every other company, and for every other card post to a `/silent/` path on those hosts or on `secureacceptance.in.cybersource.com` (India), no Agentcard payment takes the card over, and the SDK stops the post so CyberSource never receives the dummy card: a form the page submits as a page load, in the page or in a frame, ends on the page that says Agentcard stopped the payment, and a fetch or an XHR fails. The controller reads `unsupported` with reason `unsupported_cybersource_secure_acceptance` and holds the attachment until you attach again; a post with no `card_number`, such as a payment with a stored `payment_token`, continues untouched. The SDK does not stop Hosted Checkout, where your agent types the card on CyberSource's own page |
|
|
259
260
|
| 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 |
|
|
260
261
|
| 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 |
|
|
@@ -278,6 +279,7 @@ Coverage is specific to the processor request format, merchant setup, browser tr
|
|
|
278
279
|
| 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 |
|
|
279
280
|
| 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) |
|
|
280
281
|
| 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
|
+
| 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`) |
|
|
281
283
|
| 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" |
|
|
282
284
|
| Paddle checkout (Paddle's card vault; Rectangle Pro) | dark launch: only in Agentcard's own account for now, after `vault.syncRegistry()`, and 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`. On a Paddle checkout whose card method names VGS, 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 only a reviewed seller at its reviewed page is paid (Rectangle Pro at `https://rectangleapp.com`), and the approval says that seller can keep the card on file. 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. A Paddle checkout that sends the card through TokenEx or Spreedly is not supported, and no live payment has gone through this route yet |
|
|
283
285
|
|
package/dist/amadeus.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** How long after the pause a paused Amadeus SDK add may still be sent. */
|
|
2
2
|
export declare const AMADEUS_SEND_WINDOW_MS = 20000;
|
|
3
|
+
/** How long after the pause a paused Wix tokenize request may still be sent, of the 30 seconds Wix's widget waits. */
|
|
4
|
+
export declare const WIX_PAYMENTS_SEND_WINDOW_MS = 20000;
|
|
3
5
|
/**
|
|
4
6
|
* True when a request the adapters refuse must fail as a network error (ERR_FAILED) rather than as
|
|
5
7
|
* the cancellation (ERR_ABORTED) they use elsewhere. An XMLHttpRequest reports a cancellation only
|
|
@@ -9,12 +11,13 @@ export declare const AMADEUS_SEND_WINDOW_MS = 20000;
|
|
|
9
11
|
*/
|
|
10
12
|
export declare function failsAsNetworkError(url: string): boolean;
|
|
11
13
|
/** The processor a send window's events and stop cause name. */
|
|
12
|
-
export type SendWindowProcessor = 'amadeus' | 'secure_acceptance';
|
|
14
|
+
export type SendWindowProcessor = 'amadeus' | 'secure_acceptance' | 'wix_payments';
|
|
13
15
|
/** Why a send window stopped its attempt: the API confirmed the card never left the device and never can. */
|
|
14
16
|
export type SendWindowClosed = `${SendWindowProcessor}_send_window_closed`;
|
|
15
17
|
/**
|
|
16
18
|
* The send window for a request paused under a preparation for `psp`, or null when it has none: a
|
|
17
|
-
* prepared Amadeus SDK add,
|
|
19
|
+
* prepared Amadeus SDK add, a prepared CyberSource Secure Acceptance token create and a prepared Wix
|
|
20
|
+
* tokenize request, each 20 seconds.
|
|
18
21
|
*/
|
|
19
22
|
export declare function preparedSendWindowMs(psp: string | undefined, url: string): number | null;
|
|
20
23
|
/** The processor a prepared request's send window names in its events, or null when it has none. */
|
package/dist/amadeus.js
CHANGED
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
// without a word. A token CyberSource creates after that point is one the page never reads, so a
|
|
15
15
|
// paused token create gets the same 20 second window (cybersource-secure-acceptance.ts).
|
|
16
16
|
//
|
|
17
|
+
// Wix's payment widget fails the payment 30 seconds after it asks Wix Cashier's card frame for a
|
|
18
|
+
// token (cashier-payments-widget 1.4964.0), and the merchant's page charges only a token the frame
|
|
19
|
+
// handed back in time. A token Wix mints after that point is one no charge ever names, so a paused
|
|
20
|
+
// tokenize request gets the same 20 second window.
|
|
21
|
+
//
|
|
17
22
|
// So when the window closes and no answer is being handed to the page yet, the adapter asks for that
|
|
18
23
|
// cancel. Confirmed: nothing was sent and nothing can be, so the attempt stops, the page's request
|
|
19
24
|
// fails (a Secure Acceptance frame gets the stopped page), and the checkout reads failed. Not
|
|
@@ -24,13 +29,17 @@
|
|
|
24
29
|
// request can bind it, nothing can be sent; when a request already holds it, that authorization gets
|
|
25
30
|
// the same cancel and the same rule, and when the API confirms neither, the attempt keeps waiting. The
|
|
26
31
|
// Data Collection Page's store has no client timeout and gets no window. The window's events name the
|
|
27
|
-
// processor: `amadeus_send_window_closed` and `amadeus_send_window_unconfirmed`,
|
|
28
|
-
// `secure_acceptance_send_window_closed` and `secure_acceptance_send_window_unconfirmed
|
|
32
|
+
// processor: `amadeus_send_window_closed` and `amadeus_send_window_unconfirmed`,
|
|
33
|
+
// `secure_acceptance_send_window_closed` and `secure_acceptance_send_window_unconfirmed`, or
|
|
34
|
+
// `wix_payments_send_window_closed` and `wix_payments_send_window_unconfirmed`.
|
|
29
35
|
import { amadeusCardUrl } from './amadeus-checkout.generated.js';
|
|
30
36
|
import { isSecureAcceptanceTokenRequest } from './cybersource-secure-acceptance.generated.js';
|
|
31
37
|
import { SECURE_ACCEPTANCE_PSP, SECURE_ACCEPTANCE_SEND_WINDOW_MS } from './cybersource-secure-acceptance.js';
|
|
38
|
+
import { WIX_PAYMENTS_PSP, isWixPaymentsTokenizeUrl } from './wix-payments.generated.js';
|
|
32
39
|
/** How long after the pause a paused Amadeus SDK add may still be sent. */
|
|
33
40
|
export const AMADEUS_SEND_WINDOW_MS = 20_000;
|
|
41
|
+
/** How long after the pause a paused Wix tokenize request may still be sent, of the 30 seconds Wix's widget waits. */
|
|
42
|
+
export const WIX_PAYMENTS_SEND_WINDOW_MS = 20_000;
|
|
34
43
|
/**
|
|
35
44
|
* True when a request the adapters refuse must fail as a network error (ERR_FAILED) rather than as
|
|
36
45
|
* the cancellation (ERR_ABORTED) they use elsewhere. An XMLHttpRequest reports a cancellation only
|
|
@@ -43,7 +52,8 @@ export function failsAsNetworkError(url) {
|
|
|
43
52
|
}
|
|
44
53
|
/**
|
|
45
54
|
* The send window for a request paused under a preparation for `psp`, or null when it has none: a
|
|
46
|
-
* prepared Amadeus SDK add,
|
|
55
|
+
* prepared Amadeus SDK add, a prepared CyberSource Secure Acceptance token create and a prepared Wix
|
|
56
|
+
* tokenize request, each 20 seconds.
|
|
47
57
|
*/
|
|
48
58
|
export function preparedSendWindowMs(psp, url) {
|
|
49
59
|
if (psp === 'amadeus_checkout')
|
|
@@ -58,10 +68,14 @@ export function preparedSendWindowMs(psp, url) {
|
|
|
58
68
|
}
|
|
59
69
|
return tokenCreate ? SECURE_ACCEPTANCE_SEND_WINDOW_MS : null;
|
|
60
70
|
}
|
|
71
|
+
if (psp === WIX_PAYMENTS_PSP)
|
|
72
|
+
return isWixPaymentsTokenizeUrl(url) ? WIX_PAYMENTS_SEND_WINDOW_MS : null;
|
|
61
73
|
return null;
|
|
62
74
|
}
|
|
63
75
|
/** The processor a prepared request's send window names in its events, or null when it has none. */
|
|
64
76
|
export function sendWindowProcessor(psp) {
|
|
77
|
+
if (psp === WIX_PAYMENTS_PSP)
|
|
78
|
+
return 'wix_payments';
|
|
65
79
|
return psp === 'amadeus_checkout' ? 'amadeus' : psp === SECURE_ACCEPTANCE_PSP ? 'secure_acceptance' : null;
|
|
66
80
|
}
|
|
67
81
|
/** The send window for a prepared Amadeus SDK add, or null: preparedSendWindowMs for Amadeus alone. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256:
|
|
3
|
+
// artifact-sha256: e4858832550c43f6e3cbef0c64facb820d7ae9b5118a2c8c4fcadb429c94b2b0
|
|
4
4
|
// src/card-number-key.js
|
|
5
5
|
var CARD_NUMBER_KEY = /^(number|card_number|cardnumber|cardNum|card_num|pan)$/i;
|
|
6
6
|
// src/json-path.js
|
|
@@ -357,8 +357,8 @@ var CONTROL = /[\u0000-\u001f\u007f]/;
|
|
|
357
357
|
var cardLikeName = (name) => (name.match(/[0-9]/g) ?? []).length >= 12;
|
|
358
358
|
var record3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
359
359
|
var sameKeys = (value, keys) => {
|
|
360
|
-
const
|
|
361
|
-
return
|
|
360
|
+
const own2 = Object.keys(value);
|
|
361
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
362
362
|
};
|
|
363
363
|
var refuse4 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
364
364
|
var CARD4 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
@@ -425,6 +425,9 @@ var PAYFAST_BODY_KEYS = /* @__PURE__ */ Object.freeze([
|
|
|
425
425
|
"captcha_version",
|
|
426
426
|
"captcha_action"
|
|
427
427
|
]);
|
|
428
|
+
var CAPTCHA_ANSWER = "g-recaptcha-response";
|
|
429
|
+
var UNANSWERED_KEYS = /* @__PURE__ */ Object.freeze(PAYFAST_BODY_KEYS.filter((key) => key !== CAPTCHA_ANSWER));
|
|
430
|
+
var UNANSWERED_VERSIONS = /* @__PURE__ */ Object.freeze(["v0", "v1"]);
|
|
428
431
|
var BODY_LIMIT3 = 8192;
|
|
429
432
|
var URL_LIMIT = 256;
|
|
430
433
|
var PAY_PATH = /^\/eng\/method\/CreditCard\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/pay$/;
|
|
@@ -487,18 +490,20 @@ function cardNetwork(number) {
|
|
|
487
490
|
return "mastercard";
|
|
488
491
|
return AMEX.test(number) ? "amex" : null;
|
|
489
492
|
}
|
|
493
|
+
var namesKeys = (pairs, keys) => pairs.length === keys.length && pairs.every(([key], at) => key === keys[at]);
|
|
490
494
|
function judgeBody(body) {
|
|
491
495
|
const pairs = readForm(body);
|
|
492
496
|
if (pairs === null)
|
|
493
497
|
return { code: "payfast_body_unsupported", pairs: null };
|
|
494
498
|
if (pairs.some(([key]) => key === "cc_key"))
|
|
495
499
|
return { code: "payfast_saved_card", pairs: null };
|
|
496
|
-
|
|
500
|
+
const answered = namesKeys(pairs, PAYFAST_BODY_KEYS);
|
|
501
|
+
if (!answered && !namesKeys(pairs, UNANSWERED_KEYS))
|
|
497
502
|
return { code: "payfast_body_unsupported", pairs: null };
|
|
498
|
-
}
|
|
499
503
|
const value = Object.fromEntries(pairs);
|
|
500
|
-
const answer = value[
|
|
501
|
-
|
|
504
|
+
const answer = value[CAPTCHA_ANSWER];
|
|
505
|
+
const captcha = answered ? answer === "" || SOLVED_CAPTCHA2.test(answer) : UNANSWERED_VERSIONS.includes(value.captcha_version);
|
|
506
|
+
if (cardNetwork(value.card_number) === null || !SECURITY_CODE3.test(value.cvv) || !MONTH.test(value.expiry_month) || !YEAR.test(value.expiry_year) || value.cardholder_name.length > HOLDER_LIMIT || !HOLDER.test(value.cardholder_name) || !captcha || !CAPTCHA_VERSIONS.includes(value.captcha_version) || value.captcha_action !== "checkout") {
|
|
502
507
|
return { code: "payfast_body_unsupported", pairs: null };
|
|
503
508
|
}
|
|
504
509
|
if (value.consent_check === "1")
|
|
@@ -658,10 +663,10 @@ var TYPED_ARRAY = Object.getPrototypeOf(Uint8Array.prototype);
|
|
|
658
663
|
var typedArrayName = Object.getOwnPropertyDescriptor(TYPED_ARRAY, Symbol.toStringTag).get;
|
|
659
664
|
function parseRsaPublicKey(key) {
|
|
660
665
|
const spki = key && typeof key === "object" ? key.spki : void 0;
|
|
661
|
-
const
|
|
666
|
+
const pin4 = key && typeof key === "object" ? key.sha256 : void 0;
|
|
662
667
|
if (typeof spki !== "string" || spki.length > 1024 || spki.length % 4 !== 0 || !BASE64.test(spki))
|
|
663
668
|
throw refuse6("key");
|
|
664
|
-
if (typeof
|
|
669
|
+
if (typeof pin4 !== "string" || !SHA256_HEX.test(pin4))
|
|
665
670
|
throw refuse6("key");
|
|
666
671
|
let binary;
|
|
667
672
|
try {
|
|
@@ -673,7 +678,7 @@ function parseRsaPublicKey(key) {
|
|
|
673
678
|
if (btoa(binary) !== spki)
|
|
674
679
|
throw refuse6("key");
|
|
675
680
|
const der = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
676
|
-
if (sha256Hex(der) !==
|
|
681
|
+
if (sha256Hex(der) !== pin4)
|
|
677
682
|
throw refuse6("key");
|
|
678
683
|
const outer = readElement(der, 0, 48);
|
|
679
684
|
if (outer.end !== der.length || outer.start + RSA_ALGORITHM.length > der.length)
|
|
@@ -762,8 +767,8 @@ var PADDLE_TOKENEX_PSP = "paddle_tokenex";
|
|
|
762
767
|
var PADDLE_TOKENEX_FACT_KEYS = /* @__PURE__ */ Object.freeze(["card_first_six", "card_last_four", "expiry_month", "expiry_year", "card_brand"]);
|
|
763
768
|
var record4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
764
769
|
var sameKeys2 = (value, keys) => {
|
|
765
|
-
const
|
|
766
|
-
return
|
|
770
|
+
const own2 = Object.keys(value);
|
|
771
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
767
772
|
};
|
|
768
773
|
var refuse7 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
769
774
|
var CARD6 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
@@ -1233,10 +1238,219 @@ var LYRA_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
|
|
|
1233
1238
|
hosts: Object.freeze(["api.systempay.fr"])
|
|
1234
1239
|
})
|
|
1235
1240
|
]);
|
|
1241
|
+
// src/wix-payments.js
|
|
1242
|
+
var WIX_PAYMENTS_PSP = "wix_payments";
|
|
1243
|
+
var WIX_PAYMENTS_TOKENIZE_URL = "https://cashier-pci.wixapps.net/_api/payment-gateway-web/tokenize/v2/tokens";
|
|
1244
|
+
var BODY_LIMIT5 = 8192;
|
|
1245
|
+
var ROOT_KEYS3 = /* @__PURE__ */ Object.freeze(["cvv", "creditCard"]);
|
|
1246
|
+
var CARD_KEYS5 = /* @__PURE__ */ Object.freeze(["number", "expiration", "additionalInformation", "billingAddress"]);
|
|
1247
|
+
var EXPIRATION_KEYS = /* @__PURE__ */ Object.freeze(["month", "year"]);
|
|
1248
|
+
var HOLDER_KEYS = /* @__PURE__ */ Object.freeze(["holderName"]);
|
|
1249
|
+
var CARD_NUMBER6 = /^[0-9]{12,19}$/;
|
|
1250
|
+
var SECURITY_CODE5 = /^[0-9]{3,4}$/;
|
|
1251
|
+
var FIRST_YEAR = 2e3;
|
|
1252
|
+
var LAST_YEAR = 2099;
|
|
1253
|
+
var FIELD_NAME = /^[A-Za-z][A-Za-z0-9]{0,63}$/;
|
|
1254
|
+
var MAX_FIELDS = 32;
|
|
1255
|
+
var MAX_NESTED_FIELDS = 8;
|
|
1256
|
+
var TEXT_LIMIT = 256;
|
|
1257
|
+
var CONTROL4 = /[\u0000-\u001f\u007f]/;
|
|
1258
|
+
var record6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1259
|
+
var sameKeys3 = (value, keys) => {
|
|
1260
|
+
const own2 = Object.keys(value);
|
|
1261
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
1262
|
+
};
|
|
1263
|
+
var plainText = (value) => typeof value === "string" && value.length <= TEXT_LIMIT && !CONTROL4.test(value) && (value.match(/[0-9]/g) ?? []).length < 12;
|
|
1264
|
+
var refuse9 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
1265
|
+
var CARD8 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
1266
|
+
function isWixPaymentsTokenizeUrl(url) {
|
|
1267
|
+
return url === WIX_PAYMENTS_TOKENIZE_URL;
|
|
1268
|
+
}
|
|
1269
|
+
function readBody3(body) {
|
|
1270
|
+
if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT5)
|
|
1271
|
+
return null;
|
|
1272
|
+
let value;
|
|
1273
|
+
try {
|
|
1274
|
+
value = JSON.parse(body);
|
|
1275
|
+
if (!record6(value) || JSON.stringify(value) !== body)
|
|
1276
|
+
return null;
|
|
1277
|
+
}
|
|
1278
|
+
catch {
|
|
1279
|
+
return null;
|
|
1280
|
+
}
|
|
1281
|
+
return value;
|
|
1282
|
+
}
|
|
1283
|
+
var billingText = (value) => value === null || plainText(value);
|
|
1284
|
+
function billingFields(value) {
|
|
1285
|
+
if (!record6(value))
|
|
1286
|
+
return false;
|
|
1287
|
+
const names = Object.keys(value);
|
|
1288
|
+
if (names.length > MAX_FIELDS)
|
|
1289
|
+
return false;
|
|
1290
|
+
return names.every((name) => {
|
|
1291
|
+
const field = value[name];
|
|
1292
|
+
if (!FIELD_NAME.test(name))
|
|
1293
|
+
return false;
|
|
1294
|
+
if (!record6(field))
|
|
1295
|
+
return billingText(field);
|
|
1296
|
+
const nested = Object.keys(field);
|
|
1297
|
+
return nested.length <= MAX_NESTED_FIELDS && nested.every((key) => FIELD_NAME.test(key) && billingText(field[key]));
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
function wixPaymentsBodyVerdict(body) {
|
|
1301
|
+
const value = readBody3(body);
|
|
1302
|
+
if (value === null || !sameKeys3(value, ROOT_KEYS3) || !record6(value.creditCard) || !sameKeys3(value.creditCard, CARD_KEYS5)) {
|
|
1303
|
+
return refuse9("wix_payments_body_unsupported");
|
|
1304
|
+
}
|
|
1305
|
+
const card = value.creditCard;
|
|
1306
|
+
const expiration = card.expiration;
|
|
1307
|
+
const holder = card.additionalInformation;
|
|
1308
|
+
if (typeof value.cvv !== "string" || !SECURITY_CODE5.test(value.cvv) || typeof card.number !== "string" || !CARD_NUMBER6.test(card.number) || !record6(expiration) || !sameKeys3(expiration, EXPIRATION_KEYS) || !Number.isInteger(expiration.month) || expiration.month < 1 || expiration.month > 12 || !Number.isInteger(expiration.year) || expiration.year < FIRST_YEAR || expiration.year > LAST_YEAR || !record6(holder) || !sameKeys3(holder, HOLDER_KEYS) || !plainText(holder.holderName) || !billingFields(card.billingAddress)) {
|
|
1309
|
+
return refuse9("wix_payments_body_unsupported");
|
|
1310
|
+
}
|
|
1311
|
+
return CARD8;
|
|
1312
|
+
}
|
|
1313
|
+
var pin3 = (fields) => Object.freeze({ ...fields, origins: Object.freeze([...fields.origins]) });
|
|
1314
|
+
var WIX_PAYMENTS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
|
|
1315
|
+
pin3({ id: "parahouse_magazine", merchant: "ParaHouse Magazine", origins: ["https://www.parahousemagazine.com"] }),
|
|
1316
|
+
pin3({ id: "psalms_and_potions", merchant: "Psalms & Potions", origins: ["https://www.psalmsandpotions.com"] }),
|
|
1317
|
+
pin3({ id: "alexandra_hagel", merchant: "Alexandra Hagel, LLC", origins: ["https://www.alexandrahagel.com"] })
|
|
1318
|
+
]);
|
|
1319
|
+
// src/paymongo.js
|
|
1320
|
+
var PAYMONGO_PSP = "paymongo";
|
|
1321
|
+
var PAYMONGO_CHECKOUT_HOST = "checkout.paymongo.com";
|
|
1322
|
+
var PAYMONGO_ATTRIBUTE_KEYS = /* @__PURE__ */ Object.freeze(["payment_method_type", "details", "billing", "return_url"]);
|
|
1323
|
+
var PAYMONGO_CARD_KEYS = /* @__PURE__ */ Object.freeze(["card_number", "exp_month", "exp_year", "cvc"]);
|
|
1324
|
+
var PAYMONGO_BILLING_KEYS = /* @__PURE__ */ Object.freeze(["name", "email", "phone", "address"]);
|
|
1325
|
+
var PAYMONGO_ADDRESS_KEYS = /* @__PURE__ */ Object.freeze(["line1", "line2", "city", "state", "country", "postal_code"]);
|
|
1326
|
+
var CLICK_TO_PAY_KEYS = /* @__PURE__ */ Object.freeze(["merchant_transaction_id", "correlation_id", "x_src_cx_flow_id"]);
|
|
1327
|
+
var URL_LIMIT2 = 128;
|
|
1328
|
+
var BODY_LIMIT6 = 16384;
|
|
1329
|
+
var ORIGIN = `https://${PAYMONGO_CHECKOUT_HOST}/`;
|
|
1330
|
+
var SESSION2 = /^[A-Za-z0-9]{24}$/;
|
|
1331
|
+
var CHECKOUT_PATH = /^\/(?:cs_)?([A-Za-z0-9]{24})$/;
|
|
1332
|
+
var NUMBER = /^[0-9]{12,19}$/;
|
|
1333
|
+
var SECURITY_CODE6 = /^[0-9]{3,4}$/;
|
|
1334
|
+
var TEXT = /^[^\x00-\x1f\x7f]{1,256}$/;
|
|
1335
|
+
var UNDEFINED = "$undefined";
|
|
1336
|
+
var CARD_MEMBER = /^(?:card_?number|pan|cvc|cvv)$/i;
|
|
1337
|
+
var record7 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1338
|
+
var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
1339
|
+
var keysAre = (value, keys) => {
|
|
1340
|
+
const names = Object.keys(value);
|
|
1341
|
+
return names.length === keys.length && names.every((name, at) => name === keys[at]);
|
|
1342
|
+
};
|
|
1343
|
+
var refuse10 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
1344
|
+
var CARD9 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
1345
|
+
var CONTINUE4 = /* @__PURE__ */ Object.freeze({ verdict: "continue" });
|
|
1346
|
+
function paymongoCheckoutSession(url) {
|
|
1347
|
+
if (typeof url !== "string" || url.length > URL_LIMIT2 || url.includes("?") || url.includes("#"))
|
|
1348
|
+
return null;
|
|
1349
|
+
let parsed;
|
|
1350
|
+
try {
|
|
1351
|
+
parsed = new URL(url);
|
|
1352
|
+
}
|
|
1353
|
+
catch {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1356
|
+
if (parsed.href !== url || parsed.protocol !== "https:" || parsed.port || parsed.username || parsed.password || parsed.hostname !== PAYMONGO_CHECKOUT_HOST)
|
|
1357
|
+
return null;
|
|
1358
|
+
const match = CHECKOUT_PATH.exec(parsed.pathname);
|
|
1359
|
+
return match ? match[1] : null;
|
|
1360
|
+
}
|
|
1361
|
+
function readJson2(body) {
|
|
1362
|
+
if (typeof body !== "string" || body === "")
|
|
1363
|
+
return { parsed: null, exact: false };
|
|
1364
|
+
let parsed;
|
|
1365
|
+
try {
|
|
1366
|
+
parsed = JSON.parse(body);
|
|
1367
|
+
}
|
|
1368
|
+
catch {
|
|
1369
|
+
return { parsed: null, exact: false };
|
|
1370
|
+
}
|
|
1371
|
+
let exact = false;
|
|
1372
|
+
try {
|
|
1373
|
+
exact = body.length <= BODY_LIMIT6 && JSON.stringify(parsed) === body;
|
|
1374
|
+
}
|
|
1375
|
+
catch {
|
|
1376
|
+
exact = false;
|
|
1377
|
+
}
|
|
1378
|
+
return { parsed, exact };
|
|
1379
|
+
}
|
|
1380
|
+
function billingValue(value) {
|
|
1381
|
+
if (value === UNDEFINED)
|
|
1382
|
+
return true;
|
|
1383
|
+
if (typeof value !== "string" || !TEXT.test(value))
|
|
1384
|
+
return false;
|
|
1385
|
+
return value[0] !== "$" || value.startsWith("$$");
|
|
1386
|
+
}
|
|
1387
|
+
function judgeCardAttach(args, session) {
|
|
1388
|
+
if (args.length !== 3 || typeof args[0] !== "string" || !args[0].startsWith("cs_") || !SESSION2.test(args[0].slice(3))) {
|
|
1389
|
+
return "paymongo_body_unsupported";
|
|
1390
|
+
}
|
|
1391
|
+
const id = args[0].slice(3);
|
|
1392
|
+
const attributes = args[1].data.attributes;
|
|
1393
|
+
const details = attributes.details;
|
|
1394
|
+
if (record7(details) && CLICK_TO_PAY_KEYS.some((key) => own(details, key)))
|
|
1395
|
+
return "paymongo_click_to_pay";
|
|
1396
|
+
if (!keysAre(args[1], ["data"]) || !keysAre(args[1].data, ["attributes"]) || !keysAre(attributes, PAYMONGO_ATTRIBUTE_KEYS) || !record7(details) || !keysAre(details, PAYMONGO_CARD_KEYS))
|
|
1397
|
+
return "paymongo_body_unsupported";
|
|
1398
|
+
if (typeof details.card_number !== "string" || !NUMBER.test(details.card_number) || !Number.isSafeInteger(details.exp_month) || details.exp_month < 1 || details.exp_month > 12 || !Number.isSafeInteger(details.exp_year) || details.exp_year < 0 || details.exp_year > 99 || typeof details.cvc !== "string" || !SECURITY_CODE6.test(details.cvc))
|
|
1399
|
+
return "paymongo_body_unsupported";
|
|
1400
|
+
const billing = attributes.billing;
|
|
1401
|
+
if (!record7(billing) || !keysAre(billing, PAYMONGO_BILLING_KEYS) || !record7(billing.address) || !keysAre(billing.address, PAYMONGO_ADDRESS_KEYS))
|
|
1402
|
+
return "paymongo_body_unsupported";
|
|
1403
|
+
if (billing.name === UNDEFINED || !billingValue(billing.name) || !billingValue(billing.email) || !billingValue(billing.phone) || !PAYMONGO_ADDRESS_KEYS.every((key) => billingValue(billing.address[key])))
|
|
1404
|
+
return "paymongo_body_unsupported";
|
|
1405
|
+
const third = args[2];
|
|
1406
|
+
if (!record7(third) || !keysAre(third, ["viewerAccept"]) || !billingValue(third.viewerAccept))
|
|
1407
|
+
return "paymongo_body_unsupported";
|
|
1408
|
+
if (attributes.return_url !== `${ORIGIN}${id}` || session !== void 0 && session !== id)
|
|
1409
|
+
return "paymongo_session_mismatch";
|
|
1410
|
+
return null;
|
|
1411
|
+
}
|
|
1412
|
+
function namesCardValue(parsed) {
|
|
1413
|
+
const pending = parsed !== null && typeof parsed === "object" ? [parsed] : [];
|
|
1414
|
+
while (pending.length > 0) {
|
|
1415
|
+
const node = pending.pop();
|
|
1416
|
+
for (const key of Object.keys(node)) {
|
|
1417
|
+
if (!Array.isArray(node) && CARD_MEMBER.test(key))
|
|
1418
|
+
return true;
|
|
1419
|
+
const value = node[key];
|
|
1420
|
+
if (value !== null && typeof value === "object")
|
|
1421
|
+
pending.push(value);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
return false;
|
|
1425
|
+
}
|
|
1426
|
+
function attachType(parsed) {
|
|
1427
|
+
if (!Array.isArray(parsed) || parsed.length < 2 || !record7(parsed[1]) || !record7(parsed[1].data) || !record7(parsed[1].data.attributes))
|
|
1428
|
+
return null;
|
|
1429
|
+
const type = parsed[1].data.attributes.payment_method_type;
|
|
1430
|
+
return typeof type === "string" ? type : null;
|
|
1431
|
+
}
|
|
1432
|
+
function paymongoBodyVerdict(body, session) {
|
|
1433
|
+
const { parsed, exact } = readJson2(body);
|
|
1434
|
+
const type = parsed === null ? null : attachType(parsed);
|
|
1435
|
+
if (type !== "card") {
|
|
1436
|
+
if (parsed === null)
|
|
1437
|
+
return body === void 0 || body === null || body === "" ? CONTINUE4 : refuse10("paymongo_body_unsupported");
|
|
1438
|
+
return namesCardValue(parsed) ? refuse10("paymongo_body_unsupported") : CONTINUE4;
|
|
1439
|
+
}
|
|
1440
|
+
if (!exact)
|
|
1441
|
+
return refuse10("paymongo_body_unsupported");
|
|
1442
|
+
const code = judgeCardAttach(parsed, session);
|
|
1443
|
+
return code === null ? CARD9 : refuse10(code);
|
|
1444
|
+
}
|
|
1236
1445
|
// src/card-fields.js
|
|
1237
1446
|
function processorGate(recognizer, url, body) {
|
|
1238
1447
|
if (recognizer && recognizer.psp === LYRA_PSP)
|
|
1239
1448
|
return lyraBodyVerdict(body, url);
|
|
1449
|
+
if (recognizer && recognizer.psp === WIX_PAYMENTS_PSP) {
|
|
1450
|
+
if (url !== void 0 && !isWixPaymentsTokenizeUrl(url))
|
|
1451
|
+
return { verdict: "refuse", code: "wix_payments_url_refused" };
|
|
1452
|
+
return wixPaymentsBodyVerdict(body);
|
|
1453
|
+
}
|
|
1240
1454
|
if (recognizer && recognizer.psp === PADDLE_VGS_PSP) {
|
|
1241
1455
|
if (url !== void 0 && !isPaddleVgsCardsUrl(url))
|
|
1242
1456
|
return { verdict: "refuse", code: "paddle_vgs_url_refused" };
|
|
@@ -1253,6 +1467,12 @@ function processorGate(recognizer, url, body) {
|
|
|
1253
1467
|
return { verdict: "refuse", code: "paddle_tokenex_pay_unsupported" };
|
|
1254
1468
|
return paddleTokenexBodyVerdict(body, target ? target.line : void 0);
|
|
1255
1469
|
}
|
|
1470
|
+
if (recognizer && recognizer.psp === PAYMONGO_PSP) {
|
|
1471
|
+
const session = url !== void 0 ? paymongoCheckoutSession(url) : void 0;
|
|
1472
|
+
if (url !== void 0 && session === null)
|
|
1473
|
+
return { verdict: "refuse", code: "paymongo_url_refused" };
|
|
1474
|
+
return paymongoBodyVerdict(body, session);
|
|
1475
|
+
}
|
|
1256
1476
|
if (recognizer && recognizer.psp === GRAVY_PSP)
|
|
1257
1477
|
return gravyBodyVerdict(body);
|
|
1258
1478
|
if (recognizer && recognizer.psp === MONERIS_PSP) {
|
|
@@ -1370,7 +1590,7 @@ function splitCardLeg(recognizer, body) {
|
|
|
1370
1590
|
const absorb = list(split.absorb);
|
|
1371
1591
|
const pass = list(split.pass);
|
|
1372
1592
|
const sibling = list(split.siblingKeys);
|
|
1373
|
-
const
|
|
1593
|
+
const own2 = [];
|
|
1374
1594
|
const seen = /* @__PURE__ */ new Set();
|
|
1375
1595
|
for (const chunk of body.split("&")) {
|
|
1376
1596
|
const eq = chunk.indexOf("=");
|
|
@@ -1387,13 +1607,13 @@ function splitCardLeg(recognizer, body) {
|
|
|
1387
1607
|
return null;
|
|
1388
1608
|
seen.add(key);
|
|
1389
1609
|
if (!sibling.includes(key))
|
|
1390
|
-
|
|
1610
|
+
own2.push(key);
|
|
1391
1611
|
}
|
|
1392
|
-
if (
|
|
1612
|
+
if (own2.length === 0)
|
|
1393
1613
|
return null;
|
|
1394
|
-
if (
|
|
1614
|
+
if (own2.every((key) => absorb.includes(key)))
|
|
1395
1615
|
return "absorb";
|
|
1396
|
-
if (
|
|
1616
|
+
if (own2.every((key) => pass.includes(key)))
|
|
1397
1617
|
return "pass";
|
|
1398
1618
|
return null;
|
|
1399
1619
|
}
|
package/dist/cdp.js
CHANGED
|
@@ -19,6 +19,7 @@ import { isArmedEkashuUrl } from './ekashu.js';
|
|
|
19
19
|
import { isArmedRedsysUrl } from './redsys.js';
|
|
20
20
|
import { PAYPAL_HOSTED_FIELDS_PATTERNS, isPaypalHostedFieldsConfirm } from './paypal-hosted-fields.js';
|
|
21
21
|
import { EBANX_RESEND_PATTERNS, EbanxFollowUps, isEbanxResendUrl } from './ebanx.js';
|
|
22
|
+
import { PAYPAL_CARD_FORM_PATTERNS, isPaypalCardFormUrl } from './paypal-card-forms.js';
|
|
22
23
|
import { CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS, CYBERSOURCE_SECURE_ACCEPTANCE_RECIPIENT, SecureAcceptanceFollowUpWatch, isCybersourceSecureAcceptanceUrl, secureAcceptanceFollowUp, secureAcceptanceFrameAnswer, secureAcceptanceReplyLast4, } from './cybersource-secure-acceptance.js';
|
|
23
24
|
import { CheckoutAttachmentError, attachmentDeadline, attachmentFailure, withinAttachmentDeadline } from './attachment.js';
|
|
24
25
|
import { cseBody } from './cse-body.js';
|
|
@@ -1382,11 +1383,12 @@ function merchantAttempt(lifecycle, survives) {
|
|
|
1382
1383
|
let rebound = false;
|
|
1383
1384
|
let delivering = false;
|
|
1384
1385
|
let bound = null;
|
|
1385
|
-
// A send window (an Amadeus SDK add's, a Secure Acceptance token create's
|
|
1386
|
-
// confirms the card never left the device and never can (amadeus.ts): a
|
|
1387
|
-
// prepared checkout ends failed with that reason, and a fresh attachment can
|
|
1388
|
-
// other stop leaves the outcome unknown.
|
|
1386
|
+
// A send window (an Amadeus SDK add's, a Secure Acceptance token create's, a Wix tokenize request's)
|
|
1387
|
+
// closes only once the API confirms the card never left the device and never can (amadeus.ts): a
|
|
1388
|
+
// known non-payment, so the prepared checkout ends failed with that reason, and a fresh attachment can
|
|
1389
|
+
// prepare again. Every other stop leaves the outcome unknown.
|
|
1389
1390
|
const error = () => reason === 'amadeus_send_window_closed' || reason === 'secure_acceptance_send_window_closed'
|
|
1391
|
+
|| reason === 'wix_payments_send_window_closed'
|
|
1390
1392
|
? new CheckoutPreparationError(lifecycle.getState().preparationId ?? null, reason)
|
|
1391
1393
|
: new PaymentOutcomeUnknownError(lifecycle.getState().authorizationId, reason);
|
|
1392
1394
|
const attempt = {
|
|
@@ -1616,8 +1618,8 @@ function pauseForBusiness(opts, lifecycle, attemptSignal, request, pageOrigin) {
|
|
|
1616
1618
|
}
|
|
1617
1619
|
/**
|
|
1618
1620
|
* Arm the send window of a request paused under a preparation, when its processor sets one: a
|
|
1619
|
-
* prepared Amadeus SDK add
|
|
1620
|
-
* preparedSendWindowMs). Returns the disarm function, or null with no window. lifecycle.begin()
|
|
1621
|
+
* prepared Amadeus SDK add, a prepared Secure Acceptance token create and a prepared Wix tokenize
|
|
1622
|
+
* request do (amadeus.ts preparedSendWindowMs). Returns the disarm function, or null with no window. lifecycle.begin()
|
|
1621
1623
|
* cleared the authorization ID, so the one the window reads is this attempt's.
|
|
1622
1624
|
*/
|
|
1623
1625
|
function armWindowFor(opts, lifecycle, attempt, preparation, url, delivering) {
|
|
@@ -2060,6 +2062,10 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2060
2062
|
// PayPal's legacy Hosted Fields confirm is armed for every org, whatever the
|
|
2061
2063
|
// registry serves: one that carries the typed card is paused only to be
|
|
2062
2064
|
// refused, and one with no card continues (see paypal-hosted-fields.ts).
|
|
2065
|
+
// PayPal's Card Fields confirm and GraphQL on www.paypal.com are armed the
|
|
2066
|
+
// same way: a card the guard refuses is paused only to be refused, a
|
|
2067
|
+
// confirm the registry recognizes pauses for its recognizer, and a request
|
|
2068
|
+
// with no card continues (see paypal-card-forms.ts).
|
|
2063
2069
|
// CyberSource Secure Acceptance's /silent/ paths are armed the same way, form
|
|
2064
2070
|
// navigations included (see cybersource-secure-acceptance.ts), and so are
|
|
2065
2071
|
// EBANX's 3-D Secure calls and hosted confirm, which send the typed card again
|
|
@@ -2068,8 +2074,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2068
2074
|
const urlPatterns = [...new Set([...(derived.length > 0 ? derived : FALLBACK_CARD_PATTERNS), ...guards.patterns,
|
|
2069
2075
|
...(stripeCheckout.isEnabled() ? ['https://api.stripe.com/*'] : []), MERCADO_CHECKOUT_PATTERN, ...merchantPatterns,
|
|
2070
2076
|
...(recognizesGmo(opts.vault) ? GMO_TOKEN_V1_PATTERNS : []), ...juspayCheckout.urlPatterns(), ...cardEndpointPatterns(),
|
|
2071
|
-
...(payrails ? PAYRAILS_GUARD_PATTERNS : []), ...PAYPAL_HOSTED_FIELDS_PATTERNS, ...
|
|
2072
|
-
...(recognizesPaddleVgs(opts.vault) ? PADDLE_VGS_PAY_PATTERNS : []), ...EBANX_RESEND_PATTERNS])];
|
|
2077
|
+
...(payrails ? PAYRAILS_GUARD_PATTERNS : []), ...PAYPAL_HOSTED_FIELDS_PATTERNS, ...PAYPAL_CARD_FORM_PATTERNS,
|
|
2078
|
+
...CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS, ...(recognizesPaddleVgs(opts.vault) ? PADDLE_VGS_PAY_PATTERNS : []), ...EBANX_RESEND_PATTERNS])];
|
|
2073
2079
|
const arm = async (sessionId, resume = false, worker = false) => {
|
|
2074
2080
|
const key = sessionId ?? '__root__';
|
|
2075
2081
|
if (armed.has(key))
|
|
@@ -3450,6 +3456,8 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3450
3456
|
|| isArmedRedsysUrl(opts.vault, url.toString())
|
|
3451
3457
|
// PayPal's legacy Hosted Fields confirm, for every org, as attachToCdp arms it (paypal-hosted-fields.ts).
|
|
3452
3458
|
|| isPaypalHostedFieldsConfirm(url.toString())
|
|
3459
|
+
// PayPal's Card Fields confirm and GraphQL on www.paypal.com, for every org, as attachToCdp arms them (paypal-card-forms.ts).
|
|
3460
|
+
|| isPaypalCardFormUrl(url.toString())
|
|
3453
3461
|
// CyberSource Secure Acceptance's /silent/ paths, for every org, as attachToCdp arms them (cybersource-secure-acceptance.ts).
|
|
3454
3462
|
|| isCybersourceSecureAcceptanceUrl(url.toString())
|
|
3455
3463
|
// Paddle's checkout services, routed only for an org the API served Paddle VGS to (see attachToCdp).
|
package/dist/client.d.ts
CHANGED
|
@@ -241,6 +241,14 @@ export declare const SUPPORTED_REGISTRY_FEATURES: readonly string[];
|
|
|
241
241
|
* card tab then runs in the agent's browser. The cardholder's device sends the card through the
|
|
242
242
|
* checkout relay. Served on the same terms: only to a build that lists it, for a company
|
|
243
243
|
* Agentcard turned Payfast on for.
|
|
244
|
+
*
|
|
245
|
+
* `wix_payments_tokenize`: the card request Wix Cashier's card frame sends to Wix's
|
|
246
|
+
* tokenizer (cashier-pci.wixapps.net), only through a prepared checkout: Wix's payment
|
|
247
|
+
* widget fails the payment 30 seconds after it asks the frame for a token. The
|
|
248
|
+
* cardholder's device sends the card through the checkout relay and the frame gets back
|
|
249
|
+
* Wix's token. A second card request from the frame, which Wix's frame sends after a 401
|
|
250
|
+
* or 403 under a fresh instance, is never answered from the same approval. Served on the
|
|
251
|
+
* same terms: only to a build that lists it, for a company Agentcard turned Wix on for.
|
|
244
252
|
*/
|
|
245
253
|
export declare const SUPPORTED_CAPABILITIES: readonly string[];
|
|
246
254
|
/**
|
|
@@ -1164,6 +1172,9 @@ export declare class CheckoutAuthError extends Error {
|
|
|
1164
1172
|
* On Redsys's hosts the session id after `;jsessionid=` reads `{session}`:
|
|
1165
1173
|
* https://sis.redsys.es/sis/pagoTarjeta;jsessionid={session}.
|
|
1166
1174
|
* On Lyra's API hosts the form token's session id reads `{session}`.
|
|
1175
|
+
* On PayPal's hosts an order id reads `{order}`:
|
|
1176
|
+
* https://www.paypal.com/v2/checkout/orders/{order}/confirm-payment-source.
|
|
1177
|
+
* On PayMongo's checkout host the session id reads `{session}`: https://checkout.paymongo.com/{session}.
|
|
1167
1178
|
* A string that is not a URL keeps the part before any `?` or `#`, with the
|
|
1168
1179
|
* same segments masked.
|
|
1169
1180
|
*/
|