API 설정
이 섹션에서는 API와 성공적으로 통합하기 위한 필수 단계를 설명합니다. 웹훅을 통해 푸시 이벤트를 수신하든, 엔드포인트에서 데이터를 가져와서 수신하든, 저희가 도와드릴 수 있습니다.
- 푸시 API: TAAP 및 화이트 라벨 여행 플랫폼 파트너에게 적합한 푸시 이벤트는 real-time 업데이트를 시스템으로 직접 전달합니다. 구독을 설정하고, 인증을 관리하고, 재시도를 처리하는 방법을 알아보세요.
- 풀 API: 화이트 라벨 여행 플랫폼 파트너를 위해 설계된 API로, 필요할 때 데이터를 요청할 수 있습니다. 보안 액세스를 위해 token-based 인증을 설정하는 방법을 설명해 드리겠습니다.
원활한 통합과 안전한 데이터 전송을 위해 아래 지침을 따르세요.
Push-based 배달
TAAP 또는 일정 데이터에 관심이 있는 화이트 라벨 여행 플랫폼 파트너라면 관련 정보가 유용할 수 있습니다. 푸시 이벤트는 웹훅을 통해 전달되며 사용자가 제공한 URL로 HTTP POST메시지로 전송됩니다.
이벤트 수신을 시작하려면 이벤트 유형에 가입하고 발신자로 익스피디아를 인증해야 합니다.
구독 및 이벤트
구독 API를 사용하면 push-based 배달 서비스를 통해 수신하려는 이벤트를 생성하고 관리할 수 있습니다. 현재 일정 업데이트 이벤트에 대한 구독을 생성하고 관리할 수 있으며, 사용 가능한 이벤트가 추가되는 대로 더 추가할 예정입니다.
상업 담당자가 제공할 수 있는 API 클라이언트 ID와 비밀번호가 필요합니다.
구독 API에 액세스하기
모든 API 요청에 비즈니스에 고유한 액세스 토큰을 포함하게 됩니다. HTTP 기본 인증 메커니즘을 통해 API 자격 증명을 사용하여 이 토큰을 요청하세요.
- 토큰 엔드포인트에 API 클라이언트 ID와 비밀번호의 Base64로 인코딩된 문자열이 포함된 권한 부여 헤더를 추가합니다
https://analytics.ean.com/*/v1/oauth/token. 파트너십에 따라 URL의*을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 서명의 유효성을 검사합니다. 이렇게 하면 익스피디아에서 수신한 이벤트가 전송 중에 변조되지 않았는지 확인할 수 있습니다.
참고: 이 예제는 이 유효성 검사 로직을 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 요청에 비즈니스에 고유한 액세스 토큰을 포함하게 됩니다. HTTP 기본 인증 메커니즘을 통해 API 자격 증명을 사용하여 이 토큰을 요청하세요.
- 토큰 엔드포인트
https://analytics.ean.com/template/v1/oauth/token에 API 클라이언트 ID와 비밀번호의 Base64 인코딩 문자열이 포함된 권한 부여 헤더를 추가합니다. - 토큰 엔드포인트는 후속 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 서비스의 버전 번호와 일치합니다.
URL은 이 구조를 따라야 합니다:
https://analytics.ean.com/[product]/[API version]/[path]
경로를 교체하여 엔드포인트 간에 전환할 수 있지만 프로토콜, 도메인 지정, 제품 및 API 버전 번호는 OpenAPI 사양에 명시된 대로 유지해야 합니다.
엔드포인트 예제
https://analytics.ean.com/template/v1/loyalty/earn/last_update
https://analytics.ean.com/template/v1/itineraries데이터 범위와 API 구성을 살펴보려면 API 제공 스키마를 참조하세요:
일정 API 제공
로열티 적립 API 제공