それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Amazon 商品検索に署名が必要なので対応したクラスを作る

Amazonの商品検索をやろうとしたら、署名が必要らしい。署名が必要ななんて、何をいまさらと、言われるんですけど、これが地味に不便な変更です。

アドレスバーの上で手作業でGETクエリを組み立てるのが不可能になったということですから。
そのためAmazonのECS4(いまは Advertising API) を使うことが減っていた。

ruby はgems で簡単に入るし、安心して使える。phpだと・・・

phpだと、ライブラリが多くて情報も多くて迷いがちなので自作を一個作っておくほうがいいのかも。知り合いも自作ライブラリを作ってたの私も作ることにした。

署名をする箇所

get パラメータの $params 変数を作ったとに署名を作ります。

<?php
#まずリクエストのパラメータをキーの名前順に整列
ksort($params); 
#GETのクエリ引数文字列を組み立てる。
$params = array_map(function($e){ return rawurldecode($e); },$params );
$query = http_build_query($params);
# JP のストアのURLからドメインとパス名を取り出す。
$uri = parse_url($endpoint);
#GETヘッダを作る。
$str = "GET\n{$uri['host']}\n{$uri['path']}\n{$query}";
#署名を作る hash_hmac / sha256 を base64でエンコード
$signature = base64_encode(hash_hmac('sha256', $str, Access_Secret, 'true'));
#署名さらに、GET引数用にエンコード
$signature = rawurlencode($signature);

#リクエストのURLに署名を含める。
$uri = "${endpoint}?${query}&Signature=${signature}";

署名を一度作ると暫く有効なのですが、タイムスタンプのフィールドが古くなると拒否される模様。

これをクラスにまとめみた。

<?php
//Enter your IDs
define("Access_Secret", "***************************");
define("Access_Key_ID", ****************************");
define("Associate_tag", "takuya-*******-22");

class AmazonItem{
  public function __call($name, $args){
    $params = array();
    $params = $args[0];
    $params["Operation"] = $name;
    // $params[]
    return  call_user_func_array( array($this,"request"), array($params)  );
  }
  public function request($params=array()){
    $default_params = array(
      "Service"        => "AWSECommerceService",
      "Timestamp"      => gmdate("Y-m-d\TH:i:s\Z"),
      "AssociateTag"   => Associate_tag,
      "AWSAccessKeyId" => Access_Key_ID,
      "Operation"      => "ItemLookup",
      "Version"        => "2011-08-01",
      "ResponseGroup"  => "Small",
    );
    $params = array_merge($default_params,$params);
    ksort($params);
    $endpoint = "http://webservices.amazon.co.jp/onca/xml";
    $params = array_map(function($e){ return rawurldecode($e); },$params );
    $query = http_build_query($params);
    $uri = parse_url($endpoint);
    $str = "GET\n{$uri['host']}\n{$uri['path']}\n{$query}";
    $signature = base64_encode(hash_hmac('sha256', $str, Access_Secret, 'true'));
    $signature = rawurlencode($signature);
    $uri = "${endpoint}?${query}&Signature=${signature}";
    $response = file_get_contents($uri);
      $response = preg_replace("/xmlns=[^>]+/i", "",$response);    
    return $xml = simplexml_load_string($response);
  }
}
$amazon = new AmazonItem();
$ret = $amazon->ItemLookup(array("ItemId"=>"9784088741192", "IdType"=> "ISBN", "SearchIndex"=> "Books") ) ;
var_dump($ret);
$ret = $amazon->ItemSearch(array("Keywords"=>"php" , "SearchIndex"=> "Books") ) ;
var_dump($ret);


Amazon のAPIの他に、PHPのライブラリ作者の癖も学んで使うのは面倒だった。なのでService_AmazonECS4はあまり使わない。Amazon APIを直接呼べるようなシンプルな設計が分かりやすくて好き。