@agent-cards/sdk 0.23.12 → 0.23.13

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
@@ -4,6 +4,8 @@
4
4
 
5
5
  - 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.
6
6
 
7
+ - 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.
8
+
7
9
  - Keep a Gravy checkout session out of your events and error messages. Gravy's Secure Fields send the card to `/checkout/sessions/<session id>/fields` on the merchant's Gravy host with no auth header, so the session id is the only credential that request needs: anyone who holds it can write card fields into the session until it expires. Earlier releases named the full URL in the `card_request_paused` event and in the error `authorize()` throws for a request in the wrong method, so the session id reached your `onEvent` handler and every log that recorded the event or the error. `redactUrl`, every event and every error message now show it as `{session}`, as in `https://api.<merchant>.gr4vy.app/checkout/sessions/{session}/fields`. No API or Vault release is needed.
8
10
 
9
11
  - Pay FlixBus's checkout on Payrails web SDK 6.7.0. FlixBus's page now loads web SDK 6.7.0 in place of 5.50.0, and 6.7.0's Pay now button sends the card-form payment without the `enrollInstrumentToNetworkOffers` member that 5.50.0 sent as `false`. 0.23.11 required that member and aborted every such payment as `unsupported_payrails_card_payment` before creating anything, so no FlixBus payment reached an approval. A payment that names no `enrollInstrumentToNetworkOffers` now pauses as 5.50.0's does: Payrails reads a missing flag as `false`, and 6.7.0 sends no other member in its place. A payment that sets the flag `true` or keeps the card (`storeInstrument: true`, or `meta.order.processingType` `CardOnFile` or `UnscheduledCardOnFile`) still aborts as `payrails_card_on_file_consent_required`, and the flag in another case, in another place or as anything but a boolean aborts as `unsupported_payrails_card_payment`. A payment also needs the matching API and Vault releases.
@@ -96,7 +98,7 @@
96
98
 
97
99
  - Read the card number on the D-EDGE card frame the way the frame groups each card network: 4-6-5 for an American Express number, 4-6-4 for a Diners Club number starting 36 or 300 to 305, and groups of four for any other. The SDK's copy of D-EDGE's rules read a grouped number only in groups of four, so an American Express or Diners Club placeholder as the frame groups it was not D-EDGE's reviewed card request, and the same number in groups of four, which the frame never posts, was. D-EDGE stays in `observe`, so its card post still gets the page that says Agentcard has not turned it on.
98
100
 
99
- - Pay a Paddle checkout that sends the card to Paddle's card vault, from Agentcard's own account only for now (dark launch): Agentcard turns it on for another company once it has confirmed that a card stored for one Paddle seller cannot pay another. On a checkout whose card method names VGS, Paddle's checkout frame on `buy.paddle.com` posts the card to `https://vault.paddle.com/cards` (`https://sandbox-vault.paddle.com/cards` in test) and sends that request once more when it gets no answer within 10 seconds, too soon for an approval after Pay. So the card is paid only through `prepare({ psp: 'paddle_vgs', environment: 'production' })` (`'sandbox'` for Paddle's sandbox) on an attachment whose `cardId` names the card whose `placeholderCard()` facts you typed into Paddle's form; without one, `prepare()` fails with `card_required`, and the approval is locked to that card. The sync now also asks for `capabilities=paddle_vgs_cards`. Once the cardholder approves, the frame's request pauses, the cardholder's device sends the card to Paddle's vault itself, and the frame receives the stored card's id and nothing else; the frame's second request after its 10-second timeout, the same body under the same bearer, gets the same answer, and any other request is refused. Paddle's request names no amount, so the cardholder approves the `amount` and `currency` you pass. The lane pays Visa, Mastercard and American Express cards: `prepare()` fails with `paddle_vgs_card_unsupported` for a card on another network, and the create fails with `paddle_vgs_card_mismatch` when the expiry or placeholder you typed is not the card's. On Paddle's live vault, Agentcard pays only a seller it has reviewed, at the checkout page it reviewed (Rectangle Pro at `https://rectangleapp.com` today), and the approval names that seller and says it can keep the card on file, because the card stays stored in Paddle's vault. Paddle's own `/pay` call, which follows the stored card's id, still carries the placeholder's first six and last four digits that you typed; whether Paddle accepts them beside the real card is not proven yet. A Paddle checkout that sends the card through TokenEx or Spreedly is not paid. No live payment has gone through this route yet. A payment also needs the matching API and Vault releases.
101
+ - Pay a Paddle checkout that sends the card to Paddle's card vault, from Agentcard's own account only for now (dark launch): Agentcard turns it on for another company once it has confirmed that a card stored for one Paddle seller cannot pay another. On a checkout whose card method names VGS, Paddle's checkout frame on `buy.paddle.com` posts the card to `https://vault.paddle.com/cards` (`https://sandbox-vault.paddle.com/cards` in test) and sends that request once more when it gets no answer within 10 seconds, too soon for an approval after Pay. So the card is paid only through `prepare({ psp: 'paddle_vgs', environment: 'production' })` (`'sandbox'` for Paddle's sandbox) on an attachment whose `cardId` names the card whose `placeholderCard()` facts you typed into Paddle's form; without one, `prepare()` fails with `card_required`, and the approval is locked to that card. The sync now also asks for `capabilities=paddle_vgs_cards`. Once the cardholder approves, the frame's request pauses, the cardholder's device sends the card to Paddle's vault itself, and the frame receives the stored card's id and nothing else; the frame's second request after its 10-second timeout, the same body under the same bearer, gets the same answer, and any other request is refused. Paddle's request names no amount, so the cardholder approves the `amount` and `currency` you pass. The lane pays Visa, Mastercard and American Express cards: `prepare()` fails with `paddle_vgs_card_unsupported` for a card on another network, and the create fails with `paddle_vgs_card_mismatch` when the expiry or placeholder you typed is not the card's. On Paddle's live vault, Agentcard pays only a seller it has reviewed, at the checkout page it reviewed (Rectangle Pro at `https://rectangleapp.com` today), and the approval names that seller and says it can keep the card on file, because the card stays stored in Paddle's vault. Paddle's own `/pay` call, which follows the stored card's id, names the card it pays by its first six and last four digits, expiry and brand, and Paddle's form reads those from the placeholder you typed. The SDK writes the approved card's own over them, the details the cardholder's device read from the card, and changes nothing else in that call. A `/pay` that names another stored card, comes from another frame, comes more than a minute after the id reached the frame, or follows the first one is aborted, and Paddle charges nothing. A Paddle checkout that sends the card through TokenEx or Spreedly is not paid. No live payment has gone through this route yet. A payment also needs the matching API and Vault releases.
100
102
 
101
103
  ## 0.23.0
102
104
 
package/LICENSE ADDED
@@ -0,0 +1,38 @@
1
+ Copyright (c) 2026 Agentcard Corporation. All rights reserved.
2
+
3
+ This software, including its source, compiled and minified code, models,
4
+ word lists, recognizers and data files (the "Software"), is proprietary to
5
+ Agentcard Corporation and is licensed, not sold.
6
+
7
+ You may install and run the Software only to use Agentcard's services, under
8
+ the terms you accepted when you signed up for them. If you integrate
9
+ Agentcard into an application, you may include the Software, unmodified and
10
+ as Agentcard publishes it, in that application and distribute the application
11
+ to your end users so they can use Agentcard's services through it; this
12
+ license binds every copy you distribute that way. Otherwise you may not, and
13
+ may not help or allow anyone else to:
14
+
15
+ 1. copy, modify, translate or create derivative works of the Software, except
16
+ for copies made automatically when installing and running it or included
17
+ in an application as the paragraph above allows;
18
+ 2. reverse engineer, decompile, disassemble, deobfuscate or otherwise try to
19
+ derive the source, models, thresholds, word lists, recognizers or data of
20
+ the Software, except to the extent applicable law expressly permits it
21
+ despite this restriction;
22
+ 3. extract, scrape or reuse the Software's data, including its merchant,
23
+ processor and passphrase data, in any other product or service;
24
+ 4. use the Software, or anything learned from studying it, to build, train,
25
+ tune or benchmark a product or service that competes with Agentcard, or to
26
+ defeat or imitate its security checks;
27
+ 5. redistribute, sublicense, sell, rent or publish the Software apart from an
28
+ application as the paragraph above allows, or remove this notice from it.
29
+
30
+ Agentcard keeps dated fingerprints of the Software's data. A match in
31
+ another product or data set is evidence that it was copied from the
32
+ Software.
33
+
34
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ IMPLIED. IN NO EVENT SHALL AGENTCARD CORPORATION BE LIABLE FOR ANY CLAIM,
36
+ DAMAGES OR OTHER LIABILITY ARISING FROM THE SOFTWARE OR ITS USE.
37
+
38
+ Questions: legal@agentcard.sh
package/README.md CHANGED
@@ -278,7 +278,7 @@ Coverage is specific to the processor request format, merchant setup, browser tr
278
278
  | 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) |
279
279
  | 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) |
280
280
  | 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" |
281
- | 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` still carries the placeholder's first six and last four digits beside the stored card's id, and whether Paddle accepts them is not proven yet. A Paddle checkout that sends the card through TokenEx or Spreedly is not supported, and no live payment has gone through this route yet |
281
+ | 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 |
282
282
 
283
283
  The recognizer list, and the status of each reviewed Adyen merchant profile on
284
284
  your client, are fetched from the API at runtime (`agentcard.checkout.syncRegistry()`),
@@ -1 +1 @@
1
- export const BUILTIN_REGISTRY = [{ "psp": "spreedly", "match": /core\.spreedly\.com\/v1\/payment_methods\/restricted\.json/i, "hosts": ["^core\\.spreedly\\.com$"], "encoding": "json", "passthroughHeaders": [/^spreedly-environment-key$/i] }, { "psp": "shopify", "match": /(checkout\.pci\.shopifyinc\.com|deposit\.[a-z0-9-]+\.shopifycs\.com)\/sessions/i, "hosts": ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"], "encoding": "json", "passthroughHeaders": [/^shopify-identification-signature$/i] }, { "psp": "stripe", "match": /api\.stripe\.com\/v1\/(payment_methods|tokens|sources|confirmation_tokens|setup_intents\/seti_\w+\/confirm|payment_intents\/pi_\w+\/confirm|payment_pages\/cs_(?:test|live)_\w+\/confirm)/i, "hosts": ["^api\\.stripe\\.com$"], "encoding": "form", "passthroughHeaders": [/^authorization$/i, /^stripe-version$/i, /^x-stripe-client-user-agent$/i], "cardFields": ["card[number]", "payment_method_data[card][number]"], "passWithoutCard": "api\\.stripe\\.com/v1/(sources|confirmation_tokens|payment_pages/cs_(?:test|live)_\\w+/confirm)" }, { "psp": "braintree", "match": /payments(\.sandbox)?\.braintree-api\.com\/graphql/i, "hosts": ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"], "sandboxHosts": ["^payments\\.sandbox\\.braintree-api\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^braintree-version$/i] }, { "psp": "checkout_com", "match": /(api|card-acquisition-gateway)(\.sandbox)?\.checkout\.com\/tokens/i, "hosts": ["^api\\.checkout\\.com$", "^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "sandboxHosts": ["^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "adyen", "match": /(checkoutshopper-(test|live(-[a-z]+)?)\.adyen\.com|([a-z0-9-]+\.)*adyenpayments\.com)\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]+\/payments/i, "hosts": ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"], "sandboxHosts": ["^checkoutshopper-test\\.adyen\\.com$"], "encoding": "json", "passthroughHeaders": [], "clientSideEncrypted": true, "mode": "cse" }, { "psp": "tranzila", "match": /(direct\.tranzila\.com\/process\/n\/|directng\.tranzila\.com\/process\/?)/i, "hosts": ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"], "encoding": "form", "passthroughHeaders": [], "mode": "hosted_form" }, { "psp": "square", "match": /pci-connect\.squareup(?:sandbox)?\.com\/v2\/card-nonce/i, "hosts": ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"], "sandboxHosts": ["^pci-connect\\.squareupsandbox\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "authorize_net", "match": /(api2|apitest)\.authorize\.net\/xml\/v1\/request\.api/i, "hosts": ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"], "sandboxHosts": ["^apitest\\.authorize\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "worldpay", "match": /(try\.)?access\.worldpay\.com\/(sessions\/card|verifiedTokens\/sessions)/i, "hosts": ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"], "sandboxHosts": ["^try\\.access\\.worldpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^accept$/i] }, { "psp": "nuvei", "match": /(secure|ppp-test)\.safecharge\.com\/ppp\/api\/v1\/(?:cardTokenization|clientPayment|websdk\/initPaymentWithCardTokenization)\.do/i, "hosts": ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"], "sandboxHosts": ["^ppp-test\\.safecharge\\.com$"], "encoding": "json", "passthroughHeaders": [], "jsonCardFields": [["paymentOption", "card", "cardNumber"], ["cardData", "cardNumber"]], "passWithoutCard": "(?:secure|ppp-test)\\.safecharge\\.com/ppp/api/v1/(?:clientPayment|websdk/initPaymentWithCardTokenization)\\.do" }, { "psp": "airwallex", "match": /(checkout(\.sandbox)?\.airwallex\.com|pci-api(\.sandbox)?\.airwallex\.com)\/api\/v1\/pa\/(payment_intents\/[^/?#]+\/confirm|payment_consents\/[^/?#]+\/verify|payment_methods\/create)(\?|$)/i, "hosts": ["^checkout\\.airwallex\\.com$", "^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "sandboxHosts": ["^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "encoding": "json", "passthroughHeaders": [/^client-secret$/i, /^authorization$/i, /^x-auth-token$/i, /^x-on-behalf-of$/i, /^x-api-version$/i] }, { "psp": "rapyd", "match": /(api|sandboxapi)\.rapyd\.net\/v1\/(?:hosted\/collect\/card\/[^/?#]+\/payment_method|checkout\/checkout_[A-Za-z0-9]+)(?:[?#]|$)/i, "hosts": ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"], "sandboxHosts": ["^sandboxapi\\.rapyd\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "dlocal", "match": /ppmcc(-sandbox)?\.dlocal\.com\/cvault\/credit-card\/temporal/i, "hosts": ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"], "sandboxHosts": ["^ppmcc-sandbox\\.dlocal\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-fields-api-key$/i, /^x-uow$/i, /^x-dlocal-infrav2$/i] }, { "psp": "ebanx", "match": /(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\.ebanx\.com|(?:api-diamond|api|sandbox)\.ebanxpay\.com)\/ws\/token/i, "hosts": ["^customer\\.ebanx\\.com$", "^customer-frontier\\.ebanx\\.com$", "^api\\.ebanx\\.com$", "^sandbox\\.ebanx\\.com$", "^api-local-latam\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^api-diamond\\.ebanxpay\\.com$", "^api\\.ebanxpay\\.com$", "^sandbox\\.ebanxpay\\.com$"], "sandboxHosts": ["^sandbox\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^sandbox\\.ebanxpay\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "mercado_pago", "match": /api\.mercadopago\.com\/v1\/card_tokens(?![\w/-])/i, "hosts": ["^api\\.mercadopago\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-product-id$/i] }, { "psp": "payu", "match": /(secure\.payu\.com|(merch-prod|secure)\.snd\.payu\.com)\/api\/front\/tokens/i, "hosts": ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"], "sandboxHosts": ["^(merch-prod|secure)\\.snd\\.payu\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "razorpay", "match": /api\.razorpay\.com\/v1\/(?:standard_checkout\/)?payments\/create\/(?:ajax|checkout|fees)/i, "hosts": ["^api\\.razorpay\\.com$"], "encoding": "form", "passthroughHeaders": [/^x-razorpay-sessionid$/i, /^x-customer-access-token$/i] }, { "psp": "mollie", "match": /api\.cc\.mollie\.com\/v1\/card-tokens/i, "hosts": ["^api\\.cc\\.mollie\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "paysafe", "match": /(?:api(\.test)?\.paysafe\.com\/(?:paymenthub\/v1\/singleusepaymenthandles|js\/api\/v1\/tokenize)|hosted(\.test)?\.paysafe\.com\/checkout\/api\/v1\/tokenize)(?![\w/-])/i, "hosts": ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "sandboxHosts": ["^api\\.test\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-paysafe-credentials$/i, /^correlationid$/i] }, { "psp": "recurly", "match": /api(\.eu)?\.recurly\.com\/js\/v1\/token(?![\w-])/i, "hosts": ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"], "encoding": "form", "passthroughHeaders": [/^recurly-credential-checkout-hostname$/i] }, { "psp": "bambora", "match": /(api\.na\.bambora\.com|api\.bam\.shift4api\.net)\/scripts\/tokenization\/tokens/i, "hosts": ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "global_payments", "match": /(?:api\.heartlandportico\.com\/SecureSubmit\.v1\/api\/token|cert\.api2\.heartlandportico\.com\/Hps\.Exchange\.PosGateway\.Hpf\.v1\/api\/token|apis(?:\.sandbox)?(?:\.eu)?\.globalpay\.com\/ucp\/(?:merchants\/[^/]+\/)?payment-methods)/i, "hosts": ["^api\\.heartlandportico\\.com$", "^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.globalpay\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.eu\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "sandboxHosts": ["^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-gp-version$/i] }];
1
+ export const BUILTIN_REGISTRY = [{ "psp": "spreedly", "match": /core\.spreedly\.com\/v1\/payment_methods\/restricted\.json/i, "hosts": ["^core\\.spreedly\\.com$"], "encoding": "json", "passthroughHeaders": [/^spreedly-environment-key$/i] }, { "psp": "shopify", "match": /(checkout\.pci\.shopifyinc\.com|deposit\.[a-z0-9-]+\.shopifycs\.com)\/sessions/i, "hosts": ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"], "encoding": "json", "passthroughHeaders": [/^shopify-identification-signature$/i] }, { "psp": "stripe", "match": /api\.stripe\.com\/v1\/(payment_methods|tokens|sources|confirmation_tokens|setup_intents\/seti_\w+\/confirm|payment_intents\/pi_\w+\/confirm|payment_pages\/cs_(?:test|live)_\w+\/confirm)/i, "hosts": ["^api\\.stripe\\.com$"], "encoding": "form", "passthroughHeaders": [/^authorization$/i, /^stripe-version$/i, /^x-stripe-client-user-agent$/i], "cardFields": ["card[number]", "payment_method_data[card][number]"], "passWithoutCard": "api\\.stripe\\.com/v1/(sources|confirmation_tokens|payment_pages/cs_(?:test|live)_\\w+/confirm)" }, { "psp": "braintree", "match": /payments(\.sandbox)?\.braintree-api\.com\/graphql/i, "hosts": ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"], "sandboxHosts": ["^payments\\.sandbox\\.braintree-api\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^braintree-version$/i] }, { "psp": "checkout_com", "match": /(api|card-acquisition-gateway)(\.sandbox)?\.checkout\.com\/tokens/i, "hosts": ["^api\\.checkout\\.com$", "^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "sandboxHosts": ["^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "adyen", "match": /(checkoutshopper-(test|live(-[a-z]+)?)\.adyen\.com|([a-z0-9-]+\.)*adyenpayments\.com)\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]+\/payments/i, "hosts": ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"], "sandboxHosts": ["^checkoutshopper-test\\.adyen\\.com$"], "encoding": "json", "passthroughHeaders": [], "clientSideEncrypted": true, "mode": "cse" }, { "psp": "tranzila", "match": /(direct\.tranzila\.com\/process\/n\/|directng\.tranzila\.com\/process\/?)/i, "hosts": ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"], "encoding": "form", "passthroughHeaders": [], "mode": "hosted_form" }, { "psp": "square", "match": /pci-connect\.squareup(?:sandbox)?\.com\/v2\/card-nonce/i, "hosts": ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"], "sandboxHosts": ["^pci-connect\\.squareupsandbox\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "authorize_net", "match": /(api2|apitest)\.authorize\.net\/xml\/v1\/request\.api/i, "hosts": ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"], "sandboxHosts": ["^apitest\\.authorize\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "worldpay", "match": /(try\.)?access\.worldpay\.com\/(sessions\/card|verifiedTokens\/sessions)/i, "hosts": ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"], "sandboxHosts": ["^try\\.access\\.worldpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^accept$/i] }, { "psp": "nuvei", "match": /(secure|ppp-test)\.safecharge\.com\/ppp\/api\/v1\/(?:cardTokenization|clientPayment|websdk\/initPaymentWithCardTokenization)\.do/i, "hosts": ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"], "sandboxHosts": ["^ppp-test\\.safecharge\\.com$"], "encoding": "json", "passthroughHeaders": [], "jsonCardFields": [["paymentOption", "card", "cardNumber"], ["cardData", "cardNumber"]], "passWithoutCard": "(?:secure|ppp-test)\\.safecharge\\.com/ppp/api/v1/(?:clientPayment|websdk/initPaymentWithCardTokenization)\\.do" }, { "psp": "airwallex", "match": /(checkout(\.sandbox)?\.airwallex\.com|pci-api(\.sandbox)?\.airwallex\.com)\/api\/v1\/pa\/(payment_intents\/[^/?#]+\/confirm|payment_consents\/[^/?#]+\/verify|payment_methods\/create)(\?|$)/i, "hosts": ["^checkout\\.airwallex\\.com$", "^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "sandboxHosts": ["^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "encoding": "json", "passthroughHeaders": [/^client-secret$/i, /^authorization$/i, /^x-auth-token$/i, /^x-on-behalf-of$/i, /^x-api-version$/i] }, { "psp": "rapyd", "match": /(api|sandboxapi)\.rapyd\.net\/v1\/(?:hosted\/collect\/card\/[^/?#]+\/payment_method|checkout\/checkout_[A-Za-z0-9]+)(?:[?#]|$)/i, "hosts": ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"], "sandboxHosts": ["^sandboxapi\\.rapyd\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "dlocal", "match": /ppmcc(-sandbox)?\.dlocal\.com\/cvault\/credit-card\/temporal/i, "hosts": ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"], "sandboxHosts": ["^ppmcc-sandbox\\.dlocal\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-fields-api-key$/i, /^x-uow$/i, /^x-dlocal-infrav2$/i] }, { "psp": "ebanx", "match": /(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\.ebanx\.com|(?:api-diamond|api|sandbox)\.ebanxpay\.com)\/ws\/token/i, "hosts": ["^customer\\.ebanx\\.com$", "^customer-frontier\\.ebanx\\.com$", "^api\\.ebanx\\.com$", "^sandbox\\.ebanx\\.com$", "^api-local-latam\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^api-diamond\\.ebanxpay\\.com$", "^api\\.ebanxpay\\.com$", "^sandbox\\.ebanxpay\\.com$"], "sandboxHosts": ["^sandbox\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^sandbox\\.ebanxpay\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "mercado_pago", "match": /api\.mercadopago\.com\/v1\/card_tokens(?![\w/-])/i, "hosts": ["^api\\.mercadopago\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-product-id$/i] }, { "psp": "payu", "match": /(secure\.payu\.com|(merch-prod|secure)\.snd\.payu\.com)\/api\/front\/tokens/i, "hosts": ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"], "sandboxHosts": ["^(merch-prod|secure)\\.snd\\.payu\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "razorpay", "match": /api\.razorpay\.com\/v1\/(?:standard_checkout\/)?payments\/create\/(?:ajax|checkout|fees)/i, "hosts": ["^api\\.razorpay\\.com$"], "encoding": "form", "passthroughHeaders": [/^x-razorpay-sessionid$/i, /^x-customer-access-token$/i] }, { "psp": "mollie", "match": /api\.cc\.mollie\.com\/v1\/card-tokens/i, "hosts": ["^api\\.cc\\.mollie\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "paysafe", "match": /(?:api(\.test)?\.paysafe\.com\/(?:paymenthub\/v1\/singleusepaymenthandles|js\/api\/v1\/tokenize|checkout\/api\/v1\/tokenize)|hosted(\.test)?\.paysafe\.com\/checkout\/api\/v1\/tokenize)(?![\w/-])/i, "hosts": ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "sandboxHosts": ["^api\\.test\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-paysafe-credentials$/i, /^correlationid$/i, /^x-internal-correlation-id$/i, /^x-app-version$/i, /^x-transactionsource$/i] }, { "psp": "recurly", "match": /api(\.eu)?\.recurly\.com\/js\/v1\/token(?![\w-])/i, "hosts": ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"], "encoding": "form", "passthroughHeaders": [/^recurly-credential-checkout-hostname$/i] }, { "psp": "bambora", "match": /(api\.na\.bambora\.com|api\.bam\.shift4api\.net)\/scripts\/tokenization\/tokens/i, "hosts": ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "global_payments", "match": /(?:api\.heartlandportico\.com\/SecureSubmit\.v1\/api\/token|cert\.api2\.heartlandportico\.com\/Hps\.Exchange\.PosGateway\.Hpf\.v1\/api\/token|apis(?:\.sandbox)?(?:\.eu)?\.globalpay\.com\/ucp\/(?:merchants\/[^/]+\/)?payment-methods)/i, "hosts": ["^api\\.heartlandportico\\.com$", "^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.globalpay\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.eu\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "sandboxHosts": ["^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-gp-version$/i] }];
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: c8e0308b7790c3577c56ec0428577a42c308ccdbeff3eb041c6103d9e9b338a8
3
+ // artifact-sha256: 5c2d8f5671c901ef8162e61e21996671a0e6e9ca235467ea7f6267274f5c3ee1
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
@@ -406,6 +406,8 @@ var pin2 = (fields) => Object.freeze({
406
406
  var PADDLE_VGS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
407
407
  pin2({ id: "rectangle_pro", merchant: "Rectangle Pro", origins: ["https://rectangleapp.com"], sellerClaim: null })
408
408
  ]);
409
+ var FACT_KEYS = /* @__PURE__ */ Object.freeze(["card_first_six", "card_last_four", "expiry_month", "expiry_year", "card_brand"]);
410
+ var METADATA_KEYS = /* @__PURE__ */ Object.freeze(["cardholder_name", ...FACT_KEYS]);
409
411
  // src/card-fields.js
410
412
  function processorGate(recognizer, url, body) {
411
413
  if (recognizer && recognizer.psp === PADDLE_VGS_PSP) {
package/dist/cdp.js CHANGED
@@ -11,6 +11,7 @@ import { EprotectGate } from './eprotect.js';
11
11
  import { dispatchPreClaim, eventUrl } from './pre-claim.js';
12
12
  import { GMO_TOKEN_V1_PATTERNS, gmoRequestShop, isGmoTokenV1Request, recognizesGmo } from './gmo.js';
13
13
  import { isPaddleVgsCardsUrl } from './paddle-vgs.generated.js';
14
+ import { PADDLE_VGS_PAY_PATTERNS, PaddleVgsPayGate, isPaddleCheckoutService, recognizesPaddleVgs } from './paddle-vgs-pay.js';
14
15
  import { DatatransRepeats } from './datatrans.js';
15
16
  import { SHIJI_STS_COMPANION_WINDOW_MS, ShijiStsCompanions, ShijiStsError, isShijiStsHost, shijiStsTokenContinuation, } from './shiji-sts.js';
16
17
  import { isArmedEkashuUrl } from './ekashu.js';
@@ -1532,7 +1533,10 @@ function replayOf(result, timeoutMs) {
1532
1533
  || !response.headers || typeof response.headers !== 'object' || Array.isArray(response.headers)) {
1533
1534
  throw new Error('An approved token read resumes with response, the processor\'s answer (voice.awaitResult carries it once the purchase finished).');
1534
1535
  }
1535
- return { mode: 'token', authorizationId, status: response.status, headers: { ...response.headers }, body: response.body, amountVerified: null, ...execution };
1536
+ // The read's processor_context rides as it came: a Paddle VGS answer is handed to the page only with the
1537
+ // approved card's details its /pay names (paddle-vgs-pay.ts checks them before the hand-over).
1538
+ return { mode: 'token', authorizationId, status: response.status, headers: { ...response.headers }, body: response.body, amountVerified: null,
1539
+ ...(result.processor_context !== undefined ? { processorContext: result.processor_context } : {}), ...execution };
1536
1540
  }
1537
1541
  /** The recogniser that claimed a paused request, by processor name, so a caller never re-matches the URL; null on a duck-typed vault without the read. */
1538
1542
  function recognizerNameOf(opts, url, method) {
@@ -2019,6 +2023,10 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
2019
2023
  // committing a new document (Page.frameNavigated, at the top of the event handler).
2020
2024
  const shijiCompanions = new ShijiStsCompanions();
2021
2025
  lifecycle.abort.signal.addEventListener('abort', () => shijiCompanions.release(), { once: true });
2026
+ // Paddle's /pay after the stored card's id this attachment handed a frame (paddle-vgs-pay.ts): its
2027
+ // checkout services are held only when the synced registry serves Paddle VGS, and a cancel drops the binding.
2028
+ const paddleVgsPay = new PaddleVgsPayGate(() => recognizesPaddleVgs(opts.vault));
2029
+ lifecycle.abort.signal.addEventListener('abort', () => paddleVgsPay.release(), { once: true });
2022
2030
  const derived = typeof opts.vault?.cardUrlPatterns === 'function' ? opts.vault.cardUrlPatterns() : [];
2023
2031
  // The endpoint and siblings of every merchant profile this build reviewed
2024
2032
  // (merchant-hosted.ts), which a VaultClient arms from the start: a profile's card
@@ -2046,7 +2054,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
2046
2054
  const urlPatterns = [...new Set([...(derived.length > 0 ? derived : FALLBACK_CARD_PATTERNS), ...guards.patterns,
2047
2055
  ...(stripeCheckout.isEnabled() ? ['https://api.stripe.com/*'] : []), MERCADO_CHECKOUT_PATTERN, ...merchantPatterns,
2048
2056
  ...(recognizesGmo(opts.vault) ? GMO_TOKEN_V1_PATTERNS : []), ...juspayCheckout.urlPatterns(), ...cardEndpointPatterns(),
2049
- ...(payrails ? PAYRAILS_GUARD_PATTERNS : []), ...PAYPAL_HOSTED_FIELDS_PATTERNS, ...CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS])];
2057
+ ...(payrails ? PAYRAILS_GUARD_PATTERNS : []), ...PAYPAL_HOSTED_FIELDS_PATTERNS, ...CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS,
2058
+ ...(recognizesPaddleVgs(opts.vault) ? PADDLE_VGS_PAY_PATTERNS : [])])];
2050
2059
  const arm = async (sessionId, resume = false, worker = false) => {
2051
2060
  const key = sessionId ?? '__root__';
2052
2061
  if (armed.has(key))
@@ -2480,6 +2489,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
2480
2489
  payrails,
2481
2490
  shijiSts: { companions: shijiCompanions, page: () => cdpShijiPage(sessionId, frameId) },
2482
2491
  worldlineMyCheckout: worldlineProducts,
2492
+ paddleVgsPay,
2483
2493
  });
2484
2494
  const preClaim = preClaimResult instanceof Promise ? await preClaimResult : preClaimResult;
2485
2495
  if (preClaim.kind !== 'pause')
@@ -2901,6 +2911,15 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
2901
2911
  // device_encrypt answer (Payrails' 201, which the device obtained) is
2902
2912
  // handed over the same way.
2903
2913
  const cors = corsDecision(deliverRequest.url, deliverRequest.headers);
2914
+ // A Paddle card request's answer is the stored card's id: the frame's /pay that follows continues only
2915
+ // with the approved card's details, bound to that frame before the id reaches it (paddle-vgs-pay.ts).
2916
+ // An answer that came with no details is not handed over: its /pay could only name the placeholder.
2917
+ try {
2918
+ paddleVgsPay.arm(activeRequest?.frameId || undefined, deliverRequest.url, replay);
2919
+ }
2920
+ catch {
2921
+ throw new PaymentOutcomeUnknownError(replay.authorizationId, 'paddle_vgs_card_facts_unavailable');
2922
+ }
2904
2923
  await cdp.send('Fetch.fulfillRequest', {
2905
2924
  requestId: deliverTo,
2906
2925
  responseCode: replay.status,
@@ -3131,6 +3150,9 @@ export async function attachToPlaywright(page, opts) {
3131
3150
  // a new document (the handlers below).
3132
3151
  const shijiCompanions = new ShijiStsCompanions();
3133
3152
  lifecycle.abort.signal.addEventListener('abort', () => shijiCompanions.release(), { once: true });
3153
+ // Paddle's /pay after the stored card's id this attachment handed a frame, as attachToCdp binds it.
3154
+ const paddleVgsPay = new PaddleVgsPayGate(() => recognizesPaddleVgs(opts.vault));
3155
+ lifecycle.abort.signal.addEventListener('abort', () => paddleVgsPay.release(), { once: true });
3134
3156
  // An answer to a request the facts watch records began to arrive: the watch orders what the page sent after a
3135
3157
  // record by this moment, since Playwright reports the answer's end after the page's next request. Listened to only
3136
3158
  // once the page sent such a request, so a page that never meets a merchant with facts carries no extra listener.
@@ -3388,7 +3410,9 @@ export async function attachToPlaywright(page, opts) {
3388
3410
  // PayPal's legacy Hosted Fields confirm, for every org, as attachToCdp arms it (paypal-hosted-fields.ts).
3389
3411
  || isPaypalHostedFieldsConfirm(url.toString())
3390
3412
  // CyberSource Secure Acceptance's /silent/ paths, for every org, as attachToCdp arms them (cybersource-secure-acceptance.ts).
3391
- || isCybersourceSecureAcceptanceUrl(url.toString()), async (route) => {
3413
+ || isCybersourceSecureAcceptanceUrl(url.toString())
3414
+ // Paddle's checkout services, routed only for an org the API served Paddle VGS to (see attachToCdp).
3415
+ || (isPaddleCheckoutService(url.toString()) && recognizesPaddleVgs(opts.vault)), async (route) => {
3392
3416
  const request = route.request();
3393
3417
  // When this request paused, and the page it came from: the merchant's total is read as of now.
3394
3418
  const pausedAt = Date.now();
@@ -3485,6 +3509,7 @@ export async function attachToPlaywright(page, opts) {
3485
3509
  payrails,
3486
3510
  shijiSts: { companions: shijiCompanions, page: async () => (await playwrightShijiDocumentPage(request)) ?? `pw:none:${++playwrightShijiSeq}` },
3487
3511
  worldlineMyCheckout: worldlineProducts,
3512
+ paddleVgsPay,
3488
3513
  });
3489
3514
  const preClaim = preClaimResult instanceof Promise ? await preClaimResult : preClaimResult;
3490
3515
  if (preClaim.kind !== 'pause')
@@ -3847,6 +3872,13 @@ export async function attachToPlaywright(page, opts) {
3847
3872
  // none; written here anyway (replacing a stale value) so a
3848
3873
  // cross-origin answer is the same whichever adapter ran.
3849
3874
  const cors = corsDecision(deliverRequest.url(), deliverRequest.headers());
3875
+ // See the same step in attachToCdp: a Paddle stored card's id reaches its frame only with its /pay bound.
3876
+ try {
3877
+ paddleVgsPay.arm(frameOf(deliverRequest), deliverRequest.url(), replay);
3878
+ }
3879
+ catch {
3880
+ throw new PaymentOutcomeUnknownError(replay.authorizationId, 'paddle_vgs_card_facts_unavailable');
3881
+ }
3850
3882
  await deliverTo.fulfill({ status: replay.status, headers: withCorsHeaders(replay.headers, cors.headers), body: replay.body });
3851
3883
  datatransRepeats.remember(frameOf(deliverRequest), deliverRequest.url(), deliverBody, replay);
3852
3884
  assertRequestLive();
package/dist/client.d.ts CHANGED
@@ -251,7 +251,10 @@ export interface ExecutionMetadata {
251
251
  * this is the processor's answer to replay into the paused request.
252
252
  */
253
253
  export interface TokenReplay extends ExecutionMetadata {
254
- /** Authenticated selected-card metadata; the processor response body stays unchanged. */
254
+ /**
255
+ * Authenticated selected-card metadata; the processor response body stays unchanged. Paddle VGS: the
256
+ * approved card's first six and last four digits, expiry and brand, which the frame's /pay names.
257
+ */
255
258
  processorContext?: unknown;
256
259
  /** Present only after validating the native TEST Checkout completion. */
257
260
  checkoutSessionId?: string;
package/dist/client.js CHANGED
@@ -14,7 +14,7 @@ import { creditguardPageProcessUrl, creditguardPpDomainHost, creditguardPreparat
14
14
  import { CONVERGE_CAPABILITY } from './elavon-converge.js';
15
15
  import { SHIJI_STS_CAPABILITY, SHIJI_STS_PSP, isShijiStsServedCompanion, isShijiStsServedSubstitution } from './shiji-sts.js';
16
16
  import { AMADEUS_CHECKOUT_CAPABILITY, AMADEUS_CHECKOUT_PSP } from './amadeus-checkout.generated.js';
17
- import { PADDLE_VGS_CAPABILITY, PADDLE_VGS_PSP } from './paddle-vgs.generated.js';
17
+ import { PADDLE_VGS_CAPABILITY, PADDLE_VGS_PSP, paddleVgsCardFactsOf } from './paddle-vgs.generated.js';
18
18
  import { classifyStripeCheckoutRequest, encodeStripeCheckoutContext, hasStripeCheckoutMarker, parseStripeCheckoutContext, parseStripeCheckoutResponse, STRIPE_CHECKOUT_CONTEXT_HEADER, } from './stripe-checkout.generated.js';
19
19
  import { VoiceModule } from './voice.js';
20
20
  import { BuyModule } from './buy.js';
@@ -2620,6 +2620,14 @@ export class VaultClient {
2620
2620
  throw new PaymentOutcomeUnknownError(authorizationId, 'mercado_checkout_metadata_invalid');
2621
2621
  }
2622
2622
  }
2623
+ else if (rec?.psp === PADDLE_VGS_PSP) {
2624
+ // Paddle VGS: the approved card's details the phone read from the card it stored at VGS, which the
2625
+ // frame's /pay names in place of the placeholder's (paddle-vgs-pay.ts). Exactly those five, or the
2626
+ // stored card's id never reaches the frame.
2627
+ processorContext = paddleVgsCardFactsOf(s.processor_context) ?? undefined;
2628
+ if (!processorContext)
2629
+ throw new PaymentOutcomeUnknownError(authorizationId, 'paddle_vgs_card_facts_unavailable');
2630
+ }
2623
2631
  else if (s.processor_context !== undefined)
2624
2632
  throw new PaymentOutcomeUnknownError(authorizationId, 'unexpected_processor_context');
2625
2633
  return {
@@ -4,6 +4,7 @@ import type { PayrailsUnsupportedReason } from './payrails.js';
4
4
  import type { Substitutions } from './substitute.js';
5
5
  import type { GmoUnsupportedReason } from './gmo.js';
6
6
  import type { ShijiStsUnsupportedReason } from './shiji-sts.js';
7
+ import type { PaddleVgsPayRefusal } from './paddle-vgs-pay.js';
7
8
  /** The application's resolver confirmed the payment for this checkout authorization. */
8
9
  export interface MerchantPaymentConfirmation {
9
10
  kind: 'merchant_payment';
@@ -91,6 +92,11 @@ export interface ResumeResult {
91
92
  headers: Record<string, string>;
92
93
  body: string;
93
94
  };
95
+ /**
96
+ * The read's `processor_context`, as the API serves it. A Paddle VGS read carries the approved card's
97
+ * details here, which the frame's /pay names; without them the stored card's id is never handed to the page.
98
+ */
99
+ processor_context?: unknown;
94
100
  substitutions?: Substitutions;
95
101
  submitted_at?: string;
96
102
  execution_mode?: string;
@@ -239,9 +245,12 @@ export declare class CheckoutLifecycle implements CheckoutController {
239
245
  * over (unsupported_paypal_hosted_fields, paypal-hosted-fields.ts), or a card
240
246
  * a merchant's page posts to CyberSource Secure Acceptance's Silent Order
241
247
  * POST, which no Vault mode takes over either
242
- * (unsupported_cybersource_secure_acceptance, cybersource-secure-acceptance.ts).
248
+ * (unsupported_cybersource_secure_acceptance, cybersource-secure-acceptance.ts), or a
249
+ * Paddle /pay naming a stored card that its binding does not continue (the
250
+ * paddle-vgs-pay.ts reasons): once an approval exists the state keeps it, and only the
251
+ * hold changes.
243
252
  */
244
- unsupported(reason?: 'unrecognized_payment_endpoint' | 'merchant_profile_observe' | 'merchant_request_unsupported' | GmoUnsupportedReason | 'microform_unsupported' | 'juspay_request_unsupported' | 'merchant_card_endpoint_not_enabled' | 'merchant_card_endpoint_resend_refused' | 'creditguard_three_d_secure' | 'unsupported_primer_sdk' | 'unsupported_primer_request' | PayrailsUnsupportedReason | ShijiStsUnsupportedReason | 'processor_card_page' | 'unsupported_paypal_hosted_fields' | 'unsupported_cybersource_secure_acceptance'): void;
253
+ unsupported(reason?: 'unrecognized_payment_endpoint' | 'merchant_profile_observe' | 'merchant_request_unsupported' | GmoUnsupportedReason | 'microform_unsupported' | 'juspay_request_unsupported' | 'merchant_card_endpoint_not_enabled' | 'merchant_card_endpoint_resend_refused' | 'creditguard_three_d_secure' | 'unsupported_primer_sdk' | 'unsupported_primer_request' | PayrailsUnsupportedReason | ShijiStsUnsupportedReason | 'processor_card_page' | 'unsupported_paypal_hosted_fields' | 'unsupported_cybersource_secure_acceptance' | PaddleVgsPayRefusal): void;
245
254
  /**
246
255
  * A card request that only a prepared checkout pays (an Adyen merchant
247
256
  * profile's, or a Fiserv card capture) arrived with no preparation on this
package/dist/lifecycle.js CHANGED
@@ -238,7 +238,10 @@ export class CheckoutLifecycle {
238
238
  * over (unsupported_paypal_hosted_fields, paypal-hosted-fields.ts), or a card
239
239
  * a merchant's page posts to CyberSource Secure Acceptance's Silent Order
240
240
  * POST, which no Vault mode takes over either
241
- * (unsupported_cybersource_secure_acceptance, cybersource-secure-acceptance.ts).
241
+ * (unsupported_cybersource_secure_acceptance, cybersource-secure-acceptance.ts), or a
242
+ * Paddle /pay naming a stored card that its binding does not continue (the
243
+ * paddle-vgs-pay.ts reasons): once an approval exists the state keeps it, and only the
244
+ * hold changes.
242
245
  */
243
246
  unsupported(reason = 'unrecognized_payment_endpoint') {
244
247
  this.held = true;
@@ -0,0 +1,55 @@
1
+ import type { ReplayResponse } from './client.js';
2
+ /**
3
+ * Why a /pay naming a stored card was aborted, reported as the `reason` of the `unsupported_checkout` event:
4
+ * paddle_vgs_pay_unbound this attachment handed no stored card's id to the frame that sent it
5
+ * paddle_vgs_pay_expired it came more than a minute after the id reached the frame
6
+ * paddle_vgs_pay_used the one /pay for that id already went
7
+ * paddle_vgs_pay_card_mismatch it names another stored card, or the other environment's checkout service
8
+ * paddle_vgs_pay_unsupported anything else payment-core's rule refuses: another shape, a card saved at
9
+ * Paddle for later, a network the frame chose from the typed card
10
+ */
11
+ export type PaddleVgsPayRefusal = 'paddle_vgs_pay_unbound' | 'paddle_vgs_pay_expired' | 'paddle_vgs_pay_used' | 'paddle_vgs_pay_card_mismatch' | 'paddle_vgs_pay_unsupported';
12
+ /** The patterns attachToCdp holds Paddle's two checkout services under, only for an attachment the API served Paddle VGS to. */
13
+ export declare const PADDLE_VGS_PAY_PATTERNS: readonly string[];
14
+ /** Whether `url` is on one of Paddle's two checkout services: what attachToPlaywright routes, once served. */
15
+ export declare function isPaddleCheckoutService(url: string): boolean;
16
+ /** Whether the synced registry serves Paddle VGS's card request: an org Agentcard turned Paddle on for. */
17
+ export declare function recognizesPaddleVgs(vault: {
18
+ isCardRequest?: (url: string, method?: string) => boolean;
19
+ } | null | undefined): boolean;
20
+ /** The URL an event names for a /pay: its origin and path, with the checkout's id as {checkout}. */
21
+ export declare function paddleVgsPayEventUrl(url: string): string;
22
+ /**
23
+ * One attachment's /pay binding: at most one, to the stored card's id it last handed a Paddle frame. `arm` runs
24
+ * right before the id is handed over, `release` when the attachment is cancelled, and `judge` for every request
25
+ * to Paddle's checkout services. `served` says whether the synced registry serves Paddle VGS; it is asked only
26
+ * about a POST to /pay, so no other request reads the registry here.
27
+ */
28
+ export declare class PaddleVgsPayGate {
29
+ private readonly served;
30
+ private readonly now;
31
+ private bound;
32
+ constructor(served: () => boolean, now?: () => number);
33
+ /** Whether the gate judges this request: a POST to /pay, on an attachment the API served Paddle VGS to. */
34
+ holds(url: string, method: string): boolean;
35
+ /**
36
+ * Bind the next /pay of `frame`, the frame about to receive `replay` for its card request to `cardsUrl`, to the
37
+ * stored card's id in the replay and the approved card's facts the API served beside it. Does nothing for a
38
+ * request to anything but Paddle's card vault. Throws when the frame is unknown, or the replay carries no id or
39
+ * no facts payment-core reads: the id must then not reach the frame, since its /pay could only carry the
40
+ * placeholder's facts.
41
+ */
42
+ arm(frame: unknown, cardsUrl: string, replay: ReplayResponse): void;
43
+ /** Drop the binding: no /pay continues with these facts after this. */
44
+ release(): void;
45
+ /**
46
+ * What becomes of a request to Paddle's checkout services: null when it is not a /pay naming a stored card (it
47
+ * continues untouched), the body to continue it with (payment-core paddleVgsPayVerdict), or why it is aborted.
48
+ * The binding answers one /pay: the first one from its frame continues or is refused, and either way ends it.
49
+ */
50
+ judge(frame: unknown, url: string, method: string, body: string | null): {
51
+ body: string;
52
+ } | {
53
+ reason: PaddleVgsPayRefusal;
54
+ } | null;
55
+ }
@@ -0,0 +1,115 @@
1
+ // Paddle's /pay, the request that charges the card the cardholder's phone stored at VGS, as the SDK sees it
2
+ // (payment-core paddle-vgs.js has the request's shape and the rule).
3
+ //
4
+ // Once the SDK hands the stored card's id to Paddle's checkout frame, the frame posts /pay to its checkout
5
+ // service with that id beside card facts it read from the number typed into its own form: on this lane the
6
+ // agent's placeholder, so the placeholder's first six, last four and brand. This build holds that /pay and
7
+ // writes over those five values the approved card's own, which the phone read from the card it sent to VGS and
8
+ // the API served with the id (payment-core paddleVgsPayVerdict). Every other byte goes as the frame wrote it.
9
+ //
10
+ // The binding: the frame that received the stored card's id, the id, the vault's environment and the approved
11
+ // card's facts, for PADDLE_VGS_CARD_ID_WINDOW_MS after the hand-over, for one /pay. A /pay naming a stored card
12
+ // is aborted, so Paddle charges nothing, when this attachment handed no id to that frame
13
+ // (paddle_vgs_pay_unbound), after the window (paddle_vgs_pay_expired), after the one /pay already went
14
+ // (paddle_vgs_pay_used), or when payment-core's rule refuses it: another stored card or environment
15
+ // (paddle_vgs_pay_card_mismatch), or any other shape (paddle_vgs_pay_unsupported). A /pay naming no stored card
16
+ // (PayPal, a wallet) carries none of the cardholder's cards and continues untouched.
17
+ //
18
+ // Nothing is decided without the registry: an attachment the API did not serve Paddle VGS to never holds
19
+ // Paddle's checkout service, so its /pay continues untouched, exactly as before.
20
+ import { PADDLE_VGS_CARD_ID_WINDOW_MS, PADDLE_VGS_CARDS_URLS, paddleVgsCardFactsOf, paddleVgsEnvironment, paddleVgsPayCarriesStoredCard, paddleVgsPayTarget, paddleVgsPayVerdict, } from './paddle-vgs.generated.js';
21
+ const CHECKOUT_SERVICES = ['https://checkout-service.paddle.com', 'https://sandbox-checkout-service.paddle.com'];
22
+ /** The patterns attachToCdp holds Paddle's two checkout services under, only for an attachment the API served Paddle VGS to. */
23
+ export const PADDLE_VGS_PAY_PATTERNS = CHECKOUT_SERVICES.map((origin) => `${origin}/*`);
24
+ /** Whether `url` is on one of Paddle's two checkout services: what attachToPlaywright routes, once served. */
25
+ export function isPaddleCheckoutService(url) {
26
+ try {
27
+ return CHECKOUT_SERVICES.includes(new URL(url).origin);
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ /** Whether the synced registry serves Paddle VGS's card request: an org Agentcard turned Paddle on for. */
34
+ export function recognizesPaddleVgs(vault) {
35
+ if (!vault || typeof vault.isCardRequest !== 'function')
36
+ return false;
37
+ try {
38
+ return vault.isCardRequest(PADDLE_VGS_CARDS_URLS.production, 'POST') || vault.isCardRequest(PADDLE_VGS_CARDS_URLS.sandbox, 'POST');
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ /** The URL an event names for a /pay: its origin and path, with the checkout's id as {checkout}. */
45
+ export function paddleVgsPayEventUrl(url) {
46
+ return url.replace(/^(https:\/\/[^/]+\/(?:checkout|transaction-checkout)\/)[^/]+(\/pay)$/, '$1{checkout}$2');
47
+ }
48
+ const known = (frame) => frame !== undefined && frame !== null && frame !== '';
49
+ /**
50
+ * One attachment's /pay binding: at most one, to the stored card's id it last handed a Paddle frame. `arm` runs
51
+ * right before the id is handed over, `release` when the attachment is cancelled, and `judge` for every request
52
+ * to Paddle's checkout services. `served` says whether the synced registry serves Paddle VGS; it is asked only
53
+ * about a POST to /pay, so no other request reads the registry here.
54
+ */
55
+ export class PaddleVgsPayGate {
56
+ served;
57
+ now;
58
+ bound = null;
59
+ constructor(served, now = Date.now) {
60
+ this.served = served;
61
+ this.now = now;
62
+ }
63
+ /** Whether the gate judges this request: a POST to /pay, on an attachment the API served Paddle VGS to. */
64
+ holds(url, method) {
65
+ return String(method).toUpperCase() === 'POST' && paddleVgsPayTarget(url) !== null && this.served();
66
+ }
67
+ /**
68
+ * Bind the next /pay of `frame`, the frame about to receive `replay` for its card request to `cardsUrl`, to the
69
+ * stored card's id in the replay and the approved card's facts the API served beside it. Does nothing for a
70
+ * request to anything but Paddle's card vault. Throws when the frame is unknown, or the replay carries no id or
71
+ * no facts payment-core reads: the id must then not reach the frame, since its /pay could only carry the
72
+ * placeholder's facts.
73
+ */
74
+ arm(frame, cardsUrl, replay) {
75
+ const environment = paddleVgsEnvironment(cardsUrl);
76
+ if (environment === null)
77
+ return;
78
+ this.release();
79
+ const token = replay.mode === undefined || replay.mode === 'token' ? replay : null;
80
+ const facts = token ? paddleVgsCardFactsOf(token.processorContext) : null;
81
+ let cardId = null;
82
+ try {
83
+ cardId = token ? JSON.parse(token.body)?.data?.id : null;
84
+ }
85
+ catch {
86
+ cardId = null;
87
+ }
88
+ if (!known(frame) || !facts || typeof cardId !== 'string')
89
+ throw new Error('paddle_vgs_card_facts_unavailable');
90
+ this.bound = { frame, environment, cardId, facts, until: this.now() + PADDLE_VGS_CARD_ID_WINDOW_MS, used: false };
91
+ }
92
+ /** Drop the binding: no /pay continues with these facts after this. */
93
+ release() {
94
+ this.bound = null;
95
+ }
96
+ /**
97
+ * What becomes of a request to Paddle's checkout services: null when it is not a /pay naming a stored card (it
98
+ * continues untouched), the body to continue it with (payment-core paddleVgsPayVerdict), or why it is aborted.
99
+ * The binding answers one /pay: the first one from its frame continues or is refused, and either way ends it.
100
+ */
101
+ judge(frame, url, method, body) {
102
+ if (!this.holds(url, method) || !paddleVgsPayCarriesStoredCard(body))
103
+ return null;
104
+ const bound = this.bound;
105
+ if (!bound || !known(frame) || frame !== bound.frame)
106
+ return { reason: 'paddle_vgs_pay_unbound' };
107
+ if (bound.used)
108
+ return { reason: 'paddle_vgs_pay_used' };
109
+ bound.used = true;
110
+ if (this.now() > bound.until)
111
+ return { reason: 'paddle_vgs_pay_expired' };
112
+ const verdict = paddleVgsPayVerdict({ url, method, body }, { environment: bound.environment, cardId: bound.cardId, facts: bound.facts });
113
+ return verdict.verdict === 'continue' ? { body: verdict.body } : { reason: verdict.code };
114
+ }
115
+ }
@@ -47,4 +47,32 @@ declare function paddleVgsAnswerOutcome(status: any, text: any, card: any): {
47
47
  reason?: undefined;
48
48
  code?: undefined;
49
49
  };
50
- export { PADDLE_VGS_CAPABILITY, PADDLE_VGS_CARDS_URLS, PADDLE_VGS_CARD_NETWORKS, PADDLE_VGS_CONTENT_TYPE, PADDLE_VGS_MERCHANT_PINS, PADDLE_VGS_OPERATION, PADDLE_VGS_PSP, PADDLE_VGS_REFUSAL_CODES, injectPaddleVgsCard, isPaddleVgsCardsUrl, isPreparedPaddleVgsRequest, paddleVgsAnswerOutcome, paddleVgsBodyVerdict, paddleVgsCardRefusal, paddleVgsEnvironment, paddleVgsMerchantPin, paddleVgsMerchantRefusal, paddleVgsRefusalCode, paddleVgsRefusalMessage, paddleVgsRequestRefusal, paddleVgsSellerClaimsPinned, paddleVgsStoredCardRefusal, paddleVgsTypedCardRefusal };
50
+ declare var PADDLE_VGS_CARD_ID_WINDOW_MS: number;
51
+ declare function paddleVgsPayTarget(url: any): Readonly<{
52
+ environment: "production" | "sandbox";
53
+ line: "classic" | "billing";
54
+ }> | null;
55
+ declare function paddleVgsPayCarriesStoredCard(body: any): boolean;
56
+ declare function paddleVgsCardFacts(card: any): Readonly<{
57
+ card_first_six: string;
58
+ card_last_four: string;
59
+ expiry_month: number;
60
+ expiry_year: number;
61
+ card_brand: "visa" | "master" | "american_express";
62
+ }>;
63
+ declare function paddleVgsCardFactsOf(value: any): Readonly<{
64
+ card_first_six: any;
65
+ card_last_four: string;
66
+ expiry_month: any;
67
+ expiry_year: any;
68
+ card_brand: any;
69
+ }> | null;
70
+ declare function paddleVgsCardFactsAgree(facts: any, stored: any): boolean;
71
+ declare function paddleVgsPayVerdict(request: any, approved: any): Readonly<{
72
+ verdict: "refuse";
73
+ code: any;
74
+ }> | Readonly<{
75
+ verdict: "continue";
76
+ body: string;
77
+ }>;
78
+ export { PADDLE_VGS_CAPABILITY, PADDLE_VGS_CARDS_URLS, PADDLE_VGS_CARD_ID_WINDOW_MS, PADDLE_VGS_CARD_NETWORKS, PADDLE_VGS_CONTENT_TYPE, PADDLE_VGS_MERCHANT_PINS, PADDLE_VGS_OPERATION, PADDLE_VGS_PSP, PADDLE_VGS_REFUSAL_CODES, injectPaddleVgsCard, isPaddleVgsCardsUrl, isPreparedPaddleVgsRequest, paddleVgsAnswerOutcome, paddleVgsBodyVerdict, paddleVgsCardFacts, paddleVgsCardFactsAgree, paddleVgsCardFactsOf, paddleVgsCardRefusal, paddleVgsEnvironment, paddleVgsMerchantPin, paddleVgsMerchantRefusal, paddleVgsPayCarriesStoredCard, paddleVgsPayTarget, paddleVgsPayVerdict, paddleVgsRefusalCode, paddleVgsRefusalMessage, paddleVgsRequestRefusal, paddleVgsSellerClaimsPinned, paddleVgsStoredCardRefusal, paddleVgsTypedCardRefusal };
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: b33914c3bb882b868a28a7d7abb91d6cca450c0145dca0aa338474400b6f923e
3
+ // artifact-sha256: f3437a15e11b72b9958179cc9610fdafb411cc5db76be536af62c08360a45575
4
4
  // src/paddle-vgs.js
5
5
  var PADDLE_VGS_PSP = "paddle_vgs";
6
6
  var PADDLE_VGS_CAPABILITY = "paddle_vgs_cards";
@@ -254,12 +254,12 @@ function readAnswer(text) {
254
254
  return null;
255
255
  }
256
256
  }
257
+ var isStoredCardId = (id) => typeof id === "string" && STORED_CARD_ID.test(id) && /[A-Za-z]/.test(id) && !PAN_LIKE.test(id);
257
258
  function storedCardId(answer) {
258
259
  const data = answer && answer.data;
259
- if (!record(data) || data.type !== "cards" || typeof data.id !== "string")
260
+ if (!record(data) || data.type !== "cards")
260
261
  return null;
261
- const id = data.id;
262
- return STORED_CARD_ID.test(id) && /[A-Za-z]/.test(id) && !PAN_LIKE.test(id) ? id : null;
262
+ return isStoredCardId(data.id) ? data.id : null;
263
263
  }
264
264
  var boundedCode = (value) => typeof value === "string" && REFUSAL_CODE.test(value) && (value.match(/[0-9]/g) ?? []).length <= MAX_CODE_DIGITS ? value : null;
265
265
  function paddleVgsRefusalCode(text) {
@@ -286,4 +286,123 @@ function paddleVgsAnswerOutcome(status, text, card) {
286
286
  return { outcome: "unknown", reason: "paddle_vgs_unsafe_response" };
287
287
  return { outcome: "approved", body: JSON.stringify({ data: { type: "cards", id } }) };
288
288
  }
289
- export { PADDLE_VGS_CAPABILITY, PADDLE_VGS_CARDS_URLS, PADDLE_VGS_CARD_NETWORKS, PADDLE_VGS_CONTENT_TYPE, PADDLE_VGS_MERCHANT_PINS, PADDLE_VGS_OPERATION, PADDLE_VGS_PSP, PADDLE_VGS_REFUSAL_CODES, injectPaddleVgsCard, isPaddleVgsCardsUrl, isPreparedPaddleVgsRequest, paddleVgsAnswerOutcome, paddleVgsBodyVerdict, paddleVgsCardRefusal, paddleVgsEnvironment, paddleVgsMerchantPin, paddleVgsMerchantRefusal, paddleVgsRefusalCode, paddleVgsRefusalMessage, paddleVgsRequestRefusal, paddleVgsSellerClaimsPinned, paddleVgsStoredCardRefusal, paddleVgsTypedCardRefusal };
289
+ var PADDLE_VGS_CARD_ID_WINDOW_MS = 6e4;
290
+ var PAY_URL = /^https:\/\/(sandbox-)?checkout-service\.paddle\.com\/(checkout|transaction-checkout)\/[A-Za-z0-9_-]{1,64}\/pay$/;
291
+ function paddleVgsPayTarget(url) {
292
+ const match = typeof url === "string" ? PAY_URL.exec(url) : null;
293
+ return match ? Object.freeze({ environment: match[1] ? "sandbox" : "production", line: match[2] === "checkout" ? "classic" : "billing" }) : null;
294
+ }
295
+ function paddleVgsPayCarriesStoredCard(body) {
296
+ if (typeof body !== "string")
297
+ return true;
298
+ try {
299
+ return JSON.stringify(JSON.parse(body)).includes('"vgs_card"');
300
+ }
301
+ catch {
302
+ return true;
303
+ }
304
+ }
305
+ var FRAME_BRANDS = /* @__PURE__ */ Object.freeze(["visa", "master", "american_express", "discover", "diners_club", "jcb", "maestro", "dankort", "unionpay", "unknown"]);
306
+ var PAY_BRANDS = /* @__PURE__ */ Object.freeze({ visa: "visa", mastercard: "master", amex: "american_express" });
307
+ var FACT_KEYS = /* @__PURE__ */ Object.freeze(["card_first_six", "card_last_four", "expiry_month", "expiry_year", "card_brand"]);
308
+ var FIRST_SIX = /^[0-9]{6}$/;
309
+ var LAST_FOUR = /^[0-9]{4}$/;
310
+ var fullYear = (year) => {
311
+ const value = Number(year);
312
+ return Number.isInteger(value) && value >= 0 && value <= 99 ? 2e3 + value : value;
313
+ };
314
+ var expiryMonth = (month) => Number.isInteger(month) && month >= 1 && month <= 12;
315
+ var expiryYear = (year) => Number.isInteger(year) && year >= 2e3 && year <= 2099;
316
+ function paddleVgsCardFacts(card) {
317
+ const number = card ? String(card.number) : "";
318
+ const network = cardNetwork(number);
319
+ const month = card ? Number(card.month) : NaN;
320
+ const year = card ? fullYear(card.year) : NaN;
321
+ if (network === null || !expiryMonth(month) || !expiryYear(year))
322
+ throw new Error("Could not read this card's details for Paddle. Nothing was sent.");
323
+ return Object.freeze({ card_first_six: number.slice(0, 6), card_last_four: number.slice(-4), expiry_month: month, expiry_year: year, card_brand: PAY_BRANDS[network] });
324
+ }
325
+ function paddleVgsCardFactsOf(value) {
326
+ if (!record(value))
327
+ return null;
328
+ const keys = Object.keys(value);
329
+ if (keys.length !== FACT_KEYS.length || !FACT_KEYS.every((key) => keys.includes(key)))
330
+ return null;
331
+ const { card_first_six: firstSix, card_last_four: lastFour, expiry_month: month, expiry_year: year, card_brand: brand } = value;
332
+ const network = typeof firstSix === "string" && FIRST_SIX.test(firstSix) ? cardNetwork(`${firstSix}000000`) : null;
333
+ if (network === null || PAY_BRANDS[network] !== brand || typeof lastFour !== "string" || !LAST_FOUR.test(lastFour) || !expiryMonth(month) || !expiryYear(year))
334
+ return null;
335
+ return Object.freeze({ card_first_six: firstSix, card_last_four: lastFour, expiry_month: month, expiry_year: year, card_brand: brand });
336
+ }
337
+ function paddleVgsCardFactsAgree(facts, stored) {
338
+ const read = paddleVgsCardFactsOf(facts);
339
+ if (!read)
340
+ return false;
341
+ const card = record(stored) ? stored : {};
342
+ const network = Object.keys(PAY_BRANDS).find((name) => PAY_BRANDS[name] === read.card_brand);
343
+ const brand = storedBrand(card.brand);
344
+ return !(typeof card.last4 === "string" && LAST_FOUR.test(card.last4) && card.last4 !== read.card_last_four) && (brand === null || brand === network) && !(Number.isInteger(card.expiryMonth) && card.expiryMonth !== read.expiry_month) && !(Number.isInteger(card.expiryYear) && card.expiryYear % 100 !== read.expiry_year % 100);
345
+ }
346
+ var PAY_BODY_LIMIT = 16384;
347
+ var PAY_DATA_KEYS = /* @__PURE__ */ Object.freeze(["payment_method_type", "payment_method_data"]);
348
+ var PAY_CARD_KEYS = /* @__PURE__ */ Object.freeze(["metadata", "three_d_s", "vgs_card"]);
349
+ var METADATA_KEYS = /* @__PURE__ */ Object.freeze(["cardholder_name", ...FACT_KEYS]);
350
+ var BROWSER_INFO = /^[A-Za-z0-9+/]{1,4096}={0,2}$/;
351
+ var RADAR_TOKEN = /^[\x21-\x7e]{1,512}$/;
352
+ var COUNTRY_CODE = /^[A-Za-z]{2}$/;
353
+ var FRONTEND_HOST = /^https:\/\/[\x21-\x7e]{1,1024}$/;
354
+ var radarKeys = (value) => sameKeys(value, ["token", "country_code"]) || sameKeys(value, ["country_code", "token"]);
355
+ function readPay(body, line) {
356
+ if (typeof body !== "string" || body === "" || body.length > PAY_BODY_LIMIT)
357
+ return null;
358
+ let value;
359
+ try {
360
+ value = JSON.parse(body);
361
+ if (!record(value) || JSON.stringify(value) !== body)
362
+ return null;
363
+ }
364
+ catch {
365
+ return null;
366
+ }
367
+ const data = value.data;
368
+ if (!sameKeys(value, ["data"]) || !record(data) || !(sameKeys(data, PAY_DATA_KEYS) || sameKeys(data, [...PAY_DATA_KEYS, "checkout"])) || data.payment_method_type !== (line === "classic" ? "CARD" : "card"))
369
+ return null;
370
+ if (data.checkout !== void 0 && !(record(data.checkout) && sameKeys(data.checkout, ["frontend_host"]) && typeof data.checkout.frontend_host === "string" && FRONTEND_HOST.test(data.checkout.frontend_host)))
371
+ return null;
372
+ const method = data.payment_method_data;
373
+ const card = record(method) && sameKeys(method, ["card"]) ? method.card : null;
374
+ if (!record(card) || !(sameKeys(card, PAY_CARD_KEYS) || sameKeys(card, [...PAY_CARD_KEYS, "session_tracking"])))
375
+ return null;
376
+ const metadata = card.metadata;
377
+ if (!record(metadata) || !sameKeys(metadata, METADATA_KEYS) || typeof metadata.cardholder_name !== "string" || metadata.cardholder_name.length > NAME_LIMIT || CONTROL.test(metadata.cardholder_name) || cardLikeName(metadata.cardholder_name) || typeof metadata.card_first_six !== "string" || !FIRST_SIX.test(metadata.card_first_six) || typeof metadata.card_last_four !== "string" || !LAST_FOUR.test(metadata.card_last_four) || !expiryMonth(metadata.expiry_month) || !expiryYear(metadata.expiry_year) || !FRAME_BRANDS.includes(metadata.card_brand))
378
+ return null;
379
+ const details = card.three_d_s;
380
+ if (!record(details) || !sameKeys(details, ["browser_info"]) || typeof details.browser_info !== "string" || !BROWSER_INFO.test(details.browser_info))
381
+ return null;
382
+ if (!record(card.vgs_card) || !sameKeys(card.vgs_card, ["card_id"]) || !isStoredCardId(card.vgs_card.card_id))
383
+ return null;
384
+ if (card.session_tracking !== void 0) {
385
+ const tracking = card.session_tracking;
386
+ const radar = record(tracking) && sameKeys(tracking, ["stripe_radar"]) ? tracking.stripe_radar : null;
387
+ if (!record(radar) || !radarKeys(radar) || typeof radar.token !== "string" || !RADAR_TOKEN.test(radar.token) || typeof radar.country_code !== "string" || !COUNTRY_CODE.test(radar.country_code))
388
+ return null;
389
+ }
390
+ return value;
391
+ }
392
+ function paddleVgsPayVerdict(request, approved) {
393
+ const facts = record(approved) ? paddleVgsCardFactsOf(approved.facts) : null;
394
+ if (!facts || !isStoredCardId(approved.cardId) || approved.environment !== "production" && approved.environment !== "sandbox") {
395
+ return refuse("paddle_vgs_pay_unbound");
396
+ }
397
+ const target = record(request) && typeof request.method === "string" && request.method.toUpperCase() === "POST" ? paddleVgsPayTarget(request.url) : null;
398
+ const value = target ? readPay(request.body, target.line) : null;
399
+ if (!value)
400
+ return refuse("paddle_vgs_pay_unsupported");
401
+ const card = value.data.payment_method_data.card;
402
+ if (target.environment !== approved.environment || card.vgs_card.card_id !== approved.cardId)
403
+ return refuse("paddle_vgs_pay_card_mismatch");
404
+ for (const key of FACT_KEYS)
405
+ card.metadata[key] = facts[key];
406
+ return Object.freeze({ verdict: "continue", body: JSON.stringify(value) });
407
+ }
408
+ export { PADDLE_VGS_CAPABILITY, PADDLE_VGS_CARDS_URLS, PADDLE_VGS_CARD_ID_WINDOW_MS, PADDLE_VGS_CARD_NETWORKS, PADDLE_VGS_CONTENT_TYPE, PADDLE_VGS_MERCHANT_PINS, PADDLE_VGS_OPERATION, PADDLE_VGS_PSP, PADDLE_VGS_REFUSAL_CODES, injectPaddleVgsCard, isPaddleVgsCardsUrl, isPreparedPaddleVgsRequest, paddleVgsAnswerOutcome, paddleVgsBodyVerdict, paddleVgsCardFacts, paddleVgsCardFactsAgree, paddleVgsCardFactsOf, paddleVgsCardRefusal, paddleVgsEnvironment, paddleVgsMerchantPin, paddleVgsMerchantRefusal, paddleVgsPayCarriesStoredCard, paddleVgsPayTarget, paddleVgsPayVerdict, paddleVgsRefusalCode, paddleVgsRefusalMessage, paddleVgsRequestRefusal, paddleVgsSellerClaimsPinned, paddleVgsStoredCardRefusal, paddleVgsTypedCardRefusal };
@@ -1,12 +1,10 @@
1
1
  export type PaysafePreparationEnvironment = 'production' | 'sandbox';
2
2
  export declare function paysafePreparationUrl(environment: string): string;
3
- /** Exact strings refuse credentials, queries, fragments and URL normalization. */
3
+ /** The environment of a reviewed build's card URL. Exact strings refuse credentials, queries, fragments and URL normalization. */
4
4
  export declare function paysafeEnvironment(url: string): PaysafePreparationEnvironment | undefined;
5
- /** Checkout 1.8.0 starts a 40-second XHR when Pay is pressed. Prior approval may
6
- * consume only its new-card request, including native clientInfo and headers.
7
- * Saved cards, alternate APIs and 3DS-only requests require separate review.
8
- * Source: hosted.paysafe.com/checkout/1.8.0/main.bundle.js, SHA-256
9
- * e9e111ff03073cc936a47674e8e7c5db4a6d1050e1b65bf5e20723116fd5080c.
10
- * The guard checks original bytes and never changes the request.
5
+ /** A reviewed Checkout build (BUILDS) starts a 40-second XHR when Pay is pressed. Prior approval may
6
+ * consume only its new-card request, including native clientInfo and headers, on the card URL that
7
+ * build sends to in the approved environment. Saved cards, alternate APIs and 3DS-only requests
8
+ * require separate review. The guard checks original bytes and never changes the request.
11
9
  */
12
10
  export declare function isPreparedPaysafeRequest(url: string, method: string, body: string | null, environment: string, headers?: Record<string, string>): boolean;
@@ -1,10 +1,34 @@
1
1
  // Generated from @agent-cards/payment-core. Do not edit.
2
- // source-sha256: 24c7940ae8974c0d24e5a365144c1b07ccc46863ae3094eb92b3c54c8307e837
2
+ // source-sha256: afa00cd57246e51361474c66c5ab4282bd5b39f0297fdf68616e164e19ed1741
3
3
  import { readTokenizationJson } from './braintree.js';
4
+ /** The card URL a preparation names: Checkout 1.8.0's. The bind takes the exact URL of the build the request names (BUILDS). */
4
5
  const ENDPOINTS = {
5
6
  production: 'https://hosted.paysafe.com/checkout/api/v1/tokenize',
6
7
  sandbox: 'https://hosted.test.paysafe.com/checkout/api/v1/tokenize',
7
8
  };
9
+ /**
10
+ * The Paysafe Checkout builds whose new-card request a prior approval may consume, each read from
11
+ * Paysafe's public bundle. A request binds only to the build its own clientInfo.version names, on
12
+ * that build's URL and headers, so a build Paysafe ships later is refused, and nothing is sent,
13
+ * until it is read and listed here. The request moved inside 1.x: 1.8.0 to 1.12.0 post to
14
+ * hosted.paysafe.com with a correlationId header, 1.16.1 to api.paysafe.com with three other
15
+ * headers, so a version range would hold one build's request to another build's rules.
16
+ *
17
+ * 1.8.0: hosted.paysafe.com/checkout/1.8.0/main.bundle.js, SHA-256
18
+ * e9e111ff03073cc936a47674e8e7c5db4a6d1050e1b65bf5e20723116fd5080c; LIVE/TEST select hosted[.test].
19
+ * 1.16.1: the checkout/v1/latest loader (SHA-256 5fac65c4f7a74b45388ad3ce7c02282f05e832226792bebc417cd9ff191be4f0)
20
+ * names it and frames checkout/v1/latest/index.html. Its main.js (SHA-256
21
+ * 328c6443da930f3648826248afa3c342cdd07d04e49a78b0878dda187772bf80) posts to environment +
22
+ * "checkout/api/v1/tokenize", and its vendor.js (SHA-256 7348275bd07256e295f4b83b292c7fe5115dd7ac35d1b1fe3d26eda3c37d2835)
23
+ * maps LIVE/TEST to api[.test].paysafe.com and sets X-Internal-Correlation-Id, X-Paysafe-Credentials,
24
+ * X-App-Version (the clientInfo version) and X-TransactionSource (CheckoutV1 for paysafe.checkout).
25
+ * The TEST host serves the same bytes (read 2026-10-15).
26
+ */
27
+ const BUILDS = [
28
+ { version: '1.8.0', endpoints: ENDPOINTS, correlation: 'correlationid', fixed: {} },
29
+ { version: '1.16.1', correlation: 'x-internal-correlation-id', fixed: { 'x-app-version': '1.16.1', 'x-transactionsource': 'CheckoutV1' },
30
+ endpoints: { production: 'https://api.paysafe.com/checkout/api/v1/tokenize', sandbox: 'https://api.test.paysafe.com/checkout/api/v1/tokenize' } },
31
+ ];
8
32
  const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
9
33
  const keys = (value, allowed) => record(value) && Object.keys(value).every(key => allowed.includes(key));
10
34
  const exact = (pattern, value) => typeof value === 'string' && pattern.exec(value)?.[0] === value;
@@ -16,27 +40,31 @@ export function paysafePreparationUrl(environment) {
16
40
  throw new Error('unsupported_preparation_environment');
17
41
  return ENDPOINTS[environment];
18
42
  }
19
- /** Exact strings refuse credentials, queries, fragments and URL normalization. */
43
+ /** The environment of a reviewed build's card URL. Exact strings refuse credentials, queries, fragments and URL normalization. */
20
44
  export function paysafeEnvironment(url) {
21
- return url === ENDPOINTS.production ? 'production' : url === ENDPOINTS.sandbox ? 'sandbox' : undefined;
45
+ const build = BUILDS.find(candidate => url === candidate.endpoints.production || url === candidate.endpoints.sandbox);
46
+ return !build ? undefined : url === build.endpoints.production ? 'production' : 'sandbox';
22
47
  }
23
48
  const ADDRESS_FIELDS = ['city', 'country', 'zip', 'street', 'street2', 'state'];
24
49
  function billing(value) {
25
50
  return keys(value, ADDRESS_FIELDS) && Object.values(value).every(value => text(value))
26
51
  && (!own(value, 'country') || exact(/^[A-Z]{2}$/, value.country));
27
52
  }
28
- function freshCard(value) {
53
+ /** The reviewed build whose new-card body this is: never a saved card, a 3-D Secure request or another app's body. */
54
+ function freshCardBuild(value) {
29
55
  if (!keys(value, ['card', 'clientInfo', 'billingAddress']) || !keys(value.card, ['cardNum', 'cardExpiry', 'cvv', 'holderName']))
30
- return false;
56
+ return undefined;
31
57
  const card = value.card;
32
58
  if (!exact(/^[0-9]{12,19}$/, card.cardNum) || !exact(/^[0-9]{3,4}$/, card.cvv)
33
59
  || !keys(card.cardExpiry, ['month', 'year']) || !exact(/^(?:0[1-9]|1[0-2])$/, card.cardExpiry.month)
34
60
  || !exact(/^20[0-9]{2}$/, card.cardExpiry.year) || own(card, 'holderName') && !text(card.holderName)
35
61
  || own(value, 'billingAddress') && !billing(value.billingAddress))
36
- return false;
62
+ return undefined;
37
63
  const info = value.clientInfo;
38
- return keys(info, ['correlationId', 'invocationId', 'version', 'appName'])
39
- && uuid(info.correlationId) && uuid(info.invocationId) && info.version === '1.8.0' && info.appName === 'paysafe.checkout';
64
+ if (!keys(info, ['correlationId', 'invocationId', 'version', 'appName'])
65
+ || !uuid(info.correlationId) || !uuid(info.invocationId) || info.appName !== 'paysafe.checkout')
66
+ return undefined;
67
+ return BUILDS.find(build => build.version === info.version);
40
68
  }
41
69
  const BROWSER_HEADERS = new Set(['accept', 'accept-language', 'accept-encoding', 'content-length', 'origin', 'referer',
42
70
  'user-agent', 'host', 'connection', 'cache-control', 'pragma', 'priority', 'dnt',
@@ -53,9 +81,11 @@ function singleUseCredential(value) {
53
81
  return false;
54
82
  }
55
83
  }
56
- function nativeHeaders(headers, correlationId) {
84
+ /** The build's own headers, each once with its native value, and only headers a browser adds beside them. */
85
+ function nativeHeaders(headers, correlationId, build) {
57
86
  if (!record(headers))
58
87
  return false;
88
+ const native = ['content-type', 'x-paysafe-credentials', build.correlation, ...Object.keys(build.fixed)];
59
89
  const parsed = new Map();
60
90
  let bytes = 0;
61
91
  for (const [name, value] of Object.entries(headers)) {
@@ -63,25 +93,27 @@ function nativeHeaders(headers, correlationId) {
63
93
  if (parsed.size >= 64 || parsed.has(lower) || !exact(/^[A-Za-z0-9-]+$/, name) || !text(value, 8192)
64
94
  || (bytes += name.length + value.length) > 32768)
65
95
  return false;
66
- if (!['content-type', 'correlationid', 'x-paysafe-credentials'].includes(lower) && !BROWSER_HEADERS.has(lower)
96
+ if (!native.includes(lower) && !BROWSER_HEADERS.has(lower)
67
97
  && !exact(/^sec-ch-ua(?:-[a-z0-9-]+)?$/, lower))
68
98
  return false;
69
99
  parsed.set(lower, value);
70
100
  }
71
101
  return exact(/^application\/json(?:;\s*charset=utf-8)?$/i, parsed.get('content-type'))
72
- && parsed.get('correlationid') === correlationId && singleUseCredential(parsed.get('x-paysafe-credentials'));
102
+ && parsed.get(build.correlation) === correlationId && singleUseCredential(parsed.get('x-paysafe-credentials'))
103
+ && Object.entries(build.fixed).every(([name, value]) => parsed.get(name) === value);
73
104
  }
74
- /** Checkout 1.8.0 starts a 40-second XHR when Pay is pressed. Prior approval may
75
- * consume only its new-card request, including native clientInfo and headers.
76
- * Saved cards, alternate APIs and 3DS-only requests require separate review.
77
- * Source: hosted.paysafe.com/checkout/1.8.0/main.bundle.js, SHA-256
78
- * e9e111ff03073cc936a47674e8e7c5db4a6d1050e1b65bf5e20723116fd5080c.
79
- * The guard checks original bytes and never changes the request.
105
+ /** A reviewed Checkout build (BUILDS) starts a 40-second XHR when Pay is pressed. Prior approval may
106
+ * consume only its new-card request, including native clientInfo and headers, on the card URL that
107
+ * build sends to in the approved environment. Saved cards, alternate APIs and 3DS-only requests
108
+ * require separate review. The guard checks original bytes and never changes the request.
80
109
  */
81
110
  export function isPreparedPaysafeRequest(url, method, body, environment, headers) {
82
111
  if (method !== 'POST' || (environment !== 'production' && environment !== 'sandbox')
83
112
  || paysafeEnvironment(url) !== environment)
84
113
  return false;
85
114
  const value = readTokenizationJson(body);
86
- return freshCard(value) && record(value.clientInfo) && nativeHeaders(headers, value.clientInfo.correlationId);
115
+ if (!value || !record(value.clientInfo))
116
+ return false;
117
+ const build = freshCardBuild(value);
118
+ return !!build && url === build.endpoints[environment] && nativeHeaders(headers, value.clientInfo.correlationId, build);
87
119
  }
@@ -13,6 +13,7 @@ import { type CardEndpointProfile } from './card-endpoint.js';
13
13
  import { type PayrailsSessionCapture, type PayrailsSidecar } from './payrails.js';
14
14
  import { type VerifoneProfile } from './verifone.js';
15
15
  import { type WorldlineMyCheckoutProducts } from './worldline-mycheckout.js';
16
+ import { type PaddleVgsPayGate } from './paddle-vgs-pay.js';
16
17
  /** The native Stripe step the gate claimed, or null when none was: exactly what StripeCheckoutGate.claim returns. */
17
18
  type StripeStep = ReturnType<StripeCheckoutGate['claim']>;
18
19
  /** A synthetic response a gate fulfills locally. The effect applies CORS and encoding. */
@@ -189,6 +190,12 @@ export interface PreClaimContext {
189
190
  * Absent, no MyCheckout card submit is held to a product here.
190
191
  */
191
192
  worldlineMyCheckout?: WorldlineMyCheckoutProducts;
193
+ /**
194
+ * The attachment's binding of Paddle's /pay to the stored card's id it handed a frame (paddle-vgs-pay.ts),
195
+ * judged by the request's frame (`frame`). Absent, or for an org the API did not serve Paddle VGS to, Paddle's
196
+ * checkout services are not this gate's.
197
+ */
198
+ paddleVgsPay?: PaddleVgsPayGate;
192
199
  }
193
200
  /**
194
201
  * The URL an event may name for a request. For an armed merchant profile's
@@ -211,6 +218,7 @@ export declare const ekashuPreClaim: PreClaimGate;
211
218
  export declare const paypalHostedFieldsPreClaim: PreClaimGate;
212
219
  export declare const cybersourceSecureAcceptancePreClaim: PreClaimGate;
213
220
  export declare const worldlineMyCheckoutPreClaim: PreClaimGate;
221
+ export declare const paddleVgsPayPreClaim: PreClaimGate;
214
222
  /** The processor gates, in order. A new family adds one entry here; the terminal
215
223
  * arbiter runs after all of them and is not part of this list. A gate appended
216
224
  * after Stripe is never offered a step Stripe claimed: Stripe answers, aborts or
package/dist/pre-claim.js CHANGED
@@ -79,6 +79,7 @@ import { ekashuEntryUrl, isEkashuEntry, isEkashuHost } from './ekashu.js';
79
79
  import { PAYPAL_HOSTED_FIELDS_REASON, isPaypalHostedFieldsConfirm, paypalHostedFieldsBody } from './paypal-hosted-fields.js';
80
80
  import { CYBERSOURCE_SECURE_ACCEPTANCE_REASON, CYBERSOURCE_SECURE_ACCEPTANCE_RECIPIENT, cybersourceSecureAcceptanceRequest, isCybersourceSecureAcceptanceUrl, isSecureAcceptanceFramePost, } from './cybersource-secure-acceptance.js';
81
81
  import { isWorldlineMyCheckoutEndpoint } from './worldline-mycheckout.js';
82
+ import { paddleVgsPayEventUrl } from './paddle-vgs-pay.js';
82
83
  /**
83
84
  * The URL an event may name for a request. For an armed merchant profile's
84
85
  * endpoint or a sibling of it: the endpoint origin, the path TEMPLATE and the
@@ -1737,6 +1738,52 @@ async function worldlineMyCheckoutBlock(ctx, reason) {
1737
1738
  await ctx.effects.abort();
1738
1739
  return { kind: 'abort' };
1739
1740
  }
1741
+ // Paddle's /pay (paddle-vgs-pay.ts), held only on an attachment the API served Paddle VGS to: a POST to /pay
1742
+ // that names a stored card continues with the approved card's first six, last four, expiry and brand written
1743
+ // over the ones the frame read from the agent's placeholder, when this attachment handed that frame the stored
1744
+ // card's id within the minute and this is its first /pay; otherwise it aborts as an unsupported checkout, with
1745
+ // the gate's reason, so Paddle never receives the real card's id beside the placeholder's facts. Anything else
1746
+ // on Paddle's checkout services (its create, the buyer's first step, 3-D Secure's follow-up, a PayPal or wallet
1747
+ // /pay naming no stored card) defers, and the terminal arbiter continues it untouched. Synchronous until the
1748
+ // continue or the abort.
1749
+ export const paddleVgsPayPreClaim = (ctx) => {
1750
+ const { url, method } = ctx.request;
1751
+ if (!ctx.paddleVgsPay?.holds(url, method))
1752
+ return undefined;
1753
+ let body;
1754
+ // A reader that throws (Playwright's postData() can) proves nothing: judged as a body that is not the frame's.
1755
+ try {
1756
+ body = ctx.request.body;
1757
+ }
1758
+ catch {
1759
+ body = null;
1760
+ }
1761
+ const judged = ctx.paddleVgsPay.judge(ctx.frame, url, method, body);
1762
+ if (judged === null)
1763
+ return undefined;
1764
+ return 'reason' in judged ? paddleVgsPayRefused(ctx, judged.reason) : paddleVgsPayContinued(ctx, judged.body);
1765
+ };
1766
+ async function paddleVgsPayRefused(ctx, reason) {
1767
+ const { url, method } = ctx.request;
1768
+ ctx.preparationGate.invalidate('unsupported_checkout');
1769
+ ctx.lifecycle.unsupported(reason);
1770
+ ctx.onEvent?.({ type: 'unsupported_checkout', detail: { url: paddleVgsPayEventUrl(url), method, reason } });
1771
+ await ctx.effects.abort();
1772
+ return { kind: 'abort' };
1773
+ }
1774
+ async function paddleVgsPayContinued(ctx, body) {
1775
+ try {
1776
+ await ctx.effects.continueRequest(body);
1777
+ }
1778
+ catch (error) {
1779
+ ctx.onEvent?.({ type: 'failed', detail: ctx.failureSummary(error) });
1780
+ await ctx.effects.abort().catch(() => { });
1781
+ return { kind: 'abort' };
1782
+ }
1783
+ ctx.onEvent?.({ type: 'companion_continued', detail: { url: paddleVgsPayEventUrl(ctx.request.url), psp: 'paddle_vgs',
1784
+ fields: ['card_first_six', 'card_last_four', 'expiry_month', 'expiry_year', 'card_brand'] } });
1785
+ return { kind: 'continue' };
1786
+ }
1740
1787
  // The terminal arbiter, always last. A request that no gate claimed and that is
1741
1788
  // neither a native Stripe step nor a recognized card request continues, unless
1742
1789
  // it matches a declared payment endpoint we do not support (then it aborts as an
@@ -1771,7 +1818,7 @@ async function unsupportedContinue(ctx) {
1771
1818
  * after Stripe is never offered a step Stripe claimed: Stripe answers, aborts or
1772
1819
  * pauses each one itself. */
1773
1820
  export const PSP_PRE_CLAIM_GATES = [cardEndpointResendPreClaim, cardEndpointPreClaim, mercadoPreClaim, juspayPreClaim, braintreePreClaim, stripePreClaim, merchantHostedPreClaim, verifonePreClaim, fiservPreClaim, gmoPreClaim, cybersourceFlexPreClaim, datatransPreClaim, pelecardPreClaim, aciPreClaim, creditguardPreClaim, primerPreClaim, payrailsPreClaim, convergePreClaim, eprotectPreClaim, wspayPreClaim, shijiStsPreClaim, ekashuPreClaim, paypalHostedFieldsPreClaim,
1774
- cybersourceSecureAcceptancePreClaim, worldlineMyCheckoutPreClaim];
1821
+ cybersourceSecureAcceptancePreClaim, worldlineMyCheckoutPreClaim, paddleVgsPayPreClaim];
1775
1822
  /** Run each gate in order; the first to return a decision wins. If none does,
1776
1823
  * the terminal arbiter decides between continue, abort (unsupported) and pause.
1777
1824
  * Returns synchronously while every gate defers synchronously (an ordinary card
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: b3391e7d6137e269555a23062ce848aa7a7f6f02e626fd568b1e9597c02e5ce6
3
+ // artifact-sha256: 80af165492668e00d651e9a039d3f24fe336bc3601dd5d2ab937a4b79bbb7ad8
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
@@ -414,6 +414,8 @@ var pin2 = (fields) => Object.freeze({
414
414
  var PADDLE_VGS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
415
415
  pin2({ id: "rectangle_pro", merchant: "Rectangle Pro", origins: ["https://rectangleapp.com"], sellerClaim: null })
416
416
  ]);
417
+ var FACT_KEYS = /* @__PURE__ */ Object.freeze(["card_first_six", "card_last_four", "expiry_month", "expiry_year", "card_brand"]);
418
+ var METADATA_KEYS = /* @__PURE__ */ Object.freeze(["cardholder_name", ...FACT_KEYS]);
417
419
  // src/card-fields.js
418
420
  function processorGate(recognizer, url, body) {
419
421
  if (recognizer && recognizer.psp === PADDLE_VGS_PSP) {
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: dd47d4d2d97c39acb2385272d1a2bf3e647741839234283b848b67fd5ce5c114
3
+ // artifact-sha256: 7e7a9b515128df56d149df50e0c275c7f6e8e522945ed818ca714ccac7eb1cd7
4
4
  // src/deep-freeze.js
5
5
  function deepFreeze(value) {
6
6
  if (value && typeof value === "object" && !Object.isFrozen(value)) {
@@ -583,13 +583,26 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
583
583
  // Verified from https://hosted.test.paysafe.com/checkout/1.8.0/main.bundle.js
584
584
  // and its byte-identical production sibling (SHA-256 e9e111ff03073cc936a47674e8e7c5db4a6d1050e1b65bf5e20723116fd5080c).
585
585
  // Its native LIVE/TEST environment map selects exactly hosted[.test].paysafe.com.
586
+ // Checkout v1 latest (loader version 1.16.1; checkout/v1/latest/main.js SHA-256
587
+ // 328c6443da930f3648826248afa3c342cdd07d04e49a78b0878dda187772bf80) posts the same body to
588
+ // checkout/api/v1/tokenize on api[.test].paysafe.com, the hosts its vendor.js maps LIVE/TEST to.
589
+ // It and Paysafe.js latest set X-Internal-Correlation-Id, X-App-Version and X-TransactionSource
590
+ // on every card request, and Paysafe's CORS answer on all three API card paths allows them for
591
+ // any origin (2026-10-15), so a replay keeps them as the native request sends them.
586
592
  // Keep host/path pairs separate: details/log are not card requests;
587
593
  // card/tokenize uses a saved paymentToken, and 3DS-specific paths are unverified.
588
- match: "(?:api(\\.test)?\\.paysafe\\.com/(?:paymenthub/v1/singleusepaymenthandles|js/api/v1/tokenize)|hosted(\\.test)?\\.paysafe\\.com/checkout/api/v1/tokenize)(?![\\w/-])",
594
+ match: "(?:api(\\.test)?\\.paysafe\\.com/(?:paymenthub/v1/singleusepaymenthandles|js/api/v1/tokenize|checkout/api/v1/tokenize)|hosted(\\.test)?\\.paysafe\\.com/checkout/api/v1/tokenize)(?![\\w/-])",
589
595
  hosts: ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"],
590
596
  sandboxHosts: ["^api\\.test\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"],
591
597
  encoding: "json",
592
- passthroughHeaders: ["^authorization$", "^x-paysafe-credentials$", "^correlationid$"]
598
+ passthroughHeaders: [
599
+ "^authorization$",
600
+ "^x-paysafe-credentials$",
601
+ "^correlationid$",
602
+ "^x-internal-correlation-id$",
603
+ "^x-app-version$",
604
+ "^x-transactionsource$"
605
+ ]
593
606
  },
594
607
  {
595
608
  psp: "recurly",
@@ -1556,6 +1569,8 @@ var pin2 = (fields) => Object.freeze({
1556
1569
  var PADDLE_VGS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
1557
1570
  pin2({ id: "rectangle_pro", merchant: "Rectangle Pro", origins: ["https://rectangleapp.com"], sellerClaim: null })
1558
1571
  ]);
1572
+ var FACT_KEYS = /* @__PURE__ */ Object.freeze(["card_first_six", "card_last_four", "expiry_month", "expiry_year", "card_brand"]);
1573
+ var METADATA_KEYS = /* @__PURE__ */ Object.freeze(["cardholder_name", ...FACT_KEYS]);
1559
1574
  // src/cybersource-secure-acceptance.js
1560
1575
  var SA_BODY_LIMIT = 32 * 1024;
1561
1576
  var SA_REPLY_LIMIT = 64 * 1024;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-cards/sdk",
3
- "version": "0.23.12",
3
+ "version": "0.23.13",
4
4
  "description": "Agentcard's SDK: one client, Agentcard, with two modules. checkout pauses a browser purchase and pays it with the person's own card; voice connects a person to your product once and approves a purchase by voice inside it. Card data never enters your process.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -49,5 +49,5 @@
49
49
  "engines": {
50
50
  "node": ">=22"
51
51
  },
52
- "license": "UNLICENSED"
52
+ "license": "SEE LICENSE IN LICENSE"
53
53
  }