API 設定
本節將說明成功整合我們 API 的關鍵步驟。無論您是想透過 Webhooks 接收推送事件,還是從我們的端點拉取資料,我們都能滿足您的需求。
- 推送 API: 推送事件非常適合 TAAP 及白標旅遊平台合作夥伴,可將 real-time 的更新直接傳送至您的系統。瞭解如何設定訂閱、管理驗證以及處理重試。
- API 擷取: 此服務專為白標旅遊平台合作夥伴設計,讓您能在需要時隨時請求資料。我們將向您說明如何設定 token-based 驗證,以確保存取安全。
請遵循以下指引,以確保整合順利進行並確保資料傳輸安全。
Push-based 送貨
如果您是對行程資料感興趣的 TAAP 或白標旅遊平台合作夥伴,這則資訊可能與您相關。推送事件是透過 Webhook 傳送,並會以HTTP POST 訊息的形式發送至您提供的 URL。
若要開始接收事件,您需要訂閱某種事件類型,並將 Expedia 驗證為發送者。
訂閱與活動
我們的訂閱 API 可讓您建立並管理您希望透過我們的 push-based 傳送服務接收的事件。目前您可以建立並管理行程更新事件的訂閱,我們將在相關功能推出後陸續新增更多選項。
您需要一個 API 客戶端 ID 和密鑰,您的業務聯絡人可以提供這些資訊。
存取訂閱 API
您將在所有 API 請求中包含一個專屬於您企業的存取憑證。請使用您的 API 憑證,透過 HTTP 基本驗證機制來請求此憑證。
- 請在 token 端點中新增一個 Authorization 標頭,並將您的 API 客戶端 ID 和密鑰以 Base64 編碼的字串形式加入其中
https://analytics.ean.com/*/v1/oauth/token。請根據您的合作夥伴關係,將網址中的*替換為template或taap。 - 該憑證端點將傳回一個存取憑證,您可將其用於後續的 API 請求。如需更多詳細資訊,請參閱訂閱 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} 端點。您必須使用有效的憑證。
如需更多詳細資訊,請參閱訂閱 API 說明。
驗證 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}收到憑證後,您即可開始對「累積點數」或「行程」任一端點發出請求。
在請求此服務時,您需要指定 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 交付