API 設定
本節將介紹成功與我們的 API 整合所需的基本步驟。無論您是想透過 Webhooks 接收推播事件,還是從我們的端點擷取資料,我們都能滿足您的需求。
- 推播 API: 推播事件非常適合 TAAP 以及白標旅遊平台合作夥伴,可將 real-time 的更新直接傳送至您的系統。瞭解如何設定訂閱、管理驗證以及處理重試。
- Pull API: 此服務專為白標旅遊平台合作夥伴設計,讓您能在需要時隨時請求資料。我們將向您說明如何設定「token-based」驗證機制,以確保存取安全。
請遵循以下指引,以確保整合過程順利進行,並確保資料傳輸的安全性。
Push-based 送貨
如果您是 TAAP,或是對行程資料感興趣的白標旅遊平台合作夥伴,這則資訊可能與您相關。推播事件是透過 Webhook 傳送的,並會以HTTP POST 訊息的形式發送至您提供的 URL。
若要開始接收事件,您需要訂閱某種事件類型,並將 Expedia 驗證為發送者。
訂閱與活動
我們的訂閱 API 可讓您建立並管理您希望透過我們的 push-based 傳送服務接收的事件。目前您可以建立並管理「行程更新」事件的訂閱,我們將在相關功能推出後陸續新增更 多選項。
您需要一個 API 客戶端 ID 和密鑰,您的商業聯絡人可提供這些資訊。
存取訂閱 API
您將在所有 API 請求中包含一個專屬於您企業的存取憑證。請使用您的 API 憑證,透過 HTTP 基本驗證機制來請求此憑證。
- 請在令牌端點中新增一個「Authorization」標頭,並將您的 API 客戶端 ID 和密鑰以 Base64 編碼的字串形式填入其中
https://analytics.ean.com/*/v1/oauth/token。請根據您的合作夥伴關係,將網址中的*替換為template或taap。 - 該憑證端點將回傳一個存取憑證,您可將其用於後續的 API 請求。
- 請在日後的 API 端點請求中包含存取憑證的值。
初始授權標頭範例
Authorization: Basic base64.b64encode({client-id}:{client-secret})代幣請求範例
securitySchemes:
oauth:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://analytics.ean.com/taap/v1/oauth/token經過驗證的授權標頭範例
Authorization: Bearer {access-token}管理您的訂閱
在透過訂閱 API 完成驗證後,您即可管理您的訂閱,包括列出現有的訂閱、建立新的訂閱,以及刪除不再適用的訂閱。
建立訂閱
建立訂閱時,您需要指定:
- 您希望接收事件的端點 URL。
- 您要訂閱的事件類型,包括您的合作夥 伴關係標示:
taap.itinerary.change或template.itinerary.change。
注意:雖然您可以建立多個訂閱,但重複的訂閱會導致事件重複傳送。為避免這種情況,請在建立新訂閱之前,先列出您現有的訂閱。
建立訂閱時,您將收到一組唯一的密鑰,用以驗證事件傳送中包含的 HMAC (hash-based 訊息認證碼)。
若要列出或刪除訂閱,請分別使用HTTP GET /subscriptions 或HTTP DELETE /subscriptions/{subscription_id} 端點。您必須使用有效的憑證。
驗證 Expedia 為寄件者
我們將向您提供的端點發送推播事件,每個事件的授權標頭中都會包含一個 hash-based 訊息認證碼 (HMAC) 簽名。您應驗證此簽名,以確保透過我們在您建立訂閱時所提供的共享密鑰,實現安全且值得信賴的資料傳輸。
授權標頭範例
"authorization": "MAC ts='1731524372777',nonce='f88e57ed-aaf5-4edd-8e58-9105817fb4cb',bodyhash='8YLHy71r5dx3PQjdcOkRuVYXaakjhbJSROEnlreQEIA=',mac='bDxvx41INtDxtkbZwTmAMADZGiFl6/xyXC1lE5ixPuY='"請在接收推播事件的端點中加入以下邏輯,以驗證 HMAC 簽名。這將確保您收到的事件均來自 Expedia,且在傳輸過程中未遭篡改。
註: 此範例展示如何在 Java 中實作此驗證邏輯。您可以根據自己偏好的程式設計語言調整這套邏輯,但核心步驟將保持不變。
步驟 1:解析授權標頭
透過將字串解析為其基本組成部分,從授權標頭中提取所需資訊。
程式碼範例
/**
* Parse the signature string into components
* @param signature The signature string in format "MAC ts='...', nonce='...', bodyhash='...', mac='...'"
* @return Map of signature components
*/
private Map<String, String> parseSignature(String signature) {
// Pattern for key='value' or key="value"
Pattern pattern = Pattern.compile("([a-zA-Z]+)=['\"]([^'\"]*)['\"]");
Matcher matcher = pattern.matcher(signature);
Map<String, String> components = new HashMap<>();
while (matcher.find()) {
components.put(matcher.group(1), matcher.group(2));
}
return components;
}步驟 2:驗證所需元件
請確認所需元件ts(時間戳記)、nonce、bodyhash 以及mac 是否存在且格式正確。
程式碼範例
private Boolean validateSignatureComponents(Map<String, String> components) {
if (components.isEmpty()) {
return false;
}
return components.containsKey("ts") &&
components.containsKey("nonce") &&
components.containsKey("bodyhash") &&
components.containsKey("mac");
}步驟 3:產生並驗證正文雜湊值
使用 HMAC SHA-256 及sharedSecretKey 生成請求主體的雜湊值,並確認其與請求主體的雜湊值相符,以確保資料完整性。
程式碼範例
/**
* Compute body hash (HMAC-SHA256 of the request body)
* @param body The raw request body
* @return Base64 encoded body hash
*/
private String computeBodyHash(String body) {
try {
Mac mac = Mac.getInstance(HMAC_SHA256);
SecretKeySpec secretKeySpec = new SecretKeySpec(
sharedSecretKey.getBytes(StandardCharsets.UTF_8),
HMAC_SHA256);
mac.init(secretKeySpec);
byte[] hmacBytes = mac.doFinal(body.getBytes(StandardCharsets.UTF_8));
return bytesToBase64(hmacBytes);
} catch (Exception e) {
throw new RuntimeException("Failed to compute body hash", e);
}
}
private Boolean validateBodyHash(String requestBody, String bodyHashFromHeader) {
String computedBodyHash = computeBodyHash(requestBody != null ? requestBody : "");
return computedBodyHash.equals(components.get('bodyhash'));
}步驟 4:產生並驗證 HMAC 簽名
產生包含時間戳記、隨機數、HTTP 方法、請求路徑、主機網域、連接埠及生成的內容雜湊值的 HMAC 簽名,並與標頭中收到的值進行比對以驗證其真實性。
程式碼範例
/**
* Generate HMAC signature using the provided components
* @param components Parsed signature components
* @param computedBodyHash Computed body hash
* @param method HTTP method (e.g., "POST", "GET")
* @param path Request path (e.g., "/api/webhooks/events")
* @param host Host name (e.g., "api.example.com")
* @param port Port number
* @return Base64 encoded HMAC signature
*/
private String generateHmacSignature(Map<String, String> components, String computedBodyHash,
String method, String path, String host, int port) {
try {
// Normalize port (use 443 for standard HTTPS ports)
String portString = (port == 80 || port == 443) ? DEFAULT_PORT : String.valueOf(port);
// Build signature string with newline-delimited components
String signatureString = String.format("%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
components.get("ts"),
components.get("nonce"),
method.toUpperCase(),
path,
host,
portString,
computedBodyHash);
return calculateHMAC(signatureString);
} catch (Exception e) {
throw new RuntimeException("Failed to generate HMAC signature", e);
}
}
/**
* Calculate HMAC-SHA256
* @param data Data to hash
* @return Base64 encoded HMAC
*/
private String calculateHMAC(String data) {
try {
Mac mac = Mac.getInstance(HMAC_SHA256);
SecretKeySpec secretKeySpec = new SecretKeySpec(
sharedSecretKey.getBytes(StandardCharsets.UTF_8),
HMAC_SHA256);
mac.init(secretKeySpec);
byte[] hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return bytesToBase64(hmacBytes);
} catch (Exception e) {
throw new RuntimeException("Failed to calculate HMAC", e);
}
}
private Boolean validateHmacSignature(Map<String, String> components, String computedBodyHash,
String method, String path, String host, int port) {
String generatedHmacSignature = generateHmacSignature(components, computedBodyHash, method, path, host, port);
return generatedHmacSignature.equals(components.get("mac"));
}事件失敗時的重試
若某個事件執行失敗,系統將在 7 天內採用指數退避模式進行重試:首先每 5 分鐘嘗試一次,接著每 60 分鐘嘗試一次,此後則每 12 小時嘗試一次。若發生以下任何情況導致失敗,我們將重新嘗試:
- 非 200 的 HTTP 狀態碼
- 暫停
- 您的端點發生了一個例外狀況
訂閱 API 詳情
如需有關訂閱 API 的更多詳細資訊,請下載 OpenAPI 規格文件。
Pull-based 送貨
如果您擁有一個白標旅遊平台網站,您可以根據所使用的 API,為行程和忠誠度積分資料啟用 pull-based 傳輸功能。
若要存取「忠誠度積分」及「行程」端點,您需要一個 API 客戶端 ID 和密鑰,可向您的商業聯絡人索取。您將在所有 API 請求中包含一個專屬於您企業的存取憑證。請使用您的 API 憑證,透過 HTTP 基本驗證機制來請求此憑證。
- 請在令牌端點
https://analytics.ean.com/template/v1/oauth/token中,新增一個包含您的 API 客戶端 ID 和密鑰的 Base64 編碼字串的 Authorization 標頭。 - 該憑證端點將回傳一個存取憑證,您可將其用於後續的 API 請求。如需更多詳細資訊,請參閱 OpenAPI 規格。
- 請在日後的 API 端點請求中包含存取憑證的值。
初始授權標頭範例
Authorization: Basic base64.b64encode({client-id}:{client-secret})代幣請求範例
securitySchemes:
oauth:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://analytics.ean.com/template/v1/oauth/token經過驗證的授權標頭範例
Authorization: Bearer {token}收到您的存取憑證後,即可開始對「Loyalty Earn」或「Itineraries」的任何端點發出請求。
在請求此服務時,您需要指定您的 API 版本。請使用我們可下載的 OpenAPI 規格檔案頂端所列的「servers.url」值。它將始終與您正在測試的 API 服務的版本號相符。
網址應遵循以下結構:
https://analytics.ean.com/[product]/[API version]/[path]
您可以透過替換路徑在不同端點之間切換,但請務必按照 OpenAPI 規格中規定的方式,保留協定、網域標示、產品及 API 版本號。
端點範例
https://analytics.ean.com/template/v1/loyalty/earn/last_update
https://analytics.ean.com/template/v1/itineraries若要了解資料範圍及 API 設定,請參閱 API 傳輸的資料結構:
>> 進一步了解「行程 API」的提供方式
>> 了解「Loyalty Earn API」的交付方式