開發者串接規格

PMS 與通路串接 HUIDUN 房量中台

這份規格給 PMS 廠商與技術窗口。PMS 端只需要實作四支 OpenTravel 2003/05 標準訊息;下單、預留、防超賣、對帳由中台處理。範例都是沙盒的真實請求與回應。

1. 總覽

HUIDUN 有兩種串接模式。絕大多數 PMS 使用模式 A,可以自助完成;模式 B 適用於只能提供即時查詢、沒有推送能力的系統,逐案開通。

模式 A:推送與拉單(預設)模式 B:即時連接器(逐案)
誰呼叫誰PMS 呼叫 HUIDUNHUIDUN 呼叫 PMS
協定OpenTravel 2003/05 XML,HTTP Basic over TLSJSON,HMAC-SHA256 機器簽章
房量主人PMS(串接後 HUIDUN 端手動改價改量會被擋下)PMS
PMS 要做的事取房型、推房價、推房量、拉訂單四支訊息提供查詢、報價、預留、下單、取消、查單端點

沙盒

獨立的測試資料庫,申請後當下開通,30 天後停用。

https://huidun-demo.luma-e61.workers.dev/ota/cm

正式環境

通過認證後發給正式帳密;路徑與沙盒相同,只換主機名稱。

https://app.hui-dun.com/ota/cm

2. 認證

  • 每一個請求都帶 Authorization: Basic base64(帳號:密碼),全程 HTTPS。HUIDUN 只保存密碼的雜湊值,遺失只能重設。
  • HotelCode(拉單時是 hotel_id)是飯店在 HUIDUN 後台「通路串接」選擇您的 PMS 時設定的代碼。飯店沒有選擇您的 PMS,這個代碼對您的帳號就不存在(回 392)。
  • 房型代碼 InvTypeCode/RoomTypeCode 與方案代碼 RatePlanCode 以取房型回傳的為準,對應表建在 PMS 端。
  • 每個帳號每分鐘上限 1,200 次請求,超過回 366;單一請求本文上限 1 MB。

3. 取房型與方案 OTA_HotelProduct

取得這間飯店在 HUIDUN 上的房型與方案組合,每一組「房型 × 方案」一個 HotelProduct。PaymentCollection 為 HotelCollect(到店付款)或 OTACollect(線上預付)。

POST https://huidun-demo.luma-e61.workers.dev/ota/cm/OTA_HotelProduct
Authorization: Basic base64(帳號:密碼)
Content-Type: application/xml
<OTA_HotelProductRQ xmlns="http://www.opentravel.org/OTA/2003/05" Version="1.0">
<HotelProducts>
<HotelProduct HotelCode="SBX-LUVQ9U"/>
</HotelProducts>
</OTA_HotelProductRQ>
<OTA_HotelProductRS Version="1.0" TimeStamp="2026-10-25T06:01:40Z" xmlns="http://www.opentravel.org/OTA/2003/05">
<Success />
<HotelProducts HotelCode="SBX-LUVQ9U">
<HotelProduct>
<RoomTypes><RoomType RoomTypeCode="STD" RoomTypeName="標準雙人房" MaxOccupancy="2" /></RoomTypes>
<RatePlans><RatePlan RatePlanCode="RO" RatePlanName="純住宿" PaymentCollection="HotelCollect" /></RatePlans>
</HotelProduct>
<HotelProduct>
<RoomTypes><RoomType RoomTypeCode="STD" RoomTypeName="標準雙人房" MaxOccupancy="2" /></RoomTypes>
<RatePlans><RatePlan RatePlanCode="BB" RatePlanName="含早餐" PaymentCollection="OTACollect" /></RatePlans>
</HotelProduct>
<!-- 每一組「房型 × 方案」一個 HotelProduct -->
</HotelProducts>
<TPA_Extensions><Pricing Type="Standard" /></TPA_Extensions>
</OTA_HotelProductRS>

4. 推每日房價 OTA_HotelRateAmountNotif

  • Start/End 為入住日(含頭尾),格式 YYYY-MM-DD,一段最多 365 天。
  • AmountAfterTax 為每間每晚含稅價;不帶 NumberOfGuests 的那一筆是基本價,帶了就是該人數的價格。
  • CurrencyCode 必須與飯店設定的幣別一致(目前為 TWD),不一致回 61。
  • 同一個請求可以放多個 RateAmountMessage;任一筆有錯,整個請求都不寫入,並在 Errors 列出每一筆錯誤(最多 20 筆)。
POST https://huidun-demo.luma-e61.workers.dev/ota/cm/OTA_HotelRateAmountNotif
<OTA_HotelRateAmountNotifRQ xmlns="http://www.opentravel.org/OTA/2003/05" Version="1.0">
<RateAmountMessages HotelCode="SBX-LUVQ9U">
<RateAmountMessage>
<StatusApplicationControl InvTypeCode="STD" RatePlanCode="BB"/>
<Rates>
<Rate CurrencyCode="TWD" Start="2026-11-04" End="2026-11-05">
<BaseByGuestAmts>
<BaseByGuestAmt AmountAfterTax="3500"/>
<BaseByGuestAmt NumberOfGuests="1" AmountAfterTax="3200"/>
</BaseByGuestAmts>
</Rate>
</Rates>
</RateAmountMessage>
</RateAmountMessages>
</OTA_HotelRateAmountNotifRQ>
<OTA_HotelRateAmountNotifRS Version="1.0" TimeStamp="2026-10-25T06:01:40Z" xmlns="http://www.opentravel.org/OTA/2003/05">
<Success />
<UniqueID ID="1792908100105976" Type="506" />
</OTA_HotelRateAmountNotifRS>

5. 推房量與限制 OTA_HotelAvailNotif

  • BookingLimit:這段期間每晚可賣幾間(0 至 9999)。中台成立訂單時從這個數字逐晚扣量。
  • RestrictionStatus:Status="Close"/"Open";不帶 Restriction 為關房,Arrival 為該日不可入住,Departure 為該日不可退房。
  • LengthsOfStay:SetMinLOS、SetMaxLOS(以入住日計),SetMinLOS_Through、SetMaxLOS_Through(以住宿期間每一晚計)。
  • 限制類不帶 RatePlanCode 時,套用到該房型當下所有適用的方案。
POST https://huidun-demo.luma-e61.workers.dev/ota/cm/OTA_HotelAvailNotif
<OTA_HotelAvailNotifRQ xmlns="http://www.opentravel.org/OTA/2003/05" Version="1.0">
<AvailStatusMessages HotelCode="SBX-LUVQ9U">
<!-- 房量:BookingLimit=這幾天每晚可賣幾間 -->
<AvailStatusMessage BookingLimit="3">
<StatusApplicationControl Start="2026-11-04" End="2026-11-05" InvTypeCode="STD"/>
</AvailStatusMessage>
<!-- 關房/不可入住/最少住幾晚(不帶 RatePlanCode=套用到該房型所有方案) -->
<AvailStatusMessage>
<StatusApplicationControl Start="2026-12-31" End="2026-12-31" InvTypeCode="DLX" RatePlanCode="RO"/>
<RestrictionStatus Status="Close" Restriction="Arrival"/>
<LengthsOfStay><LengthOfStay MinMaxMessageType="SetMinLOS" Time="2"/></LengthsOfStay>
</AvailStatusMessage>
</AvailStatusMessages>
</OTA_HotelAvailNotifRQ>
<OTA_HotelAvailNotifRS Version="1.0" TimeStamp="2026-10-25T06:01:40Z" xmlns="http://www.opentravel.org/OTA/2003/05">
<Success />
<UniqueID ID="1792908100106824" Type="505" />
</OTA_HotelAvailNotifRS>

6. 拉訂單 OTA_HotelResNotif

依「最後異動時間」區間拉回訂單,或用 id 查單一訂單。建議每 1 至 5 分鐘拉一次,區間首尾互相重疊一點,以訂單編號去重。

參數說明
hotel_id必填,飯店代碼
from/toISO 8601 時間,依訂單最後異動時間篩選;未帶 id 時必填
limit10 至 200,預設 10;依異動時間由舊到新排序
id選填,訂單編號(ResID_Value)
  • ResStatus:Book 成立(含預留中,見第 7 節)、Modify 已變更、Cancel 已取消。
  • 訂單編號在 HotelReservationID ResID_Value,同一筆訂單的編號不會改變;變更與取消都以同一個編號再出現一次。
  • PrepaidIndicator="true" 表示旅客已線上付款,飯店不必再向旅客收房費。
GET https://huidun-demo.luma-e61.workers.dev/ota/cm/OTA_HotelResNotif?hotel_id=SBX-LUVQ9U&from=2026-10-25T06:00:00Z&to=2026-10-25T07:00:00Z&limit=50
Authorization: Basic base64(帳號:密碼)
# 只查單一訂單:
GET .../ota/cm/OTA_HotelResNotif?hotel_id=SBX-LUVQ9U&id=6193082730
<OTA_HotelResNotifRS Version="1.0" TimeStamp="2026-10-25T06:01:40Z" xmlns="http://www.opentravel.org/OTA/2017/05">
<HotelReservations>
<HotelReservation CreateDateTime="2026-10-25T06:01:40+00:00" LastModifyDateTime="2026-10-25T06:01:40+00:00" ResStatus="Book">
<RoomStays><RoomStay>
<RoomTypes><RoomType RoomTypeCode="STD" NonSmoking="true"><RoomDescription Name="標準雙人房" /></RoomType></RoomTypes>
<RatePlans><RatePlan RatePlanCode="BB" PrepaidIndicator="true"><RatePlanDescription Name="含早餐" /><MealsIncluded MealPlanCodes="19" /></RatePlan></RatePlans>
<RoomRates><RoomRate RoomTypeCode="STD" RatePlanCode="BB" NumberOfUnits="1"><Rates>
<Rate UnitMultiplier="1" RateTimeUnit="Day" EffectiveDate="2026-11-04" ExpireDate="2026-11-05"><Total AmountAfterTax="3500.00" CurrencyCode="TWD" /></Rate>
<Rate UnitMultiplier="1" RateTimeUnit="Day" EffectiveDate="2026-11-05" ExpireDate="2026-11-06"><Total AmountAfterTax="3500.00" CurrencyCode="TWD" /></Rate>
</Rates></RoomRate></RoomRates>
<BasicPropertyInfo HotelCode="SBX-LUVQ9U" HotelName="沙盒飯店 LUVQ9U" />
</RoomStay></RoomStays>
<ResGuests><ResGuest><Profiles><ProfileInfo><Profile><Customer>
<PersonName><GivenName>旅客</GivenName><Surname>測</Surname></PersonName>
<ContactPerson><PersonName><GivenName>旅客</GivenName><Surname>測</Surname></PersonName><Telephone PhoneNumber="0900000000" /><Email /></ContactPerson>
</Customer></Profile></ProfileInfo></Profiles></ResGuest></ResGuests>
<ResGlobalInfo>
<GuestCounts><GuestCount AgeQualifyingCode="10" Count="2" /></GuestCounts>
<TimeSpan Start="2026-11-04" End="2026-11-06" />
<Total AmountAfterTax="7000.00" CurrencyCode="TWD" />
<HotelReservationIDs><HotelReservationID ResID_Type="14" ResID_Value="6193082730" /></HotelReservationIDs>
</ResGlobalInfo>
</HotelReservation>
</HotelReservations>
</OTA_HotelResNotifRS>

7. 預留、確認與防超賣

通路下單一律走三段式,名額在預留當下就扣住。兩個通路同時搶最後一間時,輸的一方在付款之前就收到「已客滿」,不會發生旅客付了錢卻沒有房。

階段中台做什麼PMS 拉單看到
預留逐晚條件式扣量,任一晚不足整筆不成立;預留預設 10 分鐘、最長 30 分鐘Book(付款那幾分鐘您就能先在其他通路扣掉)
確認付款成功後確認,訂單成立並記入飯店服務費帳同一筆,狀態不變
逾時或取消房量回補看過的那一筆會以 Cancel 再出現;從未被拉到的逾時預留不會出現
  • 每間飯店可設定保留間數(預設 1 間):PMS 推進來的房量保留這幾間不賣,吸收兩邊同步之間的時間差。
  • 串接後房量的主人是 PMS:HUIDUN 後台的手動改價改量會被擋下,請在 PMS 端修改再推送。
  • PMS 拉到訂單後再推送房量時,中台會辨識已被拉走的訂單,不會重複扣量。

8. 通路 API(賣方使用;沙盒用來模擬訂單)

賣方通路透過 /api/mp 查房、報價與下單。PMS 不需要實作這一節;沙盒會給您一把測試用的通路金鑰,讓您不必等真實訂單就能看到訂單如何出現在拉單結果裡。

POST /api/mp/quote
X-API-Key: 沙盒通路金鑰
{"channel":"sbx_luvq9u","source":"huidun","merchant":"PROP-SBX-LUVQ9U",
"unit":"STD","plan":"BB","day":"2026-11-04","nights":2,"guests":2}
200 {"ok":true,"quote":{"ok":true,"sellMinor":7000,"currency":"TWD",
"detail":{"roomTypeName":"標準雙人房","ratePlanName":"含早餐","cancelPolicy":"不可取消",
"nightly":[{"date":"2026-11-04","sell":3500},{"date":"2026-11-05","sell":3500}]}}}
POST /api/mp/bookings
{ ...同上, "hold":true, "holdMinutes":10, "idemKey":"order-10086",
"guest":{"name":"王小明","phone":"0900000000"} }
201 {"ok":true,"booking":{"bookingKey":"mpb-2cf63b70-…","status":"held",
"holdUntil":"2026-10-25T06:11:40.112Z","grossMinor":7000,"currency":"TWD","reused":false}}
409 {"ok":false,"error":"quote_failed","problems":["已客滿"]}
POST /api/mp/bookings/{bookingKey}/confirm   → 200 {"ok":true,"status":"confirmed","already":false}
POST /api/mp/bookings/{bookingKey}/cancel    → 200 {"ok":true,"status":"cancelled"}
  • 金額單位為最小貨幣單位(TWD 即元)。
  • idemKey 全域唯一:同一個 idemKey 重送回同一筆(reused:true),不會變成兩筆。
  • 入住人至少要有姓名,以及電話或 Email 其中一項。

9. 即時連接器與機器簽章(模式 B)

當 PMS 只能提供即時查詢時,由 HUIDUN 呼叫 PMS 的端點。PMS 需提供:查詢可售量、報價、預留、下單、取消、查單,以及宣告能力(是否支援預留、取消、即時確認)。這個模式需要逐案開通,請在申請表選擇「由我方提供即時查詢與下單 API」。

HUIDUN 送出的每一個請求都帶 Authorization: Bearer 權杖 與兩個簽章標頭,PMS 端驗證後才處理:

x-hd-timestamp: 1792908100105     (Unix 毫秒)
x-hd-signature: hex( HMAC-SHA256( 共用密鑰, timestamp + "." + 原始請求本文 ) )
驗證規則:
1. |現在時間 − timestamp| ≤ 5 分鐘,否則拒絕(防重放)
2. 用原始位元組計算,不要先解析再序列化 JSON
3. 以常數時間比較簽章字串
// Node.js 驗證範例
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(secret, ts, body, sig) {
if (Math.abs(Date.now() - Number(ts)) > 300000) return false;
const want = createHmac("sha256", secret).update(ts + "." + body).digest("hex");
return want.length === sig.length && timingSafeEqual(Buffer.from(want), Buffer.from(sig));
}

共用密鑰在開通時另行交付,不會出現在任何頁面或信件內文。

10. 錯誤碼

解析前錯誤:HTTP 505+JSON

HTTP/1.1 505
Content-Type: application/json
{"error":{"code":"497","message":"Authentication error","timestamp":"2026-10-25T14:01:40+08:00"}}
代碼意義處理方式
497認證失敗檢查帳密;不要自動重試
450無法解析、不支援的訊息或本文過大修正請求內容
366超過每分鐘請求上限退避後重試
448系統錯誤稍後重試

業務錯誤:HTTP 200+Errors

HTTP/1.1 200
<OTA_HotelRateAmountNotifRS Version="1.0" TimeStamp="2026-10-25T06:01:40Z" xmlns="http://www.opentravel.org/OTA/2003/05">
<Errors>
<Error Type="3" ShortText="Invalid room type" Code="402">Room type SUITE is not defined for this hotel.</Error>
</Errors>
<UniqueID ID="1792908100109112" Type="506" />
</OTA_HotelRateAmountNotifRS>
代碼意義
392飯店代碼無效,或這間飯店沒有選擇您的 PMS
402房型代碼不存在
425方案代碼不存在,或不適用於這個房型
15日期格式錯誤、結束早於開始,或區間超過 365 天
61幣別與飯店設定不符
321缺少必填欄位
448單一請求的區段太多,請分批

通路 API(JSON)

HTTPerror意義
401unauthorized金鑰無效
403scope_denied/channel_forbidden金鑰沒有這個權限,或不是這個通路的金鑰
409quote_failed已客滿、關房、不符最少住宿晚數等,原因列在 problems
409idem_key_conflict這個 idemKey 已被別的通路使用
400need_contact/bad_request入住人缺電話與 Email,或請求格式錯誤
404booking_not_found訂單不存在或不屬於這個通路

11. 認證檢核七項

沙盒的檢核表依沙盒資料庫的實際紀錄自動判定,七項全部完成即可申請正式認證。

  1. 取房型與方案(OTA_HotelProduct)成功
  2. 推房價(OTA_HotelRateAmountNotif)成功
  3. 推房量(OTA_HotelAvailNotif)成功
  4. 通路預留成立一筆訂單
  5. 預留確認成立
  6. 取消一筆訂單,或讓預留逾時釋放
  7. 有訂單之後拉單(OTA_HotelResNotif)成功

檢核表網址:https://huidun-demo.luma-e61.workers.dev/sandbox/pms/progress?ref=您的飯店代碼(只顯示完成與否與時間,不含任何帳密或個資);沙盒一鍵跑通 每次執行後也會在頁面上顯示同一張表。

12. 沙盒限制

  • 每次開通一間沙盒飯店:2 個房型(STD、DLX)× 2 個方案(RO 純住宿到店付、BB 含早餐線上預付),預先放好未來 30 天的房價房量,保留間數 1 間。
  • 沙盒 30 天後停用;同一個網路一小時最多開通 3 個沙盒。
  • 沙盒通路的服務費為 0,不代表正式費率。
  • 沙盒是獨立的測試資料庫,與正式環境沒有任何資料往來;請勿放入真實旅客資料。