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

PA-APIv5版Amazon商品リンク機能

以前、PA-APIv4を利用したAmazon商品リンクの実装方法は書きました。

けれどPA-APIv4は、2019年11月30日をもって利用できなくなるとAmazonアソシエイトからのアナウンスがありました。

【2019/09/09: PA-API v5移行のご案内】 PA-API 新しいバージョン(PA-API v5)についてお知らせいたします。 現在PA-APIをご利用の方で引き続きのご利用をご希望の場合、移行手続きが必要になります。 PA-API v5への移行を2019年11月30日までに実施しなかった場合、現在のPA-APIはご利用をいただけなくなります。

今後は、PA-APIのバージョン5たるPA-APIv5を利用する必要があります。

ということで以下では、「WordPressでPA-APIv5を利用してAmazon商品リンクショートコード機能を実装するテーマカスタマイズ方法」を紹介したいと思います。

こんな感じのAmazon商品リンクを作成できます。

スポンサーリンク
レクタングル(大)広告

主な手順

実装するのに必要な主な手順はこちら。

  1. functions.phpにコードを追記
  2. style.cssにスタイルを追記
  3. 各種IDを取得し入力

基本的にコードを2回ほどコピペしてあとは、APIの認証IDや、Amazon、楽天、Yahoo!ショッピングのIDを書き加えるだけです。

以前、PA-APIv4のカスタマイズを行っている場合は、その部分の「PHPコード」は削除しておいてください。「CSSコード」は流用可。

    functions.phpにコードを追記

    まずはテーマ(子テーマ)のfunctions.phpに追記する形で以下のコードを書き加えます。

    //アクセスキー
    define('ACCESS_KEY_ID', 'PA-APIアクセスキーを入力');
    //シークレットキー
    define('SECRET_ACCESS_KEY', 'PA-APIシークレットキーを入力');
    //アソシエイトタグ
    define('ASSOCIATE_TRACKING_ID', 'AmazonアソシエイトトラッキングIDを入力');
    //楽天アフィリエイトID
    define('RAKUTEN_AFFILIATE_ID', '楽天アフィリエイトIDを入力');
    //Yahoo!バリューコマースSID
    define('SID', 'SIDを入力');
    //Yahoo!バリューコマースPID
    define('PID', 'PIDを入力');
    //キャッシュ更新間隔
    define('AMAZON_CACHE_DAYS', '90'); //90日(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($amazon_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;

    コードが長すぎるので、詳しくは説明しません。

    一応、PA-APIの制限対策用に、一度商品リンクを作成したら、90日間キャッシュ保存されるようになっています(エラーの場合は3日間)。

    そして、2回目以降はキャッシュが呼び出され、PA-APIの制限をなるべく回避するようにしてあります。

    こちらのAPIの利用コードは、以下の「PA-APIv5スクラッチパッド」が出力するものを利用させていただきました。

    参考 Product Advertising API 5.0 Scratchpad

    style.cssにスタイルを追記

    子テーマのstyle.cssに追記する形で以下のCSSコードを貼り付けます。

    .amazon-item-box {
      padding: 22px 25px;
      width: 94%;
      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 > a > 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-size: 14px;
      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 > * {
        width: 31.5%;
        margin: 2px;
        box-sizing: border-box;
      }
    }
    .shoplinkamazon a {
      background: #f79901;
    }
     
    .shoplinkrakuten a {
      background: #bf0000;
    }
     
    .shoplinkyahoo a {
      background: #e60033;
      position: relative;
    }

    これは、PA-APIv4のカスタマイズと全く同じものなので、前回カスタマイズされた方は、そのまま流用で問題ないかと思います。

    Font Awesome4適用下で、コメント部分をコメントアウトすると、商品紹介ボックス右下にAmazonアイコンが表示されます(※Font Awesome5には対応していません)。

    商品ボックス右下にAmazonアイコン表示

    Font Awesomeを使用していないテーマの場合、もしくはアイコンを表示させたくない場合は、そのままにしておいてください。

    各種IDを取得し入力

    最後に、functions.phpに追加したコード部分に、APIのIDや、AmazonトラッキングID、楽天アフィリエイトID、バリューコマース(Yahoo!ショッピング)のsid・pidを入力してください。

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

    PA-APIの「アクセスキー」と「シークレットキー」の入手方法は、以下を参照してください。

    Amazon商品紹介リンク用の認証機能を取得&登録方法です。Amazonアソシエイトからアクセスキーとシークレットキーを作成登録する方法です。

    Amazonアソシエイトの「トラッキングID」を取得するには、以下を参照してください。

    AmazonアソシエイトからトラッキングIDを取得して、Cocoonの商品紹介リンクで利用するために設定する方法です。

    「楽天アフィリエイトID」を取得するには、以下を参照してください。

    楽天ウェブサービスからアフィリエイトIDを取得して設定登録する方法です。

    バリューコマースからYahoo!ショッピングの「sid」と「pid」を取得するには、以下を参照してください。

    バリューコマースでYahoo!ショッピングと提携しsidとpidを取得し設定登録する方法です。

    Amazonアソシエイトに登録されていないサイトで利用すると、規約違反になる恐れがあるので、事前に必ず登録申請を行ってください。

    現在Amazonアソシエイトを利用して「サイトの収益化」を行っている方も多いかと思います。そういった方は当然、サイトをAma...

      使い方と動作確認

      何点か商品リンクを作成してみます。

      基本的に、asinオプションにAmazonのASINを指定すればOKです(書籍以外)。

      ASINとはAmazonの商品IDのことです。紙媒体の書籍の場合はISBNになります。

      ダイソン掃除機

      紙の書籍

      紙の書籍の場合はISBNで(ISBN-10)。

      Kindle書籍

      Kindle書籍の場合は、ASINで。

      ボタンが不要な場合

      個々のボタンの表示を切り替えるならこんな感じ。

      1が表示、0が非表示です。
      全ての検索ボタンを非表示にするのであれば、kwオプションを無くすか、未入力にしてください。※kwオプションを無くした場合は、コンパクト表示になる仕様になっています。

      詳しい使い方

      ASIN、ISBN等の取得方法や、詳しい使い方は以下を参照してください。

      Cocoonの「Amazon商品リンクタグ作成ショートコード」機能の使い方を詳しく解説しています。

      こちらのブックマークレットを使用するとクリック一発でショートコードを作成することもできます。

      Amazon商品リンクショートコードを手軽に作成するための「Amazonリンク作成ブックマークレット」の登録方法と、使い方の解説です。

      また、実用で使うのであれば、正しくトラッキングIDが設定されているか以下の方法で確認しておくことをおすすめします。

      Amazon商品リンクのアソシエイトリンクの有効性を確認する方法です。

      注意点

      当記事のコードは、なるべく簡潔に書いたサンプルコードです。

      ですので、どんな環境でも確実に動作するということは、保証はできません。

      また、他にも考えられる注意点を列挙しておきます。

      • PA-APIには、リクエスト制限があります。制限が厳しい状態になっている場合はなかなか商品リンクが表示されない可能性があります。そういう場合は時間をおいてブラウザでリロードすればいずれ読み込まれます。制限について詳しくはリンク先をご確認ください
      • 当カスタマイズコードを含む、PA-APIを利用したソフト等を有償販売する場合はPA-APIライセンス違反になると思うのでご注意ください
      • 当記事で紹介されているコードはサンプルコードです。利用することにより不利益が起こったとしても当サイトは責任を負いません
      • Cocoonのようなキャッシュの削除機能はありません。基本的に時間を置けばキャッシュは削除されます。キャッシュの削除方法機能実装の質問には、お答えできかねるのでご了承ください
      • キャッシュ期間が長いためデフォルトでは値段は表示させてません
      • PA-APIv4版と一部仕様が違います
      • Cocoon版とも一部仕様が違います

      あくまで利用は自己責任でお願いします。

      このコードについてのサポートはできませんのであらかじめご了承ください。あくまでサンプルコードなので、起こり得るトラブルについて自前で解決できる方のご利用をおすすめします。

      ちなみに、Amazonアソシエイト管理画面の「リンクタイプレポート」では現時点(2019年11月)では、PA-APIv5の売上は、空白でカテゴライズされています。

      PA-APIv5のリンクタイプレポート

      上記のように「リンクタイプ」名はありませんが、一応PA-APIからの売上にはなっていると思われます。

      なので、このような「リンクタイプ名のない売り上げ」があればAPI使用回数制限解除の役割は果たしているかと思います。

      まとめ

      ということで、以前PA-APIv4版のカスタマイズを行っていた方でも、今回のカスタマイズを行うことで2019年12月以降もPA-APIを利用できるかと思います。

      当サイトも、この記事を書く1ヶ月前からPA-APIv5版で動作テストを行っておりましたが、特に問題なく動作はしているようです。

      ただし、サーバー環境によって動作しない場合もあるかもしれないので、詳しくは以下の注意点を参照してください。

      Amazonの商品取得APIがPA-API v4からPA-API v5に変わることによる注意点の説明です。

      それでも動作しない場合は、詳しいサポートまではできかねるので、プロに依頼いただくかプラグインの利用を検討していただけると幸いです。

      参考 Amazon.co.jp Product Advertising APIライセンス契約

      参考 Product Advertising API (PA-API) の利用ガイドライン