この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:8
ページ更新者:atom
更新日時:2026-06-11 07:12:00

タイトル: 署名認証の方法(PHPサンプル ItemSearch)
SEOタイトル: Amazon PA-API 5.0 の AWS V4 署名を PHP で生成(SDK 利用 / 自前 hash_hmac 両方)

この記事の要点
  • Amazon Product Advertising API (PA-API) は 5.0 で AWS V4 署名に統一(旧 4.0 形式 ItemSearch は 2020 年廃止)
  • 推奨は 公式 amazon-paapi5-php-sdk 利用、自前実装は学習用
  • 必要なクレデンシャル: Access Key / Secret Key / Partner Tag(PA-API 専用、IAM とは別)
  • 署名の流れ: ① Canonical Request 生成 → ② String to Sign → ③ Signing Key 派生 → ④ hash_hmac("sha256", ...)
  • リクエストヘッダに Authorization: AWS4-HMAC-SHA256 Credential=...,SignedHeaders=...,Signature=...
  • POST /paapi5/searchitems エンドポイント、JSON body で SearchItems オペレーション指定

PA-API 5.0 とは

Amazon Product Advertising API は商品検索・取得用 API。2020 年 3 月 9 日に旧 4.0(XML、ItemSearch オペレーション)の新規受付終了、その後完全停止しました。現行は PA-API 5.0(JSON、AWS V4 署名)です。本記事は 5.0 を前提に説明します。

必要なクレデンシャル

項目取得元
Access KeyAmazon アソシエイト管理画面AKIAxxxxxxxxxxxxxxxx
Secret Key同上(発行時のみ表示)xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Partner Tagアソシエイト IDmyassoc-22
Marketplace対象国日本: www.amazon.co.jp
HostAPI ホスト名日本: webservices.amazon.co.jp
RegionAWS 風リージョン日本: us-west-2 (マーケット別に決まる)

方法1: 公式 SDK を使う(推奨)

# Composer で導入
composer require amazon-paapi5/sdk-php
<?php
require __DIR__ . '/vendor/autoload.php';

use Amazon\ProductAdvertisingAPI\v1\ApiException;
use Amazon\ProductAdvertisingAPI\v1\Configuration;
use Amazon\ProductAdvertisingAPI\v1\api\DefaultApi;
use Amazon\ProductAdvertisingAPI\v1\com\amazon\paapi5\v1\SearchItemsRequest;
use Amazon\ProductAdvertisingAPI\v1\com\amazon\paapi5\v1\SearchItemsResource;
use Amazon\ProductAdvertisingAPI\v1\com\amazon\paapi5\v1\PartnerType;

$config = new Configuration();
$config->setAccessKey('AKIAxxxxxxxxxxxxxxxx');
$config->setSecretKey('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
$config->setHost('webservices.amazon.co.jp');
$config->setRegion('us-west-2');

$apiInstance = new DefaultApi(new GuzzleHttp\Client(), $config);

$req = new SearchItemsRequest();
$req->setPartnerTag('myassoc-22');
$req->setPartnerType(PartnerType::ASSOCIATES);
$req->setKeywords('ルアー');
$req->setSearchIndex('All');
$req->setItemCount(10);
$req->setResources([
    SearchItemsResource::ITEM_INFOTITLE,
    SearchItemsResource::OFFERSLISTINGSPRICE,
    SearchItemsResource::IMAGESPRIMARYMEDIUM,
]);

try {
    $resp = $apiInstance->searchItems($req);
    foreach ($resp->getSearchResult()->getItems() as $item) {
        echo $item->getASIN() . "\t"
           . $item->getItemInfo()->getTitle()->getDisplayValue() . "\n";
    }
} catch (ApiException $e) {
    echo "Error: " . $e->getMessage() . "\n";
    echo "Response: " . $e->getResponseBody() . "\n";
}

方法2: 自前で AWS V4 署名を生成

学習や軽量化のため自前実装する場合。AWS V4 署名の手順は次の 4 ステップです:

<?php
// ===== 設定 =====
$accessKey  = 'AKIAxxxxxxxxxxxxxxxx';
$secretKey  = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$partnerTag = 'myassoc-22';
$host       = 'webservices.amazon.co.jp';
$region     = 'us-west-2';
$service    = 'ProductAdvertisingAPI';
$path       = '/paapi5/searchitems';
$target     = 'com.amazon.paapi5.v1.ProductAdvertisingAPIv1.SearchItems';

// ===== リクエスト body =====
$payload = json_encode([
    'Keywords'    => 'ルアー',
    'PartnerTag'  => $partnerTag,
    'PartnerType' => 'Associates',
    'Marketplace' => 'www.amazon.co.jp',
    'Resources'   => [
        'ItemInfo.Title',
        'Offers.Listings.Price',
        'Images.Primary.Medium',
    ],
]);

// ===== タイムスタンプ =====
$amzDate   = gmdate('Ymd\THis\Z');     // 20260517T120000Z
$dateStamp = gmdate('Ymd');            // 20260517

// ===== Canonical Request =====
$canonicalUri = $path;
$canonicalQueryString = '';

$canonicalHeaders =
    "content-encoding:amz-1.0\n" .
    "content-type:application/json; charset=utf-8\n" .
    "host:$host\n" .
    "x-amz-date:$amzDate\n" .
    "x-amz-target:$target\n";

$signedHeaders = 'content-encoding;content-type;host;x-amz-date;x-amz-target';

$payloadHash = hash('sha256', $payload);

$canonicalRequest =
    "POST\n" .
    "$canonicalUri\n" .
    "$canonicalQueryString\n" .
    "$canonicalHeaders\n" .
    "$signedHeaders\n" .
    "$payloadHash";

// ===== String to Sign =====
$algorithm     = 'AWS4-HMAC-SHA256';
$credentialScope = "$dateStamp/$region/$service/aws4_request";
$stringToSign  =
    "$algorithm\n" .
    "$amzDate\n" .
    "$credentialScope\n" .
    hash('sha256', $canonicalRequest);

// ===== Signing Key 派生 =====
$kDate    = hash_hmac('sha256', $dateStamp, 'AWS4' . $secretKey, true);
$kRegion  = hash_hmac('sha256', $region,    $kDate,              true);
$kService = hash_hmac('sha256', $service,   $kRegion,            true);
$kSigning = hash_hmac('sha256', 'aws4_request', $kService,       true);

// ===== Signature =====
$signature = hash_hmac('sha256', $stringToSign, $kSigning);

// ===== Authorization ヘッダ =====
$authHeader =
    "$algorithm Credential=$accessKey/$credentialScope, " .
    "SignedHeaders=$signedHeaders, " .
    "Signature=$signature";

// ===== HTTP リクエスト =====
$ch = curl_init("https://$host$path");
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $payload,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        "Authorization: $authHeader",
        "Content-Encoding: amz-1.0",
        "Content-Type: application/json; charset=utf-8",
        "Host: $host",
        "X-Amz-Date: $amzDate",
        "X-Amz-Target: $target",
    ],
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "HTTP $code\n";
$data = json_decode($body, true);
foreach (($data['SearchResult']['Items'] ?? []) as $it) {
    echo $it['ASIN'] . "\t" . ($it['ItemInfo']['Title']['DisplayValue'] ?? '') . "\n";
}

署名計算の落とし穴

ハマりどころ対処
ヘッダ名は小文字でソート済が必須host, x-amz-date 等。アルファベット順
SignedHeaders と Canonical Headers の対象を一致セミコロン区切り、同じ並び
payload hash は正確な送信 bodyのハッシュjson_encode の改行・空白に注意
タイムスタンプは UTCgmdate() 使用、ローカルタイムは NG
5 分以上ずれると InvalidSignatureNTP で時刻同期

レスポンス例

{
  &quot;SearchResult&quot;: {
    &quot;Items&quot;: [
      {
        &quot;ASIN&quot;: &quot;B08XXXXX&quot;,
        &quot;DetailPageURL&quot;: &quot;https://www.amazon.co.jp/dp/B08XXXXX?tag=myassoc-22&quot;,
        &quot;ItemInfo&quot;: {
          &quot;Title&quot;: { &quot;DisplayValue&quot;: &quot;...ルアー...&quot;, &quot;Label&quot;: &quot;Title&quot;, &quot;Locale&quot;: &quot;ja_JP&quot; }
        },
        &quot;Offers&quot;: {
          &quot;Listings&quot;: [
            { &quot;Price&quot;: { &quot;Amount&quot;: 1980, &quot;Currency&quot;: &quot;JPY&quot;, &quot;DisplayAmount&quot;: &quot;¥1,980&quot; } }
          ]
        }
      }
    ]
  }
}

FAQ

Q: 旧 ItemSearch (PA-API 4.0) サンプルはまだ使える?
A: 使えません。エンドポイントが廃止済。必ず 5.0 (/paapi5/searchitems) を使ってください。

Q: TooManyRequests エラー
A: 売上実績がないアカウントは 1 req/sec, 1 day 8640 req 制限。指数バックオフで再試行 + 売上連動の枠拡大。

Q: InvalidSignature が出る
A: Canonical Request の改行コード(LF 必須)、ヘッダの並びと小文字化、payload hash のずれを順に確認。