@agent-cards/sdk 0.23.2 → 0.23.4
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 +16 -0
- package/PREFLIGHT.md +2 -0
- package/README.md +17 -12
- package/dist/adyen-merchant-hosted.generated.js +134 -127
- package/dist/card-endpoint.d.ts +9 -1
- package/dist/card-endpoint.js +15 -2
- package/dist/cdp.js +19 -13
- package/dist/client.js +11 -8
- package/dist/lifecycle.d.ts +5 -3
- package/dist/lifecycle.js +4 -2
- package/dist/merchant-card-endpoints.generated.js +238 -136
- package/dist/merchant-hosted.js +2 -1
- package/dist/merchant-total.js +4 -1
- package/dist/paypal-hosted-fields.d.ts +52 -0
- package/dist/paypal-hosted-fields.js +80 -0
- package/dist/pre-claim.d.ts +1 -0
- package/dist/pre-claim.js +59 -6
- package/dist/preflight-catalog.json +37 -2
- package/dist/preflight-schemas.json +2 -0
- package/dist/preflight.generated.js +26 -4
- package/dist/substitutions.generated.js +134 -127
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,12 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Stop PayPal's legacy Hosted Fields from sending the card your agent typed to PayPal's payment confirm. A checkout that loads PayPal's JS SDK with `components=hosted-fields`, such as Planet Golf UK's PayPal Checkout, takes the card in frames on `assets.braintreegateway.com` and sends it to `https://cors.api.paypal.com/v2/checkout/orders/{id}/confirm-payment-source` (`cors.api.sandbox.paypal.com` in PayPal's sandbox). No Agentcard payment takes that request over, and earlier releases let it through, so PayPal received the dummy card and declined it. Both adapters now fail that request in the browser for every company, whatever `syncRegistry()` served, and the controller reads `unsupported` with reason `unsupported_paypal_hosted_fields` and holds, like every `unsupported` reason: to pay another way on the same page, attach again. A confirm with no card number, such as a PayPal wallet payment, continues untouched. A confirm whose body the SDK cannot read is failed with a `blocked` event, `unreadable_paypal_request`, and the checkout is not held. A merchant that turns on PayPal's card installments also has the card number field send each typed number to PayPal's installment lookup, `/v1/credit/calculated-financing-options`; the SDK leaves that lookup alone, and it pays nothing. TypeScript: the controller's `unsupported()` reasons gain `unsupported_paypal_hosted_fields`; code that switches exhaustively over them needs the new case.
|
|
6
|
+
|
|
7
|
+
- Name PayPal's legacy Hosted Fields as PayPal in a preflight inspection, not as Braintree. Their card fields are braintree-web builds whose version carries `-payments-sdk` (`3.32.0-payments-sdk-dev` on Planet Golf UK), and inspection read the page as `braintree` with status `supported`. The frames now name `paypal`, and the result stays `unknown` with `processor_variant_unverified`. A braintree-web script from a `-payments-sdk` build is no longer Braintree evidence either, and a variant seen in several frames lists its limitation once. The catalog's `catalog_sha256` changes; `catalog_version` stays `2026-09-15.1`.
|
|
8
|
+
|
|
9
|
+
- Stop the card request AT&T's checkout sends today. AT&T's page posts the card to `/msapi/checkoutorch/v1/mfe/paymentInfo` with no query, and the adapters armed only the exporch form, `/msapi/checkoutexporch/v1/paymentInfo?cartId=`, which none of the checkouts Agentcard read live used, so the agent's dummy card reached AT&T. The adapters now pause the request AT&T sends and refuse it as `unsupported_checkout` while AT&T's profile is in `observe`. The exporch form is a profile of its own, `att_exporch_payment_info`, refused the same way, so a cart AT&T moves there is stopped too. AT&T's card-on-file step, which posts the typed card to `cardOnFile` on either base, is aborted in every status as `card_endpoint_refused` with reason `card_resend`, and so is the exporch form with no query, with its card-on-file step: AT&T's page sends both when its checkout names no cart id, and no payment can cover them. AT&T's profile reads the body its page builds: no `X-CSRF-TOKEN` on a guest checkout, `type` `creditcard` or `debitcard`, `statusCode` and `transactionId` as null, `cardSubType` one of `VISA`, `MASTERCARD`, `AMEX` or `DISCOVER`, a two-digit month and a four-digit year.
|
|
10
|
+
|
|
11
|
+
- Recognize Target Optical's card request as its checkout sends it today: a JSON `PUT` to `/wcs/resources/store/12001/cart/@self/addPayment` with the page's `nocache` query, the total in `piAmount` with five decimals (`"49.42000"`). The profile described the form post Target Optical's older checkout sent, which no request matches now, so the adapters let the dummy card reach Target Optical. The profile stays in observe: both adapters now pause that request and refuse it as `unsupported_checkout` before any authorization, let the saved-card body to the same URL through untouched, and abort the card save a signed-in shopper's page sends to `/wcs/resources/store/12001/mypayments` in every status. They also abort, in every status, a post to `/AjaxOrderChangeServicePIAddTO`, the form endpoint the older checkout used, which they refused before and which the storefront still declares in a script it serves, so moving the profile to the `PUT` leaves no path where the dummy card reaches Target Optical. `attachToPlaywright` routes a reviewed card endpoint's request by every method the profiles name; it asked only for `POST`, so a `PUT` card request or resend never reached its gates. A card endpoint create names the profile's own method in `request.method`; an API that reads every card endpoint request as a `POST` refuses a `PUT` create with `invalid_request`, so nothing is charged. TypeScript: `CardEndpointProfile.method` can be `PUT`.
|
|
12
|
+
|
|
13
|
+
- Pay Cinemark and Dick's Sporting Goods' own storefront, two reviewed Adyen merchants that take the card on their own server. Both profiles, `cinemark_initiate` and `dsg_paygate_v2`, ship `enabled`, and the API serves them `enabled` only to a client of a company Agentcard turned them on for. For every other client the adapters keep pausing and aborting their card requests as `merchant_profile_observed`, as before. A payment there needs a preparation for the profile and the cardholder's approval in the Vault, and Autopilot never pays one. The create carries the merchant's own checkout total as your agent's browser read it, and the API refuses a payment whose amount is not that total. Dick's paygate at Golf Galaxy, Public Lands and Going Going Gone moves to its own profile, `dsg_other_stores_paygate_v2`, which ships in `observe`, so the adapters abort those card requests as before. Matching API and Vault releases are required.
|
|
14
|
+
|
|
5
15
|
- Stop the card fields adyen-web 5 encrypts at a reviewed Adyen merchant profile. adyen-web 5 seals each field as an RSA-OAEP / A256CBC-HS512 JWE, where adyen-web 6 writes RSA-OAEP-256 / A256GCM, and the SDK read only adyen-web 6's form. So the card requests of Cathay Pacific's payment page and Dunelm's legacy checkout, and Cathay's charge request after its signing step, reached the merchant with the agent's dummy card. The adapters now read both forms as a card. Both profiles ship in `observe`, so the adapters abort each of these requests before it leaves the browser and report it as `merchant_profile_observed`. The Vault writes only adyen-web 6's form, so the API refuses a create for a card request in adyen-web 5's form with `cse_template_unsupported`, and nothing is charged.
|
|
6
16
|
|
|
7
17
|
- Abort the requests in which IHG's pay page sends the typed card outside its reviewed card request, in every status, as `card_endpoint_refused` with reason `card_resend`. The page posts the same booking to IHG's enrollment service while its pre-ticked "Enroll for free" box stays ticked, posts it on other paths for more than one room, a travel agent or Business Rewards, and at a hotel that asks for 3-D Secure first posts the number to two 3-D Secure calls. The SDK let each of these reach IHG with the agent's dummy card. A booking with the box unticked reaches IHG's reviewed card request, which the adapters refuse as before while IHG is in `observe`.
|
|
8
18
|
|
|
9
19
|
- Read IHG's booking at a hotel that asks for no security code as IHG's reviewed card request. At the two US hotels a live check visited, the pay page shows no security code field and sends `cvvNumber: {token: ""}`. The adapters read that as a card in a body IHG's profile did not review and refused it with reason `unrecognized_payment_endpoint`, so the booking could never pause for the cardholder. They now refuse it with reason `merchant_card_endpoint_not_enabled`, naming IHG's profile, while IHG is in `observe`, and once Agentcard enables IHG it pauses and asks the cardholder like any other IHG booking. A security code the hotel asks for is still read as three or four digits, and any other value there is refused as before.
|
|
10
20
|
|
|
21
|
+
- Read a hotel booking at IHG, Hilton or The Verb Hotel as the hotel's reviewed card request only at a rate the hotel charges in full at booking: IHG's Book Early & Save (`ratePlanCode` `IDAPF`) in the hotel's own currency, a Hilton rate guaranteed `FP` with a deposit, and The Verb Hotel's Non-Refundable rate (`14DAYAP`). The adapters now refuse a rate that holds the card and charges it at the stay, an IHG group rate, an IHG booking that names `hotels[0].bookingCurrency` (the shopper's currency, which IHG's page sends only when the hotel prices in another one) and a Hilton booking with its pre-ticked "Join Hilton Honors for free" left on (`variables.enrollment`) with reason `unrecognized_payment_endpoint` and an event that names only the endpoint, where they refused each as `merchant_card_endpoint_not_enabled` naming the profile. All three hotels stay in `observe`, so the adapters refuse every booking there before any authorization, as before. IHG's and Hilton's profiles now carry the card network codes their pages write (VS, MC, AX and DS at IHG; VI, MC, AX and DS at Hilton): a body that names another code is unrecognized, and once a hotel is enabled the cardholder's device writes the code of the real card's network and refuses a card on any other. Hilton's profile reads `charge_timing: 'at_request'`, since Hilton charges its prepaid rate at booking. Italo's profile no longer waits on how its page sends `expire`, which the live checks of Sep 30 and Oct 6 read as left out.
|
|
22
|
+
|
|
23
|
+
- Stop Universal Orlando's card request at the checkout it runs today. Universal moved its store from `www.universalorlando.com/web-store` to `store.universalorlando.com`, whose guest checkout posts the card to `secure-api.universalparks.com/instances/{guest}/carts/{cart}/precheckout`. The SDK's profile still described the web-store request, so it let this one through and the agent's dummy card reached Universal. The adapters now refuse it before it leaves the browser with reason `merchant_card_endpoint_not_enabled`, naming Universal's profile, while Universal is in `observe`. They also abort the store's two other posts of the typed card, a signed-in customer's precheckout and the Cardinal 3-D Secure token call Universal keeps off today, in every status, as `card_endpoint_refused` with reason `card_resend`. The retired web-store checkout still loads from a deep link such as `www.universalorlando.com/web-store/en/us/checkout`, and the adapters abort its two posts of the typed card the same way: a guest's `/instances/{guest}/commerce-checkout`, the request they refused before this release, and a signed-in customer's `/guests/{guest}/commerce-checkout`. Once Agentcard enables Universal, the API takes one payment per cart and refuses another create for it with `409 merchant_checkout_in_use`.
|
|
24
|
+
|
|
25
|
+
- 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.
|
|
26
|
+
|
|
11
27
|
## 0.23.0
|
|
12
28
|
|
|
13
29
|
### Upgrading from 0.22.0
|
package/PREFLIGHT.md
CHANGED
|
@@ -207,6 +207,8 @@ The bundled direct SDK profile declares processor support for `one_time` purchas
|
|
|
207
207
|
|
|
208
208
|
The catalog also carries reviewed hosted, regional, and versioned variants observed on live checkouts: Adyen secured fields on the regional live hosts and any release, the Square card element frame, dLocal Smart Fields releases, Mercado Pago secure fields and the guest Checkout Pro card form, and the Rapyd hosted checkout page. A variant the implemented adapter does not admit, such as the Tranzila terminal-root page, the Authorize.Net hosted payment form, or a Worldpay hosted payment page, identifies its processor and returns `unknown` with `processor_variant_unverified`. A new fingerprint never declares payment support by itself.
|
|
209
209
|
|
|
210
|
+
PayPal's legacy Hosted Fields (`paypal`) are identified from the card fields PayPal's JS SDK renders with `components=hosted-fields`: braintree-web builds whose version carries `-payments-sdk`, such as `3.32.0-payments-sdk-dev`, served from `assets.braintreegateway.com` as `hosted-fields-input-frame.min.html` and `hosted-fields-payments-sdk-tokenization-frame.min.html`. PayPal's frames send the card to PayPal, not to Braintree, so a page that shows them names `paypal` and returns `unknown` with `processor_variant_unverified`, and no braintree-web file from a `-payments-sdk` build counts as Braintree evidence. The SDK fails the card request those frames send; see the PayPal Hosted Fields row in the [README's processor table](./README.md#supported-processors).
|
|
211
|
+
|
|
210
212
|
Adyen's `supported` covers the Sessions payment request on Adyen's own hosts. Some Adyen merchants post the four encrypted fields to their own server instead, and their pages load the same secured fields, so an inspection identifies Adyen on both and cannot tell the two flows apart. The SDK pays a merchant's own endpoint only through a merchant profile Agentcard reviewed for it; the reviewed profiles (Dunelm and Cinemark) are in `observe`, so the SDK aborts their card requests and nothing is charged. The `actual_payment_request_validation` requirement is where that difference shows: check the paused request's host before treating an Adyen result as payable.
|
|
211
213
|
|
|
212
214
|
Authorize.Net Direct Post Method and SIM ship dark. The SDK pauses their `transact.dll` card form only for an org Agentcard enabled for them (capability `authorize_net_dpm_form`), and the bundled catalog leaves them out, so the table above does not list them and inspection answers for their forms exactly as it did before: the SIM hosted form on `secure.authorize.net` identifies Authorize.Net and returns `unknown` with `processor_variant_unverified`.
|
package/README.md
CHANGED
|
@@ -254,7 +254,8 @@ Coverage is specific to the processor request format, merchant setup, browser tr
|
|
|
254
254
|
| Checkout.com | supported |
|
|
255
255
|
| Mercado Pago | Card tokenization and prepared checkout are implemented. Guest Checkout Pro in Mexico also corrects the issuer for one native card association when the selected card has the same brand and type. One live MXN 40 Lotería Chida purchase with published SDK 0.10.0 completed automatically through Pay; the merchant confirmed paid status and PDF fulfillment. The historical SDK result remains unknown because the private receipt adapter rejected a relative download URL; a separate read with the corrected adapter confirms that same paid receipt. Independent processor capture/settlement and other country or integration paths remain unverified. |
|
|
256
256
|
| VGS Collect (Very Good Security; Wolt) | not supported: VGS's proxy aliases only submissions from its own iframe, so a replay from the cardholder's device is refused by the merchant (verified on Wolt, 2026-09-03). Not recognized, so the agent's browser is not paused there |
|
|
257
|
-
|
|
|
257
|
+
| PayPal Hosted Fields (the legacy card fields of PayPal's JS SDK; Planet Golf UK) | not supported: no Agentcard payment takes over the card these fields send to `cors.api.paypal.com`. The SDK fails that request for every company, so PayPal's payment confirm never receives the dummy card, and the controller reads `unsupported` with reason `unsupported_paypal_hosted_fields` and holds the attachment until you attach again; a PayPal wallet payment continues untouched |
|
|
258
|
+
| Adyen | supported (mode `cse`): the vault encrypts the card for Adyen on the cardholder's device and your browser sends it. Sessions flow: the paused request is `/checkoutshopper/v1/sessions/{id}/payments` on Adyen's own hosts. A merchant that posts the encrypted fields to its own server pauses only when Agentcard reviewed it as a merchant profile. Cinemark and Dick's Sporting Goods' own storefront take payments for a company Agentcard turned them on for; every other reviewed merchant is in `observe`, so its card requests are reported and aborted and nothing pays there yet (see "Adyen merchants that take the card on their own server"). Without a preparation, approval starts at Pay and must land before Adyen Web's own request timeout (observed at 60 seconds on Adyen Web 6.41 and 6.44; not enforced by the SDK); `prepare({ psp: 'adyen' })` moves the approval before Pay |
|
|
258
259
|
| ACI Worldwide COPYandPAY (Halfords) | dark launch: served only after `syncRegistry()` and only for an org Agentcard enabled; prepared checkout only. The card-number request is replayed from the cardholder's device, the page's own CVC and expiry requests are answered locally, and `/payment` continues once. See [Pay an ACI COPYandPAY checkout](#pay-an-aci-copyandpay-checkout) |
|
|
259
260
|
| Tranzila | supported (mode `hosted_form`): the cardholder finishes on Tranzila's own page; the paused form navigation resolves to a synthetic page, and you poll the merchant's order state |
|
|
260
261
|
| Fiserv Commerce Hub | implemented (mode `cse`, prepared checkout only) and off until Agentcard turns it on for your company: the card frame's card capture on `connect.fiservapis.com` (or `connect-cert.fiservapis.com` in the sandbox), with the approved card encrypted on the cardholder's device under a key Agentcard reviewed for the merchant. A merchant whose frame posts to Fiserv's `payment-link` endpoint is not recognized. No live merchant purchase is verified yet |
|
|
@@ -424,21 +425,25 @@ mode; the difference matters if you drive `authorize()` yourself.
|
|
|
424
425
|
page's origin, the exact origin, path and query of the card request, the
|
|
425
426
|
session headers and cookies it needs, and the answer headers that may come
|
|
426
427
|
back. The adapters arm every profile's endpoint (`cardEndpointPatterns()`
|
|
427
|
-
lists the `Fetch.enable` patterns) and pause a
|
|
428
|
-
card request URL
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
428
|
+
lists the `Fetch.enable` patterns) and pause a request to exactly a
|
|
429
|
+
profile's card request URL, sent with the profile's own method (a POST, or
|
|
430
|
+
the PUT Target Optical's checkout sends), when it carries a card: anything
|
|
431
|
+
at the profile's card number field, or a Luhn-valid number of 12 to 19
|
|
432
|
+
digits anywhere in the body, however it is written. A body the adapter
|
|
433
|
+
cannot read counts as carrying one. A GET, a CORS preflight, another
|
|
434
|
+
method, a look-alike URL, or a request to the same URL with no card in it
|
|
435
|
+
(an IHG stay paid with points, a saved card, a room another SynXis hotel
|
|
436
|
+
guarantees with a token) continues untouched. While this
|
|
435
437
|
build's copy of a profile does not take payments (every profile is in
|
|
436
438
|
observe today) a card request is refused as `unsupported_checkout` before
|
|
437
439
|
any authorization exists. When the body is the request the profile
|
|
438
440
|
reviewed, the controller reads `unsupported` with reason
|
|
439
441
|
`merchant_card_endpoint_not_enabled` and the event names the profile; a
|
|
440
442
|
card in any other body reads reason `unrecognized_payment_endpoint`, and
|
|
441
|
-
the event names only the endpoint origin.
|
|
443
|
+
the event names only the endpoint origin. LookFantastic's checkout takes
|
|
444
|
+
the card in BR-DGE's card fields and then sends the masked card number
|
|
445
|
+
BR-DGE returns in its order, so the adapters refuse that order with reason
|
|
446
|
+
`unrecognized_payment_endpoint`. For a
|
|
442
447
|
profile that takes payments, the adapter captures the request's session:
|
|
443
448
|
the profile's own header names from the paused request, the endpoint's
|
|
444
449
|
cookie jar for a cookie profile (`Network.getCookies` or
|
|
@@ -1247,7 +1252,7 @@ Some Adyen merchants have adyen-web encrypt the card into four fields and post t
|
|
|
1247
1252
|
|
|
1248
1253
|
- A request that carries no encrypted card (a basket query, a preflight) continues as before.
|
|
1249
1254
|
- A body the browser did not hand over (a blob or a file, which neither adapter can read) is not proven card-free, so any request but a `GET`, `HEAD` or `OPTIONS` that carries one is treated as a card request the profile cannot finish. Playwright reports a missing body the same way, so an empty body is treated alike in both adapters.
|
|
1250
|
-
- A profile in `observe` never pays. Its card request is aborted before it reaches the merchant, and the adapters report `merchant_profile_observed` with the profile, the templated URL, the verdict and `keyPathSha256`, the SHA-256 of the body's sorted key paths (never a value; `null` for a body that was not read), then `unsupported_checkout`. The controller reads `unsupported` with reason `merchant_profile_observe`, and no authorization exists.
|
|
1255
|
+
- A profile in `observe` never pays. Its card request is aborted before it reaches the merchant, and the adapters report `merchant_profile_observed` with the profile, the templated URL, the verdict and `keyPathSha256`, the SHA-256 of the body's sorted key paths (never a value; `null` for a body that was not read), then `unsupported_checkout`. The controller reads `unsupported` with reason `merchant_profile_observe`, and no authorization exists. `cinemark_initiate` and `dsg_paygate_v2` (Dick's Sporting Goods' own storefront) ship `enabled`, and the API serves them `enabled` only to a client of a company Agentcard turned them on for. Every other reviewed profile ships in `observe`, `dunelm_was_graphql` and `dsg_other_stores_paygate_v2` (Dick's paygate at Golf Galaxy, Public Lands and Going Going Gone) among them.
|
|
1251
1256
|
- A card request the profile cannot finish (storing the card, a stored card, another operation, three fields instead of four, a sibling endpoint, an unreadable body) is aborted with `unsupported_checkout` and the reason, and reads `unsupported` with reason `merchant_request_unsupported`.
|
|
1252
1257
|
- The profile's own card request, once Agentcard enables the profile for your client and this build, pays only through a preparation for that profile. Without one it is aborted before any pause, prompt or authorization: the adapters report `blocked` with `preparation_required`, the controller reads `failed` with that reason, and `prepare()` is refused on that attachment, as after any first card request. Create a new attachment and prepare before Pay.
|
|
1253
1258
|
|
|
@@ -1261,7 +1266,7 @@ After hand-off, a `5xx` final answer (after any redirect), a network failure, or
|
|
|
1261
1266
|
|
|
1262
1267
|
#### Testing against your own Adyen TEST account
|
|
1263
1268
|
|
|
1264
|
-
A test-mode client can run the same checkout against its own server and its own Adyen TEST account,
|
|
1269
|
+
A test-mode client can run the same checkout against its own server and its own Adyen TEST account, whether or not Agentcard has enabled the reviewed profile. Declare your endpoint on the client, before any attach, as taking the card request of a profile this build reviewed:
|
|
1265
1270
|
|
|
1266
1271
|
```ts
|
|
1267
1272
|
const vault = new VaultClient({ clientId, clientSecret, sandboxMerchants: [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256:
|
|
3
|
+
// artifact-sha256: 4790fdfc656acbda50ddf8471507933ddd4938f4c5b351ec096a9549a7c8c257
|
|
4
4
|
// src/deep-freeze.js
|
|
5
5
|
function deepFreeze(value) {
|
|
6
6
|
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -1086,6 +1086,118 @@ function selectMerchantExchanges(profile, stageName, input) {
|
|
|
1086
1086
|
return { ok: true, responses: newestMerchantExchanges(kept) };
|
|
1087
1087
|
}
|
|
1088
1088
|
// src/merchant-profiles.js
|
|
1089
|
+
var DSG_PAYGATE_V2 = /* @__PURE__ */ deepFreeze({
|
|
1090
|
+
method: "POST",
|
|
1091
|
+
path: "/prod/api/paygate/v2/authorization",
|
|
1092
|
+
query: {},
|
|
1093
|
+
// The page sends true for every checkout its translator read, the only checkouts
|
|
1094
|
+
// the amount source below maps; a legacy checkout is not this card request.
|
|
1095
|
+
requires: [{ path: ["isModernOrder"], equals: true }],
|
|
1096
|
+
holder: ["tenders", "encryptedCardTenders", 0],
|
|
1097
|
+
fields: {
|
|
1098
|
+
encryptedCardNumber: "cardNumber",
|
|
1099
|
+
encryptedExpiryMonth: "expirationMonth",
|
|
1100
|
+
encryptedExpiryYear: "expirationYear",
|
|
1101
|
+
encryptedSecurityCode: "cvv"
|
|
1102
|
+
},
|
|
1103
|
+
// cardType and vinBalue describe the dummy the agent typed: the facts below
|
|
1104
|
+
// write the approved card's instead, so nothing is removed.
|
|
1105
|
+
remove: [],
|
|
1106
|
+
refuse: [
|
|
1107
|
+
{ path: ["tenders", "encryptedCardTenders", 0, "saveForLater"], when: "true" },
|
|
1108
|
+
// A saved card, or a second card tender the Vault never encrypted.
|
|
1109
|
+
{ path: ["tenders", "savedCardTender"], when: "present" },
|
|
1110
|
+
{ path: ["tenders", "encryptedCardTenders", 1], when: "present" },
|
|
1111
|
+
// A gift card pays part of the order, so the card pays less than the total.
|
|
1112
|
+
{ path: ["tenders", "giftCardTenders"], when: "present" }
|
|
1113
|
+
],
|
|
1114
|
+
amount: null,
|
|
1115
|
+
// Read Sep 25, 2026 (research amount_source/dickssportinggoods). Every checkout call
|
|
1116
|
+
// on the checkout page answers the whole checkout, camelCase on the wire: the page
|
|
1117
|
+
// load's PATCH (or the create), then every address, shipping, promotion, gift card,
|
|
1118
|
+
// tip and ScoreCard change. pricing.totalRemaining is what the card pays, a string
|
|
1119
|
+
// with exactly two decimals ("123.45"; the page itself drops the dot for minor
|
|
1120
|
+
// units). transactionReference is the checkout key, in the body and the URL. After
|
|
1121
|
+
// payment the order submit and the confirmation read return the placed checkout.
|
|
1122
|
+
amountSource: {
|
|
1123
|
+
id: "dsg_checkout_pricing",
|
|
1124
|
+
origin: "checkout",
|
|
1125
|
+
format: "json",
|
|
1126
|
+
responses: [
|
|
1127
|
+
{ method: "PATCH", path: "/api/v1/checkouts/:checkoutKey", query: {} },
|
|
1128
|
+
{ method: "POST", path: "/api/v1/checkouts", query: {} },
|
|
1129
|
+
{ method: "POST", path: "/api/v1/checkouts/:checkoutKey/addresses", queries: [{}, { type: "billing" }, { type: "shipping" }] },
|
|
1130
|
+
{ method: "PUT", path: "/api/v1/checkouts/:checkoutKey/shipping/selected/groups/:groupNumber/mode", query: {} },
|
|
1131
|
+
{ method: "PUT", path: "/api/v2/checkouts/:checkoutKey/promotion/cart", query: {} },
|
|
1132
|
+
{ method: "DELETE", path: "/api/v2/checkouts/:checkoutKey/promotion/cart", query: {} },
|
|
1133
|
+
{ method: "POST", path: "/api/v1/checkouts/:checkoutKey/gift-cards", query: {} },
|
|
1134
|
+
{ method: "DELETE", path: "/api/v1/checkouts/:checkoutKey/gift-cards/:giftCardIndex", query: {} },
|
|
1135
|
+
{ method: "PUT", path: "/api/v1/checkouts/:checkoutKey/tip", query: {} },
|
|
1136
|
+
{ method: "PUT", path: "/api/v1/checkouts/:checkoutKey/scorecard", query: {} },
|
|
1137
|
+
{ method: "DELETE", path: "/api/v1/checkouts/:checkoutKey/scorecard", query: {} },
|
|
1138
|
+
{ method: "DELETE", path: "/api/v1/checkouts/:checkoutKey/v2/gifts", query: {} }
|
|
1139
|
+
],
|
|
1140
|
+
total: { path: ["pricing", "totalRemaining"], format: "decimal2" },
|
|
1141
|
+
unit: "major",
|
|
1142
|
+
currency: "USD",
|
|
1143
|
+
currencyPath: ["pricing", "currency"],
|
|
1144
|
+
bind: [
|
|
1145
|
+
{ source: ["checkoutKey"], card: ["transactionReference"] },
|
|
1146
|
+
{ placeholder: "checkoutKey", card: ["transactionReference"] }
|
|
1147
|
+
],
|
|
1148
|
+
// The order total and the card's share are the same text. Anything that lowers the
|
|
1149
|
+
// share below the total (a gift card, ScoreCard rewards) or lands in only one of them
|
|
1150
|
+
// (a tip, a donation) is refused until a HAR shows which one the charge follows, so
|
|
1151
|
+
// the approved amount and the placed checkout's total read after payment agree.
|
|
1152
|
+
requires: [{ path: ["pricing", "total"], equalsPath: ["pricing", "totalRemaining"] }],
|
|
1153
|
+
refuse: [
|
|
1154
|
+
// A placed checkout is not the one being paid.
|
|
1155
|
+
{ path: ["orderPlacedTime"], when: "true" },
|
|
1156
|
+
// Gift cards and a checkout with no address yet (tax not estimated) wait for a HAR.
|
|
1157
|
+
{ path: ["payment", "giftCards"], when: "nonEmpty" },
|
|
1158
|
+
{ path: ["addresses"], when: "empty" }
|
|
1159
|
+
],
|
|
1160
|
+
maxAgeMs: 9e5,
|
|
1161
|
+
afterPayment: {
|
|
1162
|
+
origin: "checkout",
|
|
1163
|
+
format: "json",
|
|
1164
|
+
responses: [
|
|
1165
|
+
{ method: "POST", path: "/api/v1/checkouts/:checkoutKey", query: {} },
|
|
1166
|
+
{ method: "GET", path: "/api/v1/checkouts/submitted/:checkoutKey", query: {} }
|
|
1167
|
+
],
|
|
1168
|
+
// The order total, which the requires rule above held equal to the card's share
|
|
1169
|
+
// before payment. totalRemaining is not read here: after placement it can drop to
|
|
1170
|
+
// what is still unpaid, which would hide a higher charge.
|
|
1171
|
+
total: { path: ["pricing", "total"], format: "decimal2" },
|
|
1172
|
+
unit: "major",
|
|
1173
|
+
currency: "USD",
|
|
1174
|
+
currencyPath: ["pricing", "currency"],
|
|
1175
|
+
bind: [
|
|
1176
|
+
{ source: ["checkoutKey"], card: ["transactionReference"] },
|
|
1177
|
+
{ placeholder: "checkoutKey", card: ["transactionReference"] }
|
|
1178
|
+
],
|
|
1179
|
+
refuse: [{ path: ["orderPlacedTime"], when: "unset" }]
|
|
1180
|
+
}
|
|
1181
|
+
},
|
|
1182
|
+
facts: [
|
|
1183
|
+
// adyen-web's brand codes, the ones the page's supported list names; Dick's own
|
|
1184
|
+
// private-label (plcc, comc) cards have no Agentcard brand and are refused.
|
|
1185
|
+
{
|
|
1186
|
+
path: ["tenders", "encryptedCardTenders", 0, "cardType"],
|
|
1187
|
+
fact: "brand",
|
|
1188
|
+
map: { visa: "visa", mastercard: "mc", amex: "amex", discover: "discover", diners: "diners", jcb: "jcb" }
|
|
1189
|
+
},
|
|
1190
|
+
{ path: ["tenders", "encryptedCardTenders", 0, "vinBalue"], fact: "bin6" }
|
|
1191
|
+
],
|
|
1192
|
+
adyen: {
|
|
1193
|
+
environment: "live",
|
|
1194
|
+
clientKey: "live_YJLFKT47JJG2RI7QIYX6HTKKDEDOEGOH",
|
|
1195
|
+
publicKeySha256: "da33611149dff3ab715be8671c0550bb8aec6112983b2473db9ee7afb829c110"
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
function dsgPaygateProfile(own) {
|
|
1199
|
+
return { ...own, ...DSG_PAYGATE_V2 };
|
|
1200
|
+
}
|
|
1089
1201
|
var MERCHANT_HOSTED_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
1090
1202
|
{
|
|
1091
1203
|
// Read Sep 23, 2026. assets.dunelm.com checkout-page bundle: submitPayment
|
|
@@ -1181,10 +1293,13 @@ var MERCHANT_HOSTED_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1181
1293
|
// client key from the three securedFields iframes on
|
|
1182
1294
|
// checkoutshopper-live-us.adyen.com (adyen-web 6.x). The request body itself
|
|
1183
1295
|
// was not captured: the guest email gate stopped the run before Pay. Public
|
|
1184
|
-
// key read from checkoutshopper-live-us.adyen.com on 2026-09-25 (UTC).
|
|
1296
|
+
// key read from checkoutshopper-live-us.adyen.com on 2026-09-25 (UTC). Enabled
|
|
1297
|
+
// Oct 7, 2026 under D1 (amountSource, above): live checks on Sep 30 and Oct 6
|
|
1298
|
+
// matched its key pin, its card request as the page's script builds it, and its
|
|
1299
|
+
// checkout total.
|
|
1185
1300
|
id: "cinemark_initiate",
|
|
1186
1301
|
merchant: "Cinemark",
|
|
1187
|
-
status: "
|
|
1302
|
+
status: "enabled",
|
|
1188
1303
|
origins: [{ checkout: "https://www.cinemark.com", endpoint: "https://www.cinemark.com" }],
|
|
1189
1304
|
method: "POST",
|
|
1190
1305
|
path: "/api/adyen/initiatePayment",
|
|
@@ -1257,137 +1372,29 @@ var MERCHANT_HOSTED_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1257
1372
|
publicKeySha256: "9dd5ef549c7454d60ca970ff76c8f4cac36d79f2961e59eca9116791e86cbcc5"
|
|
1258
1373
|
}
|
|
1259
1374
|
},
|
|
1260
|
-
{
|
|
1261
|
-
//
|
|
1262
|
-
//
|
|
1263
|
-
//
|
|
1264
|
-
// beside cardType (adyen-web's first detected brand), saveForLater (the save-card
|
|
1265
|
-
// box) and vinBalue (the six digits onBinValue reports). The page posts it with
|
|
1266
|
-
// transactionReference, customer.device and any giftCardTenders to
|
|
1267
|
-
// <payments host>/prod/api/paygate/v2/authorization, the host picked from the
|
|
1268
|
-
// page's own (Dick's, Golf Galaxy, Public Lands, Going Going Gone). The same slot
|
|
1269
|
-
// carries Afterpay, CashApp and Affirm tenders with literal strings where the JWEs
|
|
1270
|
-
// go, and a saved card goes to tenders.savedCardTender instead. The body names no
|
|
1271
|
-
// amount: the paygate charges the cart it holds for transactionReference.
|
|
1272
|
-
// mountCCForm names the client key and environment 'live'. Public key read from
|
|
1273
|
-
// checkoutshopper-live.adyen.com on 2026-09-25 (UTC).
|
|
1375
|
+
/* @__PURE__ */ dsgPaygateProfile({
|
|
1376
|
+
// Dick's own storefront, the one live checks read on Sep 30 and Oct 6, 2026: its key
|
|
1377
|
+
// pin, its card request as the page's code builds it, its card facts and its checkout
|
|
1378
|
+
// pricing matched DSG_PAYGATE_V2. Enabled Oct 7, 2026 under D1 (amountSource, above).
|
|
1274
1379
|
id: "dsg_paygate_v2",
|
|
1275
1380
|
merchant: "Dick's Sporting Goods",
|
|
1381
|
+
status: "enabled",
|
|
1382
|
+
origins: [
|
|
1383
|
+
{ checkout: "https://www.dickssportinggoods.com", endpoint: "https://payments.dickssportinggoods.com" }
|
|
1384
|
+
]
|
|
1385
|
+
}),
|
|
1386
|
+
/* @__PURE__ */ dsgPaygateProfile({
|
|
1387
|
+
// The same paygate on the three other storefronts its page names. No live check has
|
|
1388
|
+
// read them, so their card requests stay paused and aborted in observe.
|
|
1389
|
+
id: "dsg_other_stores_paygate_v2",
|
|
1390
|
+
merchant: "Golf Galaxy, Public Lands and Going Going Gone",
|
|
1276
1391
|
status: "observe",
|
|
1277
1392
|
origins: [
|
|
1278
|
-
{ checkout: "https://www.dickssportinggoods.com", endpoint: "https://payments.dickssportinggoods.com" },
|
|
1279
1393
|
{ checkout: "https://www.golfgalaxy.com", endpoint: "https://payments.golfgalaxy.com" },
|
|
1280
1394
|
{ checkout: "https://www.publiclands.com", endpoint: "https://payments.publiclands.com" },
|
|
1281
1395
|
{ checkout: "https://www.goinggoinggone.com", endpoint: "https://payments.goinggoinggone.com" }
|
|
1282
|
-
]
|
|
1283
|
-
|
|
1284
|
-
path: "/prod/api/paygate/v2/authorization",
|
|
1285
|
-
query: {},
|
|
1286
|
-
// The page sends true for every checkout its translator read, the only checkouts
|
|
1287
|
-
// the amount source below maps; a legacy checkout is not this card request.
|
|
1288
|
-
requires: [{ path: ["isModernOrder"], equals: true }],
|
|
1289
|
-
holder: ["tenders", "encryptedCardTenders", 0],
|
|
1290
|
-
fields: {
|
|
1291
|
-
encryptedCardNumber: "cardNumber",
|
|
1292
|
-
encryptedExpiryMonth: "expirationMonth",
|
|
1293
|
-
encryptedExpiryYear: "expirationYear",
|
|
1294
|
-
encryptedSecurityCode: "cvv"
|
|
1295
|
-
},
|
|
1296
|
-
// cardType and vinBalue describe the dummy the agent typed: the facts below
|
|
1297
|
-
// write the approved card's instead, so nothing is removed.
|
|
1298
|
-
remove: [],
|
|
1299
|
-
refuse: [
|
|
1300
|
-
{ path: ["tenders", "encryptedCardTenders", 0, "saveForLater"], when: "true" },
|
|
1301
|
-
// A saved card, or a second card tender the Vault never encrypted.
|
|
1302
|
-
{ path: ["tenders", "savedCardTender"], when: "present" },
|
|
1303
|
-
{ path: ["tenders", "encryptedCardTenders", 1], when: "present" },
|
|
1304
|
-
// A gift card pays part of the order, so the card pays less than the total.
|
|
1305
|
-
{ path: ["tenders", "giftCardTenders"], when: "present" }
|
|
1306
|
-
],
|
|
1307
|
-
amount: null,
|
|
1308
|
-
// Read Sep 25, 2026 (research amount_source/dickssportinggoods). Every checkout call
|
|
1309
|
-
// on the checkout page answers the whole checkout, camelCase on the wire: the page
|
|
1310
|
-
// load's PATCH (or the create), then every address, shipping, promotion, gift card,
|
|
1311
|
-
// tip and ScoreCard change. pricing.totalRemaining is what the card pays, a string
|
|
1312
|
-
// with exactly two decimals ("123.45"; the page itself drops the dot for minor
|
|
1313
|
-
// units). transactionReference is the checkout key, in the body and the URL. After
|
|
1314
|
-
// payment the order submit and the confirmation read return the placed checkout.
|
|
1315
|
-
amountSource: {
|
|
1316
|
-
id: "dsg_checkout_pricing",
|
|
1317
|
-
origin: "checkout",
|
|
1318
|
-
format: "json",
|
|
1319
|
-
responses: [
|
|
1320
|
-
{ method: "PATCH", path: "/api/v1/checkouts/:checkoutKey", query: {} },
|
|
1321
|
-
{ method: "POST", path: "/api/v1/checkouts", query: {} },
|
|
1322
|
-
{ method: "POST", path: "/api/v1/checkouts/:checkoutKey/addresses", queries: [{}, { type: "billing" }, { type: "shipping" }] },
|
|
1323
|
-
{ method: "PUT", path: "/api/v1/checkouts/:checkoutKey/shipping/selected/groups/:groupNumber/mode", query: {} },
|
|
1324
|
-
{ method: "PUT", path: "/api/v2/checkouts/:checkoutKey/promotion/cart", query: {} },
|
|
1325
|
-
{ method: "DELETE", path: "/api/v2/checkouts/:checkoutKey/promotion/cart", query: {} },
|
|
1326
|
-
{ method: "POST", path: "/api/v1/checkouts/:checkoutKey/gift-cards", query: {} },
|
|
1327
|
-
{ method: "DELETE", path: "/api/v1/checkouts/:checkoutKey/gift-cards/:giftCardIndex", query: {} },
|
|
1328
|
-
{ method: "PUT", path: "/api/v1/checkouts/:checkoutKey/tip", query: {} },
|
|
1329
|
-
{ method: "PUT", path: "/api/v1/checkouts/:checkoutKey/scorecard", query: {} },
|
|
1330
|
-
{ method: "DELETE", path: "/api/v1/checkouts/:checkoutKey/scorecard", query: {} },
|
|
1331
|
-
{ method: "DELETE", path: "/api/v1/checkouts/:checkoutKey/v2/gifts", query: {} }
|
|
1332
|
-
],
|
|
1333
|
-
total: { path: ["pricing", "totalRemaining"], format: "decimal2" },
|
|
1334
|
-
unit: "major",
|
|
1335
|
-
currency: "USD",
|
|
1336
|
-
currencyPath: ["pricing", "currency"],
|
|
1337
|
-
bind: [
|
|
1338
|
-
{ source: ["checkoutKey"], card: ["transactionReference"] },
|
|
1339
|
-
{ placeholder: "checkoutKey", card: ["transactionReference"] }
|
|
1340
|
-
],
|
|
1341
|
-
// The order total and the card's share are the same text. Anything that lowers the
|
|
1342
|
-
// share below the total (a gift card, ScoreCard rewards) or lands in only one of them
|
|
1343
|
-
// (a tip, a donation) is refused until a HAR shows which one the charge follows, so
|
|
1344
|
-
// the approved amount and the placed checkout's total read after payment agree.
|
|
1345
|
-
requires: [{ path: ["pricing", "total"], equalsPath: ["pricing", "totalRemaining"] }],
|
|
1346
|
-
refuse: [
|
|
1347
|
-
// A placed checkout is not the one being paid.
|
|
1348
|
-
{ path: ["orderPlacedTime"], when: "true" },
|
|
1349
|
-
// Gift cards and a checkout with no address yet (tax not estimated) wait for a HAR.
|
|
1350
|
-
{ path: ["payment", "giftCards"], when: "nonEmpty" },
|
|
1351
|
-
{ path: ["addresses"], when: "empty" }
|
|
1352
|
-
],
|
|
1353
|
-
maxAgeMs: 9e5,
|
|
1354
|
-
afterPayment: {
|
|
1355
|
-
origin: "checkout",
|
|
1356
|
-
format: "json",
|
|
1357
|
-
responses: [
|
|
1358
|
-
{ method: "POST", path: "/api/v1/checkouts/:checkoutKey", query: {} },
|
|
1359
|
-
{ method: "GET", path: "/api/v1/checkouts/submitted/:checkoutKey", query: {} }
|
|
1360
|
-
],
|
|
1361
|
-
// The order total, which the requires rule above held equal to the card's share
|
|
1362
|
-
// before payment. totalRemaining is not read here: after placement it can drop to
|
|
1363
|
-
// what is still unpaid, which would hide a higher charge.
|
|
1364
|
-
total: { path: ["pricing", "total"], format: "decimal2" },
|
|
1365
|
-
unit: "major",
|
|
1366
|
-
currency: "USD",
|
|
1367
|
-
currencyPath: ["pricing", "currency"],
|
|
1368
|
-
bind: [
|
|
1369
|
-
{ source: ["checkoutKey"], card: ["transactionReference"] },
|
|
1370
|
-
{ placeholder: "checkoutKey", card: ["transactionReference"] }
|
|
1371
|
-
],
|
|
1372
|
-
refuse: [{ path: ["orderPlacedTime"], when: "unset" }]
|
|
1373
|
-
}
|
|
1374
|
-
},
|
|
1375
|
-
facts: [
|
|
1376
|
-
// adyen-web's brand codes, the ones the page's supported list names; Dick's own
|
|
1377
|
-
// private-label (plcc, comc) cards have no Agentcard brand and are refused.
|
|
1378
|
-
{
|
|
1379
|
-
path: ["tenders", "encryptedCardTenders", 0, "cardType"],
|
|
1380
|
-
fact: "brand",
|
|
1381
|
-
map: { visa: "visa", mastercard: "mc", amex: "amex", discover: "discover", diners: "diners", jcb: "jcb" }
|
|
1382
|
-
},
|
|
1383
|
-
{ path: ["tenders", "encryptedCardTenders", 0, "vinBalue"], fact: "bin6" }
|
|
1384
|
-
],
|
|
1385
|
-
adyen: {
|
|
1386
|
-
environment: "live",
|
|
1387
|
-
clientKey: "live_YJLFKT47JJG2RI7QIYX6HTKKDEDOEGOH",
|
|
1388
|
-
publicKeySha256: "da33611149dff3ab715be8671c0550bb8aec6112983b2473db9ee7afb829c110"
|
|
1389
|
-
}
|
|
1390
|
-
},
|
|
1396
|
+
]
|
|
1397
|
+
}),
|
|
1391
1398
|
{
|
|
1392
1399
|
// Read Sep 25, 2026, from public code only: the WooCommerce plugin super-payments 1.40.1 and
|
|
1393
1400
|
// cdn.superpayments.com/js/v2/payment.webcomponents.script.js as it stood on Sep 19.
|
package/dist/card-endpoint.d.ts
CHANGED
|
@@ -12,7 +12,8 @@ export interface CardEndpointProfile {
|
|
|
12
12
|
readonly frame: string | null;
|
|
13
13
|
/** The exact https origin the card request goes to. */
|
|
14
14
|
readonly endpoint: string;
|
|
15
|
-
|
|
15
|
+
/** A POST, or a PUT for a page that sends its card request as one (Target Optical's addPayment). */
|
|
16
|
+
readonly method: 'POST' | 'PUT';
|
|
16
17
|
readonly path: string;
|
|
17
18
|
readonly query: Readonly<Record<string, string>>;
|
|
18
19
|
/** json_api is a JSON object sent as application/vnd.api+json. */
|
|
@@ -130,6 +131,13 @@ export declare function cardEndpointById(id: string): CardEndpointProfile | null
|
|
|
130
131
|
export declare function cardEndpointPasses(profile: CardEndpointProfile, body: string | null, contentType: string | undefined): boolean;
|
|
131
132
|
/** The reviewed profile (any status) one of whose resend requests `url` and `method` are, or null: the adapters abort it. */
|
|
132
133
|
export declare function cardEndpointResendFor(url: string, method: string): CardEndpointProfile | null;
|
|
134
|
+
/**
|
|
135
|
+
* Whether a URL is any profile's card request or one of its resends, by any method the
|
|
136
|
+
* table names: the Playwright adapter's route filter, which sees the URL alone. The
|
|
137
|
+
* pre-claim gates then judge each routed request by its own method and body (a GET or a
|
|
138
|
+
* CORS preflight to the same URL defers), so routing a URL is never a decision to pause it.
|
|
139
|
+
*/
|
|
140
|
+
export declare function cardEndpointRouted(url: string): boolean;
|
|
133
141
|
/** The origins of the profiles whose merchant keeps its own record of the order: the only responses card-endpoint-facts.ts records. */
|
|
134
142
|
export declare function cardEndpointFactsOrigins(): readonly string[];
|
|
135
143
|
/**
|
package/dist/card-endpoint.js
CHANGED
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
// typed into, the exact origin, path and query of the card request, which session
|
|
12
12
|
// headers and cookies it needs, and which answer headers may travel back.
|
|
13
13
|
//
|
|
14
|
-
// The adapters arm every profile's endpoint. A
|
|
15
|
-
// request URL
|
|
14
|
+
// The adapters arm every profile's endpoint. A request that is exactly a profile's card
|
|
15
|
+
// request, its URL and its method (a POST, or Target Optical's PUT), pauses when it
|
|
16
|
+
// carries a card (cardEndpointCarriesCard); one that carries
|
|
16
17
|
// none, such as a stay paid with points or a card a processor's own fields tokenized,
|
|
17
18
|
// is the merchant's own business and continues untouched. While this build's copy of
|
|
18
19
|
// the profile does not take payments (observe, a fact only a partner HAR can pin, no
|
|
@@ -108,6 +109,18 @@ export function cardEndpointResendFor(url, method) {
|
|
|
108
109
|
return null;
|
|
109
110
|
}
|
|
110
111
|
}
|
|
112
|
+
// Every method a profile's card request or one of its resends is sent with (a POST, Target
|
|
113
|
+
// Optical's PUT addPayment and PUT card save), from this build's own table.
|
|
114
|
+
const ROUTED_METHODS = [...new Set(PROFILES.flatMap((profile) => [profile.method, ...(profile.resends ?? []).map((resend) => resend.method)]))];
|
|
115
|
+
/**
|
|
116
|
+
* Whether a URL is any profile's card request or one of its resends, by any method the
|
|
117
|
+
* table names: the Playwright adapter's route filter, which sees the URL alone. The
|
|
118
|
+
* pre-claim gates then judge each routed request by its own method and body (a GET or a
|
|
119
|
+
* CORS preflight to the same URL defers), so routing a URL is never a decision to pause it.
|
|
120
|
+
*/
|
|
121
|
+
export function cardEndpointRouted(url) {
|
|
122
|
+
return ROUTED_METHODS.some((method) => cardEndpointFor(url, method) !== null || cardEndpointResendFor(url, method) !== null);
|
|
123
|
+
}
|
|
111
124
|
/** The origins of the profiles whose merchant keeps its own record of the order: the only responses card-endpoint-facts.ts records. */
|
|
112
125
|
export function cardEndpointFactsOrigins() {
|
|
113
126
|
return [...new Set(PROFILES.filter((profile) => profile.facts).map((profile) => profile.endpoint))];
|
package/dist/cdp.js
CHANGED
|
@@ -12,6 +12,7 @@ import { GMO_TOKEN_V1_PATTERNS, gmoRequestShop, isGmoTokenV1Request, recognizesG
|
|
|
12
12
|
import { DatatransRepeats } from './datatrans.js';
|
|
13
13
|
import { SHIJI_STS_COMPANION_WINDOW_MS, ShijiStsCompanions, ShijiStsError, isShijiStsHost, shijiStsTokenContinuation, } from './shiji-sts.js';
|
|
14
14
|
import { isArmedEkashuUrl } from './ekashu.js';
|
|
15
|
+
import { PAYPAL_HOSTED_FIELDS_PATTERNS, isPaypalHostedFieldsConfirm } from './paypal-hosted-fields.js';
|
|
15
16
|
import { CheckoutAttachmentError, attachmentDeadline, attachmentFailure, withinAttachmentDeadline } from './attachment.js';
|
|
16
17
|
import { cseBody } from './cse-body.js';
|
|
17
18
|
import { STRIPE_SETUP_INTENT_CONFIRM, setupConfirmPayingWith } from './stripe-setup.js';
|
|
@@ -23,7 +24,7 @@ import { hostedFormSubmittedPage } from './hosted-form.js';
|
|
|
23
24
|
import { amadeusSendWindowMs, armSendWindow, failsAsNetworkError } from './amadeus.js';
|
|
24
25
|
import { CheckoutLifecycle, paymentEndpointGuards } from './lifecycle.js';
|
|
25
26
|
import { CaptureContexts, MicroformFrames, judgeMicroformFrameUrls, loadedCardMicroformFromUrls, refusedRequestFailure, } from './cybersource-flex.js';
|
|
26
|
-
import { CARD_ENDPOINT_REFUSED_STEP_ANSWER, CARD_ENDPOINT_WITHHELD_ANSWER, CardEndpointRefusedError, captureCardEndpointSession, cardEndpointAnswerHeaders, cardEndpointCookies, cardEndpointDetail,
|
|
27
|
+
import { CARD_ENDPOINT_REFUSED_STEP_ANSWER, CARD_ENDPOINT_WITHHELD_ANSWER, CardEndpointRefusedError, captureCardEndpointSession, cardEndpointAnswerHeaders, cardEndpointCookies, cardEndpointDetail, cardEndpointNavigationAnswer, cardEndpointPatterns, cardEndpointRouted, } from './card-endpoint.js';
|
|
27
28
|
import { CardEndpointFactsWatch } from './card-endpoint-facts.js';
|
|
28
29
|
import { PAYRAILS_GUARD_PATTERNS, PAYRAILS_INIT_BODY_LIMIT, PayrailsSessionCapture, isPayrailsHost, payrailsInitCandidate, recognizesPayrails, } from './payrails.js';
|
|
29
30
|
import { cardEndpointStoppedPage, refusedNavigationOutcome } from './card-endpoint-page.js';
|
|
@@ -621,13 +622,13 @@ export function withCorsHeaders(headers, cors) {
|
|
|
621
622
|
return out;
|
|
622
623
|
}
|
|
623
624
|
/**
|
|
624
|
-
* The request a card endpoint approval is asked for: `request` names the URL and
|
|
625
|
-
* nothing else, and the profile travels with the captured
|
|
626
|
-
* merchant_card_endpoint, the one member the API seals, with the merchant's
|
|
627
|
-
* record of the order for a profile that reads one.
|
|
625
|
+
* The request a card endpoint approval is asked for: `request` names the URL and the
|
|
626
|
+
* profile's own method and nothing else, and the profile travels with the captured
|
|
627
|
+
* session in merchant_card_endpoint, the one member the API seals, with the merchant's
|
|
628
|
+
* own cut record of the order for a profile that reads one.
|
|
628
629
|
*/
|
|
629
630
|
function cardEndpointRequest(profile, session, facts = null) {
|
|
630
|
-
return { url: session.url, method:
|
|
631
|
+
return { url: session.url, method: profile.method, headers: {}, body: '', merchant_card_endpoint: { profile: profile.id, session, ...(facts ? { facts } : {}) } };
|
|
631
632
|
}
|
|
632
633
|
/**
|
|
633
634
|
* The merchant's own record of the order for a paused card request of a profile that
|
|
@@ -1909,10 +1910,13 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1909
1910
|
// merchant, and a request with no card in it continues untouched.
|
|
1910
1911
|
// Every Payrails host is intercepted only for an org the API served Payrails
|
|
1911
1912
|
// to, so the gate can abort a card-form payment (see payrails.ts).
|
|
1913
|
+
// PayPal's legacy Hosted Fields confirm is armed for every org, whatever the
|
|
1914
|
+
// registry serves: one that carries the typed card is paused only to be
|
|
1915
|
+
// refused, and one with no card continues (see paypal-hosted-fields.ts).
|
|
1912
1916
|
const urlPatterns = [...new Set([...(derived.length > 0 ? derived : FALLBACK_CARD_PATTERNS), ...guards.patterns,
|
|
1913
1917
|
...(stripeCheckout.isEnabled() ? ['https://api.stripe.com/*'] : []), MERCADO_CHECKOUT_PATTERN, ...merchantPatterns,
|
|
1914
1918
|
...(recognizesGmo(opts.vault) ? GMO_TOKEN_V1_PATTERNS : []), ...juspayCheckout.urlPatterns(), ...cardEndpointPatterns(),
|
|
1915
|
-
...(payrails ? PAYRAILS_GUARD_PATTERNS : [])])];
|
|
1919
|
+
...(payrails ? PAYRAILS_GUARD_PATTERNS : []), ...PAYPAL_HOSTED_FIELDS_PATTERNS])];
|
|
1916
1920
|
const arm = async (sessionId, resume = false, worker = false) => {
|
|
1917
1921
|
const key = sessionId ?? '__root__';
|
|
1918
1922
|
if (armed.has(key))
|
|
@@ -3173,17 +3177,19 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3173
3177
|
// GMO token v1's JSONP GET, routed only for an org the API served GMO to (see attachToCdp).
|
|
3174
3178
|
|| (isGmoTokenV1Request(url.toString()) && recognizesGmo(opts.vault))
|
|
3175
3179
|
|| juspayCheckout.matches(url.toString())
|
|
3176
|
-
// Every reviewed card endpoint, in any status
|
|
3177
|
-
//
|
|
3178
|
-
|
|
3179
|
-
//
|
|
3180
|
-
||
|
|
3180
|
+
// Every reviewed card endpoint, in any status, and every request that sends the typed card to such a merchant
|
|
3181
|
+
// again, by any method the table names (Target Optical's addPayment and card save are PUTs). The pre-claim
|
|
3182
|
+
// gates hold only a card request that carries a card, refuse one this build does not pay, and refuse a resend
|
|
3183
|
+
// in every status.
|
|
3184
|
+
|| cardEndpointRouted(url.toString())
|
|
3181
3185
|
|| aci.matches(url.toString())
|
|
3182
3186
|
// Every Payrails host, routed only for an org the API served Payrails to (see attachToCdp).
|
|
3183
3187
|
|| (payrails !== null && isPayrailsHost(url.toString()))
|
|
3184
3188
|
// Every request to an NMI host whose entry is served (ekashu.ts), as attachToCdp arms the
|
|
3185
3189
|
// host: the card page's post and its 3-D Secure calls reach the NMI gate and fail.
|
|
3186
|
-
|| isArmedEkashuUrl(opts.vault, url.toString())
|
|
3190
|
+
|| isArmedEkashuUrl(opts.vault, url.toString())
|
|
3191
|
+
// PayPal's legacy Hosted Fields confirm, for every org, as attachToCdp arms it (paypal-hosted-fields.ts).
|
|
3192
|
+
|| isPaypalHostedFieldsConfirm(url.toString()), async (route) => {
|
|
3187
3193
|
const request = route.request();
|
|
3188
3194
|
// When this request paused, and the page it came from: the merchant's total is read as of now.
|
|
3189
3195
|
const pausedAt = Date.now();
|