目次
はじめに
Amazon に加えて楽天・自社 EC を並走させる EC 事業者が直面する最大の課題のひとつが 在庫の過剰販売(Overselling)。「Amazon で売れたのに楽天の在庫を更新するのが遅れて、楽天でも同じ商品が売れてしまった」というケースは、手動運用では防ぎようがない。
ここでは SP-API Feeds API を中心に、マルチモール間で在庫を一元管理するアーキテクチャを解説する。
設計の大前提: 在庫の「単一の真実の源」を決める
まず設計の前に決めるべきことがある。在庫数のマスターをどこに置くか。
- WMS(倉庫管理システム)
- 自社 DB
- Amazon の FBA 在庫
弊社案件では自社 DB をマスターとし、Amazon・楽天への在庫通知を Push 型で行うパターンが最も管理しやすかった。FBA 在庫をマスターにすると、FBA の在庫確認 API を都度叩く必要があり、レートリミットとの戦いになる。
アーキテクチャ全体像
注文確定イベント(各モール)
↓
SQS: 在庫差し引きキュー(FIFO)
↓
在庫更新 Lambda
├── 自社 DB: stock_quantity を atomic decrement
├── Amazon: Feeds API で在庫通知
└── 楽天: RMS API で在庫数更新
SQS FIFO を挟むことで、複数モールから同時に注文が入っても在庫差し引きが順序保証される。
Amazon への在庫通知: Feeds API
Amazon への在庫更新は POST_INVENTORY_AVAILABILITY_DATA フィードを使う。REST 的な直接更新 API は存在しないため、XML フィードを Submit してジョブ完了をポーリングする非同期フローになる。
// フィード XML の生成
function buildInventoryFeed(sku: string, quantity: number): string {
return `<?xml version="1.0" encoding="UTF-8"?>
<AmazonEnvelope>
<Header>
<DocumentVersion>1.01</DocumentVersion>
<MerchantIdentifier>${MERCHANT_ID}</MerchantIdentifier>
</Header>
<MessageType>Inventory</MessageType>
<Message>
<MessageID>1</MessageID>
<OperationType>Update</OperationType>
<Inventory>
<SKU>${sku}</SKU>
<Quantity>${quantity}</Quantity>
</Inventory>
</Message>
</AmazonEnvelope>`
}
// フィードの Submit
const createFeedResponse = await feedsApi.createFeed({
feedType: "POST_INVENTORY_AVAILABILITY_DATA",
marketplaceIds: [MARKETPLACE_JP],
inputFeedDocumentId: feedDocumentId,
})ポーリングとタイムアウト設計
Feeds API の処理は数秒〜数分かかる。Lambda の同期待ちは現実的でないため、Step Functions で非同期ポーリングするか、SQS の DelaySeconds で一定時間後にステータス確認ジョブを投入するとよい。
// 結果確認を 30 秒後に再スケジュール
await sqs.sendMessage({
QueueUrl: FEED_STATUS_CHECK_QUEUE,
MessageBody: JSON.stringify({ feedId }),
DelaySeconds: 30,
})過剰販売防止の安全弁: 安全在庫バッファ
在庫数が 1 個以下になったら Amazon 在庫を 0 にセットする「安全弁」を入れておく。同時注文で在庫を使い切った後、同期が追いつくまでの数秒〜数十秒の間に別の注文が入るリスクを防ぐ。
const safeQuantity = Math.max(0, currentStock - SAFETY_BUFFER)
await notifyAmazonInventory(sku, safeQuantity)SAFETY_BUFFER は SKU の 1 日あたり注文速度(velocity)から動的に計算するのが理想。売れ筋は 2〜3、遅いものは 0 でいい。
リカバリー: 在庫差し引きに失敗したケース
SQS の可視性タイムアウトを超えると、メッセージが再処理される。このとき在庫の二重差し引きを防ぐために、注文 ID に対して「差し引き済みフラグ」を DynamoDB で管理する。
// DynamoDB で差し引きの冪等チェック
const key = `inv:${orderId}:${sku}`
const alreadyProcessed = await ddb.get({ TableName: "idempotency", Key: { pk: key } })
if (alreadyProcessed.Item) return // 既処理
await ddb.put({
TableName: "idempotency",
Item: { pk: key, ttl: Math.floor(Date.now() / 1000) + 86400 },
})
// 実際の在庫差し引き処理
await decrementStock(sku, quantity)まとめ
| 課題 | 解決策 |
|---|---|
| 同時注文による過剰販売 | SQS FIFO で在庫差し引きを直列化 |
| Amazon 在庫更新の遅延 | Feeds API + 非同期ポーリング |
| Lambda リトライ時の二重差し引き | DynamoDB 冪等チェック |
| 同期遅延中の過剰販売 | 安全在庫バッファ |




