GET /v2/orders/{uniqueKey}

注文 1 件を取得する API の schema / 仕様設計。レスポンスの形と、その設計判断をレビューするためのドキュメントです。

GET /v2/orders/{uniqueKey} scope: orders.read 認証必須 読み取り専用

1. エンドポイント仕様

メソッド / パスGET /v2/orders/{uniqueKey}
パスパラメータuniqueKey(注文の一意キー、文字列)
必要スコープorders.read
返す情報注文ヘッダ・購入者と配送先・注文明細・金額サマリ・配送(部分発送履歴を含む)・決済とトランザクション・ショップメモを 1 回の呼び出しで返す
他ショップの注文取得不可。存在していても 404 を返し、存在の有無を漏らさない
副作用なし(読み取り専用)
正常時のレスポンス200 / OrderSchema
固有エラー404 /errors/orders/not-found400 /errors/request/invalid-params(共通の 401/403/500 も返る。第 9 章)

この API は、外部公開 API である base-api の注文取得(GET /1/orders/detail)を置き換えるものです。インターフェイス(パスやフィールド名)は揃えませんが、base-api でできていたことは漏れなく再現できることを要件にしています。base-api との対応は第 4 章で示します。

backend(社内基盤)にはこの API に 1 対 1 で対応する単一注文取得の WebAPI が存在しないため、本 API は backend をラップせず DB 直接取得で実装します(第 5 章)。

2. レスポンス例(200 OK)

正常時に返す JSON です。これがこの API の「契約」です。各オブジェクトがどのスキーマに対応するかと、補足が要るフィールドだけに短い説明を添えています。フィールドごとの正確な型と制約は第 8 章で schema と並べて示します。

オブジェクトの上にある小さな行は、対応するスキーマ名(order.ts の定義)です。値の右の短い説明はフィールドの補足で、どちらも JSON 本体には含まれません。

Order
{
  "uniqueKey": "UK4Z2X8M",
  "id": "81530", 注文ヘッダ内部 ID。文字列化
  "status": "ordered", 注文状態(base-api 互換のため自由文字列)
  "orderedAt": "2026-05-20T11:32:08Z",
  "cancelledAt": null,
  "via": "default", 注文経路
  "remark": "玄関前に置き配を希望", 購入者入力 / 要エスケープ
  "message": "発送までお時間をいただきます", ショップ→購入者へのメッセージ
  OrderShopMemo — ショップ用注文メモ(非公開)
  "shopMemo": {
    "text": "ギフトラッピング対応" 未入力は ""
  },
  "mailMagazineOptIn": true,
  OrderBuyer — 購入者(決済者)
  "buyer": {
    OrderBuyerName
    "name": {
      "first": "太郎",
      "last": "BASE",
      "firstKana": "タロウ",
      "lastKana": "ベース"
    },
    "mailAddress": "taro@example.com",
    OrderAddress
    "address": {
      "country": "Japan",
      "zipCode": "150-0001",
      "prefecture": "東京都",
      "address": "渋谷区神宮前1-2-3",
      "address2": "サンプルビル 4F",
      "tel": "090-1234-5678"
    }
  },
  OrderReceiver — 配送先(受取人)
  "receiver": {
    OrderReceiverName
    "name": {
      "first": "花子",
      "last": "BASE"
    },
    OrderAddress
    "address": {
      "country": "Japan",
      "zipCode": "150-0002",
      "prefecture": "東京都",
      "address": "渋谷区渋谷2-3-4",
      "address2": "受取人マンション 101",
      "tel": "080-9876-5432"
    }
  },
  OrderAmounts — 金額サマリ
  "amounts": {
    "subtotal": 5400, 商品合計(明細 total の合計)
    "shippingFee": 800,
    "codFee": null, 代引きでなければ null
    "total": 6200
  },
  OrderLine の配列 — 注文明細(古い順)
  "lines": [
    {
      "id": "85910",
      "itemId": "10001",
      "variationId": "200",
      "title": "サンプル Tシャツ", 注文時点のスナップショット
      "variation": "M / ホワイト",
      "itemIdentifier": "SKU-T-001",
      "variationIdentifier": "SKU-T-001-M-WH",
      "barcode": null,
      "price": 2700,
      "amount": 1, 数量
      "total": 2700,
      "taxMode": "standard", 標準税率(10%)
      "consumptionTaxRate": 10,
      "consumptionTotalTax": 245
    },
    {
      "id": "85911",
      "itemId": "10002",
      "variationId": null,
      "title": "コーヒー豆 200g",
      "variation": null,
      "itemIdentifier": "SKU-COFFEE-200",
      "variationIdentifier": null,
      "barcode": "4912345678901",
      "price": 2700,
      "amount": 1,
      "total": 2700,
      "taxMode": "reduced", 軽減税率(8%)
      "consumptionTaxRate": 8,
      "consumptionTotalTax": 200
    }
  ],
  OrderDelivery — 配送と部分発送履歴
  "delivery": {
    "status": "shipping", 注文単位の発送状態
    "deliveryCompanyId": "1",
    "trackingNumber": null, 注文ヘッダ側の伝票番号
    "deliveryDate": "2026-05-25",
    "deliveryTimeZone": "1416", 4 桁。12-14 は "1214"
    "dispatchedAt": "2026-05-22T03:10:00Z", 最終発送日時。未発送は null
    OrderDispatch の配列 — 部分発送(古い順)
    "dispatches": [
      {
        "id": "6250",
        "deliveryCompanyId": "1",
        "trackingNumber": "1234-5678-9012",
        "comment": "Tシャツのみ先行発送",
        "dispatchedAt": "2026-05-22T03:10:00Z",
        "orderLineIds": ["85910"] この発送に含まれる明細 ID
      }
    ]
  },
  OrderPayment — 決済
  "payment": {
    "method": "credit_card", 注文確定時の決済手段。未確定は null
    OrderPaymentTransaction の配列
    "transactions": [
      {
        "method": "credit_card",
        "transactionId": "txn_abc123",
        "status": "captured",
        "amount": 6200,
        "occurredAt": "2026-05-20T11:32:15Z"
      }
    ]
  }
}

null と空配列の使い分け。 「その概念がそもそも無い」ときは null(代引きでない注文の amounts.codFee、未確定な決済手段の payment.method など)、「リスト要素が 0 件」のときは空配列(部分発送がまだ無いなら delivery.dispatches: [])を返します。この使い分けは第 8 章で .nullable()z.array() として示します。

2.1 公開前の検討論点(フィールド命名)

レスポンス schema のフィールド命名で、レビュー時に意見をもらいたい論点です。いずれも一度公開すると変更が破壊的になるため、ここで決め切る価値が高い箇所です。

remarkshopMemo の対称性

現状は remark: string(scalar)/ shopMemo: { text: string }(Object)と非対称。shopMemo は将来の拡張余地(添付・タグ等)を見越して Object 化したが、同じく「メモに近い概念」である remark はそのまま scalar にしている。

  • 対称性優先案: remark: { text: string } にも Object 化する。将来 remark にも添付(例: 写真付き要望)が増える可能性を残せる
  • 現状維持案: remark は注文時に確定して以降の更新運用がない(編集 endpoint も無い)ため Object 化の必然性が弱く、無理に揃えると今度は message はどうするのかという話になり判断軸が広がる

判断軸: 「対称性」を取るか、「実運用上の更新有無」で個別判断するか。現状は後者だが、reviewer が前者を取るべきと感じるなら見直す。

buyer / receiver の命名

現状は web/shopadmin の domain class(Buyer / OrderReceiver)に揃えている。ただし業界 / 既存 API では別の用語も流通している。

  • buyer / receiver(現状): web domain と一致。「決済者」と「受取人」の区別が明示的
  • customer / shippingAddress: base-api / Shopify 寄り。ただし shippingAddress は氏名や電話番号も内包するので名前が実態と乖離する
  • customer / shipping: Stripe 寄り。簡潔だが「shipping」が誰なのか曖昧
  • billing / shipping: 決済 / 配送の対比。ただし「billing」は請求書発行の文脈に寄り、購入者一般を指すには狭い

判断軸: web/shopadmin との整合(現状)/ 業界用語との整合 / base-api 互換の優先度。

lines の命名

現状は lines: OrderLine[]。短いが、外部公開 API のフィールド名として意味が薄い可能性がある。

  • lines(現状): 簡潔。文脈で「注文明細」と読める
  • items: 直感的だが、商品カタログの itemId と紛らわしい
  • lineItems: Stripe / Shopify の業界標準(line_items)。長いが意味が明確
  • orderLines: web/backend の domain 命名と一致。ただし親 order が自明な文脈で order 接頭辞は冗長

判断軸: 簡潔さ vs 業界標準 / 既存 domain との整合。

4. base-api との対応と差分

この API は外部公開 API である base-api の注文取得(OAuth 公開エンドポイント)を置き換えます。参照したソースは次のとおりです。

ソース APIGET /1/orders/detail/:unique_key
取得範囲の基準OAuth 公開エンドポイントに揃える。管理画面用の /shop_admin/api/orders/view 由来の項目は今回のスコープ外

base-api と本 API のレスポンスを、対応する項目が同じ行に並ぶよう突き合わせています。base-api 側は本 API の構造順に並べ替えてあり、base-api の実際のキー順とは異なります。本 API で新設した項目や入れ物のカッコは base-api 側が空欄になります。base-api が決済手段ごとに別キーで持っていた取引情報は、本 API では 1 つの配列にまとめています。

base-api(旧)本 API(新) 対応が無い行は空欄(グレー)になります。値の右の小さな注記は補足です。

base-api(旧)— /1/orders/detail
本 API(新)— /v2/orders/{uniqueKey}
{Order
"unique_key": "UK4Z2X8M",
"uniqueKey": "UK4Z2X8M",
"order_id": 81530,
"id": "81530", 数値→文字列
"status": "ordered",
"status": "ordered",
"ordered": "2026-05-20 20:32:08", MySQL datetime 文字列
"orderedAt": "2026-05-20T11:32:08Z", ISO 8601 (UTC)
"cancelled": null,
"cancelledAt": null,
"via": "default",
"via": "default",
"remark": "玄関前に置き配を希望",
"remark": "玄関前に置き配を希望",
"add_comment": "発送までお時間をいただきます",
"message": "発送までお時間をいただきます", 名称変更
"shopMemo": { "text": "ギフトラッピング対応" }, 旧 order_remarks.remarks(別 API)を集約。Object 化で将来拡張余地
"mail_magazine_opt_in": 1,
"mailMagazineOptIn": true,
"buyer": {OrderBuyer — 購入者
"name": {OrderBuyerName
"first_name": "太郎",
"first": "太郎",
"last_name": "BASE",
"last": "BASE",
"first_name_kana": "タロウ",
"firstKana": "タロウ",
"last_name_kana": "ベース",
"lastKana": "ベース"
},
"mail_address": "taro@example.com",
"mailAddress": "taro@example.com",
"address": {OrderAddress
"country": null, 旧は null 多め
"country": "Japan",
"zip_code": "150-0001",
"zipCode": "150-0001",
"prefecture": "東京都",
"prefecture": "東京都",
"address": "渋谷区神宮前1-2-3",
"address": "渋谷区神宮前1-2-3",
"address2": "サンプルビル 4F",
"address2": "サンプルビル 4F",
"tel": "090-1234-5678",
"tel": "090-1234-5678"
}
},
"receiver": {OrderReceiver — 配送先
"order_receiver": { base-api でも別キー
"name": {OrderReceiverName
"first_name": "花子",
"first": "花子",
"last_name": "BASE",
"last": "BASE"
},
"address": {OrderAddress
"country": "Japan",
"country": "Japan",
"zip_code": "150-0002",
"zipCode": "150-0002",
"prefecture": "東京都",
"prefecture": "東京都",
"address": "渋谷区渋谷2-3-4",
"address": "渋谷区渋谷2-3-4",
"address2": "受取人マンション 101",
"address2": "受取人マンション 101",
"tel": "080-9876-5432"
"tel": "080-9876-5432"
}, 購入者と配送先が同一なら省略されることあり
}
},
"amounts": {OrderAmounts
"subtotal": 5400,
"subtotal": 5400,
"shipping_fee": 800,
"shippingFee": 800,
"cod_fee": null,
"codFee": null,
"total": 6200,
"total": 6200
},
"details": [ 改名 → lines
"lines": [OrderLine の配列
{
{
"order_id": 85910,
"id": "85910", 数値→文字列
"item_id": 10001,
"itemId": "10001",
"variation_id": 200,
"variationId": "200",
"title": "サンプル Tシャツ",
"title": "サンプル Tシャツ",
"variation": "M / ホワイト",
"variation": "M / ホワイト",
"item_identifier": "SKU-T-001",
"itemIdentifier": "SKU-T-001",
"variation_identifier": "SKU-T-001-M-WH",
"variationIdentifier": "SKU-T-001-M-WH",
"barcode": null,
"barcode": null,
"price": 2700,
"price": 2700,
"amount": 1,
"amount": 1,
"total": 2700,
"total": 2700,
"item_tax_type": 1, 1=標準 / 2=軽減(整数)
"taxMode": "standard", enum に変換
"consumption_tax_rate": 10,
"consumptionTaxRate": 10,
"consumption_total_tax": 245
"consumptionTotalTax": 245
}
}
],
],
"delivery": {OrderDelivery
"dispatch_status": "shipping", 名称変更
"status": "shipping",
"delivery_company_id": 1,
"deliveryCompanyId": "1",
"tracking_number": null,
"trackingNumber": null,
"delivery_date": "2026-05-25",
"deliveryDate": "2026-05-25",
"delivery_time_zone": "1416",
"deliveryTimeZone": "1416",
"dispatched": "2026-05-22 12:10:00",
"dispatchedAt": "2026-05-22T03:10:00Z", ISO 8601
"dispatches": [OrderDispatch の配列
(別 API でしか取れなかった) dispatched_logs / dispatched_orders
{
"id": "6250",
"deliveryCompanyId": "1",
"trackingNumber": "1234-5678-9012",
"comment": "Tシャツのみ先行発送",
"dispatchedAt": "2026-05-22T03:10:00Z",
"orderLineIds": ["85910"]
}
]
},
"payment": {OrderPayment
"payment": "credit_card",
"method": "credit_card",
"transaction_id": "txn_abc123", 決済手段ごとに別キー
"transactions": [OrderPaymentTransaction の配列
"transaction_type": "captured",
{
"payment_type": "credit_card",
"method": "credit_card",
"transactionId": "txn_abc123",
"status": "captured",
"amount": 6200,
"occurredAt": "2026-05-20T11:32:15Z"
}
]
}
}

base-api で取れていた項目は、本 API でも漏れなく取れます。 あわせて、base-api では別 API でしか取れなかった部分発送履歴(dispatched_logs)とショップメモ(order_remarks)を本 API に集約し、注文 1 件の取得を 1 リクエストで済むようにしました。

購入者と配送先の分離(特に注意)

base-api はトップレベルに購入者(first_name / last_name / mail_address / address など)をフラットに置き、配送先(受取人)だけを order_receiver という別キーで返していました。配送先が省略されている場合は「購入者 = 配送先」と解釈する暗黙ルールでした。

本 API では buyer(購入者)と receiver(配送先)の 2 オブジェクトに揃え、同一だった場合でも receiver を常に返します。caller 側の暗黙ルール(無ければ buyer を写す)を排し、配送先が明示的にレスポンスから読み取れるようにする狙いです。

  • buyer: 氏名(kana あり)/メール/住所。order_headers の購入者列が出所
  • receiver: 氏名(kana なし)/住所のみ。order_receivers が出所

税区分の表し方(特に注意)

base-api の item_tax_type は「税率」ではありません1=標準税率対象、2=軽減税率対象、を表す数値です。実際の税率は別フィールドの consumption_tax_rate が持ちます。

本 API では数値を文字列の列挙に変えて、誤読を防いでいます。

  • taxMode: standard(標準税率対象。旧 1)/ reduced(軽減税率対象。旧 2
  • consumptionTaxRate: 適用税率のパーセント(例: 108

5. backend との対応

このセクションは backend(社内基盤)との対応関係を示します。

重要backend には、注文 1 件の詳細を返す単独の取得 endpoint が存在しません。 注文の検索(POST /v1/orders/search)と編集系(住所変更・発送登録・キャンセル等)の endpoint しか無いことを確認済みです。そのため本 API は backend をラップせず、basedev の DB を直接読み出して構築します

取得側は backend に依存せず DB 直接で組み立てますが、編集系の Schema(住所変更や発送登録のリクエスト形)は、別 PR で追加する際に backend の編集 API スキーマを参照する方針です。本ドキュメントは取得 endpoint のみを扱うため、その対応は別途整理します。

形の土台にした backend のモデル構造と、本 API のフィールド対応を、新設計を基準に並べます。

backend モデル本 API 対応が無い側は空欄(グレー)になります。

backend モデル
本 API
@@ 構造の対応 — backend モデル → 本 API @@
customerInformation.name / mailAddress / address
buyer.name / buyer.mailAddress / buyer.address
customerInformation.shippingAddress
receiver.address + receiver.name 配送先を独立オブジェクトへ
orderLines[]
lines[] 改名
dispatchedLogs[]
delivery.dispatches[] delivery に内包
送料 / 代引手数料 / 合計
amounts
paymentInformation + transactions
payment / payment.transactions[]
orderRemark.text
shopMemo.text ヘッダ直下に持ち上げ + Object 化
@@ backend の取得 endpoint が無いため、本 API は DB 直接取得 @@
(取得 API なし)
order_headers + orders + dispatched_logs + order_remarks + order_transactions を結合

backend で別ドメインに分かれている部分も、API の利用者から見れば「1 回の注文取得で得たい情報」なので、本 API のレスポンスにまとめます。実装上は basedev のテーブル群を直接結合します(handler は別ラインで実装中)。

6. web (shopadmin) との対応

注文詳細画面(管理画面のショップオーナーが見る画面)は web (shopadmin) 側で実装されています。本 API の OrderSchema の形は、その画面が実際に扱っているドメインモデルとも整合させています。

web/shopadmin は注文詳細をドメインクラスで保持しており、購入者は Buyer、配送先は OrderReceiver という別クラスです(frontend/shopadmin/orders/src/domain/order-detail.ts)。本 API もこの分離に合わせて buyer / receiver を別オブジェクトにしています。

web ドメインクラス(order-detail.ts)本 API(OrderSchema) 対応が無い側は空欄(グレー)になります。

web ドメインクラス
本 API
@@ 購入者(決済者) @@
class Buyer
OrderBuyer
firstName / lastName
buyer.name.first / .last
firstNameKana / lastNameKana string | null
buyer.name.firstKana / .lastKana string | null
mailAddress string | null
buyer.mailAddress 本 API は非 null
address: { country, zipCode, prefecture, address1, address2 } | null
buyer.address: OrderAddress
tel string | null
buyer.address.tel address 配下に集約
crossCart: { country, state } 海外越境 EC 用
@@ 配送先(受取人) @@
class OrderReceiver
OrderReceiver
firstName / lastName kana なし
receiver.name.first / .last
address: { country, zipCode, prefecture, address1, address2 }
receiver.address: OrderAddress
tel
receiver.address.tel

UI 上の対応

注文詳細画面の対応セクションは orders-detail-container.vue にあります。

画面セクションweb のデータソース本 API の対応
「お届け先情報」orderDetail.orderReceiverreceiver
「購入者情報」orderDetail.buyerbuyer
「お届け先を編集」モーダルedit-order-receiver-container.vuePATCH …/receiver(§3.2)
「メールアドレスを編集」モーダルedit-order-mail-address-*PATCH …/buyer{ mailAddress } を送信(§3.3)

差分について

  • 「お届け先と同じ」表示の扱い: web/shopadmin は「購入者と配送先が同一の場合、購入者情報欄に『お届け先と同じ』と表示し、kana だけを購入者欄から表示」というロジックを orderReceiver の getter で持っています。本 API は buyerreceiver を独立で常に返すので、この表示判定は caller(または web 側)の責務になります。buyer.addressreceiver.address を caller 側で比較してください
  • buyer.mailAddress の non-null: web 側は mailAddress: string | null ですが、本 API は order_headers.mail_addressNOT NULL DEFAULT '' なので non-null として返します。空文字 "" の可能性は残ります(web 側の null は legacy / 海外越境注文の表現)
  • address1 vs address: web は address1 / address2、本 API は address / address2。DB カラム名(address / address2)と base-api 互換を優先しました
  • crossCart: web の Buyer は海外越境 EC 用に crossCart を持ちますが、本 API の round 1 範囲では除外しています(第 10 章の「特殊タイプ注文」と同じ扱いで、別 round で追加)

7. スキーマの全体構成

第 2 章の注釈で示したとおり、JSON の各オブジェクトは order.ts のスキーマに対応します。注文は項目が多いので、関連する固まりごとに小さなスキーマに分け、トップの Order がそれらを組み合わせています。下はその俯瞰図です(| null は null 許容、[] は配列)。

Order                          ... レスポンス全体
├─ uniqueKey / id              ... 注文の一意キー・内部 ID
├─ status                      ... 注文状態(自由文字列)
├─ orderedAt / cancelledAt     ... 日時(ISO 8601, UTC)
├─ via / remark / message      ... 経路 / 購入者備考 / ショップ→購入者メッセージ
├─ shopMemo        : OrderShopMemo     ... ショップ用注文メモ(非公開)
│   └─ text                            ... メモ本文(未入力は "")
├─ mailMagazineOptIn           ... メルマガ同意(null 可)
├─ buyer           : OrderBuyer        ... 購入者(決済者)
│   ├─ name             : OrderBuyerName  ... 氏名 + kana
│   ├─ mailAddress                         ... 連絡先メールアドレス
│   └─ address          : OrderAddress     ... 請求住所
├─ receiver        : OrderReceiver     ... 配送先(受取人)
│   ├─ name             : OrderReceiverName ... 氏名(kana なし)
│   └─ address          : OrderAddress
├─ amounts         : OrderAmounts
├─ lines[]         : OrderLine
├─ delivery        : OrderDelivery
│   └─ dispatches[]     : OrderDispatch
└─ payment         : OrderPayment
    └─ transactions[]   : OrderPaymentTransaction

右側の名前(OrderBuyer など)は order.ts のスキーマ名であり、生成される OpenAPI 上の型名でもあります。各オブジェクトの個別フィールドは第 2 章の注釈、もしくは第 8 章の schema 対応で確認できます。

8. Zod schema の詳細

ここからがレビュー対象のスキーマ定義です。order.ts に定義した Zod schema を、対応するレスポンスの JSON を添えて 1 つずつ見ていきます。左が schema 定義、右がその schema が表す JSON です。注文全体(Order)から始め、内側のオブジェクトへ降りていきます。設計上の判断は、それが最初に現れるスキーマのところに「設計メモ」として添えています。

Zod schema(order.ts)サンプル JSON schema 側の .describe() は紙幅のため一部省略しています。

7.1 注文全体

Order — 注文 1 件全体(ルート)

Zod schema
サンプル JSON
export const OrderSchema = z.object({ uniqueKey: z.string(), id: z.string(), status: z.string(), orderedAt: z.string().datetime(), cancelledAt: z.string().datetime().nullable(), via: z.string(), remark: z.string().nullable(), message: z.string().nullable(), shopMemo: ShopMemoSchema, mailMagazineOptIn: z.boolean().nullable(), buyer: BuyerSchema, receiver: ReceiverSchema, amounts: AmountsSchema, lines: z.array(OrderLineSchema), delivery: DeliverySchema, payment: PaymentSchema, }).openapi("Order");
{ "uniqueKey": "UK4Z2X8M", "id": "81530", "status": "ordered", "orderedAt": "2026-05-20T11:32:08Z", "cancelledAt": null, "via": "default", "remark": "玄関前に置き配を希望", "message": "発送までお時間をいただきます", "shopMemo": { "text": "ギフトラッピング対応" }, "mailMagazineOptIn": true, "buyer": { ... }, "receiver": { ... }, "amounts": { ... }, "lines": [ ... ], "delivery": { ... }, "payment": { ... } }

レスポンスのルート。注文 1 件を表し、各フィールドが下位のスキーマを参照します。以降はこの内側のオブジェクトを順に見ていきます。

設計メモ:日時はすべて ISO 8601 文字列(UTC)です。base-api は MySQL datetime 文字列でした。発生していない日時(cancelledAt 等)は nullremark / message / shopMemo は注文ヘッダの属性なので order 直下に置きます。購入者属性は buyer、配送先属性は receiver に分けています(base-api / web の分け方に対応)。購入者が「この注文に向けて」入力した備考は注文側(remark)に置く、という分け方です。status は base-api 互換のため z.string() で受け、enum で締めるかは将来判断します。

7.2 購入者と配送先

購入者(決済者)と配送先(受取人)は別概念として扱います。DB 上も order_headers(購入者)と order_receivers(配送先)で別テーブルに分かれており、web/shopadmin のドメインモデル(Buyer / OrderReceiver クラス)も別オブジェクトとして扱っています。本 API では buyerreceiver の 2 オブジェクトに揃え、購入者と配送先が同一の注文でも常に両方を返します。kana 氏名は購入者にのみ登録される項目で、配送先には存在しません。

OrderBuyer — 購入者(決済者)

Zod schema
サンプル JSON
const BuyerSchema = z.object({ name: BuyerNameSchema, mailAddress: z.string(), address: AddressSchema, }).openapi("OrderBuyer");
"buyer": { "name": { ... }, "mailAddress": "taro@example.com", "address": { ... } }

購入者の氏名・連絡先メール・請求住所。order_headers の購入者列が出所です。メールアドレスは購入者にのみ紐付き、配送先には存在しません。

OrderBuyerName — 購入者氏名

Zod schema
サンプル JSON
const BuyerNameSchema = z.object({ first: z.string(), last: z.string(), firstKana: z.string().nullable(), lastKana: z.string().nullable(), }).openapi("OrderBuyerName");
"name": { "first": "太郎", "last": "BASE", "firstKana": "タロウ", "lastKana": "ベース" }

購入者の氏名。firstKana / lastKana は未登録時に null。base-api は first_name / last_name / first_name_kana / last_name_kana がトップレベルにフラットに並んでいましたが、本 API は name オブジェクトに集約しています。

OrderReceiver — 配送先(受取人)

Zod schema
サンプル JSON
const ReceiverSchema = z.object({ name: ReceiverNameSchema, address: AddressSchema, }).openapi("OrderReceiver");
"receiver": { "name": { ... }, "address": { ... } }

配送先の氏名と住所。order_receivers が出所で、メールアドレスは持ちません。購入者と配送先が同一の場合でも、このオブジェクトは常に返します(base-api のように「省略されたら purchaser を写す」というルールを caller に課さない)。

設計メモ:配送先の編集は別の PATCH endpoint(PATCH …/receiver)に切り出します。「お届け先を変更」は購入者ではなく受取人を編集する操作なので、編集境界もそのままパス階層に対応します。

OrderReceiverName — 配送先氏名

Zod schema
サンプル JSON
const ReceiverNameSchema = z.object({ first: z.string(), last: z.string(), }).openapi("OrderReceiverName");
"name": { "first": "花子", "last": "BASE" }

配送先の氏名。kana は持ちません(order_receivers テーブルに該当カラムが無いため)。

OrderAddress — 住所(共通)

Zod schema
サンプル JSON
const AddressSchema = z.object({ country: z.string(), zipCode: z.string(), prefecture: z.string(), address: z.string(), address2: z.string(), tel: z.string().nullable(), }).openapi("OrderAddress");
"address": { "country": "Japan", "zipCode": "150-0001", "prefecture": "東京都", "address": "渋谷区神宮前1-2-3", "address2": "サンプルビル 4F", "tel": "090-1234-5678" }

購入者・配送先で共通の住所構造。tel は未登録時に null。郵便番号のハイフン有無は保存時のままを返します(正規化はしない)。

7.3 金額サマリ

OrderAmounts — 金額の集計

Zod schema
サンプル JSON
const AmountsSchema = z.object({ subtotal: z.number().int(), shippingFee: z.number().int().nullable(), codFee: z.number().int().nullable(), total: z.number().int(), }).openapi("OrderAmounts");
"amounts": { "subtotal": 5400, "shippingFee": 800, "codFee": null, "total": 6200 }

注文の金額サマリ。subtotal は明細 total の合計、total は注文合計です。shippingFee は無料 / 未確定なら nullcodFee は代引きでなければ null

設計メモ:クーポン値引き・コイン値引き・サービス利用料・追加手数料はこの round では含めていません。base-api 側にも対応値がありますが、本 API の最小スコープは「注文ヘッダ + 明細 + 送料 + 代引手数料 + 合計」にとどめ、別 round で amounts を拡張する設計です。

7.4 注文明細

OrderLine — 注文明細 1 行

Zod schema
サンプル JSON
const OrderLineSchema = z.object({ id: z.string(), itemId: z.string(), variationId: z.string().nullable(), title: z.string(), variation: z.string().nullable(), itemIdentifier: z.string().nullable(), variationIdentifier: z.string().nullable(), barcode: z.string().nullable(), price: z.number().int(), amount: z.number().int(), total: z.number().int(), taxMode: z.enum(["standard", "reduced"]), consumptionTaxRate: z.number().int(), consumptionTotalTax: z.number().int(), }).openapi("OrderLine");
"lines": [ { "id": "85910", "itemId": "10001", "variationId": "200", "title": "サンプル Tシャツ", "variation": "M / ホワイト", "itemIdentifier": "SKU-T-001", "variationIdentifier": "SKU-T-001-M-WH", "barcode": null, "price": 2700, "amount": 1, "total": 2700, "taxMode": "standard", "consumptionTaxRate": 10, "consumptionTotalTax": 245 } ]

注文された商品 1 行ぶん。title / variation / price は注文時点のスナップショットです。totalprice × amount をサーバ側で保存している値を返します。

設計メモ:ID 系(id / itemId / variationId)は数値ではなく文字列で返します(リポジトリ方針)。amount は「数量」を表します(金額ではありません。base-api 同じ命名で混乱しやすい点)。taxMode は base-api の item_tax_type1=標準 / 2=軽減)を standard / reduced に変換します。base-api の方式は数値だけ見ても意味が分からないため、文字列の列挙にしました(第 4 章の警告も参照)。consumptionTotalTax は明細の消費税額(円)で、税率(パーセント)の consumptionTaxRate とは別の値です。

7.5 配送

OrderDelivery — 配送情報

Zod schema
サンプル JSON
const DeliverySchema = z.object({ status: z.string(), deliveryCompanyId: z.string().nullable(), trackingNumber: z.string().nullable(), deliveryDate: z.string().nullable(), deliveryTimeZone: z.string().nullable(), dispatchedAt: z.string().datetime().nullable(), dispatches: z.array(DispatchSchema), }).openapi("OrderDelivery");
"delivery": { "status": "shipping", "deliveryCompanyId": "1", "trackingNumber": null, "deliveryDate": "2026-05-25", "deliveryTimeZone": "1416", "dispatchedAt": "2026-05-22T03:10:00Z", "dispatches": [ ... ] }

注文全体の配送情報。status は注文単位の発送状態(base-api 互換のため自由文字列。代表値は unshipped / shipping / dispatched)。trackingNumber は注文ヘッダ側の伝票番号で、部分発送ごとの伝票は dispatches[].trackingNumber にあります。deliveryTimeZone は 4 桁文字列(例: "1214" = 12-14 時帯)。

設計メモ:base-api では発送履歴(dispatched_logs)と所属明細(dispatched_orders)が別 API にあり、注文詳細だけでは部分発送の中身が分かりませんでした。本 API では delivery.dispatches[] として同一 endpoint に集約します。発送が無ければ空配列、最終発送日時の dispatchedAt は未発送時に null

OrderDispatch — 部分発送 1 回分

Zod schema
サンプル JSON
const DispatchSchema = z.object({ id: z.string(), deliveryCompanyId: z.string().nullable(), trackingNumber: z.string().nullable(), comment: z.string().nullable(), dispatchedAt: z.string().datetime(), orderLineIds: z.array(z.string()), }).openapi("OrderDispatch");
"dispatches": [ { "id": "6250", "deliveryCompanyId": "1", "trackingNumber": "1234-5678-9012", "comment": "Tシャツのみ先行発送", "dispatchedAt": "2026-05-22T03:10:00Z", "orderLineIds": ["85910"] } ]

1 回の発送に含まれる明細(orderLineIds)と、その発送の伝票・コメント・発送日時。配列は古い順で返します。

設計メモ:1 つの注文を複数回に分けて発送する(部分発送)ケースに対応するため、各 dispatch が「どの明細をまとめて発送したか」を orderLineIds で持ちます。明細側からは持たせず、dispatch 側に集約しています。

7.6 決済

OrderPayment — 決済

Zod schema
サンプル JSON
const PaymentSchema = z.object({ method: z.string().nullable(), transactions: z.array(PaymentTransactionSchema), }).openapi("OrderPayment");
"payment": { "method": "credit_card", "transactions": [ ... ] }

注文確定時の決済手段(method)と、関連する取引履歴(transactions)。手段が未確定なら methodnull、取引がまだ無ければ transactions は空配列です。

設計メモ:base-api は c_c_payment_transaction / cvs_payment_transaction など決済手段ごとに別キーで持っていました。本 API は決済手段の数だけ分岐するキーを廃止し、transactions[] という 1 つの配列に集約します。各要素の method でどの手段の取引かを区別する形式です。

OrderPaymentTransaction — 決済トランザクション 1 件

Zod schema
サンプル JSON
const PaymentTransactionSchema = z.object({ method: z.string(), transactionId: z.string().nullable(), status: z.string().nullable(), amount: z.number().int().nullable(), occurredAt: z.string().datetime().nullable(), }).openapi("OrderPaymentTransaction");
"transactions": [ { "method": "credit_card", "transactionId": "txn_abc123", "status": "captured", "amount": 6200, "occurredAt": "2026-05-20T11:32:15Z" } ]

決済手段ごとの取引 1 件。method は決済手段、status は各決済手段の生のステータス値です。amount / occurredAt は手段によって取得できないことがあり、その場合 null

設計メモ:method / status は値を確定できなかったため、いったん自由文字列にしています。値の整理がついた段階で列挙に締めたい箇所で、第 10 章の論点として挙げています。

7.7 ショップメモ

OrderShopMemo — ショップ用注文メモ(非公開)

Zod schema
サンプル JSON
const ShopMemoSchema = z.object({ text: z.string(), }).openapi("OrderShopMemo");
"shopMemo": { "text": "ギフトラッピング対応" }

ショップが書く注文メモ(非公開、購入者には見えない)。order_remarks.remarks 由来。レコード未作成の注文は { text: "" } を返します(DB の order_remarks.remarksNOT NULL なので text も non-null)。

設計メモ:現状は text のみだが、scalar string ではなく Object として表現します。理由は (a) 将来テキスト以外(添付・タグ・最終更新日時など)を追加する余地を残すため。scalar から Object への変更は破壊的変更になるが、最初から Object でラップしておけば後からフィールド追加は非破壊。(b) サブリソース endpoint PATCH /v2/orders/{uniqueKey}/shop_memo を「複数フィールドを持つ Object に対する子リソース更新」として正当化できる(§3.6)。remark / message は注文時に確定して以降更新運用がないため scalar のままで、Object 化判断は属性ごとに行います。

9. エラー応答

エラーは RFC 9457 の Problem Details 形式に統一しています(ADR-0015)。このエンドポイントが返すエラーは次のとおりです。

状況HTTPtype
注文が存在しない / 他ショップの注文404/errors/orders/not-found
uniqueKey が空などパスが不正400/errors/request/invalid-params
認証されていない401/errors/auth/unauthenticated
orders.read スコープが無い403/errors/auth/forbidden
サーバー内部エラー500/errors/internal/unexpected

呼び出し元のショップに属さない注文は、存在の有無を漏らさないよう一律 404 を返します。各エラーの定義は packages/api/src/lib/problems.ts にあります。

404 のレスポンス例

{
  "type": "/errors/orders/not-found",
  "title": "Order not found",
  "status": 404
}

10. 設計判断(レビュー論点)

レビューで判断を仰ぎたい点です。値の形・粒度・命名に絞っています。

  1. 互換基準は OAuth 公開の /1/orders/detail: 取得範囲は公開 API の範囲に揃え、管理画面用 /shop_admin/api/orders/view の独自項目は今回入れていません。後から追加する余地はあります
  2. サブリソースのネスト: buyer / receiver / amounts / delivery / payment に集約し、編集境界(住所変更、発送登録、キャンセル等)は別 PR で _links として追加予定です。購入者と配送先の分離(base-api が order_receiver でだけ別キーにしていた配送先を receiver として独立させ、購入者と同一の注文でも常に返す)の妥当性をレビューで確認したい
  3. 特殊タイプ注文(オファー / 定期 / 受取人払い / かんたん配送 / テイクアウト): 最小限の round 1 では特殊フラグや固有フィールドを含めていません。後の round で flags または optional フィールドで対応する設計
  4. 不正・チャージバック関連: 別 endpoint として後日切り出し、本 endpoint には含めません
  5. 決済を transactions[] に集約: 旧の「決済手段ごとに別キー」を 1 配列に集約。情報量は同等ですが参照方法が変わります
  6. 日時を ISO 8601 (UTC) に: base-api は MySQL datetime 文字列でした。移行負担になるなら再検討
  7. ID を文字列に: base-api は数値。リポジトリ方針に従って文字列化しています
  8. 税区分を文字列 enum に: item_tax_type1/2)を taxMode: "standard" | "reduced" に。意味が読みやすくなる代わりに値が増えます(軽減税率の対象外でも standard を必ず返す)
  9. 部分発送を本 API に統合: 旧は別 API でしか取れなかった dispatched_logsdelivery.dispatches[] に統合。注文 1 件取得の 1 リクエスト化が目的
  10. delivery.status / OrderPaymentTransaction.status を自由文字列に: base-api 互換のため全値を受け入れる z.string()。確定できた段階で enum に締めたい
  11. remark / message / shopMemoorder 直下に: 当初 buyer 配下に置こうとしましたが、DB の order_headers.remark / add_comment 直下、shopMemo は別テーブル order_remarks.remarks 由来であり、いずれも注文ヘッダの属性なので order 直下に置きました
  12. shopMemo を Object に: DB は order_remarks.remarks の scalar string ですが、レスポンスでは { text: string } の Object でラップします。将来テキスト以外(添付・タグ等)の追加余地を残すためで、scalar から Object への変更は破壊的変更になるので最初から Object 化。これによりサブリソース endpoint PATCH /v2/orders/{uniqueKey}/shop_memo(§3.6)が「真の Object に対する子リソース更新」として正当化できます。remark / message は注文時に確定して以降更新運用がないため scalar 維持
  13. backend ラップせず DB 直接取得: backend に注文 1 件取得の WebAPI が無いため、basedev の DB を直接結合して構築します。編集系の Schema は backend の編集 API スキーマを参照する方針(別 PR)

11. 対応するコード

このドキュメントが扱うレスポンス契約は、リポジトリ上の次のファイルにあります。レビューはこれらのファイルに対して行ってください。

ファイルこのドキュメントとの対応
packages/api/src/lib/schemas/order.ts第 8 章でサンプル JSON と対応づけた OrderSchema とその構成要素
packages/api/src/routes/v2/orders/get.ts第 1 章の仕様(パス・スコープ・レスポンス・固有エラー)に対応するルート定義
packages/api/src/routes/v2/orders/index.ts本 endpoint を orders ルーターにマウントする結線
packages/api/src/lib/scopes.ts追加した orders.read スコープの定義
packages/api/src/lib/problems.ts第 9 章のエラー定義(/errors/orders/not-found を追加)

handler(実データを取り出す処理)は get.ts 内でいまはスタブです。データ取得は別ラインで実装中で、このドキュメントが扱うのは入出力契約(レスポンスの形)です。


関連: ADR-0004(スコープ体系) / ADR-0007(コードから OpenAPI を生成) / ADR-0015(エラー応答形式) / ADR-0016(関連リンク機構)