Amazon Marketplace SP-API の使い方:認証・各種必要な設定から実際のリクエストまで

by 杉本和也 | May 11, 2021

こんにちは。CData Software Japanリードエンジニアの杉本です。

MWS APIを使っている方であればすでにご存知かとは思いますが、最近Amazon Marketplace からとても重要なお知らせがありましたね!

今まではMWSというAPIを通じてAmazon Marketplaceの注文データやレポートが取得できたのですが、今年9月30日でこのAPIの提供を終了し、新しい仕様のAPI・SP-APIに移行するようにとの告知です。

sellercentral.amazon.co.jp

CData Software でもこのMWS APIを使って、以下のAmazon Marketplace Driverという製品を提供しており、絶賛この対応を進めています。

www.cdata.com

そしてこの対応にあたって、最新のSP-APIを調べていたのですが、今までのAmazon Marketplaceだけで完結した内容とは違い、AWSの技術コンテキストも加わって結構認証段階から難解なAPIなんですよね。

以下のように英語はもちろん、日本語でも公式の利用ガイドが提供されているのですが、

github.com

Issueを覗くとかなり困っている人たちがたくさん居ました。

github.com

そこで今回は、このSP-APIの使い方の基本、認証段階に焦点を当てて、ClientIdや AccessKeyなどの必要な情報の取得方法から実際にAPIのリクエストの達成方法まで、おそらく一番簡単なやり方を解説したいと思います。

今回の記事の注意点

今回の記事は「一番楽に」SP-APIアクセスを達成することに焦点を当てています。

そのため、公式ドキュメントで紹介されているような、IAMロールの利用やOAuth Code Grant によるAccess Token・Refresh Tokenの取得はあえて省略しています。省略したからといって、裏口的なことをしているわけではないです。(別記事として紹介予定)

ただ、おそらくこの流れを掴むことでアクセスを達成するまでに必要な要素が明らかになり、必要に応じて利用するべき要素を追加してアプリを構成していけるでしょう。

また、本文中にはわかりやすいように実際のアクセスキーIDやシークレットキーを表示しています。これはすべて削除・リセット済みなので、あくまで参考値として見てください。

APIリクエストのために必要なもの

詳しい手順を解説するまえに、このSP-APIを実行するにあたってどのような要素が必要なのかざっくりとお伝えします。

要素はざっと3種類存在します。

  1. SP-APIのアクセス権限を持つ AWS IAMユーザー(もしくはロールとそのロールへのアクセス権を持つユーザー):Access Key・Secret Key
  2. SP-APIにアクセスするためのアプリクライアント:ClientId・ClientSecret
  3. アプリクライアントから生成したToken:AccessToken・RefreshToken

「1.」をAWS IAMで作成した上で、その情報を使って、「2.」Amazon Sellar Centralでアプリクライアントを作成。

プログラムではOAuth(Authorization Codeもしくは Refresh)を通じてAccess Tokenを取得。

取得したAccessTokenを使って、APIリクエストを作成した後、AWS IAMユーザーから取得したAccess KeyとSecret Keyを使って署名(AWS Signature )、これでリクエストを実行することができます。

言葉にするとなかなか難解ですよね。それでは実際に手順を解説していきましょう。

事前準備:開発者として登録

まずはAPIを利用する前に開発者として登録しておきましょう。

MWS API時代に開発者として登録できている人は問題ありません。

以下の「アプリの開発」から開発者登録を行います。

github.com

私の環境ではすでに開発者として登録済みのため、詳細な手順はお伝えできませんが、以下のように開発者IDが取得できる状態になっていればOKです。

AWS側の準備

アプリ開発者として登録が終わったら、早速SP-APIを使うためのAWS側の準備を行っていきましょう。

まず、前提として少し整理しておくと、今回のSP-APIを使うにはAWSの「IAMユーザー(およびそれらに付帯するポリシー)」が必要になります。

そこで最も大事な要素を一つ上げると、IAMユーザーには、以下の権限を利用できる状態にする必要があります。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "execute-api:Invoke",
      "Resource": "arn:aws:execute-api:*:*:*"
    }
  ]
}

この権限はIAMユーザーに直接ポリシー(もしくはインラインポリシー)として付与してもかまいませんし、IAMロールを作ってそのロールをIAMユーザーに付与(AssumeRole)して使ってもかまいません。(勘違いしやすいのですが、ロールを作って関連付けてもIAMユーザーはそれだけではそのロールの権限を利用できません)

ドキュメントでは回りくどく書かれていますが、基本的に必要な理解はこれだけです。

あとは、このポリシーを持ったIAMユーザーもしくはIAMロールをSP-APIのアプリに紐付けることで、APIリクエストが可能な状態が整います。

今回は簡単にアクセスを達成するため、一番シンプルな方法である、IAMユーザーにインラインポリシーとして権限を付与する方法を用います。

次回あたりでIAMロールを使った方法も紹介しようと思っていますが手順が増えるだけで本質は変わりません。

なお、Amazon SP API ドキュメントではIAMユーザー・ポリシー・ロールを作成する方法で記載されています。GithubのIssueなどを見ると、強固なセキュリティを維持するためとの記載なので、実運用ではこれを尊重すると良いでしょう。

github.com

AWSアカウントの取得

最初にAWSアカウントを持っていない場合は、新しく準備しましょう。以下のURLからアカウントを取得できます。

aws.amazon.com

IAMユーザーの作成

AWSアカウントを取得したら、IAMユーザーを作成しましょう。

サービスの一覧から「IAM」に移動し

「ユーザー」→「ユーザーを追加」をクリックします。

任意のユーザー名を入力し、アクセスの種類として「プログラムによるアクセス」を選択して「次のステップ」に移動します。

アクセス許可の設定はインラインポリシーとして後で付与するので、とりあえずそのまま「次のステップ」に移動します。

任意でタグを設定します。

これでユーザー情報の設定はOKです。「このユーザーにはアクセス権限がありません」と表示されますが、そのまま「ユーザーの作成」をクリックしてOKです。

作成が成功すると、IAMユーザーのアクセスキーIDとシークレットアクセスキーが表示されます。APIアクセスの時に使用するので、控えておきましょう。

ユーザーのARNの取得

併せてユーザーのARNという識別子を作成したIAMユーザーの画面から取得します。

インラインポリシーの追加

併せてインラインポリシーを追加しましょう。「アクセス権限」タブから「インラインポリシーの追加」をクリックし

JSON形式で以下の設定を貼り付けて「ポリシーの確認」をクリックします。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "execute-api:Invoke",
      "Resource": "arn:aws:execute-api:*:*:*"
    }
  ]
}

以下のようにExecuteAPIサービスに対する書き込みが付与されていればOKです。「ポリシーの作成」をクリックしましょう。

これでAWS IAM側の準備はOKです。

Amazon Seller Central の準備

続いてAmazon Seller Central 側でAPIアクセス用のアプリクライアントを追加しましょう。

アプリクライアントの追加

Amazon Seller Central にログインし「アプリの開発」をクリックします。

「新しいアプリクライアントを追加」をクリックし

以下のようにアプリの情報を入力します。

なお今回はアプリ画面から取得できるRefresh Tokenを使用して、Access Tokenを取得するので、OAuth Code Grantは行いません。これは別途記事として書きたいと思います。

なので、OAuthログインURIとリダイレクトURIは空白でとりあえず作成します。

プロパティ名 備考
アプリ名 例)SampleSPAIApp 任意のアプリ名を入力してください。作成したアプリを外部公開する場合は表示名となりますので要注意。
APIタイプ SP API MWS APIと併用する場合は両方付与することができます。
IAM ARN arn:aws:iam::220139336512:user/Sample IAMユーザーのARNを入力します。なおロールにExecuteAPIのポリシーを付与している場合はIAMロールのARNを入力します。
ロール 利用したいAPI機能にチェックを入れます。 今回は注文データを取得しようと思っているので「在庫と注文の追跡」だけ入れればOKです。検証用であれば全部チェックを入れておいて良いでしょう。
OAuthログインURI (空) 前述の通り今回は利用しません。

IAMユーザーのARNは以下の値です。

LWA認証情報の取得(ClientId・ClientSecret)

アプリを作成したら、LWA認証情報(ClientId・ClientSecret)を取得しましょう。

作成したアプリの以下の「表示」ボタンをクリックすることで取得できます。

これもSP APIアクセスのために必要です。必要な情報が多いので、名前など混同しないようにしましょう。

リフレッシュトークンの取得

併せてリフレッシュトークンを取得しておきます。

通常はOAuth 2.0 の Authorization Codeのプロセスを通じて、Access Token・RefreshTokenを取得できますが、開発用途などとしてRefreshTokenを簡単に取得できます。

SP-API の Authorization Codeはまた別な機会に解説したいと思いますが、詳しくは以下のリファレンスを参照してみてください。

github.com

以下の「リフレッシュトークンを生成」をクリックすることで、リフレッシュトークンが表示されます。これお控えておきます。

これで必要な情報はすべて揃いました。

注文データを取得してみる

それでは早速注文データを取得してみましょう。今回は .NET Core C#でプログラムを作成して取得します。

Postmanを使った方法はこちらの記事をどうぞ。

注文データの取得APIは以下のリファレンスに記載されています。

github.com

一番面倒な認証周りはC#・Java用のSDKが公式として公開されていますのでこのプロジェクトとソリューションを使って、さくっと試してみましょう。

まず以下のClientをダウンロードしてきます。

github.com

今回はこのソリューションにそのまま新しいプロジェクトとしてコンソールアプリを追加します。

新しいプロジェクトを選択して、コンソールアプリ(.NET Core)を作成します。

プロジェクトを作成したら、「プロジェクト参照の追加」で

「Amazon.SelingPartnerAPIAA」を追加します。これで準備が整いました。

サンプルコード

それでは実際にリクエストを試してみましょう。以下がサンプルコードの全体です。

ちなみに注文データの格納クラスはJSONから生成したので、信用せず参考レベルにしてください。

using System;
using RestSharp;
using Newtonsoft.Json;
using Amazon.SellingPartnerAPIAA;

namespace AmazonClientSample
{
    class Program
    {

        static void Main(string[] args)
        {

            var ClientId = "amzn1.application-oa2-client.9f309f65ff00422882121a4f1bf2da";
            var ClientSecret = "ce69326f0bd0a5283dd171ed7d99123125a80da42d2a3d6d5a573ac6a31ace5";
            var RefreshToken = "Atzr|IwEBIC4Pw4wLrt3a0VKoxf-p8zRXlEV591xhzEOwOIMP6WyyZyEIv123eDkUx5QCtgl_eUODxjuMP7UnFtDMhQmiU6CssldMkF8JWKTmOiEJOYNAv8DiOJv6oXeTWCV4hJFyVKi3G0F4nERpmHSPBYmUIl2vUlrgOo_kO04X123121hI2EORhBW5m1XTLtPkoB-RrEnmRKxgnXno3QPTVBqFZfhL3n_flTvQregmIIVVd_gHzw4JM4M2OCnhdj5173As6Avlv7eRtREK7B32313123EpugM4J6n8a6GOSNlSVz2LjQQQFx-aYgULMc7VHDSXhKfRopTuY";
            var TokenEndpoint = "https://api.amazon.com/auth/o2/token";

            var AccessKeyId = "AKIATGQKTWNAOWJL5CPK";
            var SecretKey = "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";
            var Region = "us-west-2";
            var LiveUrlBase = "https://sellingpartnerapi-fe.amazon.com";

            // Get Access Token
            LWAAuthorizationCredentials lwaAuthorizationCredentials = new LWAAuthorizationCredentials
            {
                ClientId = ClientId,
                ClientSecret = ClientSecret,
                RefreshToken = RefreshToken,
                Endpoint = new Uri(TokenEndpoint)
            };

            var requestTokenClient = new LWAClient(lwaAuthorizationCredentials);

            var token = requestTokenClient.GetAccessToken();

            // Create Get Orders API Request
            var restClient = new RestClient(LiveUrlBase);

            // Reference : https://github.com/amzn/selling-partner-api-docs/blob/main/references/orders-api/ordersV0.md
            IRestRequest restRequest = new RestRequest("/orders/v0/orders", Method.GET);

            restRequest.AddHeader("x-amz-access-token", token);
            restRequest.AddParameter("MarketplaceIds", "A1VC38T7YXB528", ParameterType.QueryString);
            restRequest.AddParameter("CreatedAfter", "2020-01-01", ParameterType.QueryString);

            AWSAuthenticationCredentials awsAuthenticationCredentials = new AWSAuthenticationCredentials
            {
                AccessKeyId = AccessKeyId,
                SecretKey = SecretKey,
                Region = Region
            };

            // AWS Signature
            restRequest = new AWSSigV4Signer(awsAuthenticationCredentials)
                .Sign(restRequest, restClient.BaseUrl.Host);

            restRequest.AddHeader("user-agent", "My Sample App 1.0 (Language=csharp;Platform=Windows/10)");

            try
            {
                var result = restClient.Execute(restRequest);
                if (result.StatusCode == System.Net.HttpStatusCode.OK)
                {
                    var root = JsonConvert.DeserializeObject(result.Content);

                    foreach (var order in root.payload.Orders)
                    {
                        Console.WriteLine(order.AmazonOrderId);
                    }

                }
                else if (result.StatusCode == System.Net.HttpStatusCode.Forbidden)
                {
                    Console.WriteLine(result.ErrorMessage);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            Console.ReadKey();
        }

        public class Rootobject
        {
            public Payload payload { get; set; }
        }

        public class Payload
        {
            public DateTime CreatedBefore { get; set; }
            public Order[] Orders { get; set; }
        }

        public class Order
        {
            public string AmazonOrderId { get; set; }
            public DateTime PurchaseDate { get; set; }
            public DateTime LastUpdateDate { get; set; }
            public string OrderStatus { get; set; }
            public string FulfillmentChannel { get; set; }
            public string SalesChannel { get; set; }
            public string ShipServiceLevel { get; set; }
            public Ordertotal OrderTotal { get; set; }
            public int NumberOfItemsShipped { get; set; }
            public int NumberOfItemsUnshipped { get; set; }
            public string[] PaymentMethodDetails { get; set; }
            public string IsReplacementOrder { get; set; }
            public string MarketplaceId { get; set; }
            public string ShipmentServiceLevelCategory { get; set; }
            public string OrderType { get; set; }
            public DateTime EarliestShipDate { get; set; }
            public DateTime LatestShipDate { get; set; }
            public bool IsBusinessOrder { get; set; }
            public bool IsSoldByAB { get; set; }
            public bool IsPrime { get; set; }
            public bool IsGlobalExpressEnabled { get; set; }
            public bool IsPremiumOrder { get; set; }
            public bool IsISPU { get; set; }
            public string PaymentMethod { get; set; }
            public DateTime EarliestDeliveryDate { get; set; }
            public DateTime LatestDeliveryDate { get; set; }
        }

        public class Ordertotal
        {
            public string CurrencyCode { get; set; }
            public string Amount { get; set; }
        }

    }
}

AcessTokenの取得

Access Tokenの取得には「LWAClient」クラスを使って行います。

LWAAuthorizationCredentialsには、Seller Centralから取得したClientId・ClientSecret・RefreshToken、そしてAccess Tokenを取得するためのエンドポイントである「https://api.amazon.com/auth/o2/token」を渡します。

あとは、GetAccessTokenメソッドを実行することでデータが取得できます。

            // Get Access Token
            LWAAuthorizationCredentials lwaAuthorizationCredentials = new LWAAuthorizationCredentials
            {
                ClientId = ClientId,
                ClientSecret = ClientSecret,
                RefreshToken = RefreshToken,
                Endpoint = new Uri(TokenEndpoint)
            };

            var requestTokenClient = new LWAClient(lwaAuthorizationCredentials);

            var token = requestTokenClient.GetAccessToken();

APIリクエストの作成とAWS Signatureの付与

続いてAPIリクエストを構成し、AWS Signatureを付与します。

詳しい注文データ取得APIの仕様は以下のURLからどうぞ。

github.com

注文データの取得リクエストは以下のようになっていて、/orders/v0/ordersに対して、MarketplaceIdsとCreatedAfterを指定して取得します。

その際に、取得したAccessTokenリクエスト日付、AWS Signatureを付与してリクエストを行います。

GET /orders/v0/orders?MarketplaceIds=A1VC38T7YXB528&CreatedAfter=2020-01-01 HTTP/1.1
Host: sellingpartnerapi-fe.amazon.com
x-amz-access-token: XXXXXXX
x-amz-date: 20210510T134905Z
user-agent: My Selling Tool/2.0 (Language=Java/1.8.0.221;Platform=Windows/10)
Authorization: AWS4-HMAC-SHA256 XXXXXX

リクエストに対するAWS Signature の付与は「AWSSigV4Signer」クラスを用いて行います。この際に、AWS IAMユーザーから取得したAccessKeyとSecretKey、それとRegionを付与します。

Regionは日本だと「us-west-2」、ちなみにリクエスト先のAPIエンドポイントは「https://sellingpartnerapi-fe.amazon.com」になります。

            var requestTokenClient = new LWAClient(lwaAuthorizationCredentials);

            var token = requestTokenClient.GetAccessToken();

            // Create Get Orders API Request
            var restClient = new RestClient(LiveUrlBase);

            // Reference : https://github.com/amzn/selling-partner-api-docs/blob/main/references/orders-api/ordersV0.md
            IRestRequest restRequest = new RestRequest("/orders/v0/orders", Method.GET);

            restRequest.AddHeader("x-amz-access-token", token);
            restRequest.AddParameter("MarketplaceIds", "A1VC38T7YXB528", ParameterType.QueryString);
            restRequest.AddParameter("CreatedAfter", "2020-01-01", ParameterType.QueryString);

            AWSAuthenticationCredentials awsAuthenticationCredentials = new AWSAuthenticationCredentials
            {
                AccessKeyId = AccessKeyId,
                SecretKey = SecretKey,
                Region = Region
            };

            // AWS Signature
            restRequest = new AWSSigV4Signer(awsAuthenticationCredentials)
                .Sign(restRequest, restClient.BaseUrl.Host);

            restRequest.AddHeader("user-agent", "My Sample App 1.0 (Language=csharp;Platform=Windows/10)");

APIのエンドポイントやリージョンについて、詳しくは以下のURLを参照してみてください。

github.com

あと、リクエストを実行する前に、user-agentのヘッダーを付与してどのようなアプリからリクエストを行っているかを指定しておきましょう。

これでAPIリクエストを行うことで、注文データが取得できます。

以下のようにOrderId一覧が表示されればOKです。

おわりに

なかなか難解でしたね。

まだ解説していないポイントがいくつかあるので、いずれ記事にしたいと思います。

www.cdata.com