【WordPress】Amazon PA-API5に対応したカード型商品表示を作る方法

【WordPress】Amazon PA-API5に対応したカード型商品表示を作る方法

AmazonアソシエイトのPA-API(Product Advertising API)が4.0から5.0へ変更され、大幅に仕様が変更されると知らされたのは昨年の話。以来最初に予定されていた日程が延長され、最終的には2020年3月9日から本格的に稼働し、旧バージョンの4.0を使ったアプリケーションやパーツなどは一切つかえなくなります(情報を一時的にキャッシュするようにしている場合はキャッシュが切れるまでということになります)。

現在AmazonアソシエイトのPA-APIを使って商品等の表示をサイト内にされている方は、Amazonアソシエイトからうるさい位バージョン変更のお知らせと、「まだ4.0が使われてますよ~」というメールが来ていることと思います。

PA-API?4.0?5.0?何か変更しないといけないの?という段階の方は

を参考にまずはPA-APIのバージョン変更にともなって行うべき作業等について理解していただきたく思います(Amazonアソシエイトの説明は本当に分かりづらいですので・・・)。

実は今見ていただいているサイトもフッターに記載している通り、Amazonアソシエイトの広告を、PA-APIを使って表示させています。以前はプラグインを使って表示させていましたが、そのプラグインは長年更新されておらず、当然今回の仕様変更にも対応していないため、対応するものへの変更を余儀なくされました。

そこで目にしたのがAmazonアソシエイト公式プラグインである「Amazon Associates Link Builder」すら対応せずに3月9日で完全停止するというお知らせ。今まで別のプラグインを使っていましたので最終的にはちょっと使いにくいけどサービスデベロッパーが提供しているプラグインなんだしそれを使えばいいや!程度に考えていましたので衝撃はハンパなかったです(笑)。

で、いろいろと探したところあまり有用なプラグインがない・・・で辿りついたのが今回紹介する商品表示方法になります。

といってもコード自体は文末の「参考にさせていただいた記事」で掲載している寝ログさんのページとほぼ、というか本当にほんどそのまま使わせていただいております(コードを一部改編していますのでコードも本記事内に掲載させていただいております)。

・・・とはいってもそれではコード泥棒になってしまいますので、できるだけ簡単に設置できるような手順を追加してお届けしていきます。

元記事に掲載されているfunctions.phpへの追記とCSSの追記で対応される方は

【PA-API v5版】WordPressでAmazon商品リンクショートコードを実装する方法

をご覧になり、その通りにしていただければと思います。

PA-API対応の商品カード表示機能の概要

ほぼ参考記事に書かれている通りですので主だった点と変更を加えている点のみ紹介します。

表示例

今回の方法を使ったパソコン・モバイル(レスポンシブ)・AMPそれぞれの表示例(画像)です。

パソコン表示時

【WordPress】Amazon PA-API5に対応したカード型商品表示を作る方法|Knowledge Base

モバイル(レスポンシブ)表示時

【WordPress】Amazon PA-API5に対応したカード型商品表示を作る方法|Knowledge Base

AMP表示時

【WordPress】Amazon PA-API5に対応したカード型商品表示を作る方法|Knowledge Base

いずれもこのサイトで使っているオリジナルテーマ【ha-Basic】での表示例です

独自のスクリプトなどが一切使われていませんのでAMPでもエラーなく表示できるのがすばらしいです(わいひらさんありがとうございます)。

主な仕様(簡易&変更点)

  • ショートコードでAmazon商品のASINコードなどを入れるだけで簡単にカード式商品リンクが作れる
  • Amazonに掲載されている商品の情報をPA-API5.0を使って取得して表示
  • タイトル・画像およびAmazonのボタンをクリックするとAmazonの商品詳細ページへ移動する
  • 楽天・Yahooのボタンをクリックすると、設定したキーワードでの検索結果ページへ移動する
  • PA-APIで取得した商品名等の情報は180日間キャッシュ(サイトのデータベースへ保存)される
  • PA-APIの接続制限等で情報取得のエラーが出た場合には3日後に自動で再取得する

変更しているのはAmazon用のボタンクリック時の遷移先。楽天やYahooと同様に検索結果へ飛ばすようになっていましたが、それだとPA-API経由のアクセスとならなくなり、そこから成果が出てもPA-APIへのアクセス回数制限解除の足しにならないので、詳細ページへ飛ぶようにしました。

AmazonアソシエイトのAPI利用規約ではAPI経由での情報取得は原則24時間以内のものを表示すること、さらに価格については都度最新情報になっていない場合には取得時刻を表示することとされていますが、サイト中の全商品をキャッシュなく取得するとすぐに制限回数オーバーになってしまうため、変動の少ない商品情報についてはキャッシュ(一時保存)するようにし、価格については表示させないようにしています。

もちろん価格情報を表示すれば成果が上がりやすいとは思いますが、それをすることで「サイトに掲載されている価格が実際の価格と違う・・」などと報告されたりして、規約違反に問われる可能性が高くなりますし、何よりサイトの信頼性を失うことになりますから、コードの分かる方なら簡単に追加できるものの、私個人としては全く推奨しません。

サイトへの組み込みと設定

紹介されているPHPコードは非常に長いですから、そのまま親テーマや子テーマにこのコードを追加するとごちゃごちゃとしたものになってしまいます(めったにfunctions.phpを触らない方ならいいかと思いますが、プラグインなしで何かを追加するということでfunctions.phpへ追記することの多い方は管理が大変になると思います)から、別にファイルを作成してそれを追加で読み込ませるという方法を紹介します。

まずはテキストエディタを開き以下のコードをコピペして「functions」という名前のPHP形式(UTF-8 BOMなし)で保存します

<?php

//アクセスキー
define('ACCESS_KEY_ID', 'アクセスキー');
//シークレットキー
define('SECRET_ACCESS_KEY', 'シークレットキー');
//アソシエイトタグ
define('ASSOCIATE_TRACKING_ID', 'ソシエイトタグ');
//楽天アフィリエイトID
define('RAKUTEN_AFFILIATE_ID', '楽天アフィリエイトID');
//Yahoo!バリューコマースSID
define('SID', 'Yahoo!バリューコマースSID');
//Yahoo!バリューコマースPID
define('PID', 'Yahoo!バリューコマースPID');
//キャッシュ更新間隔
define('AMAZON_CACHE_DAYS', '180'); //180日(APIで取得した商品情報を保存する期間)
//エラーキャッシュ更新間隔
define('ERROR_CACHE_DAYS', '3'); //3日(エラー情報を保存しておく期間:エラーメール間隔)

//ターゲットに文字列が含まれているか
if ( !function_exists( 'includes_string' ) ):
function includes_string($target, $searchstr){
if (strpos($target, $searchstr) === false) {
return false;
} else {
return true;
}
}
endif;

//JSONがエラーを出力しているか
if ( !function_exists( 'is_paapi_json_error' ) ):
function is_paapi_json_error($json){
return property_exists($json, 'Errors');
}
endif;

//PA-APIの返り値のJSONにアイテムが存在するか
if ( !function_exists( 'is_paapi_json_item_exist' ) ):
function is_paapi_json_item_exist($json){
return property_exists($json->{'ItemsResult'}, 'Items');
}
endif;

//Amazon APIキャッシュIDの取得
if ( !function_exists( 'get_amazon_api_transient_id' ) ):
function get_amazon_api_transient_id($asin){
return 'nlg_amazon_paapi_v5_asin_'.$asin;;
}
endif;

//WordPressで設定されているメールアドレス取得する
if ( !function_exists( 'get_wordpress_admin_email' ) ):
function get_wordpress_admin_email(){
return get_option('admin_email');
}
endif;

//PA-APIで商品情報を取得できなかった場合のエラーログ
if ( !function_exists( 'email_amazon_product_error_message' ) ):
function email_amazon_product_error_message($asin, $message = ''){
//メールで送信
$subject = 'Amazon商品取得エラー';
$mail_msg = 'Amazon商品リンクが取得できませんでした。'.PHP_EOL.
PHP_EOL.
'ASIN:'.$asin.PHP_EOL.
'URL:'.get_the_permalink().PHP_EOL.
'Message:'.$message.PHP_EOL;
wp_mail( get_wordpress_admin_email(), $subject, $mail_msg );

//エラーログに出力
$date = date_i18n("Y-m-d H:i:s");
$msg = $date.','.
$asin.','.
get_the_permalink().
PHP_EOL;
error_log($msg, 3, get_template_directory().'/amazon_errors.log');
}
endif;

//シンプルなアソシエイトURLの作成(PA-API制限時用)
if ( !function_exists( 'get_amazon_associate_url' ) ):
function get_amazon_associate_url($asin, $associate_tracking_id){
$base_url = 'https://www.amazon.co.jp/exec/obidos/ASIN';
$associate_url = $base_url.'/'.$asin.'/';
if (!empty($associate_tracking_id)) {
$associate_url .= $associate_tracking_id.'/';
}
$associate_url = esc_url($associate_url);
return $associate_url;
}
endif;

//Amazon商品紹介リンクの外枠で囲む
if ( !function_exists( 'wrap_amazon_item_box' ) ):
function wrap_amazon_item_box($message){
return '<div class="amazon-item-box no-icon amazon-item-error cf"><div>'.$message.'</div></div>';
}
endif;

if ( !class_exists( 'NelogPaapiV5' ) ):
class NelogPaapiV5 {

private $accessKey = null;
private $secretKey = null;
private $path = null;
private $regionName = null;
private $serviceName = null;
private $httpMethodName = null;
private $queryParametes = array ();
private $awsHeaders = array ();
private $payload = "";

private $HMACAlgorithm = "AWS4-HMAC-SHA256";
private $aws4Request = "aws4_request";
private $strSignedHeader = null;
private $xAmzDate = null;
private $currentDate = null;

public function __construct($accessKey, $secretKey) {
$this->accessKey = $accessKey;
$this->secretKey = $secretKey;
$this->xAmzDate = $this->getTimeStamp ();
$this->currentDate = $this->getDate ();
}

function setPath($path) {
$this->path = $path;
}

function setServiceName($serviceName) {
$this->serviceName = $serviceName;
}

function setRegionName($regionName) {
$this->regionName = $regionName;
}

function setPayload($payload) {
$this->payload = $payload;
}

function setRequestMethod($method) {
$this->httpMethodName = $method;
}

function addHeader($headerName, $headerValue) {
$this->awsHeaders [$headerName] = $headerValue;
}

private function prepareCanonicalRequest() {
$canonicalURL = "";
$canonicalURL .= $this->httpMethodName . "\n";
$canonicalURL .= $this->path . "\n" . "\n";
$signedHeaders = '';
foreach ( $this->awsHeaders as $key => $value ) {
$signedHeaders .= $key . ";";
$canonicalURL .= $key . ":" . $value . "\n";
}
$canonicalURL .= "\n";
$this->strSignedHeader = substr ( $signedHeaders, 0, - 1 );
$canonicalURL .= $this->strSignedHeader . "\n";
$canonicalURL .= $this->generateHex ( $this->payload );
return $canonicalURL;
}

private function prepareStringToSign($canonicalURL) {
$stringToSign = '';
$stringToSign .= $this->HMACAlgorithm . "\n";
$stringToSign .= $this->xAmzDate . "\n";
$stringToSign .= $this->currentDate . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . "\n";
$stringToSign .= $this->generateHex ( $canonicalURL );
return $stringToSign;
}

private function calculateSignature($stringToSign) {
$signatureKey = $this->getSignatureKey ( $this->secretKey, $this->currentDate, $this->regionName, $this->serviceName );
$signature = hash_hmac ( "sha256", $stringToSign, $signatureKey, true );
$strHexSignature = strtolower ( bin2hex ( $signature ) );
return $strHexSignature;
}

public function getHeaders() {
$this->awsHeaders ['x-amz-date'] = $this->xAmzDate;
ksort ( $this->awsHeaders );

// Step 1: CREATE A CANONICAL REQUEST
$canonicalURL = $this->prepareCanonicalRequest ();

// Step 2: CREATE THE STRING TO SIGN
$stringToSign = $this->prepareStringToSign ( $canonicalURL );

// Step 3: CALCULATE THE SIGNATURE
$signature = $this->calculateSignature ( $stringToSign );

// Step 4: CALCULATE AUTHORIZATION HEADER
if ($signature) {
$this->awsHeaders ['Authorization'] = $this->buildAuthorizationString ( $signature );
return $this->awsHeaders;
}
}

private function buildAuthorizationString($strSignature) {
return $this->HMACAlgorithm . " " . "Credential=" . $this->accessKey . "/" . $this->getDate () . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . "," . "SignedHeaders=" . $this->strSignedHeader . "," . "Signature=" . $strSignature;
}

private function generateHex($data) {
return strtolower ( bin2hex ( hash ( "sha256", $data, true ) ) );
}

private function getSignatureKey($key, $date, $regionName, $serviceName) {
$kSecret = "AWS4" . $key;
$kDate = hash_hmac ( "sha256", $date, $kSecret, true );
$kRegion = hash_hmac ( "sha256", $regionName, $kDate, true );
$kService = hash_hmac ( "sha256", $serviceName, $kRegion, true );
$kSigning = hash_hmac ( "sha256", $this->aws4Request, $kService, true );

return $kSigning;
}

private function getTimeStamp() {
return gmdate ( "Ymd\THis\Z" );
}

private function getDate() {
return gmdate ( "Ymd" );
}
}
endif;

//Amazon商品リンク作成
add_shortcode('amazon', 'generate_amazon_product_link');
if ( !function_exists( 'generate_amazon_product_link' ) ):
function generate_amazon_product_link($atts){
extract( shortcode_atts( array(
'asin' => null,
'id' => null,
'kw' => null,
'title' => null,
'desc' => null,
'amazon' => 1,
'rakuten' => 1,
'yahoo' => 1,
), $atts ) );

$asin = esc_html(trim($asin));

//ASINが取得できない場合はID
if (empty($asin)) {
$asin = $id;
}

//アクセスキー
$access_key_id = ACCESS_KEY_ID;
//シークレットキー
$secret_access_key = SECRET_ACCESS_KEY;
//アソシエイトタグ
$associate_tracking_id = ASSOCIATE_TRACKING_ID;
//楽天アフィリエイトID
$rakuten_affiliate_id = RAKUTEN_AFFILIATE_ID;
//Yahoo!バリューコマースSID
$sid = SID;
//Yahoo!バリューコマースPID
$pid = PID;
//キャッシュ更新間隔
$days = AMAZON_CACHE_DAYS;
//キーワード
$kw = trim($kw);

//アクセスキーもしくはシークレットキーがない場合
if (empty($access_key_id) || empty($secret_access_key)) {
$error_message = 'Amazon APIのアクセスキーもしくはシークレットキーが設定されていません。';
return wrap_amazon_item_box($error_message);
}

//ASINがない場合
if (empty($asin)) {
$error_message = 'Amazon商品リンクショートコード内にASINが入力されていません。';
return wrap_amazon_item_box($error_message);
}

//アソシエイトurlの取得(デフォルト)
$associate_url = get_amazon_associate_url($asin, $associate_tracking_id);

$new_cache = false;
//キャッシュの存在
$transient_id = get_amazon_api_transient_id($asin);
$json_cache = get_transient( $transient_id );
if ($json_cache) {
$res = $json_cache;
} else {

$serviceName = 'ProductAdvertisingAPI';
$region = 'us-west-2';

$payload = '{'
.' "ItemIds": ['
.' "'.$asin.'"'
.' ],'
.' "Resources": ['
.' "BrowseNodeInfo.BrowseNodes",'
.' "BrowseNodeInfo.BrowseNodes.Ancestor",'
.' "BrowseNodeInfo.BrowseNodes.SalesRank",'
.' "BrowseNodeInfo.WebsiteSalesRank",'
.' "CustomerReviews.Count",'
.' "CustomerReviews.StarRating",'
.' "Images.Primary.Small",'
.' "Images.Primary.Medium",'
.' "Images.Primary.Large",'
.' "Images.Variants.Small",'
.' "Images.Variants.Medium",'
.' "Images.Variants.Large",'
.' "ItemInfo.ByLineInfo",'
.' "ItemInfo.ContentInfo",'
.' "ItemInfo.ContentRating",'
.' "ItemInfo.Classifications",'
.' "ItemInfo.ExternalIds",'
.' "ItemInfo.Features",'
.' "ItemInfo.ManufactureInfo",'
.' "ItemInfo.ProductInfo",'
.' "ItemInfo.TechnicalInfo",'
.' "ItemInfo.Title",'
.' "ItemInfo.TradeInInfo",'
.' "Offers.Listings.Availability.MaxOrderQuantity",'
.' "Offers.Listings.Availability.Message",'
.' "Offers.Listings.Availability.MinOrderQuantity",'
.' "Offers.Listings.Availability.Type",'
.' "Offers.Listings.Condition",'
.' "Offers.Listings.Condition.SubCondition",'
.' "Offers.Listings.DeliveryInfo.IsAmazonFulfilled",'
.' "Offers.Listings.DeliveryInfo.IsFreeShippingEligible",'
.' "Offers.Listings.DeliveryInfo.IsPrimeEligible",'
.' "Offers.Listings.DeliveryInfo.ShippingCharges",'
.' "Offers.Listings.IsBuyBoxWinner",'
.' "Offers.Listings.LoyaltyPoints.Points",'
.' "Offers.Listings.MerchantInfo",'
.' "Offers.Listings.Price",'
.' "Offers.Listings.ProgramEligibility.IsPrimeExclusive",'
.' "Offers.Listings.ProgramEligibility.IsPrimePantry",'
.' "Offers.Listings.Promotions",'
.' "Offers.Listings.SavingBasis",'
.' "Offers.Summaries.HighestPrice",'
.' "Offers.Summaries.LowestPrice",'
.' "Offers.Summaries.OfferCount",'
.' "ParentASIN",'
.' "RentalOffers.Listings.Availability.MaxOrderQuantity",'
.' "RentalOffers.Listings.Availability.Message",'
.' "RentalOffers.Listings.Availability.MinOrderQuantity",'
.' "RentalOffers.Listings.Availability.Type",'
.' "RentalOffers.Listings.BasePrice",'
.' "RentalOffers.Listings.Condition",'
.' "RentalOffers.Listings.Condition.SubCondition",'
.' "RentalOffers.Listings.DeliveryInfo.IsAmazonFulfilled",'
.' "RentalOffers.Listings.DeliveryInfo.IsFreeShippingEligible",'
.' "RentalOffers.Listings.DeliveryInfo.IsPrimeEligible",'
.' "RentalOffers.Listings.DeliveryInfo.ShippingCharges",'
.' "RentalOffers.Listings.MerchantInfo"'
.' ],'
.' "PartnerTag": "'.$associate_tracking_id.'",'
.' "PartnerType": "Associates",'
.' "Marketplace": "www.amazon.co.jp"'
.'}';
$host = 'webservices.amazon.co.jp';
$uriPath = '/paapi5/getitems';
$awsv5 = new NelogPaapiV5 ($access_key_id, $secret_access_key);
$awsv5->setRegionName($region);
$awsv5->setServiceName($serviceName);
$awsv5->setPath ($uriPath);
$awsv5->setPayload ($payload);
$awsv5->setRequestMethod ("POST");
$awsv5->addHeader ('content-encoding', 'amz-1.0');
$awsv5->addHeader ('content-type', 'application/json; charset=utf-8');
$awsv5->addHeader ('host', $host);
$awsv5->addHeader ('x-amz-target', 'com.amazon.paapi5.v1.ProductAdvertisingAPIv1.GetItems');
$headers = $awsv5->getHeaders ();
$headerString = "";
foreach ( $headers as $key => $value ) {
$headerString .= $key . ': ' . $value . "\r\n";
}
$params = array (
'http' => array (
'header' => $headerString,
'method' => 'POST',
'content' => $payload,
'ignore_errors' => true,
)
);
$stream = stream_context_create( $params );

$fp = @fopen ( 'https://'.$host.$uriPath, 'rb', false, $stream );

if (!$fp) {
$error_message = 'fopenが利用できないようです。サーバーの「php.ini設定」の「allow_url_fopen」項目が「ON」になっているかを確認してください。';
email_amazon_product_error_message($asin, $error_message);
return wrap_amazon_item_box($error_message);
}

$res = false;

$res = @stream_get_contents( $fp );

//503エラーの場合はfalseを返す
if (includes_string($res, 'Website Temporarily Unavailable')) {
$res = false;
}

$new_cache = true;
}

//JSONが取得できた場合
if ($res) {
$responsed_json = $res;

// JSON取得
$json = json_decode( $res );
//_v($json);

if (is_paapi_json_error($json)) {
$error_message = '<a href="'.$associate_url.'" target="_blank" rel="nofollow noopener">'.'Amazonで詳細を見る'.'</a>';

$json_error_code = $json->{'Errors'}[0]->{'Code'};
$json_error_message = $json->{'Errors'}[0]->{'Message'};

if (is_user_logged_in()) {
$admin_message = '<b>'.'管理者用エラーメッセージ'.'</b><br>'.PHP_EOL;
$admin_message .= 'アイテムを取得できませんでした。'.'<br>'.PHP_EOL.PHP_EOL;
$admin_message .= '<pre class="nohighlight"><b>'.$json_error_code.'</b><br>'.preg_replace('/AWS Access Key ID: .+?\. /', '', $json_error_message).'</pre>'.PHP_EOL.PHP_EOL;
$admin_message .= '<span style="colof:red;">このエラーメッセージは"サイト管理者のみ"に表示されています。少し時間おいてリロードしてください。</span>'.PHP_EOL;
$error_message .= '<br><br>'.$admin_message;
}

$transient_id = get_amazon_api_transient_id($asin);
$json_cache = get_transient( $transient_id );
//キャッシュがないときメール送信
if (!$json_cache) {
//メールの送信
$msg = 'アイテムを取得できませんでした。'.PHP_EOL.
$json_error_code.PHP_EOL.
$json_error_message.PHP_EOL;
email_amazon_product_error_message($asin, $msg);

if ($json_error_code != 'TooManyRequests') {
//エラーの場合は指定期間キャッシュ
$expiration = DAY_IN_SECONDS * ERROR_CACHE_DAYS;
//Amazon APIキャッシュの保存
set_transient($transient_id, $res, $expiration);
}
}

return wrap_amazon_item_box($error_message);
}

if (!is_paapi_json_item_exist($json)) {
$error_message = '商品を取得できませんでした。存在しないASINを指定している可能性があります。';

email_amazon_product_error_message($asin, $error_message);
return wrap_amazon_item_box($error_message);
}

if (is_paapi_json_item_exist($json)) {
$item = $json->{'ItemsResult'}->{'Items'}[0];

///////////////////////////////////////
// アマゾンURL
///////////////////////////////////////
$DetailPageURL = esc_url($item->DetailPageURL);
if ($DetailPageURL) {
$associate_url = $DetailPageURL;
}

//イメージセットを取得する
$Images = $item->{'Images'};
$ImageItem = $Images->{'Primary'};

$SmallImage = $ImageItem->{'Small'};
$SmallImageUrl = $SmallImage->URL;
$SmallImageWidth = $SmallImage->Width;
$SmallImageHeight = $SmallImage->Height;
$MediumImage = $ImageItem->{'Medium'};
$MediumImageUrl = $MediumImage->URL;
$MediumImageWidth = $MediumImage->Width;
$MediumImageHeight = $MediumImage->Height;
$LargeImage = $ImageItem->{'Large'};
$LargeImageUrl = $LargeImage->URL;
$LargeImageWidth = $LargeImage->Width;
$LargeImageHeight = $LargeImage->Height;

$ImageUrl = $MediumImageUrl;
$ImageWidth = $MediumImageWidth;
$ImageHeight = $MediumImageHeight;

//小さなアマゾンリンク
$small_class = null;
if (!$kw) {
$small_class = ' pis-s';

$ImageUrl = $SmallImageUrl;
$ImageWidth = $SmallImageWidth;
$ImageHeight = $SmallImageHeight;
}

$ItemInfo = isset($item->{'ItemInfo'}) ? $item->{'ItemInfo'} : null;

///////////////////////////////////////////
// 商品リンク出力用の変数設定
///////////////////////////////////////////
if ($title) {
$Title = $title;
} else {
$Title = $ItemInfo->{'Title'}->{'DisplayValue'};
}

//説明文
$description_tag = null;
if ($desc) {
$description_tag = '<div class="amazon-item-description">'.$desc.'</div>';
}

//商品グレープ
$Classifications = $ItemInfo->{'Classifications'};
$ProductGroup = esc_html($Classifications->{'ProductGroup'}->{'DisplayValue'});
$ProductGroupClass = strtolower($ProductGroup);
$ProductGroupClass = str_replace(' ', '-', $ProductGroupClass);
//_v($ProductGroup);

$ByLineInfo = $ItemInfo->{'ByLineInfo'};
$Publisher = esc_html(isset($ByLineInfo->{'Publisher'}->{'DisplayValue'}) ? $ByLineInfo->{'Publisher'}->{'DisplayValue'} : null);
$Manufacturer = esc_html(isset($ByLineInfo->{'Manufacturer'}->{'DisplayValue'}) ? $ByLineInfo->{'Manufacturer'}->{'DisplayValue'} : null);
$Brand = esc_html(isset($ByLineInfo->{'Brand'}->{'DisplayValue'}) ? $ByLineInfo->{'Brand'}->{'DisplayValue'} : null);
$Binding = esc_html(isset($ByLineInfo->{'Binding'}->{'DisplayValue'}) ? $ByLineInfo->{'Binding'}->{'DisplayValue'} : null);
$Author = esc_html(isset($ByLineInfo->{'Author'}->{'DisplayValue'}) ? $ByLineInfo->{'Author'}->{'DisplayValue'} : null);
$Artist = esc_html(isset($ByLineInfo->{'Artist'}->{'DisplayValue'}) ? $ByLineInfo->{'Artist'}->{'DisplayValue'} : null);
$Actor = esc_html(isset($ByLineInfo->{'Actor'}->{'DisplayValue'}) ? $ByLineInfo->{'Actor'}->{'DisplayValue'} : null);
$Creator = esc_html(isset($ByLineInfo->{'Creator'}->{'DisplayValue'}) ? $ByLineInfo->{'Creator'}->{'DisplayValue'} : null);
$Director = esc_html(isset($ByLineInfo->{'Director'}->{'DisplayValue'}) ? $ByLineInfo->{'Director'}->{'DisplayValue'} : null);
if ($Author) {
$maker = $Author;
} elseif ($Artist) {
$maker = $Artist;
} elseif ($Actor) {
$maker = $Actor;
} elseif ($Creator) {
$maker = $Creator;
} elseif ($Director) {
$maker = $Director;
} elseif ($Publisher) {
$maker = $Publisher;
} elseif ($Brand) {
$maker = $Brand;
} elseif ($Manufacturer) {
$maker = $Manufacturer;
} else {
$maker = $Binding;
}

$buttons_tag = null;
if ($kw) {
//Amazonボタンの取得
$amazon_btn_tag = null;
if ($amazon) {
$amazon_url = 'https://www.amazon.co.jp/gp/search?keywords='.urlencode($kw).'&tag='.$associate_tracking_id;
$amazon_btn_tag =
'<div class="shoplinkamazon">'.
'<a href="'.esc_url($associate_url).'" target="_blank" rel="nofollow noopener">'.'Amazonで詳細を見る'.'</a>'.
'</div>';
}

//楽天ボタンの取得
$rakuten_btn_tag = null;
if ($rakuten_affiliate_id && $rakuten) {
$rakuten_url = 'https://hb.afl.rakuten.co.jp/hgc/'.$rakuten_affiliate_id.'/?pc=https%3A%2F%2Fsearch.rakuten.co.jp%2Fsearch%2Fmall%2F'.urlencode($kw).'%2F-%2Ff.1-p.1-s.1-sf.0-st.A-v.2%3Fx%3D0%26scid%3Daf_ich_link_urltxt%26m%3Dhttp%3A%2F%2Fm.rakuten.co.jp%2F';
$rakuten_btn_tag =
'<div class="shoplinkrakuten">'.
'<a href="'.esc_url($rakuten_url).'" target="_blank" rel="nofollow noopener">'.'楽天市場で探す'.'</a>'.
'</div>';
}

//Yahoo!ボタンの取得
$yahoo_tag = null;
if ($sid && $pid && $yahoo) {
$yahoo_url = 'https://ck.jp.ap.valuecommerce.com/servlet/referral?sid='.$sid.'&pid='.$pid.'&vc_url=http%3A%2F%2Fsearch.shopping.yahoo.co.jp%2Fsearch%3Fp%3D'.$kw;
$yahoo_tag =
'<div class="shoplinkyahoo">'.
'<a href="'.esc_url($yahoo_url).'" target="_blank" rel="nofollow noopener">'.'Yahoo!で探す'.'</a>'.
'</div>';
}
//ボタンコンテナ
$buttons_tag =
'<div class="amazon-item-buttons">'.
$amazon_btn_tag.
$rakuten_btn_tag.
$yahoo_tag.
'</div>';
}

$tag =
'<div class="amazon-item-box no-icon '.$small_class.' '.$ProductGroupClass.' '.$asin.' cf">'.
'<figure class="amazon-item-thumb">'.
'<a href="'.esc_url($associate_url).'" class="amazon-item-thumb-link" target="_blank" title="'.esc_attr($Title).'" rel="nofollow">'.
'<img src="'.esc_attr($ImageUrl).'" alt="'.esc_attr($Title).'" width="'.esc_attr($ImageWidth).'" height="'.esc_attr($ImageHeight).'" class="amazon-item-thumb-image">'.
'</a>'.
'</figure>'.
'<div class="amazon-item-content">'.
'<div class="amazon-item-title">'.
'<a href="'.esc_url($associate_url).'" class="amazon-item-title-link" target="_blank" title="'.esc_attr($Title).'" rel="nofollow noopener">'.
esc_html($Title).
'</a>'.
'</div>'.
'<div class="amazon-item-snippet">'.
'<div class="amazon-item-maker">'.
$maker.
'</div>'.
$description_tag.
$buttons_tag.
'</div>'.
'</div>'.
'</div>';
} else {
$error_message = '商品を取得できませんでした。存在しないASINを指定している可能性があります。';
$tag = wrap_amazon_item_box($error_message);
}

if ($new_cache) {
//キャッシュ更新間隔(randで次回のキャッシュ切れ同時読み込みを防ぐ:3時間のばらつきを与える)
$expiration = 60 * 60 * 24 * $days + (rand(0, 180) * 60);
//Amazon APIキャッシュの保存
set_transient($transient_id, $responsed_json, $expiration);
}

return $tag;
}

}
endif;

※UTF-8 BOMなしで保存しないとWordPressでは認識してくれませんのでご注意ください。どんなことになるのかは

を参照ください。

とてつもなく長いコードですね。これを普通にfunctions.phpへ追記すると結構大変です・・

続いて、テキストエディタへ次のコードをコピペして「ad-card」、ファイル形式はcssで保存します

.amazon-item-box {
padding: 22px 25px;
width: 100%;
margin: 0 auto 1.6em;
border: 3px solid #dfdfdf;
box-sizing: border-box;
text-align: center;
position: relative;
}

.amazon-item-box {
position: relative;
}
/*
Font Awesome適用下でコメントアウトすると
ボックス右下にAmazonアイコンが表示されます*/
/*
.amazon-item-box::after {
font-family: FontAwesome;
content: "?";
padding-right: 3px;
position: absolute;
bottom: 0;
right: 6px;
font-size: 24px;
color: #ccc;
}
*/

.amazon-item-error.cf {
display: block;
line-height: 1.2;
}

.amazon-item-thumb {
width: 160px;
min-width: 160px;
margin: 0 auto 5px;
float: none !important;
}

.amazon-item-thumb * {
display: block;
}

.amazon-item-thumb &gt; a &gt; img {
margin: 0 auto;
}

.amazon-item-content {
line-height: 125%;
width: 100%;
}

.amazon-item-snippet {
font-size: 0.8em;
margin-top: 6px;
}

.amazon-item-buttons a {
width: 90%;
display: block;
margin: 0px auto 8px;
padding: 10px 1px;
text-decoration: none;
 
font-weight: bold;
text-align: center;
color: #fff;
}

.amazon-item-buttons a:hover {
opacity: 0.6;
}

.amazon-item-buttons {
margin-top: 1em;
}

@media screen and (min-width: 768px) {
.amazon-item-box {
text-align: left;
display: flex;
font-size: inherit !important;
}

.amazon-item-thumb {
vertical-align: top;
box-sizing: border-box;
min-width: auto !important;
}

.amazon-item-content {
line-height: 125%;
vertical-align: top;
box-sizing: border-box;
padding-left: 25px;
width: 100%;
}

.amazon-item-buttons {
display: flex;
flex-wrap: wrap;
}

.amazon-item-buttons a {
width: auto;
text-align: center;
margin: 0;
border-radius: 3px;
}

.amazon-item-buttons a img {
position: absolute;
bottom: 0;
right: 0;
}

.amazon-item-buttons &gt; * {
width: 31.5%;
margin: 2px;
box-sizing: border-box;
}
}
.shoplinkamazon a {
background: #f79901;
}

.shoplinkrakuten a {
background: #bf0000;
}

.shoplinkyahoo a {
background: #e60033;
position: relative;
}

保存した場所に「functions.php」と「ad-card.css」というファイルができました。これを新しく「ad-card」というフォルダを作り、その中へ入れてください(ad-cardフォルダを開くと2つのファイルがある状態)。

FTPを使って親テーマまたは子テーマ(有効化されている方)へ「ad-card」フォルダをアップロードします。

ここから先はアップロードしたのが親テーマなのか子テーマなのかで若干違いますので解説を分けます

親テーマの場合

テーマのfunctions.phpへ以下のコードを追加します

/********** 商品表示用のfunctionを読み込む **********/
include 'ad-card/functions.php';// 追加functions

※functions.phpはサイトを稼働させるための重要なファイルですので、バックアップを取るなどしてトラブル時元に戻すことができるようにしておきましょう

そしてテーマのheader.phpを開いて、

</head>

というコードの上に以下のコードを追記します

<link rel="stylesheet" href="<?php echo get_template_directory_uri(); ?>/ad-card/ad-card.css">

問題なく両方とも処理できたら、次項で説明するショートコードを入れた記事を書いてみて下さい。無事に前述したサンプルのようなカードが表示されれば設定完了です。

子テーマの場合

子テーマの場合は場所をきちんと指定してあげる必要がありますので、親テーマとよく似ていますが以下のコードを子テーマのfunctions.phpへ追記します

/********** 商品表示用のfunctionを読み込む **********/
include (STYLESHEETPATH 'ad-card/functions.php');// 追加functions

そして子テーマの場合、CSSについては子テーマのCSSへ直接コピペした方が良いので↑で紹介したCSSをそのままコピペします。
※もしも子テーマに「header.php」へ何かを追加するためのファイルがあるようなら

<link rel="stylesheet" href="<?php echo get_stylesheet_directory_uri; ?>/ad-card/ad-card.css">

を追加しても同じこととして適用されます。

問題なく両方とも処理できたら、次項で説明するショートコードを入れた記事を書いてみて下さい。無事に前述したサンプルのようなカードが表示されれば設定完了です。

記事や固定ページでの表示方法

商品カードを表示したい場所に以下のショートコードを挿入します

[amazon asin="ASINコード" kw="楽天・Yahooショッピングのボタンをクリックしたときの検索結果" yahoo=1 rakuten=0 desc="カードに表示する商品説明"]

各パラメーターは以下のように設定します

  • asin・・・Amazon商品のASINコードです。商品の詳細ページの中、もしくはURL中のasin=〇〇の〇〇をコピーすればOKです。
  • kw・・・楽天市場、Yahooのリンクをクリックすると表示される一覧のキーワードになります
  • yahoo・・・Yahooのボタンを表示するか否かの設定です「0」で非表示、「1」で表示されるようになります
  • rakuten・・・楽天市場のボタンを表示するか否かの設定です「0」で非表示、「1」で表示されるようになります
  • desc・・・入力すると、API経由で取得した商品説明ではなくオリジナルの商品説明を表示できます。API経由だと結構何も表示されなかったりするので自身で設定した方がいいかもしれません。

入れ替えは気長に行うかエラー解消を待つかのいずれか

喜んでバンバン今までのものを今回のものに変えていくと、APIを叩く回数の制限や情報取得間隔の問題で一度エラーとなって表示されなかったりすることがあります。

これはショートコードの入った記事や固定ページが最初に閲覧されたときに商品情報を取りに行くのが原因ですから、多数の記事や固定ページに商品を掲載していて、かつ、商品表示をしているページへの初回アクセスが多い程エラーが出やすくなります。

今回の仕組みは一度エラーとならずに取得できた商品情報はASINコードを元にデータが180日間保存されますし、エラーとなったものも3日後に再取得を試みるようになっているので、放っておけば徐々に解消されていきますから、「取得エラー」というメールがバンバン来てもしばらく気にしないようにすれば大丈夫です。

これを知らずに焦って別の商品を掲載しようとすると、前述したように商品情報の取得がどんどん走ってしまって堂々巡りになりますから気を付けましょう。

参考にさせていただいた記事

こういうのを惜しみなく公開していただける「わいひら」さんの寝ログ、本当にありがたく、本当に頭が下がります。今回も多少コードの変更は加えたものの、ほぼまんまです

【PA-API v5版】WordPressでAmazon商品リンクショートコードを実装する方法

サイトへの支援をお願いします

最後までお読みいただきありがとうございました。本記事の内容がお役に立てましたら幸いです。
今後もよりよい情報を提供し続けることができるよう、投げ銭によるサイトへの支援、およびSNSによる拡散をお願いします

作者:

☆最後までお読みいただきありがとうございました。記事作者のひまあーとです。
☆Wordpressでサイトをカスタマイズしていく上で有用な情報を配信しつつ、「ココナラ」でサイトカスタマイズのお手伝い、不具合の修復、サイト引っ越し代行などをさせていただいております。

年齢:40代 趣味/園芸・ペット・卓球