@agent-cards/sdk 0.23.0 → 0.23.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/adyen-merchant-hosted.generated.d.ts +2 -1
- package/dist/adyen-merchant-hosted.generated.js +32 -32
- package/dist/cybersource-flex.generated.js +5 -3
- package/dist/merchant-card-endpoints.generated.js +24 -10
- package/dist/merchant-session.generated.js +29 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Stop the card fields adyen-web 5 encrypts at a reviewed Adyen merchant profile. adyen-web 5 seals each field as an RSA-OAEP / A256CBC-HS512 JWE, where adyen-web 6 writes RSA-OAEP-256 / A256GCM, and the SDK read only adyen-web 6's form. So the card requests of Cathay Pacific's payment page and Dunelm's legacy checkout, and Cathay's charge request after its signing step, reached the merchant with the agent's dummy card. The adapters now read both forms as a card. Both profiles ship in `observe`, so the adapters abort each of these requests before it leaves the browser and report it as `merchant_profile_observed`. The Vault writes only adyen-web 6's form, so the API refuses a create for a card request in adyen-web 5's form with `cse_template_unsupported`, and nothing is charged.
|
|
6
|
+
|
|
7
|
+
- Abort the requests in which IHG's pay page sends the typed card outside its reviewed card request, in every status, as `card_endpoint_refused` with reason `card_resend`. The page posts the same booking to IHG's enrollment service while its pre-ticked "Enroll for free" box stays ticked, posts it on other paths for more than one room, a travel agent or Business Rewards, and at a hotel that asks for 3-D Secure first posts the number to two 3-D Secure calls. The SDK let each of these reach IHG with the agent's dummy card. A booking with the box unticked reaches IHG's reviewed card request, which the adapters refuse as before while IHG is in `observe`.
|
|
8
|
+
|
|
9
|
+
- Read IHG's booking at a hotel that asks for no security code as IHG's reviewed card request. At the two US hotels a live check visited, the pay page shows no security code field and sends `cvvNumber: {token: ""}`. The adapters read that as a card in a body IHG's profile did not review and refused it with reason `unrecognized_payment_endpoint`, so the booking could never pause for the cardholder. They now refuse it with reason `merchant_card_endpoint_not_enabled`, naming IHG's profile, while IHG is in `observe`, and once Agentcard enables IHG it pauses and asks the cardholder like any other IHG booking. A security code the hotel asks for is still read as three or four digits, and any other value there is refused as before.
|
|
10
|
+
|
|
5
11
|
## 0.23.0
|
|
6
12
|
|
|
7
13
|
### Upgrading from 0.22.0
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
declare var MERCHANT_EXCHANGES_MAX: number;
|
|
2
2
|
declare var MERCHANT_PROFILE_RECOGNIZERS: any;
|
|
3
3
|
declare function declaredEndpointRefusal(endpoint: any): string | null;
|
|
4
|
+
declare function isAdyenWebJweCompact(s: any): boolean;
|
|
4
5
|
declare function sha256Hex(input: any): string;
|
|
5
6
|
declare function merchantAmountFromResponses(profile: any, input: any): {
|
|
6
7
|
ok: boolean;
|
|
@@ -274,4 +275,4 @@ declare function declaredUrlGlobs(endpoint: any): string[];
|
|
|
274
275
|
declare function merchantProfileRulesMatch(profileId: any, served: any): boolean;
|
|
275
276
|
declare function merchantProfileEnvironment(profileId: any): "production" | "sandbox";
|
|
276
277
|
declare function merchantProfileEndpointFor(profileId: any, checkoutOrigin: any): string | null;
|
|
277
|
-
export { MERCHANT_EXCHANGES_MAX, MERCHANT_PROFILE_RECOGNIZERS, carriesAdyenCardCiphertext, classifyDeclaredMerchantRequest, classifyMerchantBody, classifyMerchantRequest, declaredEndpointRefusal, declaredTemplatedUrl, declaredUrlGlobs, declaredUrlRelation, merchantAmountExchanges, merchantAmountFromResponses, merchantAmountPending, merchantAmountStagesOf, merchantBodyKeyPathSha256, merchantChargedFromResponses, merchantProfileEndpointFor, merchantProfileEnvironment, merchantProfileRulesMatch, merchantProfileTemplatedUrl, merchantProfileUrlGlobs, merchantProfileUrlRelation, merchantTotalFromResponses, sha256Hex };
|
|
278
|
+
export { MERCHANT_EXCHANGES_MAX, MERCHANT_PROFILE_RECOGNIZERS, carriesAdyenCardCiphertext, classifyDeclaredMerchantRequest, classifyMerchantBody, classifyMerchantRequest, declaredEndpointRefusal, declaredTemplatedUrl, declaredUrlGlobs, declaredUrlRelation, isAdyenWebJweCompact, merchantAmountExchanges, merchantAmountFromResponses, merchantAmountPending, merchantAmountStagesOf, merchantBodyKeyPathSha256, merchantChargedFromResponses, merchantProfileEndpointFor, merchantProfileEnvironment, merchantProfileRulesMatch, merchantProfileTemplatedUrl, merchantProfileUrlGlobs, merchantProfileUrlRelation, merchantTotalFromResponses, sha256Hex };
|
|
@@ -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: c979802cf1a5aa95eedd5dd00949cd56c5c478ca3c637b3232b69597cc62d4a4
|
|
4
4
|
// src/deep-freeze.js
|
|
5
5
|
function deepFreeze(value) {
|
|
6
6
|
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -1640,40 +1640,38 @@ function merchantBodyRefusal(profile, body) {
|
|
|
1640
1640
|
return null;
|
|
1641
1641
|
}
|
|
1642
1642
|
// src/adyen-jwe.js
|
|
1643
|
-
var ADYEN_JWE_HEADER = { alg: "RSA-OAEP-256", enc: "A256GCM", version: "1" };
|
|
1644
1643
|
var BASE64URL = /^[A-Za-z0-9_-]+$/;
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
if (!
|
|
1653
|
-
return
|
|
1654
|
-
if (encryptedKey.length !== 342 || iv.length !== 16 || tag.length !== 22)
|
|
1655
|
-
return false;
|
|
1656
|
-
if (ciphertext.length > 1024)
|
|
1657
|
-
return false;
|
|
1658
|
-
return isAdyenJweHeader(header);
|
|
1659
|
-
}
|
|
1660
|
-
function isAdyenJweHeader(segment) {
|
|
1661
|
-
if (!BASE64URL.test(segment) || segment.length > 128)
|
|
1662
|
-
return false;
|
|
1644
|
+
var ADYEN_WEB_JWE_FORMATS = /* @__PURE__ */ Object.freeze([
|
|
1645
|
+
/* @__PURE__ */ Object.freeze({ alg: "RSA-OAEP-256", enc: "A256GCM", iv: 16, tag: 22 }),
|
|
1646
|
+
/* @__PURE__ */ Object.freeze({ alg: "RSA-OAEP", enc: "A256CBC-HS512", iv: 22, tag: 43 })
|
|
1647
|
+
]);
|
|
1648
|
+
var ADYEN_WEB_CIPHERTEXT_MAX = 8192;
|
|
1649
|
+
var ADYEN_WEB_HEADER_MAX = 128;
|
|
1650
|
+
function adyenWebJweHeader(segment) {
|
|
1651
|
+
if (!BASE64URL.test(segment) || segment.length > ADYEN_WEB_HEADER_MAX)
|
|
1652
|
+
return null;
|
|
1663
1653
|
let parsed;
|
|
1664
1654
|
try {
|
|
1665
1655
|
parsed = JSON.parse(new TextDecoder().decode(Uint8Array.from(atob(segment.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0))));
|
|
1666
1656
|
}
|
|
1667
1657
|
catch {
|
|
1668
|
-
return
|
|
1658
|
+
return null;
|
|
1669
1659
|
}
|
|
1670
1660
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
1661
|
+
return null;
|
|
1662
|
+
return Object.keys(parsed).sort().join(",") === "alg,enc,version" && parsed.version === "1" ? parsed : null;
|
|
1663
|
+
}
|
|
1664
|
+
function isAdyenWebJweCompact(s) {
|
|
1665
|
+
if (typeof s !== "string" || s.length > ADYEN_WEB_HEADER_MAX + 342 + 22 + ADYEN_WEB_CIPHERTEXT_MAX + 43 + 4)
|
|
1671
1666
|
return false;
|
|
1672
|
-
const
|
|
1673
|
-
|
|
1674
|
-
|
|
1667
|
+
const parts = s.split(".");
|
|
1668
|
+
if (parts.length !== 5 || !parts.every((p) => p.length > 0 && BASE64URL.test(p)))
|
|
1669
|
+
return false;
|
|
1670
|
+
const [header, encryptedKey, iv, ciphertext, tag] = parts;
|
|
1671
|
+
if (encryptedKey.length !== 342 || ciphertext.length > ADYEN_WEB_CIPHERTEXT_MAX)
|
|
1675
1672
|
return false;
|
|
1676
|
-
|
|
1673
|
+
const parsed = adyenWebJweHeader(header);
|
|
1674
|
+
return parsed !== null && ADYEN_WEB_JWE_FORMATS.some((form) => parsed.alg === form.alg && parsed.enc === form.enc && iv.length === form.iv && tag.length === form.tag);
|
|
1677
1675
|
}
|
|
1678
1676
|
// src/sha256.js
|
|
1679
1677
|
var K = /* @__PURE__ */ new Uint32Array([
|
|
@@ -1873,16 +1871,18 @@ function merchantProfileUrlRelation(profileId, rawUrl) {
|
|
|
1873
1871
|
return "then";
|
|
1874
1872
|
return otherCardRequestOf(profile, url) ? "other" : null;
|
|
1875
1873
|
}
|
|
1876
|
-
var JWE_AT =
|
|
1874
|
+
var JWE_AT = /* @__PURE__ */ ADYEN_WEB_JWE_FORMATS.map(({ iv, tag }) => new RegExp(`ey[A-Za-z0-9_-]{1,126}\\.[A-Za-z0-9_-]{342}\\.[A-Za-z0-9_-]{${iv}}\\.[A-Za-z0-9_-]{1,${ADYEN_WEB_CIPHERTEXT_MAX}}\\.[A-Za-z0-9_-]{${tag}}`, "y"));
|
|
1877
1875
|
var LEGACY_BLOB = /adyenjs_0_1_\d{1,4}(?:\$|%24)/i;
|
|
1878
1876
|
function textCarriesCiphertext(text) {
|
|
1879
1877
|
if (LEGACY_BLOB.test(text))
|
|
1880
1878
|
return true;
|
|
1881
1879
|
for (let at = text.indexOf("ey"); at !== -1; at = text.indexOf("ey", at + 1)) {
|
|
1882
|
-
JWE_AT
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1880
|
+
for (const pattern of JWE_AT) {
|
|
1881
|
+
pattern.lastIndex = at;
|
|
1882
|
+
const match = pattern.exec(text);
|
|
1883
|
+
if (match && isAdyenWebJweCompact(match[0]))
|
|
1884
|
+
return true;
|
|
1885
|
+
}
|
|
1886
1886
|
}
|
|
1887
1887
|
return false;
|
|
1888
1888
|
}
|
|
@@ -1947,7 +1947,7 @@ function holderRefusal(profile, parsed) {
|
|
|
1947
1947
|
return `the paused body carries no card object where ${profile.merchant}'s card request holds it`;
|
|
1948
1948
|
}
|
|
1949
1949
|
const values = Object.values(profile.fields).map((name) => Object.prototype.hasOwnProperty.call(holder.value, name) ? holder.value[name] : void 0);
|
|
1950
|
-
if (!values.every((value) => typeof value === "string" &&
|
|
1950
|
+
if (!values.every((value) => typeof value === "string" && isAdyenWebJweCompact(value))) {
|
|
1951
1951
|
return `the paused body does not carry the four encrypted card fields ${profile.merchant}'s card request holds a new card in`;
|
|
1952
1952
|
}
|
|
1953
1953
|
if (new Set(values).size !== values.length)
|
|
@@ -2145,4 +2145,4 @@ function merchantProfileEndpointFor(profileId, checkoutOrigin) {
|
|
|
2145
2145
|
const query = Object.entries(profile.query).map(([key, value]) => `${key}=${value}`).join("&");
|
|
2146
2146
|
return `${pair.endpoint}${profile.path}${query ? `?${query}` : ""}`;
|
|
2147
2147
|
}
|
|
2148
|
-
export { MERCHANT_EXCHANGES_MAX, MERCHANT_PROFILE_RECOGNIZERS, carriesAdyenCardCiphertext, classifyDeclaredMerchantRequest, classifyMerchantBody, classifyMerchantRequest, declaredEndpointRefusal, declaredTemplatedUrl, declaredUrlGlobs, declaredUrlRelation, merchantAmountExchanges, merchantAmountFromResponses, merchantAmountPending, merchantAmountStagesOf, merchantBodyKeyPathSha256, merchantChargedFromResponses, merchantProfileEndpointFor, merchantProfileEnvironment, merchantProfileRulesMatch, merchantProfileTemplatedUrl, merchantProfileUrlGlobs, merchantProfileUrlRelation, merchantTotalFromResponses, sha256Hex };
|
|
2148
|
+
export { MERCHANT_EXCHANGES_MAX, MERCHANT_PROFILE_RECOGNIZERS, carriesAdyenCardCiphertext, classifyDeclaredMerchantRequest, classifyMerchantBody, classifyMerchantRequest, declaredEndpointRefusal, declaredTemplatedUrl, declaredUrlGlobs, declaredUrlRelation, isAdyenWebJweCompact, merchantAmountExchanges, merchantAmountFromResponses, merchantAmountPending, merchantAmountStagesOf, merchantBodyKeyPathSha256, merchantChargedFromResponses, merchantProfileEndpointFor, merchantProfileEnvironment, merchantProfileRulesMatch, merchantProfileTemplatedUrl, merchantProfileUrlGlobs, merchantProfileUrlRelation, merchantTotalFromResponses, sha256Hex };
|
|
@@ -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: c6a711b5dc964dbeb45863780001d99b79090f919b282fed9ae6fc06b4839681
|
|
4
4
|
// src/cybersource-flex-rules.js
|
|
5
5
|
var CYBERSOURCE_FLEX_RULES_SOURCE = Object.freeze({ version: "2.12.1", sha256: "8429ebae83e9745cd53ad8ff0d9ba69bee0087c1435ed0695a25444bed418327" });
|
|
6
6
|
var CYBERSOURCE_FLEX_RULES = [
|
|
@@ -631,9 +631,11 @@ async function transientTokenFacts(status, body, verified, card, { crypto } = {}
|
|
|
631
631
|
if (!/^[0-9]{12,19}$/.test(number) || typeof masked !== "string" || masked.length > 19 || !masked.endsWith(number.slice(-4))) {
|
|
632
632
|
return unconfirmed("last_four");
|
|
633
633
|
}
|
|
634
|
-
const bin = tokenCard.number
|
|
635
|
-
|
|
634
|
+
const { bin, prefix } = tokenCard.number;
|
|
635
|
+
const startsThisCard = (value, form) => typeof value === "string" && form.test(value) && number.startsWith(value);
|
|
636
|
+
if (bin === void 0 && prefix === void 0 || bin !== void 0 && !startsThisCard(bin, /^[0-9]{6,8}$/) || prefix !== void 0 && !startsThisCard(prefix, number.length >= 16 ? /^(?:[0-9]{6}|[0-9]{8})$/ : /^[0-9]{6}$/)) {
|
|
636
637
|
return unconfirmed("bin");
|
|
638
|
+
}
|
|
637
639
|
const month = tokenCard.expirationMonth?.value;
|
|
638
640
|
const year = tokenCard.expirationYear?.value;
|
|
639
641
|
const cardMonth = String(card?.month ?? "").padStart(2, "0");
|
|
@@ -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: d73edb25ace75651a72071180425f4e2b4717e5fd5504f2d2d336d70a55e0f64
|
|
4
4
|
// src/json-path.js
|
|
5
5
|
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
6
6
|
function isPlainObject(v) {
|
|
@@ -1115,9 +1115,11 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1115
1115
|
evidence: [
|
|
1116
1116
|
"IHG pay SPA pay-ng v14.144.1 (chunks 5PVRTMTV, 7U5FHKAZ and AO2OILHC, archive.org copies of Jul 24 and 27, 2026), read Sep 25, 2026; the Sep 5 Common Crawl copy of find-hotels v14.147.0 still sends checkout there.",
|
|
1117
1117
|
'ReservationService.createReservation posts the first reservation, JSON with withCredentials, to https://apis.ihg.com/reservations/v3/hotels: {id, imageStatus "COMMIT", segments: [{payment: {paymentByPoints, paymentInformation: {paymentCard}, guaranteeType, billingAddress}, ...}], userProfiles, hotels}.',
|
|
1118
|
-
|
|
1118
|
+
'paymentCard (getPaymentInformation): {cardCode, expireDate `${expirationMonth}${expirationYear}` (MMYY), cardHolderName, cvvNumber: {token: <CVV, or "" when the hotel asks for none>}, cardNumber: {token: <PAN, spaces stripped>}}, plus 3dsData after a PSD2 check.',
|
|
1119
|
+
`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.`,
|
|
1119
1120
|
"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.",
|
|
1120
|
-
"Hotels that set isPaymentServiceInformationRequired first send the PAN to /payments/threedsecure/v1/token and /paymentSetup and put the result in 3dsData, which is refused here; China UnionPay and PayPal set cupTokenId and paymentSource, also refused."
|
|
1121
|
+
"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.'
|
|
1121
1123
|
],
|
|
1122
1124
|
har: null,
|
|
1123
1125
|
checkout: "https://www.ihg.com",
|
|
@@ -1148,7 +1150,7 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1148
1150
|
},
|
|
1149
1151
|
card: {
|
|
1150
1152
|
pan: { at: ["segments", 0, "payment", "paymentInformation", "paymentCard", "cardNumber", "token"], format: "digits" },
|
|
1151
|
-
cvv: { at: ["segments", 0, "payment", "paymentInformation", "paymentCard", "cvvNumber", "token"], format: "
|
|
1153
|
+
cvv: { at: ["segments", 0, "payment", "paymentInformation", "paymentCard", "cvvNumber", "token"], format: "digits_if_asked" },
|
|
1152
1154
|
exp_month: null,
|
|
1153
1155
|
exp_year: null,
|
|
1154
1156
|
expiry: { at: ["segments", 0, "payment", "paymentInformation", "paymentCard", "expireDate"], format: "mmyy" },
|
|
@@ -1171,7 +1173,17 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
1171
1173
|
amount: null,
|
|
1172
1174
|
currency: "USD",
|
|
1173
1175
|
response: { headers: ["content-type", "set-cookie"], withhold: [] },
|
|
1174
|
-
|
|
1176
|
+
// The page's other posts of the same card (evidence above): an approval never covers them.
|
|
1177
|
+
resends: [
|
|
1178
|
+
{ method: "POST", path: "/channels/direct/quickEnroll/v8/hotelReservations", query: {} },
|
|
1179
|
+
{ method: "POST", path: "/channels/direct/quickEnroll/v9/hotelReservations", query: {} },
|
|
1180
|
+
{ method: "POST", path: "/channels/direct/quickEnroll/v8/multiRoomReservation", query: {} },
|
|
1181
|
+
{ method: "POST", path: "/reservations/v3/hotels/rooms/fromCart", query: {} },
|
|
1182
|
+
{ method: "POST", path: "/reservations/v3/hotels/rooms/agent/reservations", query: {} },
|
|
1183
|
+
{ method: "POST", path: "/reservations/v3/businessRewardReservations", query: {} },
|
|
1184
|
+
{ method: "POST", path: "/payments/threedsecure/v1/token", query: {} },
|
|
1185
|
+
{ method: "POST", path: "/payments/threedsecure/v1/paymentSetup", query: {} }
|
|
1186
|
+
],
|
|
1175
1187
|
challenge: null,
|
|
1176
1188
|
facts: null,
|
|
1177
1189
|
bootstrap: "/robots.txt",
|
|
@@ -2057,8 +2069,10 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
2057
2069
|
evidence: [
|
|
2058
2070
|
"HUMA Kotor Bay Hotel & Villas books through D-EDGE (secure-hotel-booking.com/d-edge/HUMA-Kotor-Bay-Hotel-Villas/J6UZ); D-EDGE's payment hub on pay.secure-hotel-booking.com (main.bundle.js, Last-Modified 2026-09-10) renders a CreditCard provider as an iframe at window.cardFrameUrl, card.secure-hotel-booking.com/<the session uuid>.",
|
|
2059
2071
|
"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).",
|
|
2060
|
-
"The frame also posts the
|
|
2061
|
-
"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."
|
|
2072
|
+
"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
|
+
"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.",
|
|
2075
|
+
"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."
|
|
2062
2076
|
],
|
|
2063
2077
|
har: null,
|
|
2064
2078
|
checkout: "https://card.secure-hotel-booking.com",
|
|
@@ -2077,7 +2091,7 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
2077
2091
|
required: []
|
|
2078
2092
|
},
|
|
2079
2093
|
card: {
|
|
2080
|
-
pan: { at: "CardNumber", format: "
|
|
2094
|
+
pan: { at: "CardNumber", format: "grouped4" },
|
|
2081
2095
|
cvv: { at: "Cvv", format: "digits" },
|
|
2082
2096
|
exp_month: { at: "Month", format: "m" },
|
|
2083
2097
|
exp_year: { at: "Year", format: "yyyy" },
|
|
@@ -2104,9 +2118,9 @@ var MERCHANT_CARD_ENDPOINT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
2104
2118
|
pending: [
|
|
2105
2119
|
"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)",
|
|
2106
2120
|
"which cookies the card post needs, with DataDome in front of the host",
|
|
2107
|
-
"whether
|
|
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)",
|
|
2108
2122
|
"the answer to the card post (a redirect, a 3DS step or a page), and whether a 3DS step sends the card again",
|
|
2109
|
-
|
|
2123
|
+
GUARANTEE_RATES,
|
|
2110
2124
|
"that the bootstrap path answers without a server error"
|
|
2111
2125
|
]
|
|
2112
2126
|
},
|
|
@@ -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: b114efe706f6bfbca37bdf2a2044eb3f1e0215c03f4de228f25c9155de8c5629
|
|
4
4
|
// src/deep-freeze.js
|
|
5
5
|
function deepFreeze(value) {
|
|
6
6
|
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -839,22 +839,41 @@ function attributesOf(text) {
|
|
|
839
839
|
}
|
|
840
840
|
return attributes;
|
|
841
841
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
842
|
+
var ROCKET_LOADER_ACTIVATOR = /^\/cdn-cgi\/scripts\/[0-9a-f]{8}\/cloudflare-static\/rocket-loader\.min\.js$/;
|
|
843
|
+
var ROCKET_LOADER_SETTINGS = /^([0-9a-f]{24}-)\|[0-9]+$/;
|
|
844
|
+
var ROCKET_LOADER_TYPE = /^([0-9a-f]{24}-)text\/javascript$/;
|
|
845
|
+
function scriptKind(attributes) {
|
|
845
846
|
const type = attributes.has("type") ? attributes.get("type") || "text/javascript" : attributes.get("language") ? `text/${attributes.get("language")}` : "text/javascript";
|
|
846
847
|
const typeString = type.replace(/^[\t\n\f\r ]+|[\t\n\f\r ]+$/g, "").toLowerCase();
|
|
847
848
|
if (typeString === "module")
|
|
848
|
-
return
|
|
849
|
-
return JAVASCRIPT_TYPES.has(typeString) && !attributes.has("nomodule");
|
|
849
|
+
return "module";
|
|
850
|
+
return JAVASCRIPT_TYPES.has(typeString) && !attributes.has("nomodule") ? "classic" : null;
|
|
851
|
+
}
|
|
852
|
+
function runsInline(attributes, rocketLoaderNonces) {
|
|
853
|
+
if (attributes.has("src"))
|
|
854
|
+
return false;
|
|
855
|
+
const rewritten = ROCKET_LOADER_TYPE.exec(attributes.get("type") ?? "");
|
|
856
|
+
if (rewritten)
|
|
857
|
+
return rocketLoaderNonces.has(rewritten[1]) && !attributes.has("nomodule");
|
|
858
|
+
return scriptKind(attributes) !== null;
|
|
859
|
+
}
|
|
860
|
+
function rocketLoaderNoncesOf(scripts) {
|
|
861
|
+
const nonces = /* @__PURE__ */ new Set();
|
|
862
|
+
for (const attributes of scripts) {
|
|
863
|
+
const settings = ROCKET_LOADER_SETTINGS.exec(attributes.get("data-cf-settings") ?? "");
|
|
864
|
+
if (settings && ROCKET_LOADER_ACTIVATOR.test(attributes.get("src") ?? "") && scriptKind(attributes) === "classic" && attributes.has("defer") && !attributes.has("async"))
|
|
865
|
+
nonces.add(settings[1]);
|
|
866
|
+
}
|
|
867
|
+
return nonces;
|
|
850
868
|
}
|
|
851
869
|
function inlineScriptText(html) {
|
|
852
|
-
const
|
|
870
|
+
const scripts = [];
|
|
853
871
|
for (const [, attributes, text] of html.matchAll(COMMENT_OR_SCRIPT)) {
|
|
854
|
-
if (text !== void 0
|
|
855
|
-
|
|
872
|
+
if (text !== void 0)
|
|
873
|
+
scripts.push({ attributes: attributesOf(attributes), text });
|
|
856
874
|
}
|
|
857
|
-
|
|
875
|
+
const nonces = rocketLoaderNoncesOf(scripts.map((script) => script.attributes));
|
|
876
|
+
return scripts.filter((script) => runsInline(script.attributes, nonces)).map((script) => script.text).join("\n");
|
|
858
877
|
}
|
|
859
878
|
function htmlReading(block, html) {
|
|
860
879
|
const markup = html.replace(NON_MARKUP, "");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-cards/sdk",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.2",
|
|
4
4
|
"description": "Agentcard's SDK: one client, Agentcard, with two modules. checkout pauses a browser purchase and pays it with the person's own card; voice connects a person to your product once and approves a purchase by voice inside it. Card data never enters your process.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|