Amazon、SP-APIの認証について、難しいと言ってきましたが自前で実装は可能です。
今回PHPでアクセストークンを利用し認証ヘッダを生成からの注文取得を作成したので、参考にしていただければ幸いです。 PHP5系でも動作します。
ライブラリを利用せずに、Amazon、SP-APIを利用するにはスクラッチで認証処理を実装する必要があります。
Amazonのマニュアルに認証の仕様の説明がありますが、同じように作ったのに「認証されないなぜだ!」ということがやっぱりあります。アマゾンあるあるの情報が足りていないんですが、たとえば今回の認証の部分で嵌りやすいのが認証用のパラメータを合体させてヘッダの認証パラメータに変換してあげる部分が辞書順じゃないとだめ、とかあります。どこにこの説明載ってんのよこれみたいなのが時々あります。こういうのが経験値と勘と運だけで解決するしかないというところがAmazonのAPIを使う時の難しさ、自分が読めてないだけかもだですが(泣
1.前提条件
この記事を進めるうえでの前提条件としてはAmazon、SP-APIの基礎的な知識があること、PHPの知識があることが前提です。
2.PHPでアクセストークンから認証ヘッダ情報を生成し注文情報を取得してみる
今回もソースコード載せますのでコピーして、各値をご自身の環境の物に変更し、さくっとお試しください。 値が分からないという方は、以前書いた記事「Amazonの新しいAPI、SP-APIをとにかく動かしてみる」をご確認ください。
<?php
class Signature
{
public static function calculateSignatureForService(
$host,
$method,
$uri,
$queryString,
$data,
$service,
$accessKey,
$secretKey,
$region,
$accessToken,
$securityToken,
$userAgent,
$amzdate
) {
$terminationString = 'aws4_request';
$algorithm = 'AWS4-HMAC-SHA256';
$date = substr($amzdate, 0, 8);
if (is_array($data)) {
$param = json_encode($data);
if ('[]' == $param) {
$requestPayload = '';
} else {
$requestPayload = $param;
}
} else {
$requestPayload = $data;
}
$hashedPayload = hash('sha256', $requestPayload);
$canonicalHeaders = [
'host' => $host,
'user-agent' => $userAgent,
];
if (!is_null($accessToken)) {
$canonicalHeaders['x-amz-access-token'] = $accessToken;
}
$canonicalHeaders['x-amz-date'] = $amzdate;
if (!is_null($securityToken)) {
$canonicalHeaders['x-amz-security-token'] = $securityToken;
}
$canonicalHeadersStr = '';
foreach ($canonicalHeaders as $h => $v) {
$canonicalHeadersStr .= $h.':'.$v."\n";
}
$signedHeadersStr = join(';', array_keys($canonicalHeaders));
$credentialScope = $date.'/'.$region.'/'.$service.'/'.$terminationString;
// パラメータは辞書順に並べ替える必要がある
if (!empty($queryString)) {
$queryArray = explode("&", $queryString);
sort($queryArray);
$queryString = implode("&", $queryArray);
}
$canonicalRequest = $method."\n".$uri."\n".$queryString."\n".$canonicalHeadersStr."\n".$signedHeadersStr."\n".$hashedPayload;
$stringToSign = $algorithm."\n".$amzdate."\n".$credentialScope."\n".hash('sha256', $canonicalRequest);
$kSecret = 'AWS4'.$secretKey;
$kDate = hash_hmac('sha256', $date, $kSecret, true);
$kRegion = hash_hmac('sha256', $region, $kDate, true);
$kService = hash_hmac('sha256', $service, $kRegion, true);
$kSigning = hash_hmac('sha256', $terminationString, $kService, true);
$signature = trim(hash_hmac('sha256', $stringToSign, $kSigning));
$authorizationHeader = $algorithm." Credential={$accessKey}/{$credentialScope}, SignedHeaders={$signedHeadersStr}, Signature={$signature}";
return $authorizationHeader;
}
}
$host = 'sellingpartnerapi-fe.amazon.com'; // 日本だとこの値
$service = 'execute-api';
$region = 'us-west-2'; // 日本だとこの値
$accessKey = "AKIA6***************"; // アクセスキーID: Amazonの新しいAPI、SP-APIをとにかく動かしてみる 1-6. アクセスキーIDの値
$secretKey = 'Zuh069DUjSU/****************************'; // シークレットアクセスキー: Amazonの新しいAPI、SP-APIをとにかく動かしてみる 1-6. シークレットアクセスキーの値
$accessToken = 'Atza|IwEBILzfIgHC3WPLptj_lyiwv-uPKg6xKPTPhwqqKZwjsK0GnlHR9zRnI0FfnESDZKDe4S************************************************************************************************************************************************************************************************************************************************************************************************************'; // アクセストークン: Amazonの新しいAPI、SP-APIをとにかく動かしてみる 4-7. のアクセストークンの値
$marketplaceId = 'A1VC38T7YXB528'; // 日本だとこの値
$requestUrl = 'https://sellingpartnerapi-fe.amazon.com/orders/v0/orders?MarketplaceIds=' . $marketplaceId . '&CreatedAfter=2023-01-01';
$httpRequestMethod = 'GET';
$canonicalURI = '/orders/v0/orders';
$canonicalQueryString = 'MarketplaceIds=' + $marketplaceId + '&CreatedAfter=2023-01-01'; // パラメータはSiguniture生成時に辞書順に変更する必要があるので注意
$userAgent = 'cs-php-sp-api-client/2.1';
$currentDateTime = new DateTime('UTC');
$amzdate = $currentDateTime->format('Ymd\THis\Z');
$signature = new Signature();
$authorizationHeader = $signature->calculateSignatureForService($host, $httpRequestMethod, $canonicalURI, $canonicalQueryString, '', $service, $accessKey, $secretKey, $region, $accessToken, null, $userAgent, $amzdate);
// Request headers
$headers = array();
$headers[] = 'Accept: application/json';
$headers[] = 'Content-Type: application/json';
$headers[] = 'host: ' . $host;
$headers[] = 'user-agent: ' . $userAgent;
$headers[] = 'x-amz-access-token: ' . $accessToken;
$headers[] = 'x-amz-date: ' . $amzdate;
$headers[] = 'Authorization:'. $authorizationHeader;
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $requestUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_POST => false,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $httpRequestMethod,
CURLOPT_VERBOSE => 0,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_HEADER => false,
CURLINFO_HEADER_OUT=>true,
CURLOPT_HTTPHEADER => $headers,
));
$response = curl_exec($curl);
var_dump($response);
$err = curl_error($curl);
$responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($err) {
echo "<h5>Error:" . $responseCode . "</h5>";
echo "<pre>";
echo $err;
echo "</pre>";
} else {
echo "<h5>Response:" . $responseCode . "</h5>";
echo "<pre>";
echo $response;
echo "</pre>";
}