4.

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
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 ステップです:

 'ルアー',
    '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 で時刻同期

レスポンス例

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

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 のずれを順に確認。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. アカウントの作成とキーの生成
  2. PA-APIの使い方 リクエストの構造とパラメータの指定
  3. リクエストにスペースを含める方法
  4. 署名認証の方法(PHPサンプル ItemSearch)
  5. エラー一覧
  6. レスポンスの構造(ItemSearch)