泥潭日报 uscardforum · 每日精选

HSBC travel portal 查看OTA脚本

内容摘要

脚本可显示HSBC旅行门户酒店房型的OTA来源。

1. 关键信息

  • 作者 @windrunner 发布 Tampermonkey 脚本,可拦截 HSBC travel portal API,在页面注入 Inventory Type(AGD=Agoda, PCLN=Priceline, BKG=Booking)(#1)
  • 脚本通过匹配标题、价格、支付类型、描述来定位房型,若多个 OTA 有相同房型会显示多个类型 (#1)
  • 需开启浏览器插件开发者模式 (#14),部分酒店不显示 Inventory Type 可能因数据或脚本限制 (#25 #26)
  • “pay when you stay” 选项以 Booking 为主,Agoda 也有,Priceline 较少 (#15 #17 #21 #22)

2. 羊毛/优惠信息

  • HSBC Elite 卡 $400 travel credit(#3 #11)
  • HSBC travel portal 预订可累积 3X 积分,过快取消可能导致积分被回收(#1)
  • 使用 pay when you stay 时无需在卡上预留 8k+ 额度(#11 #12)

3. 最新动态

  • 脚本已搬运至 lounge 并持续可用(#4)
  • 发现所有 POSTPAID 均为 BKG(Booking),Agoda 的“book now, pay later”在 portal 中搜不到(#22)
  • Agoda 的 Inventory Type 近期不再显示(#27),脚本可能因页面更新需刷新或重新登录(#26)

4. 争议或不同意见

  • 部分用户认为 HSBC 的 AML/Fraud review 团队是最大阻碍(#5)
  • 脚本匹配限制可能导致同一房型显示多个 OTA,需用户自行判断(#1)
  • 过快取消是否一定触发问题存在争议,作者建议谨慎操作(#1 #10 #22)

5. 行动建议

  • 安装 Tampermonkey 并启用开发者模式后添加脚本,刷新 portal 页面使用(#14)
  • 预订前务必查看 Cancellation Policy,优先选择 pay when you stay 且不要过快取消(#1)
  • 若房型显示多个 Inventory Type,建议轮流使用 Booking 和 Agoda,避免 Priceline(可能需电话退订)(#10)
  • 使用 $400 credit 时无需保留大额可用额度(#12)
原始内容
--- 第 1 楼来自 windrunner 的回复 (2024-12-06 06:57:35 PST) ---

因为经常有人问,加上最近又有好几个人来问我,于是弄了个脚本。 以下脚本配合tampermonkey食用 脚本代码 // ==UserScript== // @name HSBC Travel Inventory Type // @namespace http://tampermonkey.net/ // @version 1.0 // @description HSBC Travel Inventory Type // @author lol // @match https://hsbc-travel-membersite.podiumrewards.com/* // @grant none // @run-at document-start // ==/UserScript== (function () { 'use strict'; if (!location.hash.startsWith("#/hotels/detail/")) { return; } console.log("HSBC Inventory Script Loaded."); // Intercept XMLHttpRequest (function () { const originalXHR = window.XMLHttpRequest; function ModifiedXHR() { const xhr = new originalXHR(); // Intercept 'onreadystatechange' const originalOpen = xhr.open; xhr.open = function (method, url, ...args) { // Check if the request is for the target API this.isTargetAPI = url.includes('/api/v1/hotel/view'); return originalOpen.apply(this, [method, url, ...args]); }; const originalSend = xhr.send; xhr.send = function (...args) { if (this.isTargetAPI) { this.addEventListener('readystatechange', function () { if (this.readyState === 4 && this.status === 200) { try { const data = JSON.parse(this.responseText); waitForRoomContainers(() => processApiResponse(data)); } catch (err) { console.error('Error parsing API response:', err); } } }); } return originalSend.apply(this, args); }; return xhr; } window.XMLHttpRequest = ModifiedXHR; })(); // Intercept fetch const originalFetch = window.fetch; window.fetch = async function (...args) { const response = await originalFetch(...args); // Check if the request matches the target API if (args[0].includes('/api/v1/hotel/view')) { // Clone the response to parse its data const clonedResponse = response.clone(); clonedResponse.json().then(data => { waitForRoomContainers(() => processApiResponse(data)); }); } return response; }; // Function to compare floating-point numbers with an epsilon function floatEquals(a, b, epsilon = 1e-2) { return Math.abs(a - b) < epsilon; } // Wait for room-container elements to exist function waitForRoomContainers(callback, maxRetries = 20, interval = 500) { let retries = 0; const checkExist = setInterval(() => { const containers = document.querySelectorAll('.room-container'); if (containers.length > 0) { clearInterval(checkExist); callback(); } else if (retries >= maxRetries) { clearInterval(checkExist); console.error('Timeout: room-container elements not found.'); } retries++; }, interval); } // Process API response and inject inventory_type function processApiResponse(data) { try { const roomData = data.data.hotel_data[0].room_data; // Iterate through the room data from API roomData.forEach(apiRoom => { const apiTitle = apiRoom.rate_data[0]?.title; // Get the title const apiPrice = apiRoom.rate_data[0]?.price_details.display_price; // Get the price const inventoryType = apiRoom.rate_data[0]?.inventory_type; // Get the inventory type const apiPaymentType = apiRoom.rate_data[0]?.payment_type; // Get payment type (POSTPAID, PREPAID) const apiDescription = apiRoom.rate_data[0]?.description.trim(); // Get description // Determine payment type string from API const apiPaymentText = apiPaymentType === "PREPAID" ? "Prepaid - Book Now, Pay Now" : "Book now, pay when you stay"; // console.log(`title ${apiTitle}, price ${apiPrice}, inventoryType ${inventoryType}`); // Match the API data with DOM elements document.querySelectorAll('.room-container').forEach(container => { const roomTitle = container.querySelector('.h5.regular')?.textContent.trim(); const roomPriceText = container.querySelector('.price-position .payment-span')?.textContent.trim(); const roomPrice = roomPriceText ? parseFloat(roomPriceText.replace("$", "").replace(",", "").split(" ")[0]) : NaN; // Dynamically locate payment type element const paymentTypeElement = Array.from(container.querySelectorAll('li')) .find(li => li.id.startsWith('room-payment-type')); const roomPaymentText = paymentTypeElement ? paymentTypeElement.textContent.trim() : ''; // Dynamically locate description element const descriptionElement = container.querySelector('.col-sm-12 p'); const roomDescription = descriptionElement ? descriptionElement.textContent.trim() : ''; // console.log(`title ${roomTitle}, price ${roomPrice}`); // Compare the API title and price with the page title and price if (roomTitle === apiTitle && floatEquals(roomPrice, parseFloat(apiPrice)) && roomPaymentText.includes(apiPaymentText) && roomDescription === apiDescription) { console.log(`Matched ${roomTitle}`); // Append the inventory type to the room details const detailsList = container.querySelector('.col-sm-5.details ul'); if (detailsList) { const inventoryItem = document.createElement('li'); inventoryItem.className = 'inventory-type'; inventoryItem.innerHTML = `<strong>Inventory Type:</strong> ${inventoryType}`; detailsList.appendChild(inventoryItem); } } }); }); } catch (err) { console.error("Error processing API response:", err); } } })(); 效果如下 /uploads/short-url/lRWkWfTxTDLnzCpsG8uz7AdpTZX.png?dl=1 AGD是Agoda,PCLN是Priceline,BKG是Booking 因为脚本匹配方式的限制,一个房型里可能显示多个Inventory Type,如果显示为同一个OTA那么问题不大,如果显示不同的平台 说明不同OTA里都有一样的房型(标题,描述,价格,prepaid/postpaid均一样),脚本不能确定具体是哪个OTA 预定前请看好Cancellation Policy,如果不打算去住推荐订pay when you stay的价格,并且不要太快取消。实测Booking有概率同步取消信息给HSBC(如果取消的太快),这种情况多扣除的3X会被返还,但很可能会增大风险(正常情况的计算 https://www.uscardforum.com/t/topic/147780 )

--- 第 2 楼来自 bumblebee 的回复 (2024-12-06 07:05:17 PST) ---

感谢大佬,希望Elite能活久一点

--- 第 3 楼来自 Michael_1 的回复 (2024-12-06 07:11:47 PST) ---

感谢大佬,正好打算把今年的400credit拿了,好人一生平安

--- 第 4 楼来自 windrunner 的回复 (2024-12-06 07:29:21 PST) ---

把之前帖子也搬进lounge来了,希望能一直活下去

--- 第 5 楼来自 bumblebee 的回复 (2024-12-06 08:00:42 PST) ---

感觉最大的阻碍是HSBC搞AML/Fraud review的三哥三姐们

--- 第 6 楼来自 Zhuning 的回复 (2024-12-06 08:39:17 PST) ---

感谢大佬。 刚测试,非常方便, 一目了然 /uploads/short-url/yJSGKCZCnXNMbbgCg9xZgxRISGd.jpeg?dl=1

--- 第 7 楼来自 Yidegu 的回复 (2024-12-06 08:40:46 PST) ---

而你 我的朋友 才是真正的英雄

--- 第 8 楼来自 windrunner 的回复 (2024-12-06 08:48:01 PST) ---

hsbc这个api是真的难蚌,我搜芝加哥四季能返回100多个房型,每个房型下面显示四个BKG,疑似重复返回四遍booking的结果 貌似全是重复的

--- 第 9 楼来自 Killla 的回复 (2024-12-06 21:36:40 PST) ---

所以最好是薅Priceline或者Agoda的?

--- 第 10 楼来自 windrunner 的回复 (2024-12-07 02:29:40 PST) ---

priceline可能要打电话退 booking和agoda轮流定 别太快取消基本就没问题

--- 第 11 楼来自 Yidegu 的回复 (2024-12-07 11:44:44 PST) ---

还没实操8k pay when stay 想问一下 如果是pay when stay的话 400 credit post报销 不用留8k+额度在卡上是吧?

--- 第 12 楼来自 windrunner 的回复 (2024-12-07 11:56:38 PST) ---

不需要留额度

--- 第 13 楼来自 Savingwizard 的回复 (2024-12-15 22:27:22 PST) ---

这个怎么用啊?在Tampermonkey里create a new script, 然后copy/paste 到script window, 然后save file. installed userscripts tab里多出一个script,已经enable了。refresh 网页没有变化。

--- 第 14 楼来自 windrunner 的回复 (2024-12-16 08:35:12 PST) ---

打开网页之后tampermonkey有显示script正在运行么? 也有可能是浏览器插件的developer mode没开,我之前用edge,tampermonkey提示我要打开才能用

--- 第 15 楼来自 Jiojoe 的回复 (2024-12-21 20:04:14 PST) ---

刷了好几个,发现有pay when you stay的都是booking,这个是巧合还是只有booking有这个功能

--- 第 16 楼来自 misc 的回复 (2024-12-21 21:57:11 PST) ---

居然有comments, 楼主好人

--- 第 17 楼来自 windrunner 的回复 (2024-12-22 04:48:50 PST) ---

agoda也有 priceline好像没咋看到

--- 第 18 楼来自 windrunner 的回复 (2024-12-22 04:49:29 PST) ---

因为太懒 所以让gpt写了大部分 gpt写了很多注释

--- 第 19 楼来自 Savingwizard 的回复 (2024-12-22 10:00:45 PST) ---

看到了。原来不是所有的房型都有 Inventory Type ,没有这个的是不知道type吗?

--- 第 20 楼来自 misc 的回复 (2024-12-22 10:42:37 PST) ---

一点建议的话,直接把平台 显示在pay when you stay 那里,直接ctrl + f, pay when you stay 带上平台能精准定位

--- 第 21 楼来自 liver 的回复 (2025-01-03 16:25:09 PST) ---

same,刷了10个城市每个城市10个酒店,也是发现有pay when you stay的都是booking

--- 第 22 楼来自 ritz 的回复 (2025-01-04 19:23:21 PST) ---

same. 目前搜索所有POSTPAID的都是BKG的。 直接在agoda上搜book now, pay later的酒店房型在hsbc travel里都搜不到,可能是被屏蔽了。 结合之前dp: windrunner: 实测Booking有概率同步取消信息给HSBC 莫非这就是故意的,防止先定再取消的办法?

--- 第 23 楼来自 theo 的回复 (2025-12-12 16:30:53 PST) ---

windrunner: 预定前请看好Cancellation Policy,如果不打算去住推荐订pay when you stay的价格,并且不要太快取消。实测Booking有概率同步取消信息给HSBC(如果取消的太快),这种情况多扣除的3X会被返还,但很可能会增大风险(正常情况的计算 https://www.uscardforum.com/t/topic/147780 ) 感谢大佬的脚本 现在已经能够看到三种不同的PROVIDER, 但是还是找不到PAY WHEN STAY啊?一口气看了几十个都没有 蚌埠住了 比如找到一个有退款选项的 /uploads/short-url/kh8G8IFYrorgq4P7jd0bdBx5hoW.jpeg?dl=1 一个个点进去之后 /uploads/short-url/4CZP2hYCxcQMcEICNkZRq0bofIK.png?dl=1

--- 第 24 楼来自 mack1313 的回复 (2025-12-13 01:15:32 PST) ---

之前沒看到這個神帖 跟Priceline交流了好一陣子 謝過大哥,好人一生平安

--- 第 25 楼来自 mack1313 的回复 (2025-12-13 01:17:00 PST) ---

不過想請問沒有每一個選項都有Inventory Type是正常的嗎?

--- 第 26 楼来自 tikimad 的回复 (2026-01-30 04:57:49 PST) ---

update: 退出hsbc重新打开后可以了 请问op是挂了吗 /uploads/short-url/jbz2cRzXkgctpXGm4ricSMWJOru.jpeg?dl=1 脚本正常启动,所有的酒店都不显示Inventory Type

--- 第 27 楼来自 Hiram 的回复 (2026-04-29 04:42:20 PDT) ---

我发现现在只有 priceline 和 booking 显示了诶,agoda 是不是都不显示了?