@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
|
@@ -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: 527edb8fd48229e7404d5fda8d1f8e243b5694c350eee105bec8815a0e848df0
|
|
4
4
|
// src/json-path.js
|
|
5
5
|
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
6
6
|
function isPlainObject(v) {
|
|
@@ -645,16 +645,47 @@ var NAVIGATION_HEADERS = /* @__PURE__ */ Object.freeze([
|
|
|
645
645
|
"sec-fetch-user"
|
|
646
646
|
]);
|
|
647
647
|
var GUARANTEE_RATES = "a policy for guarantee rates: the hotel charges the card at the stay, by when a single-use card may have closed";
|
|
648
|
+
var ATT_SESSION = /* @__PURE__ */ deepFreeze({
|
|
649
|
+
cookies: true,
|
|
650
|
+
cookie_names: null,
|
|
651
|
+
// X-CSRF-TOKEN rides only when the page's sessionStorage holds csrf-token, which a guest checkout's does not.
|
|
652
|
+
headers: [...BROWSER_HEADERS, "content-type", "x-csrf-token", "appname", "idpctx-appname", "idp-source", "idpctx-source", "productsku"],
|
|
653
|
+
required: ["appname"]
|
|
654
|
+
});
|
|
655
|
+
var ATT_CARD = /* @__PURE__ */ deepFreeze({
|
|
656
|
+
pan: { at: ["cardNumber"], format: "digits" },
|
|
657
|
+
cvv: { at: ["cvv"], format: "digits" },
|
|
658
|
+
// The page writes the two-digit month, and '20' before the two digits of the year typed as mm/yy.
|
|
659
|
+
exp_month: { at: ["expDate"], format: "mm" },
|
|
660
|
+
exp_year: { at: ["expYear"], format: "yyyy" },
|
|
661
|
+
expiry: null,
|
|
662
|
+
last4: null,
|
|
663
|
+
leading: null,
|
|
664
|
+
// The page's brand reader (module 47170) also writes DINERS, which no reviewed map names, and '' for any other card.
|
|
665
|
+
brands: [{ at: ["cardSubType"], map: { visa: "VISA", mastercard: "MASTERCARD", amex: "AMEX", discover: "DISCOVER" } }],
|
|
666
|
+
bins: null
|
|
667
|
+
});
|
|
668
|
+
var ATT_REQUIRES = /* @__PURE__ */ deepFreeze([{ at: ["type"], one_of: ["creditcard", "debitcard"] }]);
|
|
669
|
+
var ATT_REFUSE = /* @__PURE__ */ deepFreeze([
|
|
670
|
+
{ at: ["savedPaymentData"], when: "present" },
|
|
671
|
+
{ at: ["autoPayInfoList"], when: "present" },
|
|
672
|
+
{ at: ["leaseId"], when: "present" },
|
|
673
|
+
// The Citi card state (module 43541), which the page copies into every body: null unless a Citi application ran.
|
|
674
|
+
{ at: ["transactionId"], when: "nonempty" },
|
|
675
|
+
{ at: ["statusCode"], when: "nonempty" },
|
|
676
|
+
{ at: ["includeAutoPay"], when: "true" }
|
|
677
|
+
]);
|
|
648
678
|
var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
649
679
|
{
|
|
650
680
|
id: "att_payment_info",
|
|
651
681
|
merchant: "AT&T",
|
|
652
682
|
status: "observe",
|
|
653
683
|
evidence: [
|
|
654
|
-
"att.com buy flow bundles (release/26.09.
|
|
655
|
-
"Headers:
|
|
656
|
-
|
|
657
|
-
"The card-free order POST (/msapi/
|
|
684
|
+
"att.com buy flow bundles (Branch release/26.09.27.REL.HF.COMMERCEUI; the _app, purchaseorder, MFE.checkoutMFE and MFE.CheckoutPaymentInfo chunks byte-identical Sep 30, Oct 6 and Oct 7, 2026), read on the Payment step of a guest accessory checkout Oct 6: the URL builder (module 29882 PT) gives CHECKOUT_MFE_PAYMENT_URL /msapi/checkoutorch/v1/mfe/paymentInfo, with no query, when the cart answer names no cartType listed in meta expOrchCartType and meta useExpOrch is off; Networker posts JSON with credentials: include. Meta isOrderPathEnabled, off on Oct 7, would move the base under /msapi/order.",
|
|
685
|
+
"Headers: getDefaultHeaders (module 20076) writes accept-language, appname and idpctx-appname onlinecart, idp-source and idpctx-source NEW_WIRELESS, and productsku; it adds X-CSRF-TOKEN only from sessionStorage csrf-token, which a guest checkout does not hold (its customerInfo and deliveryInfo posts on the same base got 200 without one).",
|
|
686
|
+
'Body (module 67295): cardNumber, cvv, expDate (the two-digit month), expYear ("20" and the two digits typed), name, type (the selected method: creditcard by default, or debitcard), zip, amount (Number(dueToday).toFixed(2)), dueToday, paymentTerms, cardSubType (module 47170: VISA, MASTERCARD, AMEX, DISCOVER, DINERS, else empty), statusCode and transactionId (the Citi card state, module 43541: null unless a Citi application ran) and isTradeIn; leaseId with progressive leasing, savedPaymentData only in validate mode.',
|
|
687
|
+
"The card-free order POST (/msapi/checkoutorch/v1/mfe/post) may open a PIN, AUTHID or SafeGuard step and is re-posted after it; AT&T holds the funds at the order and charges the card when the order ships.",
|
|
688
|
+
"The card-on-file panel (MFE.CheckoutCardOnFilePanel, read Oct 7, 2026) posts the typed card number, code and expiry to /msapi/checkoutorch/v1/mfe/cardOnFile when the checkout lists a card-on-file step, which the guest accessory checkout did not: a resend."
|
|
658
689
|
],
|
|
659
690
|
har: null,
|
|
660
691
|
checkout: "https://www.att.com",
|
|
@@ -662,42 +693,20 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
662
693
|
frame: null,
|
|
663
694
|
endpoint: "https://www.att.com",
|
|
664
695
|
method: "POST",
|
|
665
|
-
path: "/msapi/
|
|
666
|
-
query: {
|
|
696
|
+
path: "/msapi/checkoutorch/v1/mfe/paymentInfo",
|
|
697
|
+
query: {},
|
|
667
698
|
navigation: null,
|
|
668
699
|
encoding: "json",
|
|
669
|
-
session:
|
|
670
|
-
|
|
671
|
-
cookie_names: null,
|
|
672
|
-
headers: [...BROWSER_HEADERS, "content-type", "x-csrf-token", "appname", "idpctx-appname"],
|
|
673
|
-
required: ["x-csrf-token", "appname"]
|
|
674
|
-
},
|
|
675
|
-
card: {
|
|
676
|
-
pan: { at: ["cardNumber"], format: "digits" },
|
|
677
|
-
cvv: { at: ["cvv"], format: "digits" },
|
|
678
|
-
exp_month: { at: ["expDate"], format: "as_typed" },
|
|
679
|
-
exp_year: { at: ["expYear"], format: "as_typed" },
|
|
680
|
-
expiry: null,
|
|
681
|
-
last4: null,
|
|
682
|
-
leading: null,
|
|
683
|
-
brands: [{ at: ["cardSubType"], map: null }],
|
|
684
|
-
bins: null
|
|
685
|
-
},
|
|
700
|
+
session: ATT_SESSION,
|
|
701
|
+
card: ATT_CARD,
|
|
686
702
|
merchant_key: null,
|
|
687
|
-
requires:
|
|
688
|
-
refuse:
|
|
689
|
-
{ at: ["savedPaymentData"], when: "present" },
|
|
690
|
-
{ at: ["autoPayInfoList"], when: "present" },
|
|
691
|
-
{ at: ["leaseId"], when: "present" },
|
|
692
|
-
{ at: ["transactionId"], when: "present" },
|
|
693
|
-
{ at: ["statusCode"], when: "present" },
|
|
694
|
-
{ at: ["includeAutoPay"], when: "true" }
|
|
695
|
-
],
|
|
703
|
+
requires: ATT_REQUIRES,
|
|
704
|
+
refuse: ATT_REFUSE,
|
|
696
705
|
payment_method: null,
|
|
697
706
|
amount: { at: ["amount"], kind: "decimal_string" },
|
|
698
707
|
currency: "USD",
|
|
699
708
|
response: { headers: ["content-type", "set-cookie", "x-csrf-token"], withhold: [] },
|
|
700
|
-
resends: [],
|
|
709
|
+
resends: [{ method: "POST", path: "/msapi/checkoutorch/v1/mfe/cardOnFile", query: {} }],
|
|
701
710
|
challenge: null,
|
|
702
711
|
facts: null,
|
|
703
712
|
bootstrap: "/robots.txt",
|
|
@@ -705,9 +714,8 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
705
714
|
charge_timing: "deferred",
|
|
706
715
|
once_per: null,
|
|
707
716
|
pending: [
|
|
708
|
-
"
|
|
709
|
-
"
|
|
710
|
-
"whether cartId also rides in the body"
|
|
717
|
+
"the paymentInfo answer: its status, body and headers (only the sibling MFE answers were seen)",
|
|
718
|
+
"paymentTerms.paymentEventType: the value a one-time purchase sends, pinned by a requires rule"
|
|
711
719
|
]
|
|
712
720
|
},
|
|
713
721
|
{
|
|
@@ -715,9 +723,10 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
715
723
|
merchant: "Italo",
|
|
716
724
|
status: "observe",
|
|
717
725
|
evidence: [
|
|
718
|
-
"biglietti.italotreno.com public bundles (API client chunk 901970b7, payment step chunk 6297), read Sep
|
|
726
|
+
"biglietti.italotreno.com public bundles, read Sep 25, 2026 (API client chunk 901970b7, payment step chunk 6297), and the same code read live Sep 30 and Oct 6, 2026 (API client chunk 9d3fb9db-25fa9aa33ff5588f, payment step chunk 76297-bb161301521c29cc, the same bytes on both days): axios posts JSON, without withCredentials, to https://api-biglietti.italotreno.com/api/v1/booking/{bookingId}/payments/credit-card.",
|
|
719
727
|
"Headers: Content-Type application/json, Authorization Bearer <BIGSessionToken>, X-BIG-working-session-id.",
|
|
720
|
-
"Body: accountName, accountNumber (PAN, spaces stripped), CVV, expirationMonth (two-digit string), expirationYear (four-digit number), termsAndConditions true, StoreCreditCard (saves the card
|
|
728
|
+
"Body: accountName, accountNumber (PAN, spaces stripped), CVV, expirationMonth (two-digit string), expirationYear (four-digit number), termsAndConditions true, StoreCreditCard (false in a guest's body: the box that saves the card shows only to a signed-in user), paymentMethodCode (payment.fns.cardType: VI, MC, AX, DC for Discover, JC for JCB; a card type the table does not name, such as Visa Electron, leaves it out). A saved card posts {creditCardId, cvv} to /payments/registered-credit-card instead, so a creditCardId here is refused.",
|
|
729
|
+
"The payment step builds the body with expire: undefined, which JSON.stringify drops, so the body never carries expire (live checks of Sep 30 and Oct 6, 2026); a body that sends it as null passes the rules too.",
|
|
721
730
|
"The answer carries validationMetadata.PaReq, a Nexi 3DS HTML snippet the page injects; the 3DS return and the commit are card-free."
|
|
722
731
|
],
|
|
723
732
|
har: null,
|
|
@@ -765,8 +774,7 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
765
774
|
charge_timing: "at_request",
|
|
766
775
|
once_per: null,
|
|
767
776
|
pending: [
|
|
768
|
-
"whether the validationMetadata.PaReq answer carries the card number"
|
|
769
|
-
"whether expire is sent absent or as null"
|
|
777
|
+
"whether the validationMetadata.PaReq answer carries the card number"
|
|
770
778
|
]
|
|
771
779
|
},
|
|
772
780
|
{
|
|
@@ -774,18 +782,21 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
774
782
|
merchant: "Universal Orlando Resort",
|
|
775
783
|
status: "observe",
|
|
776
784
|
evidence: [
|
|
777
|
-
"www.universalorlando.com/web-store
|
|
778
|
-
"Headers: Authorization Bearer,
|
|
779
|
-
|
|
780
|
-
"
|
|
785
|
+
"store.universalorlando.com, the SAP Commerce Spartacus store (OCC base site uor_b2c) that www.universalorlando.com/web-store answers 301 to: main-CS3EMKUE.js read Sep 30 and Oct 6, 2026, and Universal's next build, main-BL5KQYHU.js (sha256 f41d4727), read Oct 7 and 8, 2026. For a guest, anonymousPreCheckout posts JSON with Angular HttpClient, without withCredentials, to https://secure-api.universalparks.com/instances/{InteractGuestId}/carts/{cart guid}/precheckout.",
|
|
786
|
+
"Headers (getUdxAuthHeaders): Authorization Bearer (the guest token the page minted), x-source-id, x-instance-id (the InteractGuestId, left out when the page holds none), x-resort-area-code uor, x-uniwebservice-apikey and cart-id (the cart guid again); Angular adds Content-Type application/json, and udx-bmak.js (sha256 b33ecc47) adds Akamai-BM-Telemetry to a URL ending in /precheckout. No cookie, WCToken, WCTrustedToken or X-IBM-Client-ID.",
|
|
787
|
+
'Body (buildPreCheckoutPayload): base_site_id, user_id ("anonymous" for a guest, the customer id when signed in), billing_address, email_address, phone_number {number}, then card {expiration {month, year} as integers, first_name_on_card, last_name_on_card, number, cvv, save_card}, or card_id for a saved card, then the Accertify device_id and uba. No amount, currency or order id: Universal prices the cart on its own server (USD 21.29 for the 1-Day Coca-Cola Freestyle Souvenir Cup on Oct 6, 2026, live-checks-oct6/universal_orlando).',
|
|
788
|
+
"On a 200 the page places the order itself with a card-free call, POST comm-api.universaldestinationsandexperiences.com/occ/v2/uor_b2c/users/anonymous/orders?cartId={cart guid}. No client-side 3-D Secure runs: the Cardinal step sits behind isCardinalTokenFeatureEnabled, which stays false.",
|
|
789
|
+
"Two other requests on the same host carry the typed card, and resends below lists both: a signed-in checkout posts the same body to /guests/{guest id}/carts/{cart guid}/precheckout, and the Cardinal step, if Universal turns it on, posts {number} to /resort-areas/uor/carts/{cart guid}/cardinal-token.",
|
|
790
|
+
"The retired web-store checkout still loads from a deep link: www.universalorlando.com/web-store/en/us/cart and /web-store/en/us/checkout answered 200 with its app, main-YEGFGVHU.js (sha256 0a535713), on Oct 8, 2026, and nothing in that app sends the page on to the new store. Its payCard posts the typed card to /instances/{instance id}/commerce-checkout for a guest (the request this profile pinned until the store moved) and to /guests/{guest id}/commerce-checkout when signed in, so resends below lists both as well.",
|
|
791
|
+
"GET https://secure-api.universalparks.com/robots.txt answers 404 text/plain from a KrakenD gateway behind Akamai (Oct 7 and 8, 2026), which the transport takes: it refuses only a bootstrap answer of 500 or more."
|
|
781
792
|
],
|
|
782
793
|
har: null,
|
|
783
|
-
checkout: "https://
|
|
794
|
+
checkout: "https://store.universalorlando.com",
|
|
784
795
|
checkout_frame: false,
|
|
785
796
|
frame: null,
|
|
786
797
|
endpoint: "https://secure-api.universalparks.com",
|
|
787
798
|
method: "POST",
|
|
788
|
-
path: "/instances/:instanceId/
|
|
799
|
+
path: "/instances/:instanceId/carts/:cartId/precheckout",
|
|
789
800
|
query: {},
|
|
790
801
|
navigation: null,
|
|
791
802
|
encoding: "json",
|
|
@@ -796,25 +807,20 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
796
807
|
...BROWSER_HEADERS,
|
|
797
808
|
"content-type",
|
|
798
809
|
"authorization",
|
|
799
|
-
"x-ibm-client-id",
|
|
800
|
-
"wctoken",
|
|
801
|
-
"wctrustedtoken",
|
|
802
810
|
"x-instance-id",
|
|
803
811
|
"x-resort-area-code",
|
|
804
812
|
"x-source-id",
|
|
805
813
|
"x-uniwebservice-apikey",
|
|
806
|
-
"
|
|
807
|
-
"x-uniwebservice-device",
|
|
808
|
-
"x-uniwebservice-platform",
|
|
814
|
+
"cart-id",
|
|
809
815
|
"akamai-bm-telemetry"
|
|
810
816
|
],
|
|
811
|
-
required: ["authorization", "x-
|
|
817
|
+
required: ["authorization", "x-instance-id", "cart-id"]
|
|
812
818
|
},
|
|
813
819
|
card: {
|
|
814
|
-
pan: { at: ["number"], format: "digits" },
|
|
815
|
-
cvv: { at: ["cvv"], format: "digits" },
|
|
816
|
-
exp_month: { at: ["expiration", "month"], format: "m_int" },
|
|
817
|
-
exp_year: { at: ["expiration", "year"], format: "yyyy_int" },
|
|
820
|
+
pan: { at: ["card", "number"], format: "digits" },
|
|
821
|
+
cvv: { at: ["card", "cvv"], format: "digits" },
|
|
822
|
+
exp_month: { at: ["card", "expiration", "month"], format: "m_int" },
|
|
823
|
+
exp_year: { at: ["card", "expiration", "year"], format: "yyyy_int" },
|
|
818
824
|
expiry: null,
|
|
819
825
|
last4: null,
|
|
820
826
|
leading: null,
|
|
@@ -822,79 +828,108 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
822
828
|
bins: null
|
|
823
829
|
},
|
|
824
830
|
merchant_key: null,
|
|
831
|
+
// A guest's order on Universal Orlando's own store: a signed-in customer's id or another base site is not this checkout.
|
|
825
832
|
requires: [
|
|
826
|
-
{ at: ["
|
|
827
|
-
{ at: ["
|
|
833
|
+
{ at: ["user_id"], equals: "anonymous" },
|
|
834
|
+
{ at: ["base_site_id"], equals: "uor_b2c" }
|
|
828
835
|
],
|
|
829
836
|
refuse: [
|
|
830
|
-
{ at: ["
|
|
831
|
-
{ at: ["
|
|
832
|
-
{ at: ["cardinal-3d-secure", "reference_id"], when: "nonempty" }
|
|
837
|
+
{ at: ["card", "save_card"], when: "true" },
|
|
838
|
+
{ at: ["card_id"], when: "present" }
|
|
833
839
|
],
|
|
834
840
|
payment_method: null,
|
|
835
|
-
amount:
|
|
836
|
-
|
|
841
|
+
// The body names no amount: Universal charges the cart its server priced, and the agent's amount is the only one checked.
|
|
842
|
+
amount: null,
|
|
843
|
+
currency: "USD",
|
|
837
844
|
response: { headers: ["content-type"], withhold: [] },
|
|
838
|
-
|
|
845
|
+
// The other posts of the typed card on this host (evidence above), from the store and from the retired
|
|
846
|
+
// web-store checkout that still loads: an approval never covers them.
|
|
847
|
+
resends: [
|
|
848
|
+
{ method: "POST", path: "/guests/:guestId/carts/:cartId/precheckout", query: {} },
|
|
849
|
+
{ method: "POST", path: "/resort-areas/uor/carts/:cartId/cardinal-token", query: {} },
|
|
850
|
+
{ method: "POST", path: "/instances/:instanceId/commerce-checkout", query: {} },
|
|
851
|
+
{ method: "POST", path: "/guests/:guestId/commerce-checkout", query: {} }
|
|
852
|
+
],
|
|
839
853
|
challenge: null,
|
|
840
854
|
facts: null,
|
|
841
855
|
bootstrap: "/robots.txt",
|
|
842
856
|
timeout_ms: 3e4,
|
|
843
|
-
charge_timing: "
|
|
844
|
-
|
|
845
|
-
|
|
857
|
+
charge_timing: "deferred",
|
|
858
|
+
// The cart guid names the checkout, so the API lets one cart take one payment at a time.
|
|
859
|
+
once_per: { path: "cartId" },
|
|
860
|
+
pending: [
|
|
861
|
+
"what precheckout answers, and whether it authorizes the card (at_request) or the card-free order the page places after it does (deferred)",
|
|
862
|
+
"how long the guest bearer token lives after the page mints it",
|
|
863
|
+
"the Akamai-BM-Telemetry value the page sends on precheckout (sensor data or missing-telemetry), and how long a captured one stays accepted"
|
|
864
|
+
]
|
|
846
865
|
},
|
|
847
866
|
{
|
|
848
867
|
id: "target_optical_pi_add",
|
|
849
868
|
merchant: "Target Optical",
|
|
850
869
|
status: "observe",
|
|
851
870
|
evidence: [
|
|
852
|
-
"www.targetoptical.com
|
|
853
|
-
|
|
854
|
-
"
|
|
871
|
+
"www.targetoptical.com headless checkout (components.targetoptical.com/to-headless-checkout/prod/v1.65.0/dist/to-checkout.js, sha256 78425e43966143926e96b27f26114868074ba8f004ac368ef648f4dfd964f913), read Sep 30 and Oct 6, 2026: axios 0.24.0 sends PUT https://www.targetoptical.com/wcs/resources/store/12001/cart/@self/addPayment?nocache=<Date.now()> with a plain JSON object and withCredentials, and sets no header of its own (accept and content-type application/json only, seen live on its updateShipping sibling; no X-Requested-With).",
|
|
872
|
+
'Guest new-card body, in order: storeId 12001, valueFromProfileOrder, valueFromPaymentTC, paymentTCId, payMethodId, piAmount (the cart grandTotal with five decimals, "49.42000"), calculationUsage, customerWebId (empty), billing_address_id, cc_brand, cc_cvc, account (the PAN, dashes removed), expire_month (MM), expire_year (YYYY) from the month picker, check_routing_number, checkingAccountNumber, checkRoutingNumber (empty), forterToken, reCaptchaToken (empty: reCAPTCHA is off), checkWarranty; orderId only in a CSR on-behalf session.',
|
|
873
|
+
"Brand codes: cc_brand Visa, Mastercard, Amex, Discover from the bundle's own card type table, and payMethodId VISA, Master Card, AMEX, Discover from window.cardTypePayMethodMapping; any other number, a 2-series Mastercard included, posts generic and CyberSource.",
|
|
874
|
+
"A saved card sends creditCardToUse, requesttype and authToken to the same URL with no card. A signed-in shopper who ticks Save also sends the typed card to PUT /wcs/resources/store/12001/mypayments in parallel. The same click then sends prepareOrder and the order submit, both card-free.",
|
|
875
|
+
"The older HCL checkout posted the card as a form to POST /AjaxOrderChangeServicePIAddTO (read Sep 25), which ShippingAndBillingServicesDeclaration.min.js declares; the storefront still serves that script (Aug 20) but did not load it on Oct 6."
|
|
855
876
|
],
|
|
856
877
|
har: null,
|
|
857
878
|
checkout: "https://www.targetoptical.com",
|
|
858
879
|
checkout_frame: false,
|
|
859
880
|
frame: null,
|
|
860
881
|
endpoint: "https://www.targetoptical.com",
|
|
861
|
-
method: "
|
|
862
|
-
path: "/
|
|
863
|
-
query: {},
|
|
882
|
+
method: "PUT",
|
|
883
|
+
path: "/wcs/resources/store/12001/cart/@self/addPayment",
|
|
884
|
+
query: { nocache: ":nocache" },
|
|
864
885
|
navigation: null,
|
|
865
|
-
encoding: "
|
|
886
|
+
encoding: "json",
|
|
866
887
|
session: {
|
|
867
888
|
cookies: true,
|
|
868
889
|
cookie_names: null,
|
|
869
|
-
headers: [...BROWSER_HEADERS, "content-type"
|
|
870
|
-
required: [
|
|
890
|
+
headers: [...BROWSER_HEADERS, "content-type"],
|
|
891
|
+
required: []
|
|
871
892
|
},
|
|
872
893
|
card: {
|
|
873
|
-
pan: { at: "account", format: "digits" },
|
|
874
|
-
cvv: { at: "cc_cvc", format: "digits" },
|
|
875
|
-
exp_month: { at: "expire_month", format: "
|
|
876
|
-
exp_year: { at: "expire_year", format: "
|
|
894
|
+
pan: { at: ["account"], format: "digits" },
|
|
895
|
+
cvv: { at: ["cc_cvc"], format: "digits" },
|
|
896
|
+
exp_month: { at: ["expire_month"], format: "mm" },
|
|
897
|
+
exp_year: { at: ["expire_year"], format: "yyyy" },
|
|
877
898
|
expiry: null,
|
|
878
899
|
last4: null,
|
|
879
900
|
leading: null,
|
|
880
|
-
brands: [
|
|
881
|
-
|
|
901
|
+
brands: [
|
|
902
|
+
{ at: ["cc_brand"], map: { visa: "Visa", mastercard: "Mastercard", amex: "Amex", discover: "Discover" } },
|
|
903
|
+
{ at: ["payMethodId"], map: { visa: "VISA", mastercard: "Master Card", amex: "AMEX", discover: "Discover" } }
|
|
904
|
+
],
|
|
905
|
+
// The page posts a 2-series Mastercard as generic and CyberSource, which no map names.
|
|
906
|
+
bins: { mastercard: ["51", "52", "53", "54", "55"] }
|
|
882
907
|
},
|
|
883
908
|
merchant_key: null,
|
|
884
909
|
requires: [
|
|
885
|
-
{ at: "
|
|
886
|
-
{ at: "
|
|
887
|
-
{ at: "piAmount", present: true }
|
|
910
|
+
{ at: ["storeId"], equals: "12001" },
|
|
911
|
+
{ at: ["billing_address_id"], present: true },
|
|
912
|
+
{ at: ["piAmount"], present: true }
|
|
888
913
|
],
|
|
889
914
|
refuse: [
|
|
890
|
-
{ at: "
|
|
891
|
-
{ at: "
|
|
915
|
+
{ at: ["creditCardToUse"], when: "present" },
|
|
916
|
+
{ at: ["saveCreditCard"], when: "present" },
|
|
917
|
+
// orderId and a customerWebId mark a CSR placing the order on a shopper's behalf.
|
|
918
|
+
{ at: ["orderId"], when: "present" },
|
|
919
|
+
{ at: ["customerWebId"], when: "nonempty" },
|
|
920
|
+
{ at: ["checkingAccountNumber"], when: "nonempty" }
|
|
892
921
|
],
|
|
893
922
|
payment_method: null,
|
|
894
|
-
amount: { at: "piAmount", kind: "
|
|
923
|
+
amount: { at: ["piAmount"], kind: "decimal_string_scale5" },
|
|
895
924
|
currency: "USD",
|
|
896
925
|
response: { headers: ["content-type", "set-cookie"], withhold: [] },
|
|
897
|
-
resends: [
|
|
926
|
+
resends: [
|
|
927
|
+
// The card save a signed-in shopper ticks, sent beside addPayment.
|
|
928
|
+
{ method: "PUT", path: "/wcs/resources/store/12001/mypayments", query: { nocache: ":nocache" } },
|
|
929
|
+
// The form endpoint the older checkout posted the card to, which this profile paused until the headless
|
|
930
|
+
// checkout replaced it: a post there is aborted in every status, never left to reach the merchant.
|
|
931
|
+
{ method: "POST", path: "/AjaxOrderChangeServicePIAddTO", query: {} }
|
|
932
|
+
],
|
|
898
933
|
challenge: null,
|
|
899
934
|
facts: null,
|
|
900
935
|
bootstrap: "/robots.txt",
|
|
@@ -902,9 +937,8 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
902
937
|
charge_timing: "deferred",
|
|
903
938
|
once_per: null,
|
|
904
939
|
pending: [
|
|
905
|
-
"
|
|
906
|
-
"card
|
|
907
|
-
"whether the visible account1_1 field is also sent"
|
|
940
|
+
"the answer to addPayment: status, body and headers, and whether it asks the page for another step",
|
|
941
|
+
"which call authorizes the card, and whether the charge when the order ships needs the card to stay open"
|
|
908
942
|
]
|
|
909
943
|
},
|
|
910
944
|
{
|
|
@@ -1044,12 +1078,15 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1044
1078
|
merchant: "Hilton",
|
|
1045
1079
|
status: "observe",
|
|
1046
1080
|
evidence: [
|
|
1047
|
-
"Hilton's reservation app dx-res-ui (www.hilton.com/dx_reservation_app): payment page chunks 70b8777e (Aug 24) and 8a158c4c (Sep 8), _app 6b38675c and c2e248fe (Sep 15), read from archive.org copies Sep 25, 2026. Every Hilton brand property books through it, Hilton San Francisco Union Square (SFOFHHH) included.",
|
|
1081
|
+
"Hilton's reservation app dx-res-ui (www.hilton.com/dx_reservation_app): payment page chunks 70b8777e (Aug 24) and 8a158c4c (Sep 8), _app 6b38675c and c2e248fe (Sep 15), read from archive.org copies Sep 25, 2026, and the live Turbopack build dx-res-ui:1080995 (deployed Oct 5, 2026), read Oct 6, 2026 (live-checks-oct6/hilton_union_square), which runs the same code paths. Every Hilton brand property books through it, Hilton San Francisco Union Square (SFOFHHH) included.",
|
|
1048
1082
|
"The GraphQL transport fetches POST ${DX_GRAPHQL_CLIENT_URI}?${new URLSearchParams({appName, appVersion, operationName, originalOpName, bl})} with body JSON.stringify({query, operationName, variables}): https://www.hilton.com/graphql/customer, appVersion dx-res-ui:<build>, which URLSearchParams writes as dx-res-ui%3A<build>.",
|
|
1049
1083
|
"Headers: Content-Type application/json, dx-platform (web when the page sets none), visitorid, hltclientmessageid, and Authorization Bearer only when there is no webGuestMetadata cookie; a same-origin fetch, so the first-party cookies go with it.",
|
|
1050
1084
|
"createReservation variables: {language, input: {totalAmountAfterTax (a number), currencyCode, guarantee: {guarMethodCode, paymentCard: {cardCode (from the typed digits), cardExpireDate, cardNumber, cvvString (only when the rate sets cvvRequired), cardholder fields, paymentId (a saved Honors card)}, deposit}, ...}, scaInput, mfaInput, enrollment, guestUpdate: {preferredAddress, updatePaymentMethod (saves the card to the account)}}.",
|
|
1051
1085
|
"SCA (PSD2 or Adyen Protect) sends scaInput {returnUrl, browserInfo} on the first call, answers error 897 and sends createReservation again with {scaId, md, paRes} and the card; MFA (errors 995, 996) sends it again with mfaInput. Both are refused here, so only stays that need neither are paid.",
|
|
1052
|
-
"The body's query is the createReservation operationString with its two fragments, which the transport sends unchanged (z = {query, operationName, variables}). requires pins it to the text the archived Sep 15 _app chunk c2e248fe builds (merchant-card-endpoint-documents.js), so an argument written inline in the document or a second createReservation under an alias is refused."
|
|
1086
|
+
"The body's query is the createReservation operationString with its two fragments, which the transport sends unchanged (z = {query, operationName, variables}). requires pins it to the text the archived Sep 15 _app chunk c2e248fe builds (merchant-card-endpoint-documents.js), so an argument written inline in the document or a second createReservation under an alias is refused. The live build of Oct 5 sends that text byte for byte (9932 characters, SHA-256 93d99a00b4447bc24dec69aa09434131a51c454a5eae813a4d95fc10d540c4e6, read Oct 6, 2026).",
|
|
1087
|
+
"Read live Oct 6, 2026: the month (01 to 12) and two-digit year (26 to 35) selects write cardExpireDate as MMYY, which as_typed reads. cardCode comes from the typed digits (visa VI, mastercard MC, americanexpress AX, discover and elo DS, dinersclub DC, jcb JC, maestro SW, unionpay CU), and the map pays the four networks a review takes. On bl=en the URL carries nothing beyond the reviewed query: language rides it only on a OneLink locale, and ctyhocn, the one variable the app copies into URLs, is no top-level createReservation variable.",
|
|
1088
|
+
"Rates read live Oct 6, 2026 for a guest at Hilton San Francisco Union Square, Oct 23 to 24: the prepaid Non-refundable rate QO is guarMethodCode FP with depositRequired and a deposit of the whole stay (192.05 USD), 'your credit card will be charged immediately', and the page sends guarantee.deposit {amount}; the Semi-Flex rate R3X is a CC guarantee with deposit null, charged at the stay. A hotel is paid only at a prepaid rate (Felipe's rule, Oct 7, 2026): requires takes guarMethodCode FP with a deposit amount, which refuses R3X and every other guarantee, and an FP rate is charged at the request.",
|
|
1089
|
+
"Every guest rate priced that day set scaRequired with scaType protect before any card or identity was typed, so the first createReservation at this hotel carries scaInput {returnUrl, browserInfo}, which the rules refuse until an SCA continuation exists. Join Hilton Honors for free is pre-ticked and enrolls the guest with no password: createReservation then carries variables.enrollment {sourceCode CPOH}, which opens an account, so a nonempty enrollment is refused."
|
|
1053
1090
|
],
|
|
1054
1091
|
har: null,
|
|
1055
1092
|
checkout: "https://www.hilton.com",
|
|
@@ -1075,19 +1112,22 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1075
1112
|
expiry: { at: ["variables", "input", "guarantee", "paymentCard", "cardExpireDate"], format: "as_typed" },
|
|
1076
1113
|
last4: null,
|
|
1077
1114
|
leading: null,
|
|
1078
|
-
brands: [{ at: ["variables", "input", "guarantee", "paymentCard", "cardCode"], map:
|
|
1115
|
+
brands: [{ at: ["variables", "input", "guarantee", "paymentCard", "cardCode"], map: { visa: "VI", mastercard: "MC", amex: "AX", discover: "DS" } }],
|
|
1079
1116
|
bins: null
|
|
1080
1117
|
},
|
|
1081
1118
|
merchant_key: null,
|
|
1082
1119
|
requires: [
|
|
1083
1120
|
{ at: ["operationName"], equals: "createReservation" },
|
|
1084
|
-
{ at: ["query"], one_of: [...HILTON_CREATE_RESERVATION_DOCUMENTS] }
|
|
1121
|
+
{ at: ["query"], one_of: [...HILTON_CREATE_RESERVATION_DOCUMENTS] },
|
|
1122
|
+
{ at: ["variables", "input", "guarantee", "guarMethodCode"], equals: "FP" },
|
|
1123
|
+
{ at: ["variables", "input", "guarantee", "deposit", "amount"], present: true }
|
|
1085
1124
|
],
|
|
1086
1125
|
refuse: [
|
|
1087
1126
|
{ at: ["variables", "input", "guarantee", "paymentCard", "paymentId"], when: "present" },
|
|
1088
1127
|
{ at: ["variables", "scaInput"], when: "nonempty" },
|
|
1089
1128
|
{ at: ["variables", "mfaInput"], when: "nonempty" },
|
|
1090
|
-
{ at: ["variables", "guestUpdate", "updatePaymentMethod"], when: "true" }
|
|
1129
|
+
{ at: ["variables", "guestUpdate", "updatePaymentMethod"], when: "true" },
|
|
1130
|
+
{ at: ["variables", "enrollment"], when: "nonempty" }
|
|
1091
1131
|
],
|
|
1092
1132
|
payment_method: null,
|
|
1093
1133
|
amount: { at: ["variables", "input", "totalAmountAfterTax"], kind: "decimal_number" },
|
|
@@ -1098,14 +1138,10 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1098
1138
|
facts: null,
|
|
1099
1139
|
bootstrap: "/robots.txt",
|
|
1100
1140
|
timeout_ms: 3e4,
|
|
1101
|
-
charge_timing: "
|
|
1141
|
+
charge_timing: "at_request",
|
|
1102
1142
|
once_per: null,
|
|
1103
1143
|
pending: [
|
|
1104
|
-
"
|
|
1105
|
-
"card.brands: the cardCode value for each brand",
|
|
1106
|
-
"the query: whether createReservation adds language or other variables to it",
|
|
1107
|
-
"the createReservation GraphQL document the live build sends, which requires pins (it holds the archived Sep 15 build's): a document that is not pinned can inline guestUpdate.updatePaymentMethod, scaInput or mfaInput, or alias createReservation twice",
|
|
1108
|
-
GUARANTEE_RATES
|
|
1144
|
+
"an SCA continuation: every guest rate priced at Hilton San Francisco Union Square on Oct 6, 2026 asked for SCA, so its first createReservation carries the scaInput refused here; paying such a stay needs the two scaInput shapes pinned, the 897 answer back on the page, the second createReservation that sends the card again, and the issuer challenge with the cardholder away"
|
|
1109
1145
|
]
|
|
1110
1146
|
},
|
|
1111
1147
|
{
|
|
@@ -1119,7 +1155,10 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1119
1155
|
`pay-ng v14.149.0, read live Oct 6, 2026 (live-checks-oct6/ihg): neither US hotel checked (AMAEH, AMARS) shows a security code field, and the page writes cvvNumber {token: ""} for each. The form asks for a code for points plus cash, and for a paid offer at Iberostar or at a hotel whose policy sets isCreditCardCVVRequired or isPaymentServiceInformationRequired; otherwise it keeps its code empty. So card.cvv is digits_if_asked, as Hilton's and SynXis' are: the rules leave an empty code empty and still read a code the hotel asks for as digits.`,
|
|
1120
1156
|
"Headers: x-ihg-api-key (public client key), content-type application/json; charset=UTF-8, ihg-language, X-IHG-SSO-TOKEN, x-accept-currency (USD), IHG-SellSource, Forter-Token; interceptors add IHG-SessionId and IHG-TransactionId.",
|
|
1121
1157
|
"Hotels that set isPaymentServiceInformationRequired first send the PAN to /payments/threedsecure/v1/token ({cardToken: <PAN>, cardType}) and /paymentSetup (card.token: <PAN>) and put the result in 3dsData, which is refused here; China UnionPay and PayPal set cupTokenId and paymentSource, also refused. Both 3-D Secure posts are in resends below.",
|
|
1122
|
-
'pay-ng v14.149.0, read live Oct 6, 2026 (live-checks-oct6/ihg): the pay page posts the same reservation body, card in the clear, to /channels/direct/quickEnroll/v8/hotelReservations while its pre-ticked "Enroll for free as you book" box stays ticked (/quickEnroll/v9/hotelReservations behind the shouldUseNewQuickEnrolV9 flag, /quickEnroll/v8/multiRoomReservation for a cart), and to /reservations/v3/hotels/rooms/fromCart for more than one room, /reservations/v3/hotels/rooms/agent/reservations for a travel agent and /reservations/v3/businessRewardReservations for Business Rewards. Each is in resends below, so the SDK aborts it in every status.'
|
|
1158
|
+
'pay-ng v14.149.0, read live Oct 6, 2026 (live-checks-oct6/ihg): the pay page posts the same reservation body, card in the clear, to /channels/direct/quickEnroll/v8/hotelReservations while its pre-ticked "Enroll for free as you book" box stays ticked (/quickEnroll/v9/hotelReservations behind the shouldUseNewQuickEnrolV9 flag, /quickEnroll/v8/multiRoomReservation for a cart), and to /reservations/v3/hotels/rooms/fromCart for more than one room, /reservations/v3/hotels/rooms/agent/reservations for a travel agent and /reservations/v3/businessRewardReservations for Business Rewards. Each is in resends below, so the SDK aborts it in every status.',
|
|
1159
|
+
"The same read: cardCode comes from the typed digits, VS Visa, MC Mastercard, AX Amex, DS Discover, and DC, JC, CB, EC, BE and CU for the networks the map leaves out, so a card of those is refused. The expiry selects offer months 01 to 12 and years 26 to 45, so expireDate is always MMYY.",
|
|
1160
|
+
"getHotel writes hotels[0] as {hotelMnemonic}, plus bookingCurrency, the shopper's currency, only when it differs from the hotel's own, which the body never names, so any bookingCurrency is refused. A hotel outside the US booked in its own currency sends none, so the body never shows that hotel charging in a currency other than the USD this profile names. setReservationRequestOffer writes segments[0].offer as {ratePlanTypeCode 'SR', productUses, ratePlanCode} for a public rate, and as {groupContractCode, ratePlanTypeCode, productUses} with no ratePlanCode for a group rate (pay-ng v14.144.1 chunk 7U5FHKAZ; v14.149.0 builds the same body).",
|
|
1161
|
+
"Rates read live Oct 6, 2026 at two Amarillo hotels: Book Early & Save (IDAPF, Yellow City Studios AMAEH) is non-refundable, 'A deposit for the entire stay is due at time of booking'; Best Flexible Rate (IGCOR, Holiday Inn Amarillo West Medical Center AMARS) says 'You pay nothing today. A card simply holds your reservation.' A hotel is paid only at a prepaid rate (Felipe's rule, Oct 7, 2026): requires takes ratePlanCode IDAPF only, which refuses IGCOR, a group rate and every code whose terms no review read."
|
|
1123
1162
|
],
|
|
1124
1163
|
har: null,
|
|
1125
1164
|
checkout: "https://www.ihg.com",
|
|
@@ -1156,18 +1195,20 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1156
1195
|
expiry: { at: ["segments", 0, "payment", "paymentInformation", "paymentCard", "expireDate"], format: "mmyy" },
|
|
1157
1196
|
last4: null,
|
|
1158
1197
|
leading: null,
|
|
1159
|
-
brands: [{ at: ["segments", 0, "payment", "paymentInformation", "paymentCard", "cardCode"], map:
|
|
1198
|
+
brands: [{ at: ["segments", 0, "payment", "paymentInformation", "paymentCard", "cardCode"], map: { visa: "VS", mastercard: "MC", amex: "AX", discover: "DS" } }],
|
|
1160
1199
|
bins: null
|
|
1161
1200
|
},
|
|
1162
1201
|
merchant_key: null,
|
|
1163
1202
|
requires: [
|
|
1164
1203
|
{ at: ["imageStatus"], equals: "COMMIT" },
|
|
1165
|
-
{ at: ["segments", 0, "payment", "guaranteeType"], equals: "STANDARD_GUARANTEE" }
|
|
1204
|
+
{ at: ["segments", 0, "payment", "guaranteeType"], equals: "STANDARD_GUARANTEE" },
|
|
1205
|
+
{ at: ["segments", 0, "offer", "ratePlanCode"], one_of: ["IDAPF"] }
|
|
1166
1206
|
],
|
|
1167
1207
|
refuse: [
|
|
1168
1208
|
{ at: ["segments", 0, "payment", "paymentInformation", "paymentCard", "3dsData"], when: "present" },
|
|
1169
1209
|
{ at: ["segments", 0, "payment", "paymentInformation", "cupTokenId"], when: "present" },
|
|
1170
|
-
{ at: ["segments", 0, "payment", "paymentInformation", "paymentSource"], when: "present" }
|
|
1210
|
+
{ at: ["segments", 0, "payment", "paymentInformation", "paymentSource"], when: "present" },
|
|
1211
|
+
{ at: ["hotels", 0, "bookingCurrency"], when: "present" }
|
|
1171
1212
|
],
|
|
1172
1213
|
payment_method: null,
|
|
1173
1214
|
amount: null,
|
|
@@ -1190,12 +1231,7 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1190
1231
|
timeout_ms: 3e4,
|
|
1191
1232
|
charge_timing: "deferred",
|
|
1192
1233
|
once_per: null,
|
|
1193
|
-
pending: [
|
|
1194
|
-
"card.brands: the cardCode value for each brand",
|
|
1195
|
-
"card.expiry: that expirationMonth is always two digits, so expireDate is MMYY",
|
|
1196
|
-
"the currency: a hotel outside the US charges in its own, which the body names only as hotels[0].bookingCurrency when it differs from the shopper's",
|
|
1197
|
-
GUARANTEE_RATES
|
|
1198
|
-
]
|
|
1234
|
+
pending: []
|
|
1199
1235
|
},
|
|
1200
1236
|
{
|
|
1201
1237
|
id: "aa_process_payment",
|
|
@@ -1331,7 +1367,9 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1331
1367
|
"The confirm loop sends each room through route updateReservation: POST https://be.synxis.com/gw/itinerary/v1/updateReservation, Content-Type application/json; charset=utf-8, Accept application/json,application/x-javascript, Context BE, Accept-Language, ActivityID (sbeSessionID), Authorization only for a signed-in member, credentials include (apisession, sbeSessionID, Imperva and AWSALB cookies).",
|
|
1332
1368
|
'Body: {version, Chain: {id}, Reservation: {action "Confirmed", Hotel: {id}, GuestList: [{Role "Primary", PaymentList: [{role "Primary", type "CreditCard", PaymentCard: {cardCode (VI, MC, AX, DS...), cardNumber, cardSecurityCode, expireDate "MM/YY", cardHolder}}], ...}], ...}, Source: {BrowserDetails}}; the expiry input is masked 99/99.',
|
|
1333
1369
|
"The hotel's configuration shows no cvvCode field, so cardSecurityCode is sent empty; a later room sends the PaymentToken SynXis returned, and a PSD2 hotel sends consentToStoreCardData, both refused here.",
|
|
1334
|
-
"A RedirectHtml answer with VendorStatus Redirected sends the page through a vendor or 3DS step and then itinerary/v2/ReservationPaymentCallback, with no card."
|
|
1370
|
+
"A RedirectHtml answer with VendorStatus Redirected sends the page through a vendor or 3DS step and then itinerary/v2/ReservationPaymentCallback, with no card.",
|
|
1371
|
+
"SynXis build 721fa2f4a19d8fde7485 (Sep 28, 2026), read live Oct 6, 2026 (live-checks-oct6/the_verb_hotel): the same card path, re-transpiled. The confirm builder writes the primary guest first in GuestList and the card first in its PaymentList, loyalty points after it, which requires pins; and RoomStay as {redeemLoyalty, payNow, startDate, endDate, GuestCount, NumRooms, RateList: [{code, primary: true}], Room: {code}, RoomRequestList}.",
|
|
1372
|
+
"Rates read the same day for Dec 6 to 7, 2026: Non-Refundable (14DAYAP) is policy DP, GuaranteeLevel Deposit, 'Full prepayment of room and tax', a DepositFee of 100% of the stay before tax due AtTimeOfBooking with IsPrePayment, and AmountPayAtProperty 0. The page takes no payment itself, so SynXis or the hotel charges after the request, outside the page; it shows a 134.10 USD deposit (the room before tax) due now beside AmountPayableNow 202.74 USD, and which of the two posts at booking is the recorded purchase's to show. The Best Available Rate takes no deposit and only holds the card. Both send RoomStay.payNow false, so the body names only the rate code. A hotel is paid only at a prepaid rate (Felipe's rule, Oct 7, 2026): requires takes RateList[0].code 14DAYAP only, which refuses the Best Available Rate and every code whose terms no review read."
|
|
1335
1373
|
],
|
|
1336
1374
|
har: null,
|
|
1337
1375
|
checkout: "https://be.synxis.com",
|
|
@@ -1366,7 +1404,8 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1366
1404
|
{ at: ["Reservation", "Hotel", "id"], one_of: [61148, "61148"] },
|
|
1367
1405
|
{ at: ["Reservation", "action"], equals: "Confirmed" },
|
|
1368
1406
|
{ at: ["Reservation", "GuestList", 0, "Role"], equals: "Primary" },
|
|
1369
|
-
{ at: ["Reservation", "GuestList", 0, "PaymentList", 0, "type"], equals: "CreditCard" }
|
|
1407
|
+
{ at: ["Reservation", "GuestList", 0, "PaymentList", 0, "type"], equals: "CreditCard" },
|
|
1408
|
+
{ at: ["Reservation", "RoomStay", "RateList", 0, "code"], one_of: ["14DAYAP"] }
|
|
1370
1409
|
],
|
|
1371
1410
|
refuse: [
|
|
1372
1411
|
{ at: ["Reservation", "GuestList", 0, "PaymentList", 0, "PaymentCard", "PaymentToken"], when: "nonempty" },
|
|
@@ -1385,11 +1424,7 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1385
1424
|
timeout_ms: 3e4,
|
|
1386
1425
|
charge_timing: "deferred",
|
|
1387
1426
|
once_per: null,
|
|
1388
|
-
pending: [
|
|
1389
|
-
"that the primary guest is GuestList[0] and its card PaymentList[0]",
|
|
1390
|
-
"whether the hotel's rates take a deposit at booking or only hold the card",
|
|
1391
|
-
GUARANTEE_RATES
|
|
1392
|
-
]
|
|
1427
|
+
pending: []
|
|
1393
1428
|
},
|
|
1394
1429
|
{
|
|
1395
1430
|
id: "runsignup_register_pay",
|
|
@@ -2071,7 +2106,8 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
2071
2106
|
"The card frame, PciCardForm (archive.org 2026-07-06, 2026-07-23 and 2026-09-04), posts form action /validate/<session uuid>/Default natively, as a navigation of the iframe: hidden Id (the same uuid), CardNumber (maxlength 22), Month (1 to 12, no leading zero), Year (four digits), Cvv (maxlength 4) and CardHolderName. No processor SDK in the frame; D-EDGE charges from its own servers (WSPay for this hotel, by its terms).",
|
|
2072
2107
|
"The frame also posts the first 8 characters of CardNumber as the field holds them (7 digits and a space once it groups a Visa or Mastercard number) to /cardCss/<session uuid>/Default to pick the card logo, which carries no full card and is not this request.",
|
|
2073
2108
|
"secure-hotel-booking.com sits behind DataDome. The same card frame serves every D-EDGE hotel, so the profile names D-EDGE rather than one hotel; its currency is HUMA's.",
|
|
2074
|
-
"HUMA's own payment step, read live Oct 6, 2026 (live-checks-oct6/huma_kotor_bay): the frame's cleanCardNumber rewrites CardNumber on every keypress and on blur into groups of four with one space between them for a Visa or Mastercard number (4242 4242 4242 4242; 4-6-5 for an Amex, 4-6-4 for a Diners), so the post carries the spaces.
|
|
2109
|
+
"HUMA's own payment step, read live Oct 6, 2026 (live-checks-oct6/huma_kotor_bay): the frame's cleanCardNumber rewrites CardNumber on every keypress and on blur into groups of four with one space between them for a Visa or Mastercard number (4242 4242 4242 4242; 4-6-5 for an Amex, 4-6-4 for a Diners), so the post carries the spaces.",
|
|
2110
|
+
"cleanCardNumber strips every non-digit and writes the number in the format of the first CARD_TYPES row whose pattern it matches (the frame's inline script, the same in the archive.org copies and live on Oct 6): amex /^3[47]/ format_465, the first 15 digits as 4-6-5; diners_club_international /^36/ and diners_club_carte_blanche /^30[0-5]/ format_464, the first 14 digits as 4-6-4; every other row (visa, master, jcb, maestro, discover, instapayment, diners_club /^54/), and a number no row matches, format_4444, groups of four with the rest last. card.pan is grouped_by_type: the rules take the number grouped that way or as plain digits, and write the real number the way the placeholder was written, a grouped one in its own network's grouping, so a real American Express card behind a placeholder grouped in fours still goes out 4-6-5. Behind a grouped placeholder, a real 34 or 37 number of any length but 15, or a 36 or 300 to 305 number of any length but 14, is refused: the frame has no grouping for it and cuts a longer one short.",
|
|
2075
2111
|
"The same check read when HUMA charges: its Non Refundable rate charges the whole stay at this post (the hub shows Due to pay now and its button reads PAY), while its flexible rate only guarantees the booking with the card, which the hotel charges on a late cancellation or a no-show."
|
|
2076
2112
|
],
|
|
2077
2113
|
har: null,
|
|
@@ -2091,7 +2127,7 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
2091
2127
|
required: []
|
|
2092
2128
|
},
|
|
2093
2129
|
card: {
|
|
2094
|
-
pan: { at: "CardNumber", format: "
|
|
2130
|
+
pan: { at: "CardNumber", format: "grouped_by_type" },
|
|
2095
2131
|
cvv: { at: "Cvv", format: "digits" },
|
|
2096
2132
|
exp_month: { at: "Month", format: "m" },
|
|
2097
2133
|
exp_year: { at: "Year", format: "yyyy" },
|
|
@@ -2118,7 +2154,6 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
2118
2154
|
pending: [
|
|
2119
2155
|
"HUMA's own payment step: whether its pay hub offers this card frame or a WSPay redirect (a HAR of one real booking's payment step)",
|
|
2120
2156
|
"which cookies the card post needs, with DataDome in front of the host",
|
|
2121
|
-
"whether D-EDGE takes a real Amex or Diners number grouped in fours, which grouped4 writes and the frame never does (it groups those 4-6-5 and 4-6-4)",
|
|
2122
2157
|
"the answer to the card post (a redirect, a 3DS step or a page), and whether a 3DS step sends the card again",
|
|
2123
2158
|
GUARANTEE_RATES,
|
|
2124
2159
|
"that the bootstrap path answers without a server error"
|
|
@@ -2318,6 +2353,54 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
2318
2353
|
"the session currency for US buyers",
|
|
2319
2354
|
"a card-on-file policy for the monthly plans and the add-on, before any order that renews"
|
|
2320
2355
|
]
|
|
2356
|
+
},
|
|
2357
|
+
{
|
|
2358
|
+
// AT&T's other card request URL, which its page posts to when the URL builder picks
|
|
2359
|
+
// the exporch form: refused in observe, so a cart AT&T moves there is stopped as
|
|
2360
|
+
// unsupported_checkout instead of reaching AT&T unpaused. The same form with no cart
|
|
2361
|
+
// id is one of its resends, which the SDK aborts in every status.
|
|
2362
|
+
id: "att_exporch_payment_info",
|
|
2363
|
+
merchant: "AT&T",
|
|
2364
|
+
status: "observe",
|
|
2365
|
+
evidence: [
|
|
2366
|
+
"att.com buy flow URL builder (module 29882, read Sep 25 to Oct 7, 2026): with meta isUnifiedMfeEnabled on, a cart whose answer names a cartType listed in meta expOrchCartType (prepaid, aia, postpaid, broadband and accessory on Oct 7), or any cart once meta useExpOrch is on, posts its card to /msapi/checkoutexporch/v1/paymentInfo, with ?cartId= and the cart id checkoutInfo named (sessionStorage CHECKOUT_MFE_CART_ID), or with no query when it named none. Meta isUnifiedOrderEnabled, off on Oct 7, would move the base under /msapi/order.",
|
|
2367
|
+
"The same Networker, headers and body as the checkout path (att_payment_info), and the card-on-file step on the same base and query (/msapi/checkoutexporch/v1/cardOnFile).",
|
|
2368
|
+
"Never seen live: the Oct 6 guest accessory cart named no cartType, so it posted to the checkout path."
|
|
2369
|
+
],
|
|
2370
|
+
har: null,
|
|
2371
|
+
checkout: "https://www.att.com",
|
|
2372
|
+
checkout_frame: false,
|
|
2373
|
+
frame: null,
|
|
2374
|
+
endpoint: "https://www.att.com",
|
|
2375
|
+
method: "POST",
|
|
2376
|
+
path: "/msapi/checkoutexporch/v1/paymentInfo",
|
|
2377
|
+
query: { cartId: ":cartId" },
|
|
2378
|
+
navigation: null,
|
|
2379
|
+
encoding: "json",
|
|
2380
|
+
session: ATT_SESSION,
|
|
2381
|
+
card: ATT_CARD,
|
|
2382
|
+
merchant_key: null,
|
|
2383
|
+
requires: ATT_REQUIRES,
|
|
2384
|
+
refuse: ATT_REFUSE,
|
|
2385
|
+
payment_method: null,
|
|
2386
|
+
amount: { at: ["amount"], kind: "decimal_string" },
|
|
2387
|
+
currency: "USD",
|
|
2388
|
+
response: { headers: ["content-type", "set-cookie", "x-csrf-token"], withhold: [] },
|
|
2389
|
+
resends: [
|
|
2390
|
+
{ method: "POST", path: "/msapi/checkoutexporch/v1/cardOnFile", query: { cartId: ":cartId" } },
|
|
2391
|
+
// The card request and its card-on-file step as the page sends them when checkoutInfo
|
|
2392
|
+
// named no cart id: with no query. No payment covers a form that names no checkout
|
|
2393
|
+
// (once_per), so each is aborted in every status instead of reaching AT&T unpaused.
|
|
2394
|
+
{ method: "POST", path: "/msapi/checkoutexporch/v1/paymentInfo", query: {} },
|
|
2395
|
+
{ method: "POST", path: "/msapi/checkoutexporch/v1/cardOnFile", query: {} }
|
|
2396
|
+
],
|
|
2397
|
+
challenge: null,
|
|
2398
|
+
facts: null,
|
|
2399
|
+
bootstrap: "/robots.txt",
|
|
2400
|
+
timeout_ms: 3e4,
|
|
2401
|
+
charge_timing: "deferred",
|
|
2402
|
+
once_per: { query: "cartId" },
|
|
2403
|
+
pending: ["the exporch form, never seen live: a HAR of a cart AT&T sends to /msapi/checkoutexporch/v1"]
|
|
2321
2404
|
}
|
|
2322
2405
|
]);
|
|
2323
2406
|
function wireProjection(profiles) {
|
|
@@ -3442,8 +3525,20 @@ function codeNotAsked(reader, spec, variants) {
|
|
|
3442
3525
|
function groupedFour(digits) {
|
|
3443
3526
|
return digits.replace(/(.{4})/g, "$1 ").trim();
|
|
3444
3527
|
}
|
|
3528
|
+
function groupedByType(digits) {
|
|
3529
|
+
const amex = /^3[47]/.test(digits);
|
|
3530
|
+
if (!amex && !/^(?:36|30[0-5])/.test(digits))
|
|
3531
|
+
return groupedFour(digits);
|
|
3532
|
+
return digits.length === (amex ? 15 : 14) ? `${digits.slice(0, 4)} ${digits.slice(4, 10)} ${digits.slice(10)}` : null;
|
|
3533
|
+
}
|
|
3534
|
+
function groupedPan(spec, digits) {
|
|
3535
|
+
if (spec.format === "grouped4")
|
|
3536
|
+
return groupedFour(digits);
|
|
3537
|
+
return spec.format === "grouped_by_type" ? groupedByType(digits) : null;
|
|
3538
|
+
}
|
|
3539
|
+
var CLEAR_PAN_FORMATS = /* @__PURE__ */ Object.freeze(["digits", "grouped4", "grouped_by_type"]);
|
|
3445
3540
|
function panDigits(spec, value) {
|
|
3446
|
-
return spec.format === "grouped4" && typeof value === "string" ? value.replace(/ /g, "") : value;
|
|
3541
|
+
return (spec.format === "grouped4" || spec.format === "grouped_by_type") && typeof value === "string" ? value.replace(/ /g, "") : value;
|
|
3447
3542
|
}
|
|
3448
3543
|
function placeholderFits(p, slot, spec, value) {
|
|
3449
3544
|
if (spec.format === "encrypted")
|
|
@@ -3457,7 +3552,7 @@ function placeholderFits(p, slot, spec, value) {
|
|
|
3457
3552
|
if (!text)
|
|
3458
3553
|
return false;
|
|
3459
3554
|
const digits = panDigits(spec, value);
|
|
3460
|
-
return /^\d{12,19}$/.test(digits) && (digits === value || value ===
|
|
3555
|
+
return /^\d{12,19}$/.test(digits) && (digits === value || value === groupedPan(spec, digits));
|
|
3461
3556
|
}
|
|
3462
3557
|
case "cvv":
|
|
3463
3558
|
return text && /^\d{3,4}$/.test(value);
|
|
@@ -3488,6 +3583,8 @@ function placeholderFits(p, slot, spec, value) {
|
|
|
3488
3583
|
}
|
|
3489
3584
|
}
|
|
3490
3585
|
var DECIMAL_AMOUNT = /^\d{1,9}(?:\.\d{1,2})?$/;
|
|
3586
|
+
var SCALE5_AMOUNT = /^\d{1,9}\.\d{2}000$/;
|
|
3587
|
+
var AMOUNT_KIND_NAMES = /* @__PURE__ */ Object.freeze({ decimal_string_scale5: "five-decimal string of whole cents" });
|
|
3491
3588
|
function isNothing(value) {
|
|
3492
3589
|
return value === null || value === "" || Array.isArray(value) && value.length === 0 || isPlainObject(value) && Object.keys(value).length === 0;
|
|
3493
3590
|
}
|
|
@@ -3540,7 +3637,7 @@ function rulesRefusal(p, reader, body) {
|
|
|
3540
3637
|
return `${request}: ${label} does not name a key Agentcard pinned for ${p.merchant}`;
|
|
3541
3638
|
}
|
|
3542
3639
|
}
|
|
3543
|
-
const typed = p.card.pan &&
|
|
3640
|
+
const typed = p.card.pan && CLEAR_PAN_FORMATS.includes(p.card.pan.format) ? panDigits(p.card.pan, reader.exact(p.card.pan.at).value) : null;
|
|
3544
3641
|
if (typed !== null) {
|
|
3545
3642
|
if (countRuns(body, typed) !== 1) {
|
|
3546
3643
|
return `${request}: the card number appears outside ${reader.label(p.card.pan.at)}`;
|
|
@@ -3569,9 +3666,10 @@ function rulesRefusal(p, reader, body) {
|
|
|
3569
3666
|
if (p.amount) {
|
|
3570
3667
|
const label = reader.label(p.amount.at);
|
|
3571
3668
|
const at = reader.exact(p.amount.at);
|
|
3572
|
-
const fits = p.amount.kind === "decimal_string" ? typeof at.value === "string" && DECIMAL_AMOUNT.test(at.value) : p.amount.kind === "decimal_number" ? jsonBody(p) && typeof at.value === "number" && DECIMAL_AMOUNT.test(String(at.value)) : jsonBody(p) ? Number.isSafeInteger(at.value) && at.value >= 0 : typeof at.value === "string" && /^\d{1,12}$/.test(at.value);
|
|
3573
|
-
if (!at.found || reader.variants(p.amount.at).length !== 1 || !fits)
|
|
3574
|
-
return `${request}: the amount at ${label} is not a ${p.amount.kind.replace("_", " ")}`;
|
|
3669
|
+
const fits = p.amount.kind === "decimal_string" ? typeof at.value === "string" && DECIMAL_AMOUNT.test(at.value) : p.amount.kind === "decimal_string_scale5" ? typeof at.value === "string" && SCALE5_AMOUNT.test(at.value) : p.amount.kind === "decimal_number" ? jsonBody(p) && typeof at.value === "number" && DECIMAL_AMOUNT.test(String(at.value)) : jsonBody(p) ? Number.isSafeInteger(at.value) && at.value >= 0 : typeof at.value === "string" && /^\d{1,12}$/.test(at.value);
|
|
3670
|
+
if (!at.found || reader.variants(p.amount.at).length !== 1 || !fits) {
|
|
3671
|
+
return `${request}: the amount at ${label} is not a ${AMOUNT_KIND_NAMES[p.amount.kind] ?? p.amount.kind.replace("_", " ")}`;
|
|
3672
|
+
}
|
|
3575
3673
|
}
|
|
3576
3674
|
if (isPlainObject(p.currency)) {
|
|
3577
3675
|
const at = reader.exact(p.currency.at);
|
|
@@ -3624,7 +3722,8 @@ function merchantCardEndpointAmount(profile, body, contentType) {
|
|
|
3624
3722
|
if (!TWO_DECIMAL_CURRENCIES.has(currency))
|
|
3625
3723
|
throw new Error(`Refusing to pay: ${p.merchant}'s amount is in a currency this build does not read.`);
|
|
3626
3724
|
const [whole, fraction = ""] = String(value).split(".");
|
|
3627
|
-
|
|
3725
|
+
const cents = p.amount.kind === "decimal_string_scale5" ? fraction.slice(0, 2) : fraction.padEnd(2, "0");
|
|
3726
|
+
return { amountCents: Number(whole) * 100 + Number(cents), currency };
|
|
3628
3727
|
}
|
|
3629
3728
|
var WRITTEN_DIGIT = String.raw `(?:[0-9]|\\u003[0-9]|%3[0-9]|�*(?:4[89]|5[0-7]);|�*3[0-9];)`;
|
|
3630
3729
|
var WRITTEN_RUN = /* @__PURE__ */ new RegExp(`${WRITTEN_DIGIT}+(?:${SEPARATOR}+${WRITTEN_DIGIT}+)*`, "gi");
|
|
@@ -3764,7 +3863,7 @@ function assertMerchantCardEndpoint(authorization) {
|
|
|
3764
3863
|
catch {
|
|
3765
3864
|
throw new Error("Refusing to pay: malformed payment endpoint.");
|
|
3766
3865
|
}
|
|
3767
|
-
const method = authorization.method === void 0 ?
|
|
3866
|
+
const method = authorization.method === void 0 ? profile.method : authorization.method;
|
|
3768
3867
|
if (!endpointMatches(profile, authorization.url, method)) {
|
|
3769
3868
|
throw new Error(`Refusing to pay: ${url.hostname} is not ${profile.merchant}'s reviewed card endpoint.`);
|
|
3770
3869
|
}
|
|
@@ -3914,7 +4013,7 @@ function merchantCardEndpointCheckoutKey(profile, bundle) {
|
|
|
3914
4013
|
}
|
|
3915
4014
|
function slotValue(slot, spec, placeholder, facts) {
|
|
3916
4015
|
if (slot === "pan")
|
|
3917
|
-
return
|
|
4016
|
+
return placeholder.includes(" ") ? groupedPan(spec, facts.number) : facts.number;
|
|
3918
4017
|
if (slot === "cvv")
|
|
3919
4018
|
return facts.cvc;
|
|
3920
4019
|
if (slot === "last4")
|
|
@@ -3970,7 +4069,7 @@ function substitutionPlan(p, body, contentType, card) {
|
|
|
3970
4069
|
if (refusal)
|
|
3971
4070
|
throw new SubstitutionError(refusal);
|
|
3972
4071
|
const facts = cardFacts(card);
|
|
3973
|
-
const typed = p.card.pan &&
|
|
4072
|
+
const typed = p.card.pan && CLEAR_PAN_FORMATS.includes(p.card.pan.format) ? panDigits(p.card.pan, read.reader.exact(p.card.pan.at).value) : null;
|
|
3974
4073
|
if (typed === facts.number)
|
|
3975
4074
|
throw new SubstitutionError("the paused body already carries the real card number");
|
|
3976
4075
|
const bins = p.card.bins && facts.brand && Object.prototype.hasOwnProperty.call(p.card.bins, facts.brand) ? p.card.bins[facts.brand] : null;
|
|
@@ -3993,7 +4092,10 @@ function substitutionPlan(p, body, contentType, card) {
|
|
|
3993
4092
|
writes.push({ at: spec.at, found: at, value: null, plaintext: plaintextFor(slot, spec, facts) });
|
|
3994
4093
|
}
|
|
3995
4094
|
else {
|
|
3996
|
-
|
|
4095
|
+
const value = slotValue(slot, spec, at.value, facts);
|
|
4096
|
+
if (value === null)
|
|
4097
|
+
throw new SubstitutionError(`${p.merchant} does not take this card's number length`);
|
|
4098
|
+
writes.push({ at: spec.at, found: at, value });
|
|
3997
4099
|
}
|
|
3998
4100
|
}
|
|
3999
4101
|
return { read, facts, typed, writes };
|