1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Amazon SP-API Feeds APIで出品者出荷の在庫を一括更新(Typescript)

Last updated at Posted at 2024-09-30

要点

  • フィードのアップロードステップは下記
    1. createFeedDocument でフィードドキュメントを作成(S3の署名付きURLが発行される)
    2. S3にフィードの内容をJSONでアップロード
    3. createFeed でフィードを生成
  • 2025年3月以降、 JSON_LISTINGS_FEED のフィードタイプでアップロードする必要がある(本記事で対応)
    • XMLのアップロードは非推奨
  • フィードのアップロードステータスはセラーセントラル上で確認できる
  • 在庫と価格は同時に変更可能
  • 最大1万件まで同時に更新可能
  • 処理はかなり高速(3000件でも5分以内)

事前準備

セラーセントラルでアプリの作成

Amazonの新しいAPI、SP-APIをとにかく動かしてみる
を参考に、次の3つの情報を用意し環境変数に設定

  • リフレッシュトークン (YOUR_REFRESH_TOKEN)
  • クライアントID (SELLING_PARTNER_APP_CLIENT_ID)
  • クライアント機密情報 (SELLING_PARTNER_APP_CLIENT_SECRET)

なお、2024年9月現在はIAMユーザーは不要

セラーIDの確認

AmazonセラーIDを取得し、環境変数に設定(AMAZON_SELLER_ID)

セラーセントラルのユーザー作成

開発時にセラーセントラルの中を確認できると便利なので、作成しておく
下記の権限をつけておく

  1. 在庫の管理/商品の追加
  2. トランザクション (表示と編集)

コード

下記の createFeed() を使用

createFeed.ts
import axios from 'axios';
import { config } from 'dotenv';
import { SellingPartner } from 'amazon-sp-api';

const JP_MARKET_PLACE_ID = "A1VC38T7YXB528"; //日本のマーケットプレイスID(固定値)

/**
 * 型定義 ============================================
 */

// 商品情報
type Item = {
    readonly sku: string;
    readonly quantity: number;
    readonly leadTime: number;
};

// createFeedDocumentのレスポンス
type ICreateFeedDocumentResBody = {
    readonly feedDocumentId: string;
    readonly url: string;
};

// 以下、Feeds JSONのフォーマット
type IFeedJsonMessageFulfillmentPatchValue = {
    readonly fulfillment_channel_code: "DEFAULT";
    readonly quantity: number;
    readonly lead_time_to_ship_max_days: number;
};

type IFeedJsonMessageFulfillmentPatch = {
    readonly op: "replace";
    readonly path: "/attributes/fulfillment_availability";
    readonly value: IFeedJsonMessageFulfillmentPatchValue[];
};

type IFeedJsonMessage = {
    readonly messageId: number;
    readonly sku: string;
    readonly operationType: "PATCH";
    readonly productType: "PRODUCT";
    readonly patches: IFeedJsonMessageFulfillmentPatch[];
};

type IFeedJson = {
    readonly header: {
        readonly sellerId: string;
        readonly version: "2.0";
        readonly issueLocale: "en_US";
    };
    readonly messages: IFeedJsonMessage[];
};

/**
 * ==================================================================
 */

config(); // 環境変数を読み込む

// Selling Partner APIクライアントの初期化
const spApi = new SellingPartner({
    region: 'fe', // 日本ならfe
    refresh_token: process.env.YOUR_REFRESH_TOKEN,
    credentials: {
        SELLING_PARTNER_APP_CLIENT_ID: process.env.SELLING_PARTNER_APP_CLIENT_ID,
        SELLING_PARTNER_APP_CLIENT_SECRET: process.env.SELLING_PARTNER_APP_CLIENT_SECRET,
    },
    options: {
        auto_request_tokens: true,
    }
});

/**
 * フィードJSONを生成する
 */
const generateFeedJson = async (items: Item[]): Promise<IFeedJson> => {
    const feedJson: IFeedJson = {
        "header": {
            "sellerId": process.env.AMAZON_SELLER_ID,
            "version": "2.0",
            "issueLocale": "en_US"
        },
        "messages": []
    };

    let index = 1;
    for (let item of items) {
        const message = {
            messageId: index,
            sku: item.sku,
            operationType: "PATCH",
            productType: "PRODUCT",
            patches: [{
                "op": "replace",
                "path": "/attributes/fulfillment_availability",
                "value": [
                    {
                        "fulfillment_channel_code": "DEFAULT",
                        "quantity": item.quantity, // 在庫数
                        "lead_time_to_ship_max_days": item.leadTime // 在庫を更新する時はリードタイムも同時に更新する必要がある
                    }
                ]
            }],
        };

        feedJson.messages.push(message);
        index++;
    }

    return feedJson;
};

/**
 * S3に フィードをアップロードする
 */
const uploadToS3 = async (url: string, feedJson: IFeedJson): Promise<void> => {
    const response = await axios.put(url, feedJson, {
        headers: {
            'Content-Type': 'application/json; charset=utf-8'
        }
    });

    if (response.status !== 200) {
        throw new Error(`フィードアップロードに失敗 code: ${response.status} message: ${response.statusText}`)
    }
}

/**
 * SP-APIのCreateFeedを呼び出す
 * @param feedDocumentId 
 */
const callCreateFeed = async (feedDocumentId: string) => {
    return spApi.callAPI({
        operation: 'createFeed',
        endpoint: 'feeds',
        body: {
            feedType: 'JSON_LISTINGS_FEED',
            marketplaceIds: [JP_MARKET_PLACE_ID],
            inputFeedDocumentId: feedDocumentId
        }
    });
}

/**
* 商品情報を元にフィードを作成し、Amazonにアップロードする
*/
export const createFeed = async (items: Item[]): Promise<IFeedJson | null> => {
    // フィードの中身を生成
    const feedJson = await generateFeedJson(items);
    if (feedJson.messages.length === 0) {
        return null
    }

    // フィードドキュメントの作成
    //@ts-ignore
    const feedDocument: ICreateFeedDocumentResBody = await spApi.callAPI({
        operation: 'createFeedDocument',
        endpoint: 'feeds',
        body: {
            contentType: 'application/json; charset=utf-8'
        }
    });

    // フィードデータを S3にアップロード
    await uploadToS3(feedDocument.url, feedJson);

    // フィードドキュメントを元に、フィードの作成
    const feed = await callCreateFeed(feedDocument.feedDocumentId);

    console.log('Feed created:', feed); // Feed created: { feedId: '618797019996' }

    return feed.feedId;
}

参考ドキュメント

SP-APIを利用するうえで非常に参考になる記事

Amazon公式のFeeds API ユースケースガイド
(非推奨となったXMLフィードのアップについて触れていたりと、情報が古いところも多い)

備考

  • 複数のフィードが同じfeedIDであることも多いので注意
    • 約12時間に1ずつ増加する
      image.png
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?