@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.
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: d73edb25ace75651a72071180425f4e2b4717e5fd5504f2d2d336d70a55e0f64
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.23.REL.COMMERCEUI), read Sep 25, 2026: with isUnifiedMfeEnabled on, Networker posts JSON with credentials: include to /msapi/checkoutexporch/v1/paymentInfo?cartId=<CHECKOUT_MFE_CART_ID>.",
655
- "Headers: content-type application/json, appname and idpctx-appname onlinecart, X-CSRF-TOKEN from sessionStorage csrf-token.",
656
- "Body: flat JSON with cardNumber, cvv, expDate (the month), expYear, name, zip, type, amount, cardSubType (the brand, derived from the PAN), and savedPaymentData, includeAutoPay, autoPayInfoList, leaseId, transactionId, statusCode on other flows.",
657
- "The card-free order POST (/msapi/checkoutexporch/v1/post) carries the SMS PIN step-up."
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/checkoutexporch/v1/paymentInfo",
666
- query: { cartId: ":cartId" },
696
+ path: "/msapi/checkoutorch/v1/mfe/paymentInfo",
697
+ query: {},
667
698
  navigation: null,
668
699
  encoding: "json",
669
- session: {
670
- cookies: true,
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: [{ at: ["type"], one_of: ["CREDIT", "DEBIT", "ANYCARD"] }],
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
- "card.brands: the cardSubType value for each brand",
709
- "card.exp_month and card.exp_year: how many digits expDate and expYear carry",
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 25, 2026: axios posts JSON, without withCredentials, to https://api-biglietti.italotreno.com/api/v1/booking/{bookingId}/payments/credit-card.",
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), creditCardId (saved cards), paymentMethodCode (VI, MC, AX, DC, JC, OT from payment.fns.cardType).",
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 bundle (main-YEGFGVHU.js payCard and buildPaymentUrl), read Sep 25, 2026: Angular HttpClient posts JSON, without withCredentials, to https://secure-api.universalparks.com/instances/{instance-id}/commerce-checkout for a guest checkout.",
778
- "Headers: Authorization Bearer, X-IBM-Client-ID, WCToken, WCTrustedToken, x-instance-id, x-resort-area-code, x-source-Id, X-UNIWebService-ApiKey, -AppVersion, -Device, -Platform, Akamai-BM-Telemetry.",
779
- "Body: order_id, currency_code USD, amount (a string), number, cvv, expiration {month, year} as integers, save_to_wallet, cardinal-3d-secure {reference_id, jwt, window_size}, billing and contact fields.",
780
- "A Cardinal challenge answers 400 CARDINAL_3DS_MORE_INFO_REQUIRED and re-posts the card with the Cardinal jwt, which the refuse rules stop."
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://www.universalorlando.com",
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/commerce-checkout",
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
- "x-uniwebservice-appversion",
807
- "x-uniwebservice-device",
808
- "x-uniwebservice-platform",
814
+ "cart-id",
809
815
  "akamai-bm-telemetry"
810
816
  ],
811
- required: ["authorization", "x-ibm-client-id", "wctoken", "wctrustedtoken"]
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: ["currency_code"], equals: "USD" },
827
- { at: ["order_id"], present: true }
833
+ { at: ["user_id"], equals: "anonymous" },
834
+ { at: ["base_site_id"], equals: "uor_b2c" }
828
835
  ],
829
836
  refuse: [
830
- { at: ["save_to_wallet"], when: "true" },
831
- { at: ["cardinal-3d-secure", "jwt"], when: "nonempty" },
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: { at: ["amount"], kind: "decimal_string" },
836
- currency: { at: ["currency_code"] },
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
- resends: [],
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: "at_request",
844
- once_per: null,
845
- pending: ["how long a captured akamai-bm-telemetry header stays accepted"]
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 HCL Commerce storefront scripts (CheckoutPayments.populateProtocolData, wc.service.invoke over $.ajax), read Sep 25, 2026: a same-origin form POST to /AjaxOrderChangeServicePIAddTO with X-Requested-With XMLHttpRequest and the WCS and Akamai cookies.",
853
- "Body: storeId, catalogId, langId, payMethodId (brand code), piAmount (order total), billing_address_id, account (the PAN), cc_cvc, expire_month, expire_year, cc_brand, cc_nameoncard, requesttype ajax, authToken (WCS anti-CSRF), forterToken; saveCreditCard=on or creditCardToUse for a saved card.",
854
- "The card is checked or authorized at this step; the later PIUpdate, OrderPrepare and SubmitOrder calls are card-free."
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: "POST",
862
- path: "/AjaxOrderChangeServicePIAddTO",
863
- query: {},
882
+ method: "PUT",
883
+ path: "/wcs/resources/store/12001/cart/@self/addPayment",
884
+ query: { nocache: ":nocache" },
864
885
  navigation: null,
865
- encoding: "form",
886
+ encoding: "json",
866
887
  session: {
867
888
  cookies: true,
868
889
  cookie_names: null,
869
- headers: [...BROWSER_HEADERS, "content-type", "x-requested-with"],
870
- required: ["x-requested-with"]
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: "as_typed" },
876
- exp_year: { at: "expire_year", format: "as_typed" },
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: [{ at: "cc_brand", map: null }, { at: "payMethodId", map: null }],
881
- bins: null
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: "requesttype", equals: "ajax" },
886
- { at: "authToken", present: true },
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: "saveCreditCard", when: "present" },
891
- { at: "creditCardToUse", when: "present" }
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: "decimal_string" },
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
- "card.brands: the cc_brand and payMethodId values for each brand",
906
- "card.exp_month and card.exp_year: how many digits expire_month and expire_year carry",
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: null }],
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: "deferred",
1141
+ charge_timing: "at_request",
1102
1142
  once_per: null,
1103
1143
  pending: [
1104
- "card.expiry: the form cardExpireDate is sent in",
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: null }],
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. card.pan is grouped4: the rules take the number grouped in fours or as plain digits, and write the real number the way the placeholder was written.",
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: "grouped4" },
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 === groupedFour(digits));
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 && ["digits", "grouped4"].includes(p.card.pan.format) ? panDigits(p.card.pan, reader.exact(p.card.pan.at).value) : null;
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
- return { amountCents: Number(whole) * 100 + Number(fraction.padEnd(2, "0")), currency };
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]|&#0*(?:4[89]|5[0-7]);|&#x0*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 ? "POST" : authorization.method;
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 spec.format === "grouped4" && placeholder.includes(" ") ? groupedFour(facts.number) : facts.number;
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 && ["digits", "grouped4"].includes(p.card.pan.format) ? panDigits(p.card.pan, read.reader.exact(p.card.pan.at).value) : null;
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
- writes.push({ at: spec.at, found: at, value: slotValue(slot, spec, at.value, facts) });
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 };