CONTENT
ここから
この章でわかること
この章では、Square Orders APIと自前DBの注文データを連携する考え方を整理します。
第9章では、自前DBに保存する注文データを設計しました。
orders
↓
注文全体の情報
order_items
↓
注文された商品の明細
次に必要なのは、この自前DBの注文と、Square側の注文をつなげることです。
SquareにはOrders APIがあります。
Orders APIを使うと、Square側に注文情報を作成できます。
Square公式ドキュメントでは、Orders APIのCreateOrderエンドポイントを使ってOrderオブジェクトを作成でき、Orderにはline items、fulfillments、taxes、discountsなどを含められると説明されています。 (Square)
攻略本でいうと、この章は「自社の注文書をSquareの世界にも登録するステージ」です。
10.1 Orders APIの役割
Orders APIは、Square側で注文情報を扱うためのAPIです。
ECにおける注文は、単に金額だけではありません。
次のような情報を含みます。
何を買ったか
いくつ買ったか
単価はいくらか
小計はいくらか
送料はいくらか
税金はいくらか
割引はあるか
合計金額はいくらか
どの店舗・Locationの注文か
Square Orders APIは、こうした注文情報をSquare側に作るために使います。
SquareのAPIリファレンスでは、Orders APIは販売データ、支払いの商品明細、過去の売上や返品の明細データなどを扱うAPIとして説明されています。 (Square)
なぜOrders APIを使うのか
第7章では、quick_payを使って、1商品をすぐに決済ページへつなげる方法を扱いました。
これは最小構成としては便利です。
しかし、カート機能が入ると、注文の中に複数の商品が入ります。
商品A × 2
商品B × 1
商品C × 3
このような注文を扱う場合は、Square側にも明細付きのOrderを作るほうが管理しやすくなります。
自前DB
↓
orders / order_items
Square
↓
Order / line_items
Orders APIの役割
Orders APIの役割は、次の通りです。
Square側に注文を作る
商品明細を持たせる
税・割引・送料を含める
Squareの決済と注文を紐づける
Square Dashboard上で確認しやすくする
攻略ポイント
Orders APIは、決済そのものではありません。
注文情報をSquare側に作るための道具です。
Orders API
↓
何を買うのかを記録する
Checkout API / Payments API
↓
どう支払うのかを処理する
この違いを押さえておくと、Square連携が理解しやすくなります。
10.2 自前注文とSquare Orderの対応関係
本書では、自前DBの注文とSquare Orderを対応づける設計にします。
つまり、注文情報を2つの場所で扱います。
自前DB
↓
EC運用のための注文データ
Square
↓
決済・Square連携のための注文データ
どちらか一方だけに寄せるのではなく、役割を分けます。
自前DBの注文
自前DBでは、EC運用に必要な情報を管理します。
自社注文ID
注文番号
注文ステータス
決済ステータス
購入者情報
配送先情報
発送ステータス
管理メモ
メール送信履歴
Webhook処理状況
Square Order
Square Orderでは、Square側で扱う注文情報を管理します。
Square Order ID
Location ID
line_items
taxes
discounts
service_charges
fulfillments
対応づけに必要なカラム
自前DBのordersテーブルには、Square側のIDを保存するカラムを持たせます。
alter table orders
add column square_order_id text,
add column square_payment_link_id text,
add column square_payment_id text;
役割は次の通りです。
| カラム | 役割 |
|---|---|
square_order_id | Square側のOrder ID |
square_payment_link_id | Square側のPayment Link ID |
square_payment_id | 実際の支払いID |
order_number | 自社EC側の注文番号 |
対応関係のイメージ
orders.id
↓
自前DBの内部ID
orders.order_number
↓
お客様に見せる注文番号
orders.square_order_id
↓
Square側の注文ID
orders.square_payment_id
↓
Square側の支払いID
攻略ポイント
自前注文とSquare Orderをつなぐ鍵は、IDです。
自前DBの注文ID
↓
自社運用で使う
Square Order ID
↓
Square側の確認で使う
問い合わせやトラブル時に、両方をたどれる状態にしておきます。
10.3 line_itemsの作り方
line_itemsは、Square Orderの中に入る商品明細です。
自前DBのorder_itemsに近いものです。
order_items
↓
自前DBの注文明細
line_items
↓
Square Order側の商品明細
SquareのCreateOrderでは、Orderオブジェクトの中にline items、fulfillments、taxes、discountsなどを含められます。 (Square)
line_itemsに入れる基本情報
line itemでは、最低限次のような情報を扱います。
商品名
数量
単価
通貨
Square APIへ送るイメージは次のようになります。
{
"name": "クラシックだし",
"quantity": "2",
"base_price_money": {
"amount": 1200,
"currency": "JPY"
}
}
注意点として、SquareのOrderLineItemのquantityは文字列として扱われます。
そのため、TypeScript側で数値として持っている数量を、Squareへ送るときに文字列へ変換します。
quantity: String(item.quantity)
自前DBのorder_itemsから変換する
自前DBのorder_itemsが次の形だとします。
type OrderItem = {
productId: string;
productName: string;
quantity: number;
unitPriceAmount: number;
currency: 'JPY';
};
Squareのline_itemsへ変換します。
type SquareLineItem = {
name: string;
quantity: string;
base_price_money: {
amount: number;
currency: 'JPY';
};
};
/**
* 役割: 自前DBの注文明細をSquare Orderのline_items形式へ変換する
* 入力: 注文明細配列
* 出力: Square APIへ送信できるline_items配列
*/
export function toSquareLineItems(orderItems: OrderItem[]): SquareLineItem[] {
return orderItems.map((item) => ({
name: item.productName,
quantity: String(item.quantity),
base_price_money: {
amount: item.unitPriceAmount,
currency: item.currency,
},
}));
}
攻略ポイント
order_itemsとline_itemsは似ていますが、同じものではありません。
order_items
↓
自前DBに保存する注文時点の商品明細
line_items
↓
Squareへ送るための商品明細
自前DB用の型とSquare API送信用の型は分けると安全です。
10.4 税・送料・割引の扱い
ECでは、商品代金だけでなく、税、送料、割引も考える必要があります。
商品小計
税
送料
割引
合計金額
Square Orders APIでは、注文にtaxes、discounts、service chargesなどを含められます。Square公式ドキュメントでは、Orders APIでtaxes、discounts、service chargesを注文価格計算に適用する方法が案内されています。 (Square)
税の扱い
日本国内のECでは、税込価格で表示することが一般的です。
本書では、最初は税込価格として商品価格を扱う設計にします。
商品価格
↓
税込価格として扱う
この場合、Square Order側では、まずは商品価格そのものを税込金額として送る形にします。
税額を細かく分けて管理する必要がある場合は、後からtaxesの設計を追加します。
送料の扱い
送料は、Square Order上ではservice chargeとして扱う設計が考えられます。
Square公式ドキュメントでは、Order Service Chargesが注文に追加料金を適用する仕組みとして説明されています。Service chargeには計算フェーズなどの考え方があります。 (Square)
最小構成では、送料を次のように扱います。
商品小計
↓
5,000円以上なら送料無料
↓
それ以外は送料700円
Squareへ送るときは、送料をservice chargeとして追加する設計にします。
{
"name": "送料",
"amount_money": {
"amount": 700,
"currency": "JPY"
},
"calculation_phase": "TOTAL_PHASE"
}
割引の扱い
割引には、いくつかの種類があります。
定額割引
割合割引
クーポン割引
送料無料
初回購入割引
Square Orders APIでは、discountsを使って割引を扱えます。OrderLineItemの説明では、ORDERスコープのdiscountは全line itemに自動適用され、LINE_ITEMスコープのdiscountはline item側に適用指定が必要だと説明されています。 (Square)
最初は、割引なしで進めても構いません。
割引は、運用ルールが増えるため、後から追加するのが安全です。
攻略ポイント
税・送料・割引は、最初から複雑にしすぎないことです。
初期構成
↓
税込価格
一律送料
割引なし
拡張構成
↓
税分離
地域別送料
クーポン割引
最初の目的は、注文を正しく作り、決済へ進めることです。
10.5 Square側の注文IDを保存する
Square Orderを作成すると、Square側のOrder IDが返ってきます。
このIDを自前DBのorders.square_order_idに保存します。
SquareのCreateOrder APIでは、Orderを作成するためにPOST /v2/ordersを使い、作成されたOrder情報が返ります。CreateOrderのリクエストでは、Orderオブジェクトとidempotency_keyを指定できます。 (Square)
保存する流れ
1. 自前DBに仮注文を作る
2. order_itemsを保存する
3. Square CreateOrderを呼ぶ
4. Square Order IDを受け取る
5. orders.square_order_idに保存する
6. Checkout APIで決済リンクを作る
なぜ保存するのか
Square Order IDを保存しておくと、後からSquare側の注文を確認できます。
Webhookが届いた
↓
Square Order IDを確認
問い合わせが来た
↓
自前DBの注文番号からSquare Order IDを見る
Square Dashboardで確認したい
↓
Square Order IDを使う
TypeScriptの例
type CreateSquareOrderResult = {
squareOrderId: string;
};
/**
* 役割: Square APIのレスポンスからSquare Order IDを取り出す
* 入力: Square CreateOrderのレスポンス
* 出力: Square Order ID
*/
function extractSquareOrderId(value: unknown): CreateSquareOrderResult | null {
if (typeof value !== 'object' || value === null) {
return null;
}
const order = (value as { order?: unknown }).order;
if (typeof order !== 'object' || order === null) {
return null;
}
const id = (order as { id?: unknown }).id;
if (typeof id !== 'string') {
return null;
}
return {
squareOrderId: id,
};
}
攻略ポイント
Square Order IDは、Square世界の注文番号です。
自前DBに必ず保存します。
自社注文番号
↓
お客様・管理画面用
Square Order ID
↓
Square連携用
両方を持っておくと、運用が安定します。
10.6 注文作成に失敗した場合の処理
Square Orderの作成に失敗することがあります。
たとえば、次のようなケースです。
Access Tokenが間違っている
Location IDが間違っている
line_itemsの形式が不正
Square APIが一時的に失敗している
通信が切れた
環境がSandboxとProductionで混ざっている
このとき、何も考えずに処理を進めると、注文データが中途半端な状態になります。
起きやすい中途半端な状態
自前DBには注文がある
↓
Square Orderは作れていない
Square Orderは作れた
↓
自前DBへの保存に失敗した
Square Orderが作れたか不明
↓
通信エラーでレスポンスが返らない
このような状態を想定して設計します。
自前DB側のステータスを用意する
注文作成中や失敗を表すステータスを持つと安全です。
export type OrderStatus =
| 'draft'
| 'pending_payment'
| 'paid'
| 'square_order_failed'
| 'canceled';
流れは次のようにします。
自前DBにdraft注文を作る
↓
Square Order作成を試す
↓
成功したらpending_paymentへ進む
↓
失敗したらsquare_order_failedにする
ユーザー向け表示
Square Order作成に失敗した場合、ユーザーには内部情報を出しません。
表示してよい
↓
購入手続きを開始できませんでした。
時間をおいて再度お試しください。
表示してはいけない
↓
Square Access Tokenが無効です。
CreateOrderのline_itemsが不正です。
攻略ポイント
外部API連携では、失敗する前提で設計します。
成功したら進む
失敗したら止める
不明なら二重作成を避けて確認する
「たぶん成功したはず」で進めないことが大切です。
10.7 冪等性キーの考え方
冪等性キーは、同じ処理が重複して実行されるのを防ぐためのキーです。
Square公式ドキュメントでは、SquareはAPI操作にidempotency keyを提供できるようにすることで、事故的な重複呼び出しから保護すると説明されています。 (Square)
CreateOrder APIでも、idempotency_keyを指定します。
SquareのCreateOrder APIリファレンスでは、idempotency_keyは作成するOrderを一意に識別する値で、Orderが作成されたか不明な場合に同じキーで再試行しても重複作成を心配しなくてよいと説明されています。 (Square)
なぜ必要なのか
ECでは、同じ処理が複数回走ることがあります。
ユーザーがボタンを連打する
通信が遅くて再送される
サーバーがタイムアウトする
ブラウザが再読み込みされる
Webhookが複数回届く
もし毎回新しい注文を作ってしまうと、二重注文になります。
冪等性キーを使うと、同じ処理を同じキーで再試行できます。
1回目
↓
idempotency_key = abc123
通信エラーで不明
↓
もう一度同じキーで送る
Square
↓
同じ注文として扱う
自前DBの注文IDを使う
おすすめは、自前DBの注文IDや注文番号をもとに冪等性キーを作ることです。
/**
* 役割: 自前DBの注文IDからSquare Order作成用の冪等性キーを作る
* 入力: 自前DBの注文ID
* 出力: Square APIへ送る冪等性キー
*/
export function createSquareOrderIdempotencyKey(orderId: string): string {
return `create-square-order-${orderId}`;
}
こうすると、同じ注文に対して同じ冪等性キーを使えます。
攻略ポイント
冪等性キーは、二重作成を防ぐお守りです。
同じ注文
↓
同じ冪等性キー
別の注文
↓
別の冪等性キー
crypto.randomUUID()を毎回作るだけでは、再試行時に別処理として扱われる可能性があります。
「同じ処理には同じキー」を意識します。
10.8 同じ注文が二重作成されない設計
同じ注文が二重作成されると、ECでは大きなトラブルになります。
同じ商品が2回注文される
同じ金額が2回請求される
在庫が2回減る
メールが2通届く
発送作業が重複する
これを防ぐために、画面側、サーバー側、Square API側の3段階で対策します。
画面側の対策
購入ボタンを押したら、処理中にします。
購入ボタンを押す
↓
ボタンを無効化
↓
二度押しを防ぐ
これはユーザー体験としても重要です。
サーバー側の対策
サーバー側では、同じカート・同じ注文に対して、すでに処理中の注文がないか確認します。
同じ注文が作成済みか確認
↓
作成済みなら再利用
↓
未作成なら新規作成
DBにも一意制約を付けます。
create unique index orders_order_number_unique
on orders(order_number);
Square Order IDも、重複しないようにします。
create unique index orders_square_order_id_unique
on orders(square_order_id)
where square_order_id is not null;
Square API側の対策
CreateOrderでは、冪等性キーを使います。
{
"idempotency_key": "create-square-order-自前注文ID",
"order": {
"location_id": "LOCATION_ID",
"line_items": []
}
}
Squareの共通APIパターンのドキュメントでも、多くの作成・更新・削除系APIでは、重複呼び出しによる悪影響を防ぐために冪等性キーが使われると説明されています。 (Square)
攻略ポイント
二重注文対策は、1箇所だけでは不十分です。
画面側
↓
二度押し防止
サーバー側
↓
既存注文チェック
DB側
↓
一意制約
Square側
↓
冪等性キー
何重にも守ることで、事故を減らします。
10.9 テスト注文を作成する
最後に、Square Sandboxでテスト注文を作成します。
Square Sandboxは、本物のお金を動かさずにSquare APIや決済を試せるテスト環境です。
Square公式ドキュメントでは、Sandboxは決済や注文などをシミュレーションできる環境として案内されています。 (Square)
テストの流れ
1. カートに商品を入れる
2. 自前DBにdraft注文を作る
3. Square CreateOrderを呼ぶ
4. Square Order IDを保存する
5. Checkout APIでPayment Linkを作る
6. Square Sandbox決済ページへ移動する
7. テストカードで支払う
8. 自前DBの注文を確認する
CreateOrderの最小リクエスト例
type SquareCreateOrderRequest = {
idempotency_key: string;
order: {
location_id: string;
line_items: Array<{
name: string;
quantity: string;
base_price_money: {
amount: number;
currency: 'JPY';
};
}>;
};
};
/**
* 役割: Square CreateOrder APIへ送るリクエストボディを作る
* 入力: Location ID、冪等性キー、line_items
* 出力: Square CreateOrder API用のリクエストボディ
*/
export function buildCreateSquareOrderRequest(
locationId: string,
idempotencyKey: string,
lineItems: SquareCreateOrderRequest['order']['line_items'],
): SquareCreateOrderRequest {
return {
idempotency_key: idempotencyKey,
order: {
location_id: locationId,
line_items: lineItems,
},
};
}
CreateOrderを呼び出す例
type CreateSquareOrderResponse = {
squareOrderId: string;
};
/**
* 役割: Square Orders APIを呼び出してSquare側に注文を作成する
* 入力: Square APIの接続情報とCreateOrderリクエスト
* 出力: 作成されたSquare Order ID
*/
export async function createSquareOrder(
accessToken: string,
squareBaseUrl: string,
requestBody: SquareCreateOrderRequest,
): Promise<CreateSquareOrderResponse> {
const response = await fetch(`${squareBaseUrl}/v2/orders`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'Square-Version': '2026-05-20',
},
body: JSON.stringify(requestBody),
});
const responseData: unknown = await response.json();
if (!response.ok) {
throw new Error('Square Orderの作成に失敗しました。');
}
const result = extractSquareOrderId(responseData);
if (!result) {
throw new Error('Square Order IDを取得できませんでした。');
}
return result;
}
テストで確認すること
Square Order IDが返る
自前DBにsquare_order_idが保存される
line_itemsの数量が正しい
単価が正しい
送料が意図通りに入る
同じ冪等性キーで再試行しても二重作成されない
失敗時に注文ステータスが壊れない
攻略ポイント
Sandboxテストでは、成功パターンだけを見て終わらないことです。
成功する注文
失敗する注文
二重送信
売り切れ商品
不正な数量
Square APIエラー
失敗時の動きを確認しておくと、本番公開後の事故を減らせます。
実装全体の流れ
この章の内容を実装フローとしてまとめると、次のようになります。
カートを確認
↓
サーバー側で商品価格を再計算
↓
自前DBにdraft注文を作成
↓
order_itemsを保存
↓
Square CreateOrderを呼び出す
↓
square_order_idを自前DBに保存
↓
Checkout APIでPayment Linkを作成
↓
ユーザーをSquare決済ページへ移動
この流れを作ることで、自前DBとSquareの注文情報をつなげられます。
この章のまとめ
この章では、Square Orders APIと自前DBの注文データを連携する考え方を整理しました。
Orders APIは、Square側に注文情報を作成するためのAPIです。
CreateOrderでは、Orderオブジェクトを作成でき、line items、fulfillments、taxes、discountsなどを含められます。 (Square)
この章で扱った内容は、次の通りです。
Orders APIの役割
自前注文とSquare Orderの対応関係
line_itemsの作り方
税・送料・割引の扱い
Square側の注文IDを保存する
注文作成に失敗した場合の処理
冪等性キーの考え方
同じ注文が二重作成されない設計
テスト注文の作成
特に重要なのは、次の3つです。
1. 自前DBの注文IDとSquare Order IDを対応づける
2. line_itemsはサーバー側で正しい商品情報から作る
3. 冪等性キーで二重作成を防ぐ
Square Orderは、Square側の注文情報です。
自前DBの注文は、自社EC運用の注文情報です。
Square Order
↓
Square連携・決済側の注文
自前DB orders
↓
管理画面・配送・メール・社内運用の注文
この2つを正しくつなぐことで、Webhook、在庫管理、メール通知、管理画面へ進みやすくなります。
次の章でやること
次の章では、在庫管理を実装します。
注文を作れるようになると、次に問題になるのが在庫です。
商品が購入されたら、いつ在庫を減らすのか。
決済前に減らすのか、決済後に減らすのか。
Square Inventory APIを使うのか、自前DBで管理するのか。
次章では、在庫管理の難しさを整理しながら、最初に作るべき現実的な在庫設計を攻略していきます。