API 설정
이 섹션에서는 당사 API와 성공적으로 연동하기 위한 필수 단계를 다룹니다. 웹훅을 통해 푸시 이벤트를 수신하든, 당사 엔드포인트에서 데이터를 직접 가져오든 상관없이, 저희가 모두 지원해 드립니다.
- 푸시 API: TAAP 및 화이트 라벨 여행 플랫폼 파트너에게 이상적인 이 푸시 이벤트는 real-time 의 최신 정보를 귀사의 시스템으로 직접 전달합니다. 구독 설정 방법, 인증 관리 방법, 재시도 처리 방법을 알아보세요.
- Pull API: 화이트 라벨 여행 플랫폼 파트너를 위해 설계된 이 API를 통해 필요할 때 데이터를 요청할 수 있습니다. 보안 접속을 위해 token-based 인증을 설정하는 방법을 안내해 드리겠습니다.
원활한 통합과 안전한 데이터 전송을 위해 아래 지침을 따르십시오.
Push-based 배송
TAAP 이거나 일정 데이터에 관심이 있는 화이트 라벨 여행 플랫폼 파트너라면 이 내용이 도움이 될 수 있습니다. 푸시 이벤트는 웹훅을 통해 전달되며, 사용자가 지정한 URL로 ‘ HTTP POST ’ 메시지로 전송됩니다.
이벤트 수신을 시작하려면 이벤트 유형을 구독하고, 발신자로 익스피디아를 인증해야 합니다.
구독 및 행사
당사의 구독 API를 사용하면 ‘ push-based ’ 전송 서비스를 통해 수신하고자 하는 이벤트를 생성하고 관리할 수 있습니다. 현재 일정 업데이트 이벤트에 대한 구독을 생성하고 관리할 수 있으며, 향후 추가되는 이벤트가 있을 때마다 이를 반영할 예정입니다.
API 클라이언트 ID와 시크릿이 필요하며, 이는 귀사의 영업 담당자가 제공해 드릴 수 있습니다.
구독 API에 액세스하기
모든 API 요청에 귀사의 비즈니스에 고유한 액세스 토큰을 포함해야 합니다. API 자격 증명을 사용하여 HTTP 기본 인증 방식을 통해 이 토큰을 요청하십시오.
- API 클라이언트 ID와 시크릿을 Base64로 인코딩한 문자열을 포함하는 Authorization 헤더를 토큰 엔드포인트에 추가하십시오
https://analytics.ean.com/*/v1/oauth/token. URL에 있는*을 귀하의 파트너십 유형에 따라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}엔드포인트를 사용하십시오. 유효한 토큰을 사용해야 합니다.
익스피디아를 발신자로 인증하기
사용자가 지정한 엔드포인트로 푸시 이벤트를 전송할 예정이며, 각 이벤트의 인증 헤더에는 HMAC( hash-based ) 메시지 인증 코드 서명이 포함됩니다. 구독을 생성할 때 당사에서 제공해 드리는 공유 비밀 키를 사용하여 안전하고 신뢰할 수 있는 데이터 전송을 보장하기 위해 이 서명을 검증해야 합니다.
인증 헤더 예시
"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 전송 기능을 구현할 수 있습니다.
Loyalty Earn 및 일정 엔드포인트에 액세스하려면 API 클라이언트 ID와 시크릿이 필요하며, 이는 담당 영업 담당자에게 문의하여 받을 수 있습니다. 모든 API 요청에 귀사의 비즈니스에 고유한 액세스 토큰을 포함해야 합니다. API 자격 증명을 사용하여 HTTP 기본 인증 방식을 통해 이 토큰을 요청하십시오.
- API 클라이언트 ID와 시크릿을 Base64로 인코딩한 문자열을 포함하는 Authorization 헤더를 토큰 엔드포인트
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}토큰을 수령한 후에는 Loyalty Earn 또는 일정 엔드포인트 중 어느 곳에나 요청을 보낼 수 있습니다.
서비스를 요청할 때는 API 버전을 명시해야 합니다. 다운로드 가능한 OpenAPI 사양 파일 상단에 있는 ‘ servers.url ’ 값을 사용하십시오. 이는 항상 테스트 중인 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 제공에 대해 알아보기