CONTENT
ここから
この章でわかること
この章では、ECサイトにおける在庫管理を整理します。
第10章では、Square Orders APIと自前DBの注文データをつなげました。
注文が作れるようになると、次に必要になるのが在庫管理です。
在庫管理は、ECの中でもトラブルが起きやすい場所です。
在庫があると思って販売した
↓
実は在庫がなかった
在庫が1個しかない
↓
2人が同時に購入してしまった
決済前に在庫を減らした
↓
ユーザーが支払わずに離脱した
実店舗で売れた
↓
ECの在庫が更新されていなかった
こうした問題を防ぐために、この章では在庫を次の3つに分けて考えます。
表示
↓
ユーザーに在庫状態を見せる
確保
↓
購入手続き中の商品を一時的に押さえる
減算
↓
決済完了後に正式に在庫を減らす
攻略本でいうと、在庫管理は「アイテム数を正しく守るステージ」です。
数を間違えると、売れない、届けられない、信頼を失うという問題につながります。
11.1 ECにおける在庫管理の難しさ
在庫管理が難しい理由は、在庫がいろいろな場所で変わるからです。
ECサイトだけで販売している場合は、比較的シンプルです。
しかし、実店舗、催事、電話注文、LINE注文などがあると、在庫は複数の場所で動きます。
ECサイトで売れる
実店舗で売れる
催事で売れる
電話で取り置きされる
LINEで個別注文が入る
返品される
破損する
管理者が手動で修正する
在庫管理で起きやすいトラブルは、次の通りです。
売り切れなのに購入できてしまう
在庫があるのに売り切れ表示になる
同じ商品が複数人に売れてしまう
決済キャンセル後に在庫が戻らない
催事で売れた分がECに反映されない
在庫数を手動修正した理由が残らない
在庫管理では、まず「在庫の正本」を決める必要があります。
在庫の正本とは、どこにある在庫数を正しいものとして扱うか、という意味です。
Squareを正本にする
↓
Square POSや実店舗販売と連携しやすい
自前DBを正本にする
↓
Next.jsの独自ECに合わせて柔軟に管理しやすい
Square Inventory APIは、Square販売者の商品在庫数や在庫変更をプログラムから管理するAPIとして提供されています。Square側の商品・店舗・POSと在庫をつなげたい場合は重要な選択肢になります。 (Square)
攻略ポイント
在庫管理は、最初から完璧を目指さなくて大丈夫です。
段階的に作ります。
レベル1
売り切れ表示だけ
レベル2
在庫状態を表示する
レベル3
決済完了後に在庫を減らす
レベル4
購入手続き中に在庫を確保する
レベル5
実店舗・催事・ECの在庫を連携する
最初のECでは、事業に必要なレベルを見極めることが大切です。
11.2 在庫を表示するだけの場合
最初に作りやすいのは、在庫を表示するだけの仕組みです。
この段階では、購入されたときに在庫を自動で減らす処理はまだ入れません。
商品データに在庫状態を持たせます。
export type ProductStockStatus =
| 'in_stock'
| 'low_stock'
| 'out_of_stock'
| 'preorder';
それぞれの意味は次の通りです。
| ステータス | 表示例 | 意味 |
|---|---|---|
in_stock | 在庫あり | 通常販売できる |
low_stock | 残りわずか | 在庫が少ない |
out_of_stock | 売り切れ | 購入できない |
preorder | 予約受付中 | 予約販売として受け付ける |
商品型に追加します。
export type Product = {
id: string;
slug: string;
name: string;
shortDescription: string;
description: string;
price: number;
currency: 'JPY';
imageUrl: string;
isAvailable: boolean;
stockStatus: ProductStockStatus;
stockLabel: string;
shippingLabel: string;
};
商品詳細ページでは、stockLabelを表示します。
<p className="text-sm font-semibold text-gray-900">
{product.stockLabel}
</p>
在庫数をそのまま出すかどうかは、事業によって判断します。
在庫数を出す
↓
残り数がわかり、購入を後押ししやすい
在庫数を出さない
↓
在庫ズレや問い合わせを減らしやすい
小規模ECや少量販売では、具体的な在庫数よりも状態表示のほうが扱いやすい場合があります。
在庫あり
残りわずか
売り切れ
予約受付中
お問い合わせください
攻略ポイント
最初は、在庫数ではなく在庫状態を表示するだけでも十分です。
在庫数
↓
正確な管理が必要
在庫状態
↓
運用しやすい
最初の目的は、買えるか、買えないかをユーザーにわかりやすく伝えることです。
11.3 購入時に在庫を減らす場合
購入時に在庫を減らす方法があります。
ここでいう購入時とは、ユーザーが購入手続きへ進んだタイミングです。
カート確認
↓
購入手続きへ進む
↓
在庫を減らす
↓
Square決済ページへ進む
この方法のメリットは、在庫を先に押さえられることです。
在庫1個の商品や限定商品では、同時購入による売り越しを防ぎやすくなります。
メリット
在庫を先に押さえられる
同時購入の競合を減らせる
少量在庫の商品に向いている
限定商品に向いている
デメリット
決済キャンセル時に在庫を戻す必要がある
途中離脱で在庫が減ったままになる可能性がある
在庫確保の期限管理が必要
実装が複雑になる
たとえば、在庫1個の商品を購入手続き開始時に減らしたとします。
在庫1個
↓
購入手続き開始で0個にする
↓
ユーザーが決済せず離脱
↓
在庫を戻さないと販売できない
この方式では、在庫を戻す処理が必須になります。
在庫確保の期限
購入時に在庫を減らす場合は、期限を決めます。
15分以内に決済完了
↓
注文確定
15分以内に決済されない
↓
在庫を戻す
この期限切れ処理を作るには、定期的な確認や管理画面での復旧機能が必要になります。
攻略ポイント
購入時に在庫を減らす方式は強力ですが、初心者向けではありません。
在庫を先に守れる
↓
メリット
キャンセル時に戻す必要がある
↓
注意点
一点ものや限定商品でなければ、最初は次の「決済完了後に在庫を減らす方式」から始めるほうがわかりやすいです。
11.4 決済完了後に在庫を減らす場合
もうひとつの方法は、決済完了後に在庫を減らす方式です。
購入手続きへ進む
↓
Square決済ページで支払う
↓
Webhookで決済完了を確認する
↓
在庫を減らす
この方式は、初心者にも理解しやすいです。
支払いが完了した注文だけ在庫を減らします。
Square Webhookは、Squareアカウント内でデータ更新が起きたときにPOSTで通知する仕組みです。決済完了や在庫変更のようなイベントをNext.js側で受け取る設計にできます。 (Square)
メリット
決済されなかった注文で在庫が減らない
キャンセル時の戻し処理が少ない
実装が比較的シンプル
Webhookと相性がよい
デメリット
決済前に在庫を押さえられない
同時購入で在庫不足になる可能性がある
在庫1個の商品では競合対策が必要
たとえば、在庫が1個の商品を2人がほぼ同時に決済した場合、両方の決済が進んでしまう可能性があります。
この場合、片方をキャンセルまたは返金する対応が必要になるかもしれません。
最初のおすすめ
最初のECでは、決済完了後に在庫を減らす方式がわかりやすいです。
ただし、在庫が少ない商品では、在庫確保や購入前確認も検討します。
攻略ポイント
決済完了後に在庫を減らす方式は、最初の実装に向いています。
支払い完了
↓
Webhookで確認
↓
在庫を減らす
ただし、同時購入のリスクは残るため、在庫1個の商品や一点ものでは追加対策が必要です。
11.5 在庫確保方式のメリット・デメリット
在庫確保とは、購入手続き中の商品を一時的に押さえることです。
在庫10個
↓
ユーザーが2個購入手続き中
↓
販売可能在庫は8個として扱う
在庫確保を使うと、同時購入による売り越しを防ぎやすくなります。
ただし、仕組みは少し複雑になります。
在庫確保の基本フロー
1. ユーザーが購入手続きへ進む
2. サーバー側で在庫を確認する
3. 在庫があれば確保する
4. Square決済ページへ進む
5. 決済完了なら確保を正式減算にする
6. 決済されなければ期限後に確保を解除する
メリット
売り越しを防ぎやすい
在庫1個の商品に強い
限定商品に向いている
同時購入対策になる
デメリット
実装が複雑
確保期限の管理が必要
期限切れ処理が必要
在庫を戻す処理が必要
途中離脱への対応が必要
在庫確保テーブルの例
自前DBで在庫確保を管理する場合、次のようなテーブルを作れます。
create table inventory_reservations (
id uuid primary key default gen_random_uuid(),
order_id uuid not null references orders(id) on delete cascade,
product_id text not null,
quantity integer not null,
status text not null,
expires_at timestamptz not null,
created_at timestamptz not null default now()
);
statusには、次のような値を入れます。
reserved
confirmed
expired
released
攻略ポイント
在庫確保は、限定商品向けの上級装備です。
通常商品
↓
決済完了後減算でもよい
一点もの・少量在庫
↓
在庫確保を検討する
すべての商品に最初から導入する必要はありません。
11.6 少量販売・催事販売での在庫設計
小規模ECや催事販売では、在庫管理の考え方が少し変わります。
実店舗や催事では、オンライン以外でも商品が売れます。
ECで売れる
実店舗で売れる
催事で売れる
電話注文が入る
LINEで取り置きされる
この場合、ECだけを見ていても正しい在庫はわかりません。
少量販売で起きやすい問題
催事で売れたのにECが在庫ありのまま
ECで売れたのに店頭在庫が減っていない
電話注文分を取り置き忘れる
一点ものが二重販売される
現実的な設計
少量販売では、在庫数を細かく出さず、状態表示にする方法が有効です。
在庫あり
残りわずか
売り切れ
お問い合わせください
予約受付中
一点ものや高額商品では、いきなり決済ではなく、問い合わせ導線にする方法もあります。
商品詳細を見る
↓
購入相談する
↓
在庫確認
↓
個別決済リンクを送る
SquareはPOSやオンライン決済など店舗・オンライン両方の販売に関わる機能を提供しており、実店舗販売とオンライン販売を組み合わせる小規模事業者でも検討しやすいサービスです。 (Square)
催事販売でのおすすめ
催事中は、EC在庫を一時的に絞る方法もあります。
催事前
↓
EC在庫を少なめに設定
催事中
↓
一部商品を問い合わせ対応にする
催事後
↓
残在庫を反映してEC販売を再開
攻略ポイント
少量販売や催事販売では、在庫数を正確に見せようとしすぎないことも大切です。
大量在庫
↓
在庫数表示しやすい
少量在庫・一点もの
↓
状態表示や問い合わせ導線が安全
売り越しが一番危険な場合は、決済前に人が確認する運用も選択肢です。
11.7 Square Inventory APIを使う場合
Square Inventory APIを使うと、Square側の商品在庫をプログラムから扱えます。
SquareのInventory APIは、Square販売者の在庫数や在庫変更をプログラムから管理するAPIです。Inventory APIのAPIバージョンは公式リファレンス上で更新されており、2026年7月時点で最新系のAPIバージョンも確認できます。 (Square)
Square Inventory APIが向いているケース
Square POSを使っている
Square Catalogで商品管理している
実店舗とECの在庫を近づけたい
Square Dashboard上でも在庫を見たい
Catalog item variation単位で在庫を扱いたい
Square Inventory APIは、Square Catalogとセットで考える必要があります。
在庫は、基本的にSquare Catalog上の商品バリエーションに紐づきます。
在庫数を取得する
在庫数を取得する場合は、Inventory APIの在庫取得系エンドポイントを使います。
SquareのInventory APIには、在庫数や在庫変更履歴を取得するためのエンドポイントがあります。たとえば、Inventory Changesの一括取得では、履歴上の物理カウントや調整を条件に基づいて取得できます。 (Square)
在庫を変更する
在庫を変更する場合は、BatchChangeInventoryを使います。
Square公式APIリファレンスでは、POST /v2/inventory/changes/batch-createは在庫調整や物理カウントを適用し、成功時には対象オブジェクトの現在の計算済み在庫数を返すと説明されています。 (Square)
自前商品との対応づけ
Square Inventory APIを使う場合、自前DBの商品IDとSquare CatalogのVariation IDを対応づける必要があります。
自前DBの商品ID
↓
product_001
Square Catalog Variation ID
↓
Square側の商品バリエーションID
この対応が曖昧だと、在庫連携が壊れます。
攻略ポイント
Square Inventory APIは、Square中心に在庫を管理したい場合に有効です。
Square POS中心
↓
Square Inventory APIが向いている
Next.js独自EC中心
↓
自前DB在庫のほうが扱いやすい場合もある
Squareを在庫の正本にするのか、自前DBを正本にするのかを先に決めます。
11.8 自前DBで在庫を持つ場合
自前DBで在庫を持つ場合、Next.js側のDBに在庫数を保存します。
この方式は、独自ECに合わせた柔軟な在庫管理ができます。
自前DB在庫が向いているケース
EC独自の商品構成がある
セット商品が多い
ギフト商品がある
予約販売がある
Square Catalogとは別の商品説明を使いたい
管理画面を自由に作りたい
inventoriesテーブルの例
create table inventories (
id uuid primary key default gen_random_uuid(),
product_id text not null unique,
quantity_available integer not null default 0,
quantity_reserved integer not null default 0,
quantity_sold integer not null default 0,
updated_at timestamptz not null default now()
);
それぞれの意味です。
| カラム | 意味 |
|---|---|
quantity_available | 販売可能在庫 |
quantity_reserved | 確保中在庫 |
quantity_sold | 販売済み数量 |
販売可能数は、次のように考えます。
販売可能数
↓
quantity_available - quantity_reserved
たとえば、在庫10個、確保中2個なら、購入可能数は8個です。
quantity_available = 10
quantity_reserved = 2
↓
購入可能数 = 8
在庫減算の例
決済完了後に在庫を減らす場合、次のような処理になります。
Webhookで決済完了を受け取る
↓
注文の商品明細を取得
↓
対象商品の在庫を減らす
↓
注文ステータスをpaidにする
SQLのイメージです。
update inventories
set
quantity_available = quantity_available - 1,
quantity_sold = quantity_sold + 1,
updated_at = now()
where product_id = 'product_001'
and quantity_available >= 1;
quantity_available >= 1を条件に入れることで、在庫が足りない場合は更新されません。
攻略ポイント
自前DB在庫では、在庫数を直接書き換えるだけでなく、条件付き更新を使います。
在庫が足りる
↓
減らす
在庫が足りない
↓
減らさない
この考え方が、同時購入対策につながります。
11.9 売り切れ、残りわずか、予約販売
在庫管理では、ユーザーにどう見せるかも重要です。
ただ数字を表示するだけではなく、購入判断しやすい表現にします。
売り切れ
売り切れの場合、購入ボタンを無効化します。
<button
type="button"
disabled
className="w-full rounded-md bg-gray-300 px-6 py-3 text-sm font-semibold text-white"
>
売り切れ
</button>
商品一覧でも、画像の上に売り切れ表示を重ねます。
商品画像
↓
売り切れラベル
残りわずか
残りわずかは、購入を後押しする効果があります。
ただし、根拠なく表示してはいけません。
在庫が3個以下
↓
残りわずか
在庫が0個
↓
売り切れ
TypeScriptで表示を決める関数を作れます。
export type StockDisplayStatus =
| 'in_stock'
| 'low_stock'
| 'out_of_stock'
| 'preorder';
/**
* 役割: 在庫数と予約受付状態から画面表示用の在庫ステータスを返す
* 入力: 在庫数、予約受付中かどうか
* 出力: 画面表示用の在庫ステータス
*/
export function getStockDisplayStatus(
availableQuantity: number,
isPreorder: boolean,
): StockDisplayStatus {
if (isPreorder) {
return 'preorder';
}
if (availableQuantity <= 0) {
return 'out_of_stock';
}
if (availableQuantity <= 3) {
return 'low_stock';
}
return 'in_stock';
}
予約販売
予約販売では、在庫がなくても注文を受け付ける場合があります。
通常販売
↓
在庫がある分だけ売る
予約販売
↓
入荷予定や製造予定を前提に受け付ける
予約販売では、配送予定日やキャンセル条件を明確に表示します。
予約商品です
発送予定日は○月下旬です
通常商品と同時購入できない場合があります
キャンセル条件をご確認ください
攻略ポイント
在庫表示は、ユーザーとの約束です。
在庫あり
↓
基本的に買える状態
売り切れ
↓
買えない状態
予約受付中
↓
通常販売とは違う条件
誤解されない表現を使います。
11.10 同時購入による在庫ズレを防ぐ
在庫管理で最も注意すべきなのが、同時購入です。
たとえば、在庫が1個の商品を、2人が同時に購入しようとするケースです。
在庫1個
↓
ユーザーAが購入
ユーザーBも同時に購入
↓
両方通ると在庫がマイナスになる
これを防ぐには、サーバー側で在庫を確認し、条件付きで更新します。
やってはいけない処理
在庫数を取得する
↓
1個あると判断する
↓
別処理で在庫を減らす
この方法だと、取得してから減らすまでの間に、別の購入が入る可能性があります。
よい処理
在庫が足りる場合だけ更新する
↓
更新できたら成功
↓
更新できなければ在庫不足
SQLの例です。
update inventories
set
quantity_available = quantity_available - 1,
quantity_sold = quantity_sold + 1,
updated_at = now()
where product_id = 'product_001'
and quantity_available >= 1;
この更新で影響行数が0なら、在庫不足です。
複数商品を同時に買う場合
複数商品がある場合は、さらに慎重に考えます。
商品Aは在庫あり
商品Bは在庫不足
↓
注文全体をどう扱うか
基本的には、注文全体を失敗にするほうが安全です。
全商品在庫あり
↓
注文を進める
1つでも在庫不足
↓
注文を止める
本格的には、DBトランザクションを使って、すべての在庫更新をまとめて成功または失敗にします。
Webhookの二重処理にも注意する
Webhookは、同じイベントが複数回届く可能性があります。
そのため、在庫減算は一度だけ行う必要があります。
Webhookを受け取る
↓
すでに処理済みか確認
↓
未処理なら在庫を減らす
↓
処理済みとして記録する
Next.jsでは、Squareから届くWebhookの受け口をRoute Handlerで作れます。Route Handlerはappディレクトリ内でWeb RequestとResponse APIを使ったカスタム処理を作る仕組みです。 (Next.js)
攻略ポイント
在庫ズレ対策は、次の3つが重要です。
条件付き更新
DBトランザクション
Webhook処理済みチェック
在庫は、画面ではなくサーバーとDBで守ります。
在庫管理の完成イメージ
この章の内容をまとめると、在庫管理は次のような流れになります。
商品ページ
↓
在庫状態を表示
購入手続き
↓
サーバー側で在庫を確認
Square決済
↓
支払いを処理
Webhook
↓
決済完了を確認
在庫更新
↓
在庫を減らす
注文更新
↓
注文ステータスをpaidへ変更
少量販売や一点ものの場合は、在庫確保を追加します。
購入手続き開始
↓
在庫を一時確保
決済完了
↓
在庫を正式減算
期限切れ
↓
在庫確保を解除
この章のまとめ
この章では、ECにおける在庫管理を整理しました。
在庫管理は、単に数字を表示するだけではありません。
表示する
確保する
減らす
戻す
同期する
このような処理を考える必要があります。
この章で扱った内容は、次の通りです。
ECにおける在庫管理の難しさ
在庫を表示するだけの場合
購入時に在庫を減らす場合
決済完了後に在庫を減らす場合
在庫確保方式のメリット・デメリット
少量販売・催事販売での在庫設計
Square Inventory APIを使う場合
自前DBで在庫を持つ場合
売り切れ、残りわずか、予約販売
同時購入による在庫ズレを防ぐ
特に重要なのは、在庫の正本を決めることです。
Squareを正本にする
↓
Square POSや実店舗連携に向いている
自前DBを正本にする
↓
独自ECや柔軟な運用に向いている
最初から完璧な在庫管理を作る必要はありません。
小規模ECでは、次のような段階で育てていくのがおすすめです。
レベル1
売り切れ表示だけ
レベル2
在庫状態を表示
レベル3
決済完了後に在庫を減らす
レベル4
在庫確保を入れる
レベル5
Square Inventory APIや実店舗在庫と連携する
在庫管理は、売上だけでなく信頼に関わる部分です。
買えたはずの商品が届けられない、という状態をできるだけ防ぐ設計にします。
次の章でやること
次の章では、Webhookの基礎を扱います。
在庫を正しく減らすには、Squareで決済が完了したことを確実に知る必要があります。
そのために使うのがWebhookです。
次章では、SquareからNext.jsへイベント通知を受け取り、決済完了を安全に確認する仕組みを攻略していきます。