TEXTBOOK SECTION / AI LEARNING

注文データを設計する

Square API × Next.js 実践EC開発|自社ECを低コストで作る、決済・在庫・注文管理の設計と実装の「カート・注文・在庫を本格化する」より、注文データを設計するを解説。生成AI、AI活用、DX、業務改善を実践しながら学べるオンライン教材です。

2カート・注文・在庫を本格化するEC

OVERVIEW

この節で学べること

概要を表示する
項目内容
教材名Square API × Next.js 実践EC開発|自社ECを低コストで作る、決済・在庫・注文管理の設計と実装
カート・注文・在庫を本格化する
注文データを設計する
カテゴリEC
学習内容生成AI、AI活用、DX、業務改善を実践しながら理解するための教材です。

TABLE OF CONTENTS

目次

CONTENT

ここから

この章でわかること

この章では、ECサイトに必要な注文データを設計します。

第8章では、カート機能を作りました。

カートは、ユーザーが購入予定の商品を一時的に保存する場所でした。

しかし、カートだけではECは完成しません。

購入手続きへ進んだら、次は注文データを作る必要があります。

カート
↓
購入予定の商品を一時保存する場所

注文
↓
購入手続きとして記録する正式なデータ

攻略本でいうと、カートは「買い物かご」です。

注文データは「注文書」です。

買い物かごに商品を入れただけでは、まだ注文は成立していません。

注文として記録し、決済と紐づけ、発送や管理に使える形にする必要があります。


9.1 なぜ注文DBが必要なのか

Squareには、注文や決済を扱う機能があります。

SquareのOrders APIは、購入商品を記録したり、合計金額を計算したり、支払い確認、注文の進行管理、在庫更新などに使えるAPIとして説明されています。(developer.squareup.com)

では、Squareに注文情報があるなら、自前DBは不要なのでしょうか。

結論から言うと、実務では自前DBを持つほうが管理しやすいです。

理由は、ECサイト側で必要な情報がSquareだけでは足りない場合があるからです。

たとえば、次のような情報です。

自社サイト側の注文ID
注文ステータス
発送ステータス
管理メモ
メール送信履歴
購入者への案内状況
配送先情報
社内確認フラグ
Webhook処理済みかどうか

Squareは決済や注文に強いサービスです。

しかし、自社ECの運用ルールまで全部管理してくれるわけではありません。

たとえば、次のような業務がある場合、自前DBが必要になります。

管理画面で注文一覧を見たい
発送済み・未発送を管理したい
注文ごとに社内メモを残したい
メール送信状況を確認したい
トラブル時に処理履歴を追いたい
Webhookの二重処理を防ぎたい

攻略ポイント

Squareは、決済と商取引データに強い場所です。

自前DBは、自社ECの運用を管理する場所です。

Square
↓
支払いが本当に完了したかを見る場所

自前DB
↓
自社として注文をどう処理するかを管理する場所

この分担を最初に決めることが、注文設計の第一歩です。


9.2 Squareだけに注文情報を置く場合の限界

Squareだけに注文情報を置く構成も、技術的には考えられます。

たとえば、Square Orders APIで注文を作り、Square側の注文IDを見れば、購入情報を確認できます。

Square APIリファレンスでも、Orders APIは販売データを取得し、支払いへ商品明細を付け、POSへ注文を送るなどの用途で使えると説明されています。(developer.squareup.com)

ただし、Squareだけに寄せすぎると、次のような限界が出ることがあります。

自社独自の注文ステータスを持ちにくい
管理画面を自由に作りにくい
配送業務に合わせた情報を持ちにくい
メール送信履歴を管理しにくい
Webhook処理ログを残しにくい
社内メモや確認フラグを持ちにくい
独自の商品ページ情報と紐づけにくい

たとえば、食品ECなら「製造確認済み」「梱包中」「発送準備中」のような社内ステータスが必要になるかもしれません。

工芸品ECなら「在庫確認中」「作家確認中」「個別連絡済み」のような状態が必要になるかもしれません。

これらは、Square標準の情報だけでは足りない場合があります。

Squareだけに置く構成が向くケース

商品数が少ない
注文管理をSquare管理画面で十分に行える
発送や社内処理がシンプル
独自管理画面が不要
まずは最小構成で販売したい

自前DBが必要になりやすいケース

管理画面を作りたい
注文ごとに細かいステータスを持ちたい
配送先や発送状態を独自管理したい
Webhook処理履歴を残したい
複数スタッフで注文対応したい
売上分析や顧客分析をしたい

攻略ポイント

Squareだけで始めるのは悪くありません。

ただし、事業として運用するなら、自前DBに注文を残す設計を考えておくと後で困りにくくなります。

最小構成
↓
Square中心でもよい

実務運用
↓
自前DBも持つほうが安全

9.3 自前DBに保存すべき情報

自前DBに保存する情報は、何でも多ければよいわけではありません。

必要な情報だけを保存します。

注文データには、主に次の情報があります。

注文ID
注文ステータス
決済ステータス
SquareのPayment ID
SquareのOrder ID
注文日時
商品明細
小計
送料
合計金額
購入者情報
配送先情報
管理メモ

ここで重要なのは、

Square側のIDと自前DBのIDを紐づけることです。

たとえば、次のような関係です。

自前DBのorder.id
↓
自社EC内の注文ID

square_payment_id
↓
Squareの支払いID

square_order_id
↓
Square側の注文ID

この紐づけがあると、トラブル時に確認しやすくなります。

お客様から問い合わせ
↓
自前DBの注文を見る
↓
Square Payment IDを確認
↓
Square側の決済状況を見る

保存すべき情報

自社注文ID
Square関連ID
注文ステータス
決済ステータス
注文明細
金額
配送情報
作成日時
更新日時

保存しすぎないほうがよい情報

カード番号
セキュリティコード
不要な個人情報
使わない顧客属性
過剰な行動履歴

カード情報は保存しません。

Squareホスト型決済を使う場合、自社サイト側でカード番号を扱わない構成にできます。

攻略ポイント

注文DBは、あとで確認するための冒険ログです。

ただし、ログだからといって何でも保存してよいわけではありません。

必要な情報
↓
保存する

不要な個人情報
↓
保存しない

カード情報
↓
保存しない

9.4 ordersテーブルの設計

ordersテーブルは、注文の親データを保存するテーブルです。

1つの注文につき、1行作ります。

たとえば、次のような情報を保存します。

注文ID
注文番号
注文ステータス
決済ステータス
Square Payment ID
Square Order ID
小計
送料
合計金額
購入者名
メールアドレス
配送先
作成日時
更新日時

ordersテーブルの例

SQLで書くと、次のようなイメージです。

create table orders (
  id uuid primary key default gen_random_uuid(),
  order_number text not null unique,

  status text not null,
  payment_status text not null,

  square_payment_id text,
  square_order_id text,
  square_checkout_id text,

  subtotal_amount integer not null,
  shipping_fee_amount integer not null,
  total_amount integer not null,
  currency text not null default 'JPY',

  customer_name text,
  customer_email text,

  shipping_postal_code text,
  shipping_prefecture text,
  shipping_city text,
  shipping_address_line1 text,
  shipping_address_line2 text,
  shipping_phone text,

  admin_note text,

  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

金額はintegerで持つ

日本円の場合、金額は整数で扱います。

1200
↓
1,200円

小数を使うと、計算誤差が問題になることがあります。

最小通貨単位の整数で持つのが基本です。

order_numberを持つ理由

idはシステム内部のIDです。

お客様に見せる注文番号としては、別にorder_numberを持つと便利です。

内部ID
↓
550e8400-e29b-41d4-a716-446655440000

注文番号
↓
ORD-20260706-0001

問い合わせ対応では、注文番号のほうが扱いやすくなります。

攻略ポイント

ordersテーブルは、注文の親玉です。

ここには、注文全体を表す情報を保存します。

orders
↓
注文全体の情報

order_items
↓
注文された商品の明細

親と明細を分けることが大切です。


9.5 order_itemsテーブルの設計

order_itemsテーブルは、注文された商品明細を保存するテーブルです。

1つの注文に複数の商品が入る場合、ordersorder_itemsを分けます。

1つの注文
↓
複数の商品明細

たとえば、次のような注文があるとします。

注文番号: ORD-20260706-0001

商品A: 2個
商品B: 1個
商品C: 3個

この場合、ordersは1行、order_itemsは3行になります。

order_itemsテーブルの例

create table order_items (
  id uuid primary key default gen_random_uuid(),
  order_id uuid not null references orders(id) on delete cascade,

  product_id text not null,
  product_slug text not null,
  product_name text not null,

  unit_price_amount integer not null,
  quantity integer not null,
  line_total_amount integer not null,
  currency text not null default 'JPY',

  created_at timestamptz not null default now()
);

商品名と価格を保存する理由

order_itemsには、商品IDだけでなく、注文時点の商品名や価格も保存します。

理由は、商品情報が後から変わる可能性があるからです。

注文時の商品名
↓
クラシックだし

後から商品名を変更
↓
クラシックだし プレミアム

もし商品IDだけを保存していると、過去の注文内容が後から変わって見えてしまう可能性があります。

そのため、注文時点の商品名、単価、小計を保存します。

商品マスター
↓
現在の商品情報

order_items
↓
注文時点の商品情報

攻略ポイント

注文明細は、過去の記録です。

現在の商品情報ではなく、

注文された時点の情報を残します。

現在の商品名
↓
変わる可能性がある

注文時の商品名
↓
変えてはいけない記録

9.6 顧客情報の扱い

注文には、顧客情報が必要です。

ただし、顧客情報は慎重に扱います。

必要以上に保存すると、管理責任が重くなります。

ECで最低限必要になりやすい顧客情報は、次の通りです。

氏名
メールアドレス
電話番号
配送先住所

会員機能がない場合

最初のECでは、会員機能なしで始めても構いません。

その場合、注文ごとに顧客情報を保存します。

注文1
↓
購入者名・メール・配送先

注文2
↓
購入者名・メール・配送先

シンプルで実装しやすい方法です。

会員機能がある場合

会員機能を作る場合は、customersテーブルを別に作り、注文と紐づけます。

customers
↓
顧客の基本情報

orders
↓
注文情報

ただし、最初から会員機能を作ると、認証、退会、個人情報管理など考えることが増えます。

本書では、最初は会員機能なしでも進められる設計にします。

Square Customers APIとの関係

SquareにはCustomers APIもあります。

Square API Referenceでは、Customers APIは顧客プロフィールを作成・管理し、CRMシステムとの同期などに利用できるAPIとして説明されています。(developer.squareup.com)

ただし、最初のECでは必ず使う必要はありません。

まずは注文に必要な顧客情報を自前DBに保存し、必要になったらSquare Customers APIとの連携を検討します。

攻略ポイント

顧客情報は、便利だから保存するのではありません。

必要だから保存します。

配送に必要
↓
保存する

問い合わせ対応に必要
↓
保存する

使う予定がない
↓
保存しない

9.7 配送先情報の扱い

物販ECでは、配送先情報が必要です。

配送先には、次のような情報があります。

郵便番号
都道府県
市区町村
番地
建物名
電話番号
宛名

注文者と配送先が同じとは限りません。

ギフト販売では、購入者と届け先が違うこともあります。

購入者
↓
支払う人

配送先
↓
商品を受け取る人

そのため、設計上は購入者情報と配送先情報を分けて考えると安全です。

最小構成

最初は、ordersテーブルに配送先情報を直接持たせても構いません。

orders
├─ customer_name
├─ customer_email
├─ shipping_postal_code
├─ shipping_prefecture
├─ shipping_city
├─ shipping_address_line1
├─ shipping_address_line2
└─ shipping_phone

本格構成

配送先を複数管理したい場合は、shipping_addressesテーブルを分けます。

orders
↓
注文情報

shipping_addresses
↓
配送先情報

ただし、最初から分けると複雑になるため、必要になってからで大丈夫です。

攻略ポイント

ギフト販売を考えるなら、購入者と配送先を分けて考えます。

購入者
↓
支払い・連絡先

配送先
↓
商品のお届け先

この区別ができていると、後からギフト機能を追加しやすくなります。


9.8 個人情報を最小限にする設計

ECでは個人情報を扱います。

そのため、最初から「できるだけ持たない」設計にします。

個人情報をたくさん保存すると、便利になる一方で、管理責任も重くなります。

保存する可能性が高い情報

氏名
メールアドレス
電話番号
配送先住所
注文内容

保存しない情報

カード番号
セキュリティコード
不要な生年月日
不要な性別
使わない職業情報
使わない詳細な属性情報

カード情報は保存しません。

Squareホスト型決済を使う場合、購入者はSquareの決済ページでカード情報を入力します。

自社サイト側では、カード番号を直接扱わない構成にします。

個人情報を最小化する理由

漏洩リスクを下げる
管理負担を減らす
開発範囲を小さくする
不要な責任を持たない

管理画面での表示も考える

個人情報は、保存するだけでなく、誰が見られるかも大切です。

注文担当者
↓
配送に必要な情報だけ見る

管理者
↓
必要な範囲で全体を見る

開発者
↓
本番個人情報を不用意に見ない

攻略ポイント

個人情報は、持てば持つほど強くなる装備ではありません。

むしろ、守るべき荷物が増えます。

必要な情報
↓
最小限だけ持つ

不要な情報
↓
最初から持たない

9.9 注文ステータスの定義

注文ステータスは、注文が今どの状態にあるかを表します。

ECでは、注文が発生してから発送されるまでに、いくつかの段階があります。

たとえば、次のような流れです。

決済待ち
↓
支払い済み
↓
発送準備中
↓
発送済み
↓
完了

注文ステータスを定義しておくと、管理画面やメール通知、発送処理が作りやすくなります。

注文ステータスの例

TypeScriptでは、次のように定義できます。

export type OrderStatus =
  | 'pending_payment'
  | 'paid'
  | 'preparing'
  | 'shipped'
  | 'completed'
  | 'canceled';

それぞれの意味は次の通りです。

ステータス意味
pending_payment決済待ち
paid支払い済み
preparing発送準備中
shipped発送済み
completed完了
canceledキャンセル

決済ステータスも分ける

注文ステータスとは別に、決済ステータスも持つと管理しやすくなります。

export type PaymentStatus =
  | 'unpaid'
  | 'paid'
  | 'failed'
  | 'refunded'
  | 'canceled';

注文ステータスと決済ステータスは似ていますが、役割が違います。

注文ステータス
↓
自社ECとして注文をどう処理しているか

決済ステータス
↓
支払いがどうなっているか

なぜ分けるのか

たとえば、支払い済みでも、まだ発送していない注文があります。

payment_status
↓
paid

order_status
↓
preparing

また、発送済みでも、返金が発生することがあります。

order_status
↓
shipped

payment_status
↓
refunded

このように、注文と決済は別の軸として考えます。

攻略ポイント

注文ステータスは、業務の現在地です。

決済ステータスは、お金の現在地です。

注文ステータス
↓
発送や管理のため

決済ステータス
↓
支払い確認のため

この2つを混ぜないことが大切です。


9.10 決済前注文と決済後注文を分ける

注文設計で特に重要なのが、決済前注文と決済後注文を分けることです。

ECでは、購入ボタンを押した瞬間に注文が確定するわけではありません。

正確には、次の段階があります。

カートを確認する
↓
注文予定データを作る
↓
Square決済ページへ進む
↓
支払いが完了する
↓
Webhookで確認する
↓
注文を確定する

決済前注文とは

決済前注文は、まだ支払いが完了していない注文です。

たとえば、ユーザーが購入手続きへ進んだ時点で、自前DBに仮の注文を作ります。

order_status
↓
pending_payment

payment_status
↓
unpaid

この時点では、まだ正式な注文として発送してはいけません。

決済後注文とは

決済後注文は、Square側で支払いが完了し、Webhookなどで確認できた注文です。

SquareのPayments API Webhooksでは、支払いが作成または更新されたときに通知を受け取ることができ、支払い完了時にはpayment.updatedイベントが生成される例が示されています。(developer.squareup.com)

Webhookを受け取って、Square側の支払い状態を確認した後に、注文を支払い済みにします。

order_status
↓
paid

payment_status
↓
paid

なぜ分ける必要があるのか

理由は、決済ページへ進んでも、必ず支払うとは限らないからです。

Square決済ページを開いた
↓
まだ支払い済みではない

決済ページから戻ってきた
↓
まだ確定とは限らない

Webhookで支払い完了を確認した
↓
注文確定として扱える

ユーザーが途中でブラウザを閉じることもあります。

決済をキャンセルすることもあります。

通信が切れることもあります。

そのため、決済前注文と決済後注文を分けます。

注文確定フロー

実務では、次のような流れにします。

1. カートから注文予定データを作る
2. ordersにpending_paymentで保存する
3. order_itemsに注文時点の商品情報を保存する
4. Square決済リンクを作る
5. ユーザーがSquareで支払う
6. SquareからWebhookが届く
7. Square側の支払い状態を確認する
8. ordersをpaidへ変更する
9. 在庫を減らす
10. 注文確認メールを送る

Next.jsでの受け口

Webhookを受け取る場合は、Next.jsのRoute Handlerを使います。

Next.js公式ドキュメントでは、Route Handlersはappディレクトリ内でroute.tsを使って、Web RequestとResponse APIによるカスタムリクエストハンドラーを作れると説明されています。(nextjs.org)

たとえば、後の章で次のようなURLを作ります。

app/api/square/webhook/route.ts

このRoute HandlerでSquareからのWebhookを受け取り、注文ステータスを更新します。

攻略ポイント

決済ページへ進んだだけでは、注文確定ではありません。

決済前注文
↓
仮注文

決済後注文
↓
正式注文

注文を発送してよいのは、決済完了を確認した後です。


注文データの完成イメージ

この章の設計をまとめると、注文データは次のような形になります。

orders
├─ id
├─ order_number
├─ status
├─ payment_status
├─ square_payment_id
├─ square_order_id
├─ subtotal_amount
├─ shipping_fee_amount
├─ total_amount
├─ currency
├─ customer_name
├─ customer_email
├─ shipping_address
├─ admin_note
├─ created_at
└─ updated_at

order_items
├─ id
├─ order_id
├─ product_id
├─ product_slug
├─ product_name
├─ unit_price_amount
├─ quantity
├─ line_total_amount
├─ currency
└─ created_at

カートから注文へ進む流れは、次の通りです。

カート
↓
商品IDと数量を持つ

サーバー
↓
商品マスターから価格を再取得する

orders
↓
注文全体を保存する

order_items
↓
注文時点の商品明細を保存する

Square
↓
決済ページを作る

Webhook
↓
決済完了後に注文を確定する

この章のまとめ

この章では、注文データの設計を整理しました。

Squareには注文や決済を扱う機能があります。

しかし、自社ECとして管理画面、発送、メール通知、社内ステータス、Webhook処理履歴などを扱うなら、自前DBに注文情報を保存する設計が必要になります。

この章で扱った内容は、次の通りです。

なぜ注文DBが必要なのか
Squareだけに注文情報を置く場合の限界
自前DBに保存すべき情報
ordersテーブルの設計
order_itemsテーブルの設計
顧客情報の扱い
配送先情報の扱い
個人情報を最小限にする設計
注文ステータスの定義
決済前注文と決済後注文を分ける

特に重要なのは、次の考え方です。

Square
↓
決済の正本

自前DB
↓
EC運用の正本

そして、注文は最初から確定ではありません。

pending_payment
↓
決済前の仮注文

paid
↓
決済確認後の注文

この分離ができていると、決済、在庫、メール、管理画面を安全につなげやすくなります。


次の章でやること

次の章では、Square Orders APIと連携します。

この章では、自前DB側の注文データを設計しました。

次は、Square側の注文情報と自前DBの注文情報をどう対応づけるかを見ていきます。

Square Orders APIを使って注文を作り、SquareのOrder IDを自前DBに保存することで、決済・注文・在庫のつながりをより明確にしていきます。

FAQ

よくある質問

注文データを設計するは医療関係者向けだけの内容ですか。
医療分野の例が含まれる場合もありますが、医療関係者だけに限定した内容ではありません。生成AI、AI活用、DX、業務改善、プロトタイプ開発など、一般的なAI学習の事例として読める内容です。
AI初心者でも読めますか。
はい。AIをこれから学ぶ方、数学が苦手な方、仕事でAIを使いたい方にも読み進めやすいように、教材の章と節の流れに沿って整理しています。
サムネイル画像は必ず表示されますか。
はい。教材にcoverUrlが設定されている場合はその画像を表示し、未設定の場合は代替サムネイル画像を表示します。
Square API × Next.js 実践EC開発|自社ECを低コストで作る、決済・在庫・注文管理の設計と実装のほかの章も読めますか。
はい。教材トップから章立てを確認でき、前後の節へもページ下部のナビゲーションから移動できます。