API 密钥验证和访问

所有 API 请求都需要采用 EAN 的签名验证方法。除了您的 CID 和 API 密钥之外,您还必须根据共享密码、API 密钥和当前 UNIX 时间戳发送一个签名哈希值。发送此哈希值作为每个请求的 sig 通用元素值。

您可以通过登录联盟中心并查看 API 顶部菜单下的密钥详细信息,查找 API 密钥的共享秘密。

在开发期间,您可以使用我们的 sig 生成器快速生成您自己的 sig 值以用于沙盒中的测试请求,或验证由您的实施创建的值。

仅支持将 IP 验证用于以前建立的帐户。

使用共享秘密进行验证,并在代码中创建数字签名

使用数字签名时,sig 值的计算方法是,生成包含 API 密钥、API 用户共享秘密以及 UNIX 时间戳的 MD5 哈希值

系统接受早于或晚于服务器时间戳长达五分钟的时间戳,以顺应合理的时间漂移。

EAN 会使用网络时间协议 (NTP) 来同步内部服务器的时间。如果还使用 NTP,则不会出现时钟同步问题。大多数现代操作系统都支持此服务或类似的时间同步服务,请查看您的操作系统文档。

大多数编程语言都提供“MD5()”函数的某个版本,该版本应用来生成签名。以下示例阐释了如何通过 PHP 生成有效签名:

$apiKey = 'xxx-YourAPIkey-xxx'; $secret = 'xxYourSecretxx'; $timestamp = gmdate('U'); // 1427233130 (Tue, 24 Mar 2015 21:38:50 +0000) $sig = md5($apiKey . $secret . $timestamp);

以下 URL 示例阐释了如何使用数字签名、API 密钥、时间戳和共享秘密来提出针对酒店版本 3 服务的 REST 请求:

http://api.ean.com/ean-services/rs/hotel/v3/avail? cid=[yourCID] &apiKey=[yourAPIKey] &sig=[youSigValue] &minorRev=[current minorRev #] &customerUserAgent=[xxx] &customerIpAddress=[xxx] &locale=en_US &currencyCode=USD &hotelId=201252

注意:在生成数字签名的过程中,经常会因时间戳而导致出现错误。请确保:

  • 使用 GMT 时间,而不是本地时间。
  • 使用秒,而不是毫秒。(默认情况下,有些系统返回的时间按毫秒计)。
  • 如果您正在接收带有 sig 请求的验证错误消息,请使用错误消息中返回的服务器信息时间戳来发送另一请求,以便您的时间能够与服务器同步。示例:<ServerInfo serverTime="19:11:13.082-0500" timestamp="1311725473" instance="48" />
参数 说明
apiKey 客户端应用程序标识符。它是一个最长可包含 255 个字符的字符串,且其中不包含空格或特殊字符。它是在注册您的应用程序时,分配给您的唯一密钥。
sig 用于请求的数字签名,创建的目的是作为 apiKey 的 MD5 哈希值、您的共享秘密及时间戳。

所有的 sig 值在长度上要求至少包含 32 个小写字符,因此可能有必要对该字符串进行处理,在最终值前添加一个 0 或更改为全部小写的字符。

如果您收到了验证错误或在生成自己的数字签名时遇到了其他问题,请使用 EAN 的 Sig 生成器检查值。

其他常见请求元素

查看常见请求元素列表,这些元素用于日常的数据调用

代码示例

您可以使用任何编程语言发送请求。以下是一些热门语言(PHP、Python、Java、Ruby、Perl 以及 C#)的代码示例。每个示例均通过传递包含 API 密钥 (apiKey) 和数字签名 (sig) 的查询参数来检索数据。回复中的数据不会因语言不同而发生变化。

要在不使用数字签名的情况下发送请求并使用 API 帐户中设置的 IP 验证,请从请求中删除共享秘密和 sig 进程。

PHP

所有的 sig 值在长度上要求至少包含 32 个字符,因此可能有必要对该字符串进行处理,在最终值前添加一个 0。

<?php $secret = 'xxYourSecretxx'; $host = 'http://api.ean.com/';  // build path $ver = 'v3/'; $method = 'list/'; $path = "ean-services/rs/hotel/{$ver}{$method}";  // query parameters $apiKey = '[YourAPIkey]'; $cid = '[yourCID]'; $minorRev = '[currentMinorRev]'; $customerUserAgent = 'Mozilla/4.0'; $customerIpAddress = '172.16.82.13'; $locale = 'en_US'; $currencyCode = 'USD'; $hotelId = '201252';  $timestamp = gmdate('U'); $sig = md5($apiKey . $secret . $timestamp);  $query = "?apikey={$apiKey}&cid={$cid}&sig={$sig}&minorRev={$minorRev}" . "&customerUserAgent={$customerUserAgent}&customerIpAddress={$customerIpAddress}" . "&locale={$locale}&currencyCode={$currencyCode}&hotelIdList={$hotelId}";  // initiate curl $ch = curl_init();  curl_setopt($ch, CURLOPT_URL, $host . $path . $query); curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_setopt($ch, CURLOPT_HTTPHEADER,array('Accept:application/json')); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $data = curl_exec($ch); $headers = curl_getinfo($ch);  // close curl curl_close($ch);  // return XML data if ($headers['http_code'] != '200') {  echo "An error has occurred."; return false; } else {  echo $data;  return($data); } ?>

Python

如果凭据有效,则调用会在一个名为“xml”的变量中返回 XML 有效负载。

所有的 sig 值在长度上要求至少包含 32 个字符,因此可能有必要对该字符串进行处理,在最终值前添加一个 0。

import urllib2 import md5 import time  service = 'http://api.ean.com/ean-services/rs/hotel/' version = 'v3/' method = 'list/' hotelId = '201252' otherElementsStr = 'cid=55505&minorRev=[x]&customerUserAgent=[xxx]&customerIpAddress=[xxx]&locale=en_US&currencyCode=USD'   apiKey = 'YourAPIkey' secret = 'YourSecret'  hash = md5.new() # seconds since GMT Epoch timestamp = str(int(time.time())) # print timestamp sig = md5.new(apiKey + secret + timestamp).hexdigest() url = service + version + method+ '?apiKey=' + apiKey + '&sig=' + sig + otherElementsStr + '&hotelIdList=' + hotelId # print url xml = urllib2.urlopen(url).read()  print xml

Java

以下的 Java 示例说明了如何通过 HTTP GET 执行网络服务数据请求。这要依靠 Apache 软件基金会提供的 HttpComponents 包:

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException;  import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient;  public class RestfulCallExample {   public static String service = "http://api.ean.com/ean-services/rs/hotel/";  public static String version = "v3/";  public static String method = "list";  public static String hotelId = "201252";  public static String otherElementsStr = "&cid=55505&minorRev=[x]" + "&customerUserAgent=[xxx]&customerIpAddress=[xxx]&locale=en_US&currencyCode=USD";  public static String apikey = "xxx-YourAPIkey-xxx";  public static String secret = "xxYourSecretxx";   public static void main(String[] args) throws NoSuchAlgorithmException {  MessageDigest md = MessageDigest.getInstance("MD5"); long timeInSeconds = (System.currentTimeMillis() / 1000); String input = apikey + secret + timeInSeconds; md.update(input.getBytes()); String sig = String.format("%032x", new BigInteger(1, md.digest()));  String url = service + version + method+ "?apikey=" + apikey  + "&sig=" + sig + otherElementsStr + "hotelIdList=" + hotelId; System.out.println("URL = " + url); DefaultHttpClient httpclient = new DefaultHttpClient();  // Create an HTTP GET request HttpGet httpget = new HttpGet(url);  // Execute the request httpget.getRequestLine(); HttpResponse response = null; try {  response = httpclient.execute(httpget); } catch (IOException e) {  e.printStackTrace(); return; }   HttpEntity entity = response.getEntity(); // Print the response System.out.println(response.getStatusLine());  if (entity != null) {  try { InputStream inputStream = entity.getContent(); // Process the response BufferedReader bufferedReader = new BufferedReader(  new InputStreamReader(inputStream)); String line; while ((line = bufferedReader.readLine()) != null) {  System.out.println(line); } bufferedReader.close();  } catch (IOException e) { e.printStackTrace();  } } // shut down the connection manager to free up system resources. httpclient.getConnectionManager().shutdown();  } }

Ruby

以下 Ruby 示例使用 Digest::MD5 生成数字签名,并且使用通过 Net::HTTP 发送请求。

所有的 sig 值在长度上要求至少包含 32 个字符,因此可能有必要对该字符串进行处理,在最终值前添加一个 0。

require 'net/http' require 'digest/md5'  # hotelId to send hotelId = '201252'  #additional parameters set in a single string for brevity $otherElemntsStr = '&cid=55505&minorRev=[x]&customerUserAgent=[xxx]&customerIpAddress=[xxx]&locale=en_US&currencyCode=USD'   # API location PRODUCTION_ENDPOINT = 'api.ean.com/ean-services/rs/hotel/' PRODUCTION_PORT = 80  # Your credentials API_KEY = 'YourAPIkey' SHARED_SECRET = 'YourSecret' API_VERSION = 'v3'  current_time = Time.now timestamp = Time.now.to_i.to_s sig = Digest::MD5.hexdigest( API_KEY+SHARED_SECRET+timestamp )  request_url = "/#{API_VERSION}/list}?apiKey=#{API_KEY} & sig=#{sig} & #{otherElementsStr} & hotelIdList=#{hotelId}"  http = Net::HTTP.new( PRODUCTION_ENDPOINT , PRODUCTION_PORT ) http.start do |http| req = Net::HTTP::Get.new(request_url)  resp, data = http.request(req)  print data end

Perl

所有的 sig 值在长度上要求至少包含 32 个字符,因此可能有必要对该字符串进行处理,在最终值前添加一个 0。

# perl  use strict; # use LWP::Simple; use LWP::UserAgent; use Digest::MD5 qw(md5 md5_hex);  my $service = 'http://api.ean.com/ean-services/rs/hotel/'; my $method = 'list/'; my $version= 'v3/'; my $hotelId = '201252'; my $otherElemntsStr = '&cid=[xx]&minorRev=[x]&customerUserAgent=[xxx]&customerIpAddress=[xxx]&locale=en_US&currencyCode=USD';  # key and shared secret obtained from registering application my $apiKey = 'YourAPIkey'; my $secret = 'YourSecret';  my $timestamp = time; #above timestamp needs to be created in PERL !!! Placeholder my $sig = md5_hex($apiKey . $secret . $timestamp);  # print $timestamp,"", $sig, "\n";  my $ua = new LWP::UserAgent; $ua->timeout(120);  my $url=$service . $version . $method. '?apiKey=' . $apiKey . '&sig=' . $sig . $otherElementsStr . '&hotelIdList=' . $hotelId; # print $url, "\n";  my $request = new HTTP::Request('GET', $url); my $response = $ua->request($request); my $xml = $response->content(); print $xml,"\n";

C#

以下 C# 示例使用 HttpWebRequest 和 HttpWebResponse。

所有的 sig 值在长度上要求至少包含 32 个字符,因此可能有必要对该字符串进行处理,在最终值前添加一个 0。

using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Net; using System.Security.Cryptography;  namespace QuovaRESTSample { class Program { static void Main(string[] args) { string service = "http://api.ean.com/ean-services/rs/hotel/"; string version = "v3/"; string method = "list/"; string hotelId = "201252"; 	string otherElemntsStr = "&cid=[x]&minorRev=[x]&customerUserAgent=[xxx]&customerIpAddress=[xxx]&locale=en_US&currencyCode=USD"; string apiKey = "xxx-YourAPIkey-xxx"; string secret = "xxYourSecretxx"; string sig = MD5GenerateHash(apiKey + secret + (Int32)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds); string fullURL = service + version + method+ "?apiKey=" + apiKey + "&sig=" + sig + otherElementsStr + "&hotelIdList=" + hotelId;  // Create the web request HttpWebRequest request = WebRequest.Create(fullURL) as HttpWebRequest;  // Get response using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) { // Get the response stream StreamReader reader = new StreamReader(response.GetResponseStream());  // Write response to the console Console.WriteLine(reader.ReadToEnd()); }  }  private static string MD5GenerateHash(string strInput) { // Create a new instance of the MD5CryptoServiceProvider object. MD5 md5Hasher = MD5.Create();  // Convert the input string to a byte array and compute the hash. byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(strInput));  // Create a new Stringbuilder to collect the bytes and create a string. StringBuilder sBuilder = new StringBuilder();  // Loop through each byte of the hashed data and format each one as a hexadecimal string. for (int nIndex = 0; nIndex < data.Length; ++nIndex) { sBuilder.Append(data[nIndex].ToString("x2")); }  // Return the hexadecimal string. return sBuilder.ToString(); } } }