読者です 読者をやめる 読者になる 読者になる

俺の報告

RoomClipを運営するエンジニアの日報(多分)です。

Product Advertising API の SOAPリクエスト aws:Client.RequiresSSL エラーについて - 日報 #121

さて金曜日になりました。
RoomClipは曜日変動が微妙にあるサービスなのですが、
想像しやすい事象とはいえ、中々この曜日変動というのは不思議なものだと感じます。
1週間というループが仮に10日だったら、10日なりの変動周期になっていただろうと考えると、
それと7日ループとを比べてみたいものです。
相似形になるのでしょうかね。

さて、前回に引き続いて、
突発案件について少し。

直近でいうと、Amazon Product Advertising のAPI応答エラーについて。
早い話が、Amazon商品情報APIに対して何らかクエリを投げて、
検索結果一覧を得るというシステムが、ある日突然動かなくなりました。という事件です。

AmazonECS.class.phpというここで手に入る ライブラリを使ってSOAP接続をして結果をパースしていたのですが、
ある日こんなエラーが吐出されるように。

Fatal error: Uncaught SoapFault exception: [aws:Client.RequiresSSL] SSL connection required for SOAP authentication

このエラー文で検索すればワンサカ出てきますが、
大体取りまとめるとこんな助言が得られます。

  • SOAPクライアント本当に入ってる?ちゃんとインストールしな。
  • なんかco.jpだけエラーでてるみたいだから.comとかにしてみたら?
  • Amazon側の問題みたいだから、復旧を祈るしかないべ

乱暴ですが、こんな感じです。まぁ結論「よく分からん!」という感じでした。
そもそもSOAPとはプロトコルの1つで、HTTPとかと同じようなジャンルのものです。
一般的に、ブラウザでページアクセスをする時というのは、
ブラウザソフトがサーバソフトに対して「決まった書式と段取り」に従ってリクエスト情報を投げたり、
レスポンスを受けたりするわけです。
この「決まった書式と段取り」のことをプロトコルと呼び、いまスタンダードなのはHTTPプロトコルなわけですね。
簡単に言うと。
SOAPはその「決まった書式や段取り」、プロトコルの1つです。
特にXMLでリクエスト文やレスポンスを受けるルールになっているため、
HTTPよりしっかりと構造化されているプロトコルで一時期は「凄いかも」と目されていたはずです。
今はどうか、、、知らないですけど、、、
とにかく、AmazonはそのSOAPで商品情報APIを取得することができるわけなんです。
その細かなやり取りを担ってくれるのがAmazonECS.class.phpだったはずなのですが、、、
上記のエラーなわけです。
最初からエラーだったらいいのですが、いきなりなるってことは気味が悪いですね。

で、色々なドキュメントを見ると、
驚くことにSOAPのエンドポイントがドキュメントごとで結構違います。
https://images-na.ssl-images-amazon.com/images/G/09/associates/paapi/dg/index.html?SOAPEndpoints.html
ここと、
http://docs.aws.amazon.com/ja_jp/AWSECommerceService/latest/DG/SOAPEndpoints.html
ここで、異なります。
なんだかこんがらがってきてよく分からんので、
この辺は目をつぶって「きっと何らかの仕様が変更されたんだろう」という予測のもと、
ライブラリの方をアップデートしてみました。

AmazonECS.class.phpはレガシーとなり、今はApaiというライブラリになっております。
ここgithubリポジトリです。
かなりナウい構造になっているので、面くらいますが、大人しくcomposerでインストールします。
composerでインストールしたらautoload.phpをインクルードして、さっそく使ってみます。

が、
やはりSOAPでつなぐと同じエラーが!
もーーやだ!
SOAPなんて使わないぞ!
ということで、別の方法としてRESTを使います。
コードはこんな感じ。

<?php

require_once(LIB_PATH.'/libraries/ApaiIO/vendor/autoload.php');
use ApaiIO\ApaiIO;
use ApaiIO\Configuration\GenericConfiguration;
use ApaiIO\Operations\Search;
use ApaiIO\Operations\Lookup;

$devId = "AMAZON_ACCESS_KEY"; // アマゾンキー
$secret = "AMAZON_ACCESS_SECRET_KEY"; // シークレットキー
$aflId = "AFIL_ID"; // アフィリエイトID

$conf = new GenericConfiguration();
try {
  $conf
    ->setCountry('co.jp') // 日本
    ->setAccessKey($devId)
    ->setSecretKey($secret)
    ->setAssociateTag($aflId)
    ->setResponseTransformer('\ApaiIO\ResponseTransformer\XmlToSimpleXmlObject'); // レスポンスをXmlObjectに変更するトランスフォーマークラス
    /*
      ApaiIO/vendor/exeu/apai-o/lib/ApaiIO/ResponseTransformer/ 配下にいっぱい変換クラスがありますので、
      目的のを選ぶといいです
    */
} catch (\Exception $e) {
    echo $e->getMessage();
}
$apaiIO = new ApaiIO($conf);

// Keywordで検索
$search = new Search();
$search->setCategory('All');
$search->setKeywords($keyword);
$search->setPage(1);
$search->setResponseGroup(array('ItemAttributes', 'Offers', 'Images', 'EditorialReview'));
$search_response = $apaiIO->runOperation($search);
$search_response = json_decode(json_encode($response), TRUE);

// ItemIDで検索
$lookup = new Lookup();
$lookup->setItemId($this->params['item_id']);
$lookup->setResponseGroup(array('ItemAttributes','Offers','Images','EditorialReview'));
$lookup_response = $apaiIO->runOperation($lookup);
$lookup_response = json_decode(json_encode($response), TRUE);

?>

RESTはとても順調光速で機能しました。
ありがたいことです。
ちなみに、Xmlをそのままオブジェクトにするのがだるかったので、

$lookup_response = json_decode(json_encode($response), TRUE);

このように1回jsonにしてから引き戻してArray化するという手法をとっています。
Xmlの属性値が使えなくなりますが、、まぁ一応機能は果たしましたのでよしとします。

以上でした。
でも結局原因が分からず、非常に消化不良です。
どなたか何か情報ございましたら教えて頂けると嬉しいです。
それでは!