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 基本身份验证机制请求此令牌。
- 向令牌端点添加一个 Authorization 标头,其中包含以 Base64 编码的 API 客户端 ID 和密钥字符串
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}管理您的订阅
通过 Subscription 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 交付。
要访问 Loyalty Earn 和 Itineraries 端点,您需要一个 API 客户端 ID 和密钥,您可以从您的商业联系人处获得这些信息。您需要在所有 API 请求中包含一个特定于您企业的访问令牌。使用您的 API 凭据通过 HTTP 基本身份验证机制请求此令牌。
- 在令牌端点中添加一个 Authorization 标头,其中包含以 Base64 编码的 API 客户端 ID 和密钥字符串。
https://analytics.ean.com/template/v1/oauth/token。 - 令牌端点将返回一个访问令牌,您将使用该令牌进行后续的 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 版本。使用servers.url该值位于我们可下载的 OpenAPI 规范文件顶部。它始终与您正在测试的 API 服务的版本号相对应。
URL 应遵循以下结构:
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 交付
忠诚度赚取 API 交付