` で生成する。
diff --git a/src/Eccube/Attribute/README.html b/src/Eccube/Attribute/README.html
new file mode 100644
index 00000000000..39146cde6d6
--- /dev/null
+++ b/src/Eccube/Attribute/README.html
@@ -0,0 +1,100 @@
+
+
+
+
+
+Attribute — 仕様
+
+
+
+
+Attribute — EC-CUBE 独自の PHP 属性
+
+ 索引: README.md
+ / 関連: PurchaseFlow
+ ・ DependencyInjection
+
+
+
+ 概要
+ このディレクトリは、EC-CUBE が独自に定義する PHP8 属性(Attribute)を集めた場所です。
+ いずれも クラス・プロパティ・メソッドに「印(マーカー)」を付けるためだけの宣言で、
+ 属性そのものはロジックを持ちません。付けられた印を別の場所(CompilerPass・EventListener・拡張サービス)が
+ Reflection で拾って動作を変える、という使われ方をします。
+
+ 用途で分けると 3 系統あります。
+
+ | 系統 | 属性 | 拾う側 |
+ | 受注処理フローへの登録マーカー | CartFlow / ShoppingFlow / OrderFlow | PurchaseFlowPass |
+ | 拡張機構(エンティティ・フォーム) | EntityExtension / FormAppend | EntityProxyService / DoctrineOrmExtension |
+ | コントローラのアクセス制限 | ForwardOnly | ForwardOnlyListener |
+
+
+
+
+ 属性一覧
+
+ | 属性 | 対象 | 意味 |
+ #[CartFlow] | クラス | この Processor/Validator を cart フローに登録する印。 |
+ #[ShoppingFlow] | クラス | 同じく shopping フローへの登録マーカー。 |
+ #[OrderFlow] | クラス | 同じく order フローへの登録マーカー。 |
+ #[EntityExtension('...')] | クラス(繰り返し可) | トレイトを既存エンティティへ合成する対象クラス名を指定。プロキシ生成の入力になる。 |
+ #[FormAppend] | プロパティ | エンティティ拡張で追加したプロパティを、既存フォームへ差し込む設定(type / options / form_theme / auto_render / style_class)。 |
+ #[ForwardOnly] | メソッド | そのコントローラアクションを forward(内部転送)専用にし、直接 URL アクセスを拒否する印。 |
+
+ CartFlow / ShoppingFlow / OrderFlow は引数なしの純粋なマーカーです。
+ 対象フローが名前で決まるだけで、優先度などの制御は持ちません(順序制御が必要なら YAML タグ側で行う)。
+
+
+
+ 受注フロー登録マーカーの仕組み開発者向け
+ #[CartFlow] / #[ShoppingFlow] / #[OrderFlow] は、プラグインや app/Customize が
+ 受注処理の部品(Processor/Validator)をどのフローに載せるかを宣言するためのものです。
+ Processor/Validator クラスに #[ShoppingFlow] を付与
+ ↓ 基底クラスの継承で自動タグ付け
+PurchaseFlowPass が ReflectionAttribute::IS_INSTANCEOF で属性を検出
+ ↓
+eccube.purchase.flow.shopping へ addMethodCall で配線(YAML 配線済みなら重複登録しない)
+ 詳しい配線ロジックは DependencyInjection の PurchaseFlowPass、
+ 受注処理そのものの全体像は PurchaseFlow を参照してください。
+
+
+
+ 拡張機構マーカーの仕組み開発者向け
+ #[EntityExtension] と #[FormAppend] は、コアを改変せずにエンティティ・フォームを
+ 拡張するための印です(アップグレード安全なカスタマイズ)。
+
+ | 属性 | 拾う側 | 動作 |
+ #[EntityExtension(Target::class)] | EntityProxyService | トレイトに付けると、value で指定した対象エンティティにそのトレイトを合成したプロキシを生成する(IS_INSTANCEOF で検出)。複数指定のため IS_REPEATABLE。 |
+ #[FormAppend] | DoctrineOrmExtension(FormTypeExtension) | 拡張プロパティに付けると、その type・options 等を読み取り、対応する既存フォームへ自動でフィールドを追加する。 |
+
+ 実運用のサンプルは app/Plugin/EntityExtension/・app/Plugin/EntityForm/ にあります。
+
+
+
+ 注意点開発者向け
+
+ - 属性は宣言だけ・ロジックは持たない。挙動を追うときは「付けた側」ではなく「拾う側」(
PurchaseFlowPass / EntityProxyService / DoctrineOrmExtension / ForwardOnlyListener)を読む。
+ #[EntityExtension] を足したらプロキシ再生成が必要(bin/console eccube:generate:proxies)。付けただけでは反映されない。
+ - フロー登録マーカーは順序を持たない。実行順の制御が必要なときは
purchaseflow.yaml の priority 付きタグを使う(属性とタグの併用はしない)。
+ #[ForwardOnly] は直接アクセス時に AccessDeniedHttpException を投げる。認可(ロール)ではなく内部転送か否かで弾く仕組みである点に注意。
+
+
+
+
+
diff --git a/src/Eccube/Attribute/README.md b/src/Eccube/Attribute/README.md
new file mode 100644
index 00000000000..4c136bb6f53
--- /dev/null
+++ b/src/Eccube/Attribute/README.md
@@ -0,0 +1,15 @@
+# Attribute — EC-CUBE 独自の PHP 属性
+
+クラス・プロパティ・メソッドに「印(マーカー)」を付けるための PHP8 属性群。
+属性自体はロジックを持たず、付けられた印を CompilerPass・EventListener・拡張サービスが
+Reflection で拾って挙動を変える。受注フロー登録・エンティティ/フォーム拡張・アクセス制限の 3 系統。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🔗 関連: [PurchaseFlow](../Service/PurchaseFlow/README.md) ・ [DependencyInjection](../DependencyInjection/README.md)
+
+## 主要ファイル
+
+- `CartFlow.php` / `ShoppingFlow.php` / `OrderFlow.php` — 受注処理パイプラインへの登録マーカー(`PurchaseFlowPass` が拾う)
+- `EntityExtension.php` — トレイトを既存エンティティへ合成する対象を指定(`IS_REPEATABLE`。`EntityProxyService` が拾う)
+- `FormAppend.php` — 拡張プロパティを既存フォームへ差し込む設定(`DoctrineOrmExtension` が拾う)
+- `ForwardOnly.php` — コントローラアクションを forward 専用にし直接アクセスを拒否(`ForwardOnlyListener` が拾う)
diff --git a/src/Eccube/Command/README.html b/src/Eccube/Command/README.html
new file mode 100644
index 00000000000..592f433f154
--- /dev/null
+++ b/src/Eccube/Command/README.html
@@ -0,0 +1,116 @@
+
+
+
+
+
+Command — 仕様
+
+
+
+
+Command — コンソールコマンド/バッチ
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): command/SKILL.md
+
+
+
+ 概要
+ この Command/ ディレクトリは、EC-CUBE の コンソールコマンド(bin/console から実行するコマンド/バッチ)が置かれる場所です。
+ インストール・プロキシ生成・プラグイン管理・ダミーデータ生成・不要データの削除など、
+ Web 画面を介さずに実行する運用・保守処理をここに集約します。
+
+ 各コマンドの共通の性質:
+
+ Symfony\Component\Console\Command\Command を継承し、クラスに #[AsCommand(name: ..., description: ...)] 属性を付ける。
+ - コマンド名は
eccube: 接頭辞のコロン区切り(eccube:install / eccube:plugin:enable 等)。
+ - サービス登録は不要。
services.yaml の autoconfigure: true で #[AsCommand] を付けるだけで console.command として自動登録される。
+ - コマンドは「もう 1 つの入口」であって、業務ロジックの置き場所ではない。処理は Service/Repository へ委譲する。
+
+ プロジェクト固有のコマンドは app/Customize/Command/、プラグインは各プラグインの Command/ に置きます。
+ 名前空間が Customize\ / Plugin\ でも autoconfigure の対象なので、置くだけで登録されます。
+
+
+
+ 代表的なコマンド
+ コアが標準搭載する主なコマンド(全一覧は bin/console list eccube):
+
+ | コマンド名 | 役割 |
+ eccube:install | EC-CUBE の初期インストール(DB スキーマ作成・初期データ投入)。 |
+ eccube:generate:proxies | エンティティプロキシの再生成(トレイト拡張の反映)。 |
+ eccube:schema:update | エンティティ属性からのスキーマ更新。 |
+ eccube:fixtures:generate / eccube:fixtures:load | ダミーデータの生成・フィクスチャの投入。 |
+ eccube:delete-carts | 不要になったカートデータの削除(バッチの手本)。 |
+ eccube:plugin:install|enable|disable|uninstall|update | プラグインのライフサイクル操作。 |
+ eccube:composer:install|require|update|remove | プラグイン導入に伴う Composer 操作のラッパ。 |
+
+
+
+
+ コマンドの実行フロー
+ Symfony Console の実行順は決まっています。EC-CUBE のコマンドはこの型に沿って
+ 「引数の取得 → Service へ委譲 → 結果の出力 → 終了コード」に徹します。
+ #[AsCommand(name: 'eccube:...')] … コマンド名を宣言(autoconfigure で自動登録)
+ │
+ ▼
+__construct(...依存...) … Service / Repository / EntityManager を DI
+ │ parent::__construct() が必須
+ ▼
+configure() … addArgument() / addOption() で入力を宣言
+ │
+ ▼
+execute(InputInterface, OutputInterface): int
+ │ $io = new SymfonyStyle(...) … 出力は SymfonyStyle に寄せる
+ │ 業務処理は Service へ委譲
+ ▼
+return 0; … 正常 0 / 異常は非 0(コアは 0 リテラルで統一)
+ 大量データを扱うバッチは、ループ内で毎回 flush() せず
+ 一定件数ごとにまとめて flush() する(端数も最後に flush)。トランザクション境界が要るなら
+ beginTransaction()/commit()/失敗時 rollback() で囲む(DeleteCartsCommand が手本)。
+
+
+
+ コマンドの追加開発者向け
+ 新しいコマンドは 1 クラス足すだけです。
+ 実装の書き方・DO/DON'T は command/SKILL.md が正典です。ここでは要点だけ示します。
+
+ | やること | 作法 |
+ | クラスを宣言する | Command を継承し #[AsCommand(name: 'eccube:...')] を付ける |
+ | 依存を受ける | コンストラクタインジェクション。parent::__construct() を必ず呼ぶ |
+ | 入力を宣言する | configure() で addArgument()/addOption()(VALUE_REQUIRED/VALUE_NONE を使い分け) |
+ | 処理を書く | execute() で入力取得 → Service へ委譲 → SymfonyStyle で出力 → int を返す |
+
+ cron/定期実行: EC-CUBE 4.4 のコアに独自スケジューラ(Symfony Scheduler 等)は無い。
+ 定期実行は OS の cron 等から bin/console <コマンド名> を叩く前提で、冪等・安全に再実行できる設計にする。
+
+
+
+ 注意点開発者向け
+
+ parent::__construct() を呼び忘れない(呼ばないと実行時エラー)。
+ execute() は int を返す(正常 0、異常は非 0)。void にしない。
+ execute() に業務ロジックを直書きしない。複数 Repository 横断や計算・判定は Service へ委譲する。
+ #[AsCommand]+autoconfigure 任せにし、services.yaml に手書きで console.command タグを足さない。
+ - コマンド名は独自命名にせず
eccube: 接頭辞のコロン区切り(既存コマンドに倣う)。
+ - 「Symfony Scheduler で定期実行」と推測で書かない。コアに機構は無い。
+ - 登録できているかは
bin/console list に自分のコマンドが現れるかで確認する。
+
+
+
+
+
diff --git a/src/Eccube/Command/README.md b/src/Eccube/Command/README.md
new file mode 100644
index 00000000000..9802ab6a69f
--- /dev/null
+++ b/src/Eccube/Command/README.md
@@ -0,0 +1,17 @@
+# Command — コンソールコマンド/バッチ
+
+`bin/console` から実行するコンソールコマンド(バッチ・cron 用途含む)の置き場所。
+インストール・プロキシ生成・プラグイン管理・ダミーデータ生成・不要データ削除など、
+Web 画面を介さない運用・保守処理を集約する。コマンドは「もう 1 つの入口」であり、
+業務ロジックは Service/Repository へ委譲して薄く保つ。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`command/SKILL.md`](../../../.claude/skills/command/SKILL.md)
+
+## 主要ファイル
+
+- `InstallerCommand.php` — `eccube:install`(初期インストール)
+- `GenerateProxyCommand.php` — `eccube:generate:proxies`(エンティティプロキシ再生成)
+- `DeleteCartsCommand.php` — `eccube:delete-carts`(トランザクション境界を持つバッチの手本)
+- `GenerateDummyDataCommand.php` — `eccube:fixtures:generate`(オプション/バッチ flush の手本)
+- `PluginEnableCommand.php` ほか `Plugin*Command.php` — プラグインのライフサイクル操作
diff --git a/src/Eccube/Controller/Admin/Order/README.html b/src/Eccube/Controller/Admin/Order/README.html
new file mode 100644
index 00000000000..bdd842d551e
--- /dev/null
+++ b/src/Eccube/Controller/Admin/Order/README.html
@@ -0,0 +1,84 @@
+
+
+
+
+
+Admin/Order — 仕様
+
+
+
+
+Admin/Order — 管理画面の受注管理
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): controller/SKILL.md
+ / ドメイン詳細: doc4「受注」仕様
+
+
+
+ 概要
+ src/Eccube/Controller/Admin/Order/ は、管理画面から受注(Order)を操作するコントローラ群です。
+ 受注の一覧・検索・登録・編集、出荷(Shipping)の編集・出荷通知、受注メールの送信、出荷情報の CSV 取込などを担います。
+ 受注ドメインの詳細(受注ステータスの遷移・明細種別・出荷単位など)は複製せず
+ doc4「受注」仕様 を参照してください。
+
+
+
+ 主要な役割・分類
+
+ | コントローラ | 役割 |
+ OrderController | 受注一覧・検索・一括削除・CSV/PDF 出力・出荷ステータス/伝票番号の更新。 |
+ EditController | 受注の新規登録(admin_order_new)と編集(admin_order_edit)。商品・会員・明細種別の検索補助も持つ。 |
+ ShippingController | 出荷(Shipping)の編集と、出荷完了通知メールのプレビュー/送信。 |
+ MailController | 受注に対する任意メールの作成・プレビュー・送信(MailHistory に記録)。 |
+ CsvImportController | 出荷情報 CSV の取込とテンプレート配布(AbstractCsvImportController 継承)。 |
+
+
+
+
+ 代表的な処理の流れ
+ 受注の登録・編集(EditController / ShippingController)は、店頭の購入と同じ受注処理パイプライン(PurchaseFlow)を
+ order フローで実行します。管理者が組んだ受注も、金額計算・在庫・採番の扱いを店頭購入と一貫させるためです。
+ ① フォーム処理 受注編集フォームを handleRequest() → isValid()
+② 検証・計算 PurchaseFlow.validate() … warning / error を収集して画面に表示
+③ 確定(登録時) PurchaseFlow.prepare() → commit() … 在庫引当・注文番号採番など
+④ ステータス遷移 変更時は OrderStateMachine.apply() で受注ステータスを更新
+ 詳細は PurchaseFlow の仕様 を参照してください。
+
+
+
+ 拡張ポイント開発者向け
+
+ - 受注編集の途中に処理を差し込むには テンプレートイベント/コントローライベント(例:
ADMIN_ORDER_EDIT_INDEX_PROGRESS)を購読する。コントローラ本体は改変しない。
+ - 受注に対する検証・計算・確定ロジックの追加は PurchaseFlow の
order フローに部品を登録する(Processor / Validator)。コントローラには書かない。
+ - プロジェクト固有の振る舞いは
app/Customize/ 側で拡張・上書きする。
+
+
+
+
+ 注意点開発者向け
+
+ - 管理操作が実世界を変える「二重副作用」に注意。
EditController / ShippingController は管理画面の操作から PurchaseFlow.prepare()/commit() を呼ぶため、在庫引当・注文番号採番・(設定によっては)メール送信が管理画面から発火する。確定系を安易に増やさない。
+ - 受注ステータスは
OrderStateMachine 経由で遷移させる。 直接 setOrderStatus() で書き換えず、遷移可否チェックを通す。
+ - Fat コントローラを増やさない。 受注の計算・検証・確定は PurchaseFlow へ。コントローラは HTTP 入出力と画面遷移に徹する。
+ - 認可・CSRF。 パスは
%eccube_admin_route% 配下(admin ファイアウォールで保護)。一括削除・ステータス更新・伝票番号更新など GET 以外の状態変更は CSRF トークンを検証する。
+
+
+
+
+
diff --git a/src/Eccube/Controller/Admin/Order/README.md b/src/Eccube/Controller/Admin/Order/README.md
new file mode 100644
index 00000000000..49b84db82bc
--- /dev/null
+++ b/src/Eccube/Controller/Admin/Order/README.md
@@ -0,0 +1,17 @@
+# Admin/Order — 管理画面の受注管理
+
+管理画面から受注(`Order`)を操作するコントローラ群。受注の一覧・検索・登録・編集、
+出荷(`Shipping`)の編集・出荷通知、受注メール送信、出荷 CSV 取込を担う。
+受注の登録・編集は店頭購入と同じ受注処理パイプライン(PurchaseFlow)を `order` フローで実行する。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`controller/SKILL.md`](../../../../../.claude/skills/controller/SKILL.md)
+- 📚 ドメイン詳細: https://doc4.ec-cube.net/spec_order
+
+## 主要ファイル
+
+- `OrderController.php` — 受注一覧・検索・一括削除・CSV/PDF 出力・出荷ステータス/伝票番号更新
+- `EditController.php` — 受注の新規登録・編集。PurchaseFlow の `validate`/`prepare`/`commit` を管理操作から実行
+- `ShippingController.php` — 出荷編集と出荷完了通知メールのプレビュー/送信
+- `MailController.php` — 受注に対する任意メールの作成・送信(`MailHistory` に記録)
+- `CsvImportController.php` — 出荷情報 CSV の取込とテンプレート配布(`AbstractCsvImportController` 継承)
diff --git a/src/Eccube/Controller/README.html b/src/Eccube/Controller/README.html
new file mode 100644
index 00000000000..cf66cde7d3a
--- /dev/null
+++ b/src/Eccube/Controller/README.html
@@ -0,0 +1,91 @@
+
+
+
+
+
+Controller — 仕様
+
+
+
+
+Controller — HTTP 入出力層
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): controller/SKILL.md
+
+
+
+ 概要
+ src/Eccube/Controller/ は、EC-CUBE の HTTP リクエストを受け取り、レスポンスを返す入口です。
+ URL(ルーティング)ごとに 1 つのアクション(メソッド)が対応し、店頭(フロント)と管理画面の両方の画面・API を提供します。
+
+ コントローラの仕事は「HTTP の入出力変換」に徹することです。実際の業務処理(金額計算・在庫引当・メール送信など)は
+ Service や受注処理パイプライン(PurchaseFlow)へ委譲し、コントローラ自身は薄く保ちます。
+
+
+
+ 主要な役割・分類
+ ディレクトリは「誰が使う画面か」で大きく分かれます。
+
+ | 場所 | 役割 |
+ 直下(CartController / ShoppingController / ProductController / EntryController 等) | 店頭(一般利用者向け)の画面。商品閲覧・カート・購入手続き・会員登録・お問い合わせなど。 |
+ Mypage/ | 会員(ログイン済み Customer)向けのマイページ。注文履歴・お気に入り・会員情報変更など。 |
+ Admin/ | 管理画面。受注・商品・会員・コンテンツ・設定・店舗管理など機能単位でサブディレクトリに分割。 |
+ Block/ | ブロック(画面部品)の描画コントローラ。 |
+ Install/ | 初期インストール用ウィザード。 |
+ AgentCommerce/ | エージェントコマース(外部エージェント連携)向けのエンドポイント。 |
+
+ 共通の基底として AbstractController(フラッシュメッセージ・CSRF 検証・リダイレクトのヘルパを提供)と、
+ 購入系で受注処理を扱う AbstractShoppingController があります。
+
+
+
+ 代表的な処理の流れ
+ 1 アクションは原則として次の 4 ステップで構成されます。業務ロジックは自分で書かず委譲するのが要点です。
+ ① リクエスト受取 Request・ルートパラメータ($id 等)を受け取る
+② フォーム処理 createForm() → handleRequest() → isValid()(CSRF 検証込み)
+③ 委譲 Service / Repository を呼んで処理を任せる
+④ 応答組み立て addSuccess() / redirectToRoute() / render() で結果を返す
+ 購入手続き系(ShoppingController 等)は AbstractShoppingController::executePurchaseFlow() を通じて
+ 受注処理パイプライン(PurchaseFlow)に検証・計算を委譲します。
+ コントローラは PurchaseFlow の結果(warning / error)を受け取り、画面遷移に変換するだけです。
+
+
+
+ 拡張ポイント開発者向け
+
+ - プロジェクト固有の追加・上書きは
app/Customize/Controller/ へ。コア(src/Eccube/Controller/)を直接改変しない(アップグレード安全のため)。
+ - プラグインは自身のディレクトリ内にコントローラを持ち、ルーティング属性で URL を登録する。
+ - ルーティングは
#[Route] 属性(Symfony\Component\Routing\Attribute\Route)。管理画面のパスは %eccube_admin_route% プレフィックス配下に置く。
+ - リクエスト/レスポンスのライフサイクルへの割り込みは EventSubscriber / EventListener で行う(コントローラを直接いじらない)。
+
+ 実装の書き方・DO/DON'T は controller/SKILL.md が正典です。
+
+
+
+ 注意点開発者向け
+
+ - Fat コントローラを増やさない。 金額・在庫・送料・ポイント等の計算、
$em->persist()/$em->flush() を伴う業務的な永続化、複数 Repository をまたぐ処理・外部 API・メール送信・ファイル IO がアクションに混ざり始めたら Service(受注処理は PurchaseFlow)へ抽出する。行数で線引きしない。
+ - 認可はファイアウォール+ロールで制御。 管理アクションは必ず
%eccube_admin_route% 配下に置く(admin ファイアウォールで認証が要求される)。EC-CUBE コアは個別の #[IsGranted] ではなくファイアウォールで守る点に注意。
+ - CSRF: GET 以外の状態変更はトークン検証。 フォーム経由(
handleRequest()+isValid())は保護込み。フォームを介さない削除・Ajax は基底の $this->isTokenValid() を明示的に呼ぶ(無効時は AccessDeniedHttpException)。
+ AbstractController が #[Required] セッター注入で提供する依存(eventDispatcher・entityManager・translator・formFactory・session・router・eccubeConfig)はコンストラクタで再注入しない。
+
+
+
+
+
diff --git a/src/Eccube/Controller/README.md b/src/Eccube/Controller/README.md
new file mode 100644
index 00000000000..9ed1b5612b2
--- /dev/null
+++ b/src/Eccube/Controller/README.md
@@ -0,0 +1,16 @@
+# Controller — HTTP 入出力層
+
+EC-CUBE の HTTP リクエストを受け取りレスポンスを返す入口。URL(ルーティング)ごとに 1 アクションが対応し、
+店頭(フロント)と管理画面の画面・API を提供する。業務ロジックは自分で持たず、
+Service や受注処理パイプライン(PurchaseFlow)へ委譲して薄く保つ。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`controller/SKILL.md`](../../../.claude/skills/controller/SKILL.md)
+
+## 主要ファイル
+
+- `AbstractController.php` — 全コントローラの基底。フラッシュメッセージ(`addSuccess`/`addError` 等)・CSRF 検証(`isTokenValid()`)・`forwardToRoute()` を提供
+- `AbstractShoppingController.php` — 購入系の基底。`executePurchaseFlow()` で受注処理パイプラインへ検証・計算を委譲
+- `CartController.php` / `ShoppingController.php` — 店頭のカート・購入手続き
+- `ProductController.php` / `EntryController.php` — 商品閲覧・会員登録
+- `Admin/` — 管理画面コントローラ群(受注・商品・会員・設定などを機能単位で分割)/`Mypage/` — 会員向けマイページ
diff --git a/src/Eccube/DependencyInjection/README.html b/src/Eccube/DependencyInjection/README.html
new file mode 100644
index 00000000000..f397cfb6b95
--- /dev/null
+++ b/src/Eccube/DependencyInjection/README.html
@@ -0,0 +1,119 @@
+
+
+
+
+
+DependencyInjection — 仕様
+
+
+
+
+DependencyInjection — DI 拡張とコンテナ組み立て
+
+ 索引: README.md
+ / 関連: Attribute
+ ・ PurchaseFlow
+
+
+
+ 概要
+ このディレクトリは、EC-CUBE を Symfony の DI コンテナに載せるための拡張ポイントを集めた場所です。
+ 「アプリ起動時に、設定を読み込み、プラグインの有効/無効を判定し、各種サービスをタグで自動配線する」といった
+ コンテナのブート/組み立て処理をここが担います。実行時のリクエスト処理ではなく、
+ コンテナのコンパイル(ビルド)フェーズで動くコードが中心です。
+
+ 大きく 4 種類の部品で構成されます。
+
+ | 部品 | 役割 |
+ EccubeExtension | バンドル拡張(Extension)。eccube 設定の読み込みと、他バンドル設定への prepend(動的な差し込み)を行う。 |
+ Configuration | eccube 名前空間の設定スキーマ定義。現状は rate_limiter(レートリミッタ)セクションを定義。 |
+ Compiler/ | コンテナのコンパイル時に走る CompilerPass 群。タグ収集・自動配線・定義の書き換えを行う。 |
+ Facade/ | DI コンテナの外(静的コンテキスト)から一部サービスへアクセスするための singleton ファサード。 |
+
+
+
+
+ Compiler パス一覧
+ 各 CompilerPass は「特定タグの付いたサービスを集めて、別のサービスへ配線する/定義を書き換える」責務を持ちます。
+ 登録は Kernel::build()(src/Eccube/Kernel.php)で行い、一部は実行順のため優先度を指定しています。
+
+ | パス | やること |
+ AutoConfigurationTagPass | doctrine.event_subscriber / rate_limiter / payment_method のタグを条件に応じて自動付与。優先度 11(PluginPass より先)。 |
+ PluginPass | 無効なプラグインのサービスタグをクリアし、拡張機構を無効化(doctrine.repository_service は除く)。優先度 10。 |
+ PurchaseFlowPass | 受注処理の 3 フロー(cart/shopping/order)へ Processor/Validator を配線。詳細は下記および PurchaseFlow。 |
+ QueryCustomizerPass | eccube.query_customizer タグを Queries に addCustomizer() で登録。 |
+ NavCompilerPass | eccube.nav タグの EccubeNav::getNav() を集約し、eccube_nav パラメータへマージ。 |
+ PaymentMethodPass | eccube.payment.method タグのサービスを public 化。 |
+ TwigBlockPass | eccube.twig_block タグの EccubeTwigBlock::getTwigBlock() を eccube_twig_block_templates へ集約。 |
+ TwigExtensionPass | 本番環境のみ、twig に IgnoreRoutingNotFoundExtension を追加(未定義ルートで例外を投げない)。 |
+ WebServerDocumentRootPass | web_server コマンドのドキュメントルートを public からプロジェクトルートへ変更。 |
+ StripReportFieldsArgPass | ORM3 環境で AttributeDriver の余分な引数を paths 1 引数へ統一(DoctrineBundle #1844 回避)。優先度 -1000。 |
+
+
+
+
+ EccubeExtension の prepend と access_control 動的生成開発者向け
+ EccubeExtension は PrependExtensionInterface を実装し、prepend() で
+ 他バンドルの設定を起動時に動的に差し込みます。
+ prepend()
+ ├ configureFramework() … security の access_control を動的生成 + framework の rate_limiter を組み立て
+ └ configurePlugins() … DB に直接接続して dtb_plugin を読み、有効/無効プラグインを判定
+ → 有効プラグインの twig paths・翻訳 paths を framework/twig に差し込む
+
+ 非自明な挙動: /admin・/mypage の access_control は security.yaml ではなく、ここ(configureFramework())で動的生成されます。
+ security.yaml だけを見て認可仕様を判断すると誤読するので注意(認可レビューで ^/mypage/ を「未保護」と誤検出しない)。
+
+ 生成される access_control(この順序=上から評価。%eccube_admin_route% は管理画面プレフィックス):
+
+ | path | roles |
+ ^/{admin}/login | IS_AUTHENTICATED_ANONYMOUSLY |
+ ^/{admin}/ | ROLE_ADMIN |
+ ^/mypage/login | IS_AUTHENTICATED_ANONYMOUSLY |
+ ^/mypage/withdraw_complete | IS_AUTHENTICATED_ANONYMOUSLY |
+ ^/mypage/change | IS_AUTHENTICATED_FULLY |
+ ^/mypage/ | ROLE_USER |
+
+ 環境変数 ECCUBE_FORCE_SSL が有効なときは、全エントリに requires_channel: https を付与し https に限定します。
+
+
+
+ PurchaseFlow の配線開発者向け
+ PurchaseFlowPass は受注処理パイプラインの 3 フロー(eccube.purchase.flow.cart /
+ .shopping / .order)へ、Processor / Validator を 2 経路で配線します。
+
+ - YAML タグ
purchaseflow.yaml の flow_type + priority 指定。
+ findAndSortTaggedServices() の並びを flow_type ごとに再ソートして登録する。
+ - 属性
#[CartFlow] / #[ShoppingFlow] / #[OrderFlow](Attribute)を
+ ReflectionAttribute::IS_INSTANCEOF で拾って対象フローへ登録。YAML で配線済みなら重複登録しない(alreadyWired())。
+
+ タグ ↔ 追加メソッドの対応は getProcessorTags() に定義(eccube.item.validator → addItemValidator など 7 種)。
+
+
+
+ 注意点開発者向け
+
+ - ここは基本的にコンパイル時のコード。CompilerPass や
prepend() の変更はコンテナの再ビルド(bin/console cache:clear)で反映される。
+ configurePlugins() は prepend フェーズでコンテナが未完成のため、DBAL の接続を直接生成して dtb_plugin を読む。DB 未接続・テーブル未作成時は安全に空扱いで抜ける(インストール前を考慮)。
+ - 認可仕様を確認するときは
security.yaml だけでなく必ず EccubeExtension::configureFramework() を併読する(上記 access_control 動的生成のため)。
+ - CompilerPass の実行順が重要なものは優先度で制御されている(
AutoConfigurationTagPass=11 → PluginPass=10、StripReportFieldsArgPass=-1000)。順序前提を崩さないこと。
+ Facade/ の LoggerFacade / TranslatorFacade は、DI が使えない静的コンテキスト向けの避難口。通常のサービスではコンストラクタ注入を優先する。
+
+
+
+
+
diff --git a/src/Eccube/DependencyInjection/README.md b/src/Eccube/DependencyInjection/README.md
new file mode 100644
index 00000000000..d317e3b212f
--- /dev/null
+++ b/src/Eccube/DependencyInjection/README.md
@@ -0,0 +1,17 @@
+# DependencyInjection — DI 拡張とコンテナ組み立て
+
+EC-CUBE を Symfony DI コンテナに載せるための拡張ポイント。設定読み込み・プラグイン有効/無効判定・
+サービスのタグ自動配線といった、コンテナのブート/コンパイル処理を担う。
+実行時のリクエスト処理ではなく、コンテナのビルドフェーズで動くコードが中心。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🔗 関連: [Attribute](../Attribute/README.md) ・ [PurchaseFlow](../Service/PurchaseFlow/README.md)
+
+## 主要ファイル
+
+- `EccubeExtension.php` — バンドル拡張。`prepend()` で `/admin`・`/mypage` の access_control を**動的生成**し、DB を直接読んでプラグインの有効/無効を判定
+- `Configuration.php` — `eccube` 設定スキーマ(`rate_limiter` セクション)
+- `Compiler/PurchaseFlowPass.php` — 受注処理 3 フローへ Processor/Validator を配線(YAML タグ+属性の 2 経路)
+- `Compiler/PluginPass.php` — 無効プラグインのサービスタグをクリアして拡張機構を無効化
+- `Compiler/AutoConfigurationTagPass.php` — doctrine.event_subscriber 等のタグを自動付与
+- `Facade/` — DI 外の静的コンテキスト向け singleton ファサード(`LoggerFacade` / `TranslatorFacade`)
diff --git a/src/Eccube/Doctrine/README.html b/src/Eccube/Doctrine/README.html
new file mode 100644
index 00000000000..a9a80200a54
--- /dev/null
+++ b/src/Eccube/Doctrine/README.html
@@ -0,0 +1,130 @@
+
+
+
+
+
+Doctrine — 仕様
+
+
+
+
+Doctrine — EC-CUBE 固有の Doctrine 拡張
+
+ 索引: README.md
+
+
+
+ 概要
+ このディレクトリは、EC-CUBE が Doctrine ORM / DBAL に被せている独自拡張を収めます。
+ エンティティ定義(src/Eccube/Entity/)そのものではなく、
+ 「クエリの後付け拡張」「日時の UTC 保存」「日本語検索用の DQL 関数」「表示制御用のフィルタ」「Proxy を安全に読むマッピングドライバ」
+ 「CSV からの初期データ投入」といった基盤側の仕組みが対象です。
+
+
+ | サブ | 目的 |
+ | Query/ | 既存クエリに WHERE / JOIN / ORDER BY を後付けで差し込むカスタマイズ機構(プラグイン拡張の要) |
+ | DBAL/Types/ | DB へは常に UTC で保存し、PHP 側でアプリのタイムゾーンへ変換するカスタム型 |
+ | ORM/Query/ | 日本語検索・日時抽出のための独自 DQL 関数(NORMALIZE / EXTRACT) |
+ | Filter/ | 全クエリに横断的に効く SQL フィルタ(在庫なし非表示・仮受注除外) |
+ | ORM/Mapping/Driver/ | エンティティプロキシ機構を成立させる属性マッピングドライバ |
+ | EventSubscriber/ | Doctrine ライフサイクルへのフック(作成/更新日時の自動セット・税込価格算出・接続時 TZ 設定) |
+ | Common/CsvDataFixtures/ | インストール時に CSV からマスタ等を一括投入する仕組み |
+
+
+
+
+ クエリカスタマイズ機構(Query/)
+ EC-CUBE の一覧・検索クエリ(ProductRepository / OrderRepository / CustomerRepository 等)は、
+ プラグインやカスタマイズが後から条件を足せるよう作られています。中心が QueryCustomizer インターフェイスです。
+ Repository が queryKey(例: "product_search")付きで
+ ↓ Queries::customize($queryKey, $qb, $params)
+Queries が queryKey に登録された QueryCustomizer 群を順に適用
+ ↓
+WhereCustomizer → createStatements() が返す WhereClause[] を andWhere で追加
+JoinCustomizer → createStatements() が返す JoinClause[] を join で追加
+OrderByCustomizer→ OrderByClause[] を addOrderBy で追加
+
+ | クラス | 役割 |
+ QueryCustomizer(interface) | customize() と getQueryKey() を持つ。対象クエリは queryKey で識別。 |
+ Queries | queryKey ごとに Customizer を保持し、customize() で該当分をまとめて適用。 |
+ WhereCustomizer / JoinCustomizer / OrderByCustomizer(abstract) | 用途別の基底。createStatements() だけ実装すればよい。 |
+ WhereClause | WHERE 句のファクトリ(eq / like / in / between / gt …)。パラメータバインドまで面倒を見る。 |
+ JoinClause / OrderByClause | JOIN / ORDER BY を組み立てる値オブジェクト。JoinClause は付随する WHERE / ORDER BY も持てる。 |
+
+ 登録は DI タグ eccube.query_customizer(QueryCustomizerPass が収集)。実装が QueryCustomizer を継承していないとコンパイル時に弾かれます。
+
+
+
+ DBAL 型・DQL 関数・フィルタ
+
+ DBAL カスタム型(DBAL/Types/)
+ UTCDateTimeType / UTCDateTimeTzType が Doctrine の datetime / datetimetz を差し替えます(doctrine.yaml の types)。
+ DB へは常に UTC で保存し、PHP 側で読むときにアプリのタイムゾーン(既定 Asia/Tokyo、setTimeZone())へ変換します。
+ 接続時にも InitSubscriber がセッションのタイムゾーンを UTC に固定します。
+
+ 独自 DQL 関数(ORM/Query/)
+
+ | 関数 | 用途 |
+ NORMALIZE | ひらがな→カタカナ変換+小文字化。DB 差異を吸収したあいまい検索(PostgreSQL は TRANSLATE、MySQL は照合順序、他は LOWER)。 |
+ EXTRACT | 日時から YEAR/MONTH/DAY 等を取り出す。UTC 保存分をアプリ TZ へ補正した上で抽出(DB 別に SQL を出し分け)。 |
+
+
+ SQL フィルタ(Filter/)
+ Doctrine の SQLFilter で、有効化すると対象エンティティの全クエリに WHERE 条件が自動付与されます。いずれも doctrine.yaml では既定 enabled: falseで、必要な箇所で明示的に有効化します。
+
+ | フィルタ | 効果 |
+ NoStockHiddenFilter(option_nostock_hidden) | ProductClass を在庫あり(または無制限)だけに絞る。 |
+ OrderStatusFilter(incomplete_order_status_hidden) | 仮受注(PROCESSING / PENDING)の Order を除外する。 |
+
+
+
+
+ マッピングドライバとライフサイクル開発者向け
+
+ 属性マッピングドライバ(ORM/Mapping/Driver/)
+ EC-CUBE のエンティティプロキシ機構(トレイト追加で既存エンティティを拡張する仕組み)を成立させるための Doctrine AttributeDriver 派生です。
+
+ | ドライバ | 役割 |
+ TraitProxyAttributeDriver | クラスをスキャンする際、対応する Proxy ファイルがあれば元エンティティの代わりにそれを読む。 |
+ ReloadSafeAttributeDriver | 同一プロセス内で新規生成した Proxy を再読込するときの Fatal を回避(一時的にクラス名を変えてロードしメタデータ抽出)。 |
+ NopAttributeDriver | 常に空を返す no-op ドライバ。 |
+
+
+ ライフサイクルフック(EventSubscriber/)
+
+ | Subscriber | フック |
+ SaveEventSubscriber | prePersist / preUpdate で createDate / updateDate / currencyCode / creator を自動セット(メソッドが在れば)。 |
+ TaxRuleEventSubscriber | ProductClass の読込・保存時に税込価格(price01IncTax 等)を TaxRuleService で算出。 |
+ InitSubscriber | postConnect で DB セッションのタイムゾーンを UTC に固定。 |
+
+
+
+
+ 注意点開発者向け
+
+ - 日時は必ず UTC で DB に入る。生 SQL で日時を条件に使うと TZ ずれを起こす。DQL の
EXTRACT は補正込みだが、直書き SQL は自前で補正が要る。
+ - SQL フィルタは既定で無効。「在庫なしを隠す」「仮受注を除く」は自動では効かない。フロント商品一覧や売上集計など、必要な経路で明示的に
enableFilter() する(コアの Repository / Controller が手本)。
+ - クエリに条件を足したいときは Repository を書き換えず、
QueryCustomizer を eccube.query_customizer タグで登録して差し込む(アップグレード安全)。
+ WhereClause は必ずパラメータバインドを通す。生値を DQL 文字列へ連結しない(SQL インジェクション回避)。
+ - Mapping ドライバ/Proxy まわりは
app/proxy/entity/ 生成物と密結合。トレイト追加後の再生成は bin/console eccube:generate:proxies。
+ CsvDataFixtures はインストール/初期データ投入専用の低レベル一括 INSERT(同階層の definition.yml で投入順を定義)。通常の CSV 入出力(商品・受注)は Service/Csv*Service 側で、これとは別物。
+
+
+
+
+
diff --git a/src/Eccube/Doctrine/README.md b/src/Eccube/Doctrine/README.md
new file mode 100644
index 00000000000..711dcb7885b
--- /dev/null
+++ b/src/Eccube/Doctrine/README.md
@@ -0,0 +1,19 @@
+# Doctrine — EC-CUBE 固有の Doctrine 拡張
+
+エンティティ定義そのものではなく、Doctrine ORM / DBAL に被せた EC-CUBE 独自の基盤拡張。
+クエリの後付けカスタマイズ機構、日時の UTC 保存型、日本語検索用の DQL 関数、
+表示制御用 SQL フィルタ、エンティティプロキシ用マッピングドライバ、
+CSV からの初期データ投入、ライフサイクルフックを収める。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+
+## 主要ファイル
+
+- `Query/QueryCustomizer.php` / `Query/Queries.php` — 既存クエリに WHERE/JOIN/ORDER BY を後付けする機構。`WhereClause` / `JoinClause` で句を組み立て、DI タグ `eccube.query_customizer` で登録
+- `DBAL/Types/UTCDateTimeType.php` — DB へは UTC 保存、PHP 側でアプリ TZ へ変換するカスタム日時型(`doctrine.yaml` の `types` で差し替え)
+- `ORM/Query/Normalize.php` / `ORM/Query/Extract.php` — 日本語あいまい検索・日時抽出の独自 DQL 関数(`NORMALIZE` / `EXTRACT`)
+- `Filter/OrderStatusFilter.php` / `Filter/NoStockHiddenFilter.php` — 仮受注除外・在庫なし非表示の SQL フィルタ(既定は無効、必要な経路で有効化)
+- `EventSubscriber/SaveEventSubscriber.php` — prePersist/preUpdate で作成・更新日時や creator を自動セット
+
+> クエリに条件を足すときは Repository を直接書き換えず `QueryCustomizer` を登録して差し込む(アップグレード安全)。
+> SQL フィルタは既定で無効なので、必要な箇所で `enableFilter()` を明示的に呼ぶ点に注意。
diff --git a/src/Eccube/Entity/README.html b/src/Eccube/Entity/README.html
new file mode 100644
index 00000000000..b92a8b319d6
--- /dev/null
+++ b/src/Eccube/Entity/README.html
@@ -0,0 +1,108 @@
+
+
+
+
+
+Entity — 仕様
+
+
+
+
+Entity — Doctrine エンティティ
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): entity/SKILL.md
+ / ドメイン詳細: doc4「受注」仕様
+
+
+
+ 概要
+ このディレクトリは、EC-CUBE のデータ構造を表す Doctrine エンティティを置きます。
+ 会員・商品・受注・カートといった業務データは、ここに定義したクラスがそのまま DB のテーブルに対応します。
+ マッピング(クラス/プロパティと DB テーブル/カラムの対応)は PHP8 属性 #[ORM\...] で
+ クラスに直接書きます(XML・アノテーションは使いません)。スキーマの源泉はこの属性であり、
+ カラムを足したいときは属性を書き換えます。
+
+ | 種類 | 置き場所 / 継承 | 例 |
+ データ系エンティティ(dtb_*) | Eccube\Entity/AbstractEntity を継承 | Customer Product Order |
+ マスタ系エンティティ(mtb_*) | Eccube\Entity\Master/AbstractMasterEntity を継承 | OrderStatus SaleType Pref |
+
+
+
+
+ 主要エンティティと関連
+ 受注ドメインの詳細(ER 図・受注ステータスの遷移)は複製せず
+ doc4「受注」仕様 を参照してください。ここでは代表的なクラスと関連だけを示します。
+
+ | エンティティ | 役割 | 主な関連 |
+ Customer | 会員 | 複数の CustomerAddress / Order を持つ |
+ Product / ProductClass | 商品とその規格(サイズ・カラー) | 1 商品は複数 ProductClass を持つ。在庫・価格は ProductClass 単位 |
+ Order / OrderItem | 受注と明細 | 1 受注は複数 OrderItem・複数 Shipping を持つ |
+ Shipping | 出荷単位 | 1 受注に複数あり得る(配送先・お届け日違い)。送料は Shipping 単位 |
+ Cart / CartItem | カートと明細 | 確定前の一時データ。確定で Order になる |
+ Member | 管理者ユーザー | — |
+ BaseInfo | 店舗設定(店名・住所・税設定) | 単一レコード |
+
+ 関連は #[ORM\OneToMany] / #[ORM\ManyToOne] 等の属性で定義し、
+ コレクション(1 対多の「多」側)はコンストラクタで ArrayCollection を初期化します。
+
+
+
+ データの持ち方
+
+ - 在庫・価格は
Product ではなく ProductClass が持つ。 規格を持たない商品も内部的に
+ ProductClass を 1 つ持ちます(規格の有無は Product::hasProductClass() で判定)。
+ - 金額は文字列で持つ。
Types::DECIMAL のカラム(Order の total・subtotal 等)は
+ Doctrine ORM 3.x で ?string になります。計算は float ではなく bcmath(bcadd/bcmul/bccomp、スケール 2)で行います。
+ - 単一テーブル継承(STI)。 マスタ系や
Order・Block 等は 1 テーブルに複数の型を格納し、
+ discriminator_type 列で型を区別します(#[ORM\InheritanceType('SINGLE_TABLE')])。
+ - 作成・更新日時は自動。
create_date/update_date/creator はコアの SaveEventSubscriber が
+ 永続化時に自動セットするため、setter を生やすだけでよく、自前の #[ORM\PrePersist] は書きません。
+
+ 「
Order が存在する=確定済み注文」ではありません。カート確定の入口で受注は
PROCESSING(仮受注)で作られ、
+ 購入完了で
NEW に遷移します。受注ステータスの詳細は
doc4「受注」仕様 を参照。
+
+
+
+ 拡張ポイント開発者向け
+ 既存エンティティへのフィールド追加はコアを書き換えず trait で行い、app/Customize/Entity/ に置きます。
+ 反映には bin/console eccube:generate:proxies でプロキシを再生成します(app/proxy/entity/ に拡張後のエンティティが生成される)。
+ 実装の書き方・DO/DON'T は entity/SKILL.md が正典です。
+
+ | やりたいこと | やり方 |
+ | コアエンティティにカラムを足す | trait を作り app/Customize/Entity/ に置く → プロキシ再生成 |
+ | 新規テーブルを追加する | Eccube\Entity にクラスを作り #[ORM\...] でマッピング(class_exists ラッパで囲う) |
+ | スキーマへ反映する | doctrine:schema:update --force(単純なカラム追加に ALTER マイグレーションは不要) |
+
+
+
+
+ 注意点開発者向け
+
+ - コアエンティティは
class_exists ラッパで囲う。 プラグイン/カスタマイズによる trait 追加(プロキシ生成)に対応するため
+ if (!class_exists(X::class)) { ... } で定義を囲みます。
+ - ID の getter が
?int(nullable)なのは意図的。 IDENTITY 採番は永続化まで ID が未採番(null)だからです。
+ - 金額 getter の戻り値を int/float 扱いしない。
DECIMAL は ?string。型宣言・代入・計算をこれに合わせます。
+ - 他エンティティへの関連は親削除時の挙動を決める。 FK は既定で削除を止めます(RESTRICT 相当)。未指定だと退会・商品削除が FK 違反で失敗し得るため、
+
onDelete を指定するか Service 側で後始末します。
+ - STI テーブルへの INSERT は
discriminator_type の指定が必須(例: mtb_sale_type は 'saletype')。
+
+
+
+
+
diff --git a/src/Eccube/Entity/README.md b/src/Eccube/Entity/README.md
new file mode 100644
index 00000000000..c57f479eb72
--- /dev/null
+++ b/src/Eccube/Entity/README.md
@@ -0,0 +1,16 @@
+# Entity — Doctrine エンティティ
+
+EC-CUBE のデータ構造を表す Doctrine エンティティ。会員・商品・受注・カート等の業務データを、
+PHP8 属性 `#[ORM\...]` でマッピングしたクラスとして持つ。**スキーマの源泉はこの属性**。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`entity/SKILL.md`](../../../.claude/skills/entity/SKILL.md)
+- 📚 ドメイン詳細: https://doc4.ec-cube.net/spec_order
+
+## 主要ファイル
+
+- `AbstractEntity.php` — 全データ系エンティティの基底
+- `Master/AbstractMasterEntity.php` — マスタ系(`mtb_*`)の基底
+- `Customer.php` / `Member.php` — 会員・管理者ユーザー
+- `Product.php` / `ProductClass.php` — 商品と規格(在庫・価格は `ProductClass` 単位)
+- `Order.php` / `OrderItem.php` / `Shipping.php` — 受注・明細・出荷(STI、`discriminator_type` で型区別)
diff --git a/src/Eccube/EventListener/README.html b/src/Eccube/EventListener/README.html
new file mode 100644
index 00000000000..6044bc53161
--- /dev/null
+++ b/src/Eccube/EventListener/README.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+EventListener — 仕様
+
+
+
+
+EventListener — イベント購読による拡張
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): event-subscriber/SKILL.md
+
+
+
+ 概要
+ EC-CUBE の拡張は「コアを直接書き換えるのではなくイベントを購読する」のが基本方針です。
+ この EventListener/ ディレクトリには、コア自身が Symfony の EventDispatcher に登録する
+ イベントサブスクライバが置かれています。いずれも EventSubscriberInterface を実装し、
+ リクエスト/レスポンス/認証などアプリケーションのライフサイクルの節目にフックして横断的な処理を行います。
+
+ 各リスナーの共通の性質:
+
+ Symfony\Component\EventDispatcher\EventSubscriberInterface を実装し、static な getSubscribedEvents() で購読するイベントを宣言する。
+ - サービス登録は不要。
services.yaml の autoconfigure: true で Eccube\ 配下は自動的に kernel.event_subscriber タグが付く。
+ - 業務ロジックはリスナー本体に溜め込まず、注入した Service へ委譲する薄い層として作られている。
+
+ このディレクトリはコアが標準搭載するリスナーです。プロジェクト固有の購読は
+ app/Customize/EventListener/、プラグインは各プラグインの EventListener/ に置きます(配置場所が違うだけで作り方は同じ)。
+
+
+
+ イベントの種類と代表リスナー
+ EC-CUBE のイベントは大きく 4 種類あります。このディレクトリの中身はほぼ「Symfony Kernel/Security イベント」で、
+ コントローラ前後にフックする「EC-CUBE 独自イベント」やテンプレートへの差し込みは、隣の Event/ が定義を提供し、主にプラグイン側のリスナーが購読します。
+
+ | 種類 | 購読キー | 用途 |
+ | Symfony Kernel イベント | KernelEvents::REQUEST 等 | リクエスト/レスポンスのライフサイクル |
+ | Security イベント | LoginFailureEvent::class 等 | 認証の成否にフック |
+ | EC-CUBE 独自イベント | EccubeEvents::XXX 定数(../Event/) | コントローラ処理の前後にフック |
+ | Doctrine イベント | #[AsDoctrineListener(event: ...)](../Doctrine/EventSubscriber/) | エンティティの永続化前後 |
+
+
+ コアの代表的な Kernel/Security リスナー:
+
+ | リスナー | 役割 |
+ TransactionListener | リクエスト単位で DB トランザクションを開始し、正常終了で commit・例外で rollback する(REQUEST/EXCEPTION/TERMINATE)。 |
+ TwigInitializeListener | リクエスト時に Twig のグローバル変数(店舗情報・ログイン状態等)を初期化する。 |
+ MaintenanceListener | メンテナンスモード時にレスポンスを差し替える(RESPONSE)。 |
+ SecurityListener / LoginHistoryListener | 認証成功・失敗(LoginFailureEvent 等)にフックしてログイン履歴やロック処理を行う。 |
+ TwoFactorAuthListener | CONTROLLER_ARGUMENTS にフックし、二段階認証が必要なコントローラを制御する。 |
+ MobileTemplatePathListener | モバイル端末を判定し、テンプレート探索パスを切り替える。 |
+ ExceptionListener / LogListener | 例外時のエラーページ描画・アクセスログ/エラーログ出力。 |
+
+
+
+
+ 隣接する定義(Event / Doctrine)との関係
+ 「イベントの定義」と「イベントの購読」は場所が分かれています。全体像は次の通りです。
+ コントローラ
+ │ new EventArgs(['key' => $v], $request) を dispatch
+ ▼
+Event/EccubeEvents.php … EC-CUBE 独自イベント名の定数(命名: CONTEXT_CONTROLLER_ACTION_PHASE)
+Event/EventArgs.php … 独自イベントのペイロード(getArgument / setArgument / setResponse)
+Event/TemplateEvent.php … 画面差し込み(addSnippet / addAsset / setSource)
+ │
+ ▼ ← これらを購読する側が…
+EventListener/ … コア標準のリスナー(Kernel / Security 中心)※このディレクトリ
+app/Customize/EventListener/ … プロジェクト固有の購読
+app/Plugin/*/EventListener/ … プラグインの購読(独自イベント購読の主戦場)
+
+Doctrine/EventSubscriber/ … エンティティ永続化前後のフック(#[AsDoctrineListener])
+ SaveEventSubscriber(作成日時/更新日時の自動設定)が手本
+ Doctrine 系(../Doctrine/EventSubscriber/)は Kernel/Security リスナーと別ディレクトリで、
+ #[AsDoctrineListener(event: Events::prePersist)] のように属性でイベントを宣言します。
+ InitSubscriber(postConnect)・SaveEventSubscriber(prePersist/preUpdate)・TaxRuleEventSubscriber が実例です。
+
+
+
+ 拡張ポイント開発者向け
+ 独自の処理をフックするには、購読したいイベントに応じてリスナーを 1 つ足すだけです。
+ 実装の書き方・DO/DON'T は event-subscriber/SKILL.md が正典です。ここでは要点だけ示します。
+
+ | やりたいこと | フックするイベント |
+ | コントローラ処理の前後に割り込む | EC-CUBE 独自イベント(EccubeEvents::XXX 定数)を購読し EventArgs で読み書き |
+ | リクエスト/レスポンスを横断的に変える | Kernel イベント(KernelEvents::REQUEST 等)。if (!$event->isMainRequest()) return; でサブリクエストを除外 |
+ | 画面に HTML/アセットを差し込む | テンプレートイベント(ファイル名がイベント名。Skill twig-template) |
+ | エンティティの永続化前後に処理を足す | Doctrine イベント(#[AsDoctrineListener]) |
+
+ 登録の実際:
+
+ - 共通 クラスに
EventSubscriberInterface を実装し public static function getSubscribedEvents() を定義するだけ。Eccube\ / Customize\ / Plugin\ 配下は autoconfigure で自動登録される(services.yaml への手動登録は二重発火の元)。
+ - 優先度 同一イベントを複数が購読するとき、
[メソッド名, 優先度] の 数値が大きいほど先に実行される。
+
+
+
+
+ 注意点開発者向け
+
+ getSubscribedEvents() は必ず static。非 static だと登録されず、リスナーが黙って動かない。
+ - 独自イベント名は文字列直書きせず
EccubeEvents 定数を使う(タイポの温床)。定義は ../Event/EccubeEvents.php。
+ autoconfigure 済みなのに services.yaml に手動登録しない(二重発火する)。
+ - Kernel イベントでは サブリクエストの除外(
isMainRequest())を忘れない。TransactionListener のようにライフサイクル全体を跨ぐリスナーは特に注意。
+ - リスナーに業務ロジックを溜め込まない。重い処理は Service へ委譲し、リスナーは「フック点で Service を呼ぶ」薄い層に保つ。
+ - 登録状況・優先度は
bin/console debug:event-dispatcher で確認できる。効かないときはまずここに出ているかを見る。
+
+
+
+
+
diff --git a/src/Eccube/EventListener/README.md b/src/Eccube/EventListener/README.md
new file mode 100644
index 00000000000..1b4de0649f4
--- /dev/null
+++ b/src/Eccube/EventListener/README.md
@@ -0,0 +1,18 @@
+# EventListener — イベント購読による拡張
+
+EC-CUBE の拡張は「コアを書き換えず、イベントを購読する」のが基本方針。
+このディレクトリには、コア自身が Symfony の `EventDispatcher` に登録する
+イベントサブスクライバ(`EventSubscriberInterface` 実装)が置かれ、
+リクエスト/レスポンス/認証などライフサイクルの節目にフックして横断的な処理を行う。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`event-subscriber/SKILL.md`](../../../.claude/skills/event-subscriber/SKILL.md)
+- 📚 関連: イベント定義 [`../Event/`](../Event/)(`EccubeEvents` / `EventArgs` / `TemplateEvent`)・Doctrine イベント [`../Doctrine/EventSubscriber/`](../Doctrine/EventSubscriber/)
+
+## 主要ファイル
+
+- `TransactionListener.php` — リクエスト単位の DB トランザクション(正常時 commit・例外時 rollback)
+- `TwigInitializeListener.php` — Twig グローバル変数の初期化(店舗情報・ログイン状態)
+- `MaintenanceListener.php` — メンテナンスモード時のレスポンス差し替え
+- `SecurityListener.php` / `LoginHistoryListener.php` — 認証成否にフック(ログイン履歴・ロック)
+- `TwoFactorAuthListener.php` — 二段階認証が必要なコントローラの制御
diff --git a/src/Eccube/Form/Type/README.html b/src/Eccube/Form/Type/README.html
new file mode 100644
index 00000000000..1cd95266ee0
--- /dev/null
+++ b/src/Eccube/Form/Type/README.html
@@ -0,0 +1,98 @@
+
+
+
+
+
+Form/Type — 仕様
+
+
+
+
+Form/Type — フォーム型
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): formtype/SKILL.md
+
+
+
+ 概要
+ ここは EC-CUBE の フォーム定義(FormType)を置くディレクトリです。会員登録・お問い合わせ・
+ 購入手続き(フロント)や、商品・受注・会員の編集画面(管理画面)で使う入力フォームを、
+ 1 つの型 = 1 クラスとして定義します。
+ 各クラスは Symfony の AbstractType を継承し、次の 3 つで構成されます。
+
+ | メソッド | 役割 |
+ buildForm() | フィールド(add())とバリデーション制約(Assert\*)を組み立てる |
+ configureOptions() | 既定オプションを設定。エンティティに紐づくフォームは data_class を指定する |
+ getBlockPrefix(): string | テンプレートのブロック名・フィールド名の接頭辞。戻り値型 : string は必須 |
+
+
+
+
+ ディレクトリ構成と代表的な型
+ 用途ごとにサブディレクトリへ分かれています。直下には「どの画面からも使い回す部品型」を置きます。
+
+ | 場所 | 役割 | 代表クラス |
+ | 直下 | 再利用される汎用フィールド型 | AddressType / NameType / KanaType / PriceType / PhoneNumberType / MasterType |
+ Front/ | 店頭(フロント)画面のフォーム | EntryType(会員登録)/ ContactType / CustomerLoginType / ShoppingShippingType |
+ Admin/ | 管理画面のフォーム(編集・検索) | ProductType / OrderType / CustomerType / SearchProductType |
+ Shopping/ | 購入手続き(受注確定)まわり | OrderType / ShippingType / OrderItemType |
+ Master/ | マスタ(mtb_*)を選択肢にするドロップダウン | PrefType(都道府県)/ SexType / SaleTypeType / PaymentType |
+ Install/ | インストーラの各ステップ | Step1Type … Step5Type |
+
+
+
+
+ 使いどころ:部品型を組み合わせる
+ 複雑なフォームは、汎用フィールド型を add() で埋め込んで作ります。同じ入力(住所・金額・都道府県 等)を毎回手書きしないのが原則です。
+
+ | やりたいこと | 使う型 |
+ | 金額入力(通貨・桁区切り・上下限を EC-CUBE 設定から自動適用) | PriceType(内部で MoneyType を親に持つ) |
+ | 住所(都道府県 + 住所1 + 住所2 をまとめて) | AddressType |
+ | マスタ選択肢(都道府県・性別・支払方法 等) | Master/*Type(MasterType = EntityType ベース) |
+ | 確認用にメール/パスワードを 2 回入力 | RepeatedEmailType / RepeatedPasswordType |
+
+ 入力値の動的制御(他フィールドの値でバリデーションを変える等)は $builder->addEventListener(FormEvents::PRE_SET_DATA, ...) /
+ POST_SUBMIT で行います。AddressType は required に応じて NotBlank を後付けする実例です。
+
+
+
+ 拡張ポイント:既存フォームへ項目を足す開発者向け
+ 既存フォームにフィールドを追加したいときは、コアの FormType を書き換えず FormTypeExtension を使うのが鉄則です。
+ 詳しい DO/DON'T は formtype/SKILL.md が正典です。
+
+ - コア 横断的な拡張は
src/Eccube/Form/Extension/ に AbstractTypeExtension を継承して置く。
+ getExtendedTypes() で対象 Type を指定する(例: HelpTypeExtension は全 FormType に help を追加、HTMLPurifierTextTypeExtension は入力を無害化)。
+ - プラグイン / Customize プロジェクト固有の項目追加は
app/Customize/Form/Extension/ に置く(コアはアップグレードで上書きされるため)。
+
+ 新規フォームを一から作る場合は名前空間 Eccube\Form\Type・AbstractType 継承・ライセンスヘッダ・型宣言(PHPStan level 6)を守り、依存はコンストラクタ DI で受けます。
+
+
+
+ 注意点開発者向け
+
+ getBlockPrefix(): string の戻り値型は必須。省略すると動かない(Symfony 6+)。
+ - 管理画面の検索フォームでも CSRF を無効化しない。
csrf_protection => true(既定)を保ち、テンプレートでトークンを出力する。
+ - バリデーションは制約クラス(
Assert\NotBlank 等)で付ける。Range で min/max 両方指定時は notInRangeMessage を使う。
+ - 既存の EC-CUBE 提供 Type(
AddCartType / AddressType / PriceType 等)があれば再利用し、具象クラス直依存を避ける。
+ - パスワード入力を扱う場合、対象エンティティに
plain_password プロパティが必要。
+
+
+
+
+
diff --git a/src/Eccube/Form/Type/README.md b/src/Eccube/Form/Type/README.md
new file mode 100644
index 00000000000..6d4f1078259
--- /dev/null
+++ b/src/Eccube/Form/Type/README.md
@@ -0,0 +1,16 @@
+# Form/Type — フォーム型
+
+会員登録・お問い合わせ・購入手続き(フロント)や商品・受注・会員の編集/検索(管理画面)で使う
+入力フォームを、`AbstractType` を継承した「1 型 = 1 クラス」として定義するディレクトリ。
+汎用フィールド型(住所・金額・マスタ選択)を組み合わせて複雑なフォームを組み立てる。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`formtype/SKILL.md`](../../../../.claude/skills/formtype/SKILL.md)
+
+## 主要ファイル
+
+- `PriceType.php` — 金額入力。通貨・桁区切り・上下限を EC-CUBE 設定から自動適用(`MoneyType` を親に持つ)
+- `AddressType.php` — 都道府県 + 住所1 + 住所2 をまとめた住所入力(`required` に応じ `NotBlank` を後付け)
+- `MasterType.php` — マスタ(`mtb_*`)を選択肢にするドロップダウンの基底(`EntityType` ベース)。`Master/` 配下が個別実装
+- `Front/EntryType.php` — 会員登録フォーム。フロント系フォームの代表例
+- `Admin/ProductType.php` / `Admin/OrderType.php` — 管理画面の商品・受注編集フォーム
diff --git a/src/Eccube/Plugin/README.html b/src/Eccube/Plugin/README.html
new file mode 100644
index 00000000000..7955dddd3e7
--- /dev/null
+++ b/src/Eccube/Plugin/README.html
@@ -0,0 +1,98 @@
+
+
+
+
+
+Plugin 基盤(コア) — 仕様
+
+
+
+
+Plugin 基盤(コア)
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): plugin/SKILL.md
+
+
+
+ 概要
+ このディレクトリ(src/Eccube/Plugin/)は、プラグインのライフサイクル基盤を提供します。
+ 実体は AbstractPluginManager.php ただ 1 ファイルで、各プラグインの PluginManager はこれを継承します。
+
+ プラグインそのものの置き場所は app/Plugin/{PluginCode}/(別ディレクトリ。app/Plugin/README.md)です。
+ ここはあくまでコア側の共通処理を持ち、プラグイン本体のコードは含みません。
+
+ プラグインの install/enable/disable/update/uninstall を駆動するコア機構本体は、この階層ではなく
+ src/Eccube/Service/PluginService.php・src/Eccube/Service/Composer/・src/Eccube/Command/Plugin*Command.php にあります。
+ このディレクトリは「プラグインが継承する基底クラス」の置き場です。
+
+
+
+ ライフサイクルと AbstractPluginManager
+ AbstractPluginManager は 5 つのライフサイクルメソッドを持ち、いずれもデフォルト no-opです。
+ プラグインは必要なものだけ override します(すべて任意)。呼び出しはコアの PluginService が行います。
+
+ | メソッド | 呼ばれる契機 | 用途の例 |
+ install | インストール時 | 初期データ投入 |
+ enable | 有効化時 | マイグレーション適用 |
+ disable | 無効化時 | マイグレーションを戻す |
+ update | 更新時 | 差分マイグレーション |
+ uninstall | アンインストール時 | クリーンアップ |
+
+ シグネチャは全メソッド共通で (array $meta, ContainerInterface $container)。$meta['code'] は
+ プラグインの composer.json の extra.code(PluginCode)です。
+
+ migration() ヘルパ
+ AbstractPluginManager::migration() は、プラグイン専用のマイグレーションを実行するための共通処理です。
+ $this->migration($connection, $meta['code']);
+ → 対象ディレクトリ: app/Plugin/{code}/DoctrineMigrations/(省略時の既定)
+ → 名前空間 : Plugin\{code}\DoctrineMigrations
+ → 管理テーブル : migration_{code}(小文字。コア本体の migration_versions とは別)
+ PluginManager 実行時にはコアが Doctrine の schema:update を自動で行うため、
+ このメソッドは主にデータの更新(INSERT 等)に使います(カラム追加は属性+schema:update で足りる)。
+
+
+
+ PluginManager の書き方開発者向け
+ ライフサイクル処理が必要なときだけ、プラグイン側に Plugin\{Code}\PluginManager(クラス名は固定)を作り、
+ AbstractPluginManager を継承します。実装の書き方・DO/DON'T は
+ plugin/SKILL.md が正典です。
+ namespace Plugin\Example;
+
+class PluginManager extends AbstractPluginManager
+{
+ public function enable(array $meta, ContainerInterface $container): void
+ {
+ $conn = $container->get('doctrine')->getManager()->getConnection();
+ $this->migration($conn, $meta['code']); // migration_example テーブルで管理
+ }
+}
+
+
+
+ 注意点開発者向け
+
+ - ここにプラグイン本体を置かない。プラグインは
app/Plugin/{PluginCode}/。このディレクトリは基底クラス専用。
+ - install 直後はデフォルト無効(
enabled=false)。有効化は eccube:plugin:enable --code=... か管理画面で行う。
+ - プラグインのマイグレーションはコアの
app/DoctrineMigrations/ とは別管理(migration_{code} テーブル)。混同しない。
+ - コア機構(インストール・Composer 連携・コマンド)を追う場合は
PluginService / src/Eccube/Command/Plugin*Command.php を見る。
+
+
+
+
+
diff --git a/src/Eccube/Plugin/README.md b/src/Eccube/Plugin/README.md
new file mode 100644
index 00000000000..268083acde6
--- /dev/null
+++ b/src/Eccube/Plugin/README.md
@@ -0,0 +1,22 @@
+# Plugin 基盤(コア)
+
+プラグインの**ライフサイクル基盤**を提供する階層。実体は `AbstractPluginManager.php` のみで、
+各プラグインの `PluginManager` はこれを継承する。プラグイン本体は置かず(本体は `app/Plugin/{PluginCode}/`)、
+install/enable/disable/update/uninstall の共通処理と `migration()` ヘルパを持つ。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`plugin/SKILL.md`](../../../.claude/skills/plugin/SKILL.md)
+- 📦 プラグイン本体の置き場: [`app/Plugin/`](../../../app/Plugin/README.md)
+
+## 主要ファイル
+
+- `AbstractPluginManager.php` — 全 `PluginManager` の基底。5 つのライフサイクルメソッド(すべて no-op 既定)と、
+ プラグイン専用マイグレーションを回す `migration()`(既定で `app/Plugin/{code}/DoctrineMigrations`、管理テーブルは `migration_{code}`)
+
+## コア機構本体(別階層)
+
+ライフサイクルを駆動する実処理はこの階層ではなく次にある。
+
+- `src/Eccube/Service/PluginService.php` — install/enable/disable/uninstall の中核
+- `src/Eccube/Service/Composer/` — Composer 連携(依存取得・削除)
+- `src/Eccube/Command/Plugin*Command.php` — `eccube:plugin:generate|install|enable|disable|update|uninstall`
diff --git a/src/Eccube/Repository/README.html b/src/Eccube/Repository/README.html
new file mode 100644
index 00000000000..ff9ff549079
--- /dev/null
+++ b/src/Eccube/Repository/README.html
@@ -0,0 +1,90 @@
+
+
+
+
+
+Repository — 仕様
+
+
+
+
+Repository — データアクセス
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): repository/SKILL.md
+ / ドメイン詳細: doc4「受注」仕様
+
+
+
+ 概要
+ このディレクトリは、エンティティの取得・保存・削除といったデータアクセスを担う Doctrine リポジトリを置きます。
+ 「商品を条件で検索する」「会員の受注一覧を引く」といった DB への問い合わせは、コントローラやサービスに直接書かず、
+ 対応するリポジトリのメソッドに集約します。
+ 各リポジトリは Eccube\Repository\AbstractRepository<T>(Symfony の ServiceEntityRepository を継承)を継承し、
+ コンストラクタで担当エンティティのクラスを親へ渡します。1 エンティティにつき 1 リポジトリが対応します。
+ 責務はデータアクセスのみ。 金額計算・状態遷移・在庫引当などの業務ロジックはここに書かず Service / PurchaseFlow へ寄せます。
+
+
+
+ 主要リポジトリ
+
+ | リポジトリ | 担当エンティティ | 主な役割 |
+ ProductRepository | Product | 店頭・管理画面の商品検索(規格の並び替え込み取得) |
+ OrderRepository | Order | 受注検索・ステータス変更・会員別受注一覧 |
+ CustomerRepository | Customer | 会員検索・認証まわりの取得 |
+ CartRepository | Cart | カートの取得・保存 |
+ マスタ系(Master/) | OrderStatus 等 | マスタ(mtb_*)の取得 |
+
+ 検索系メソッドの定石は getQueryBuilderBySearchData(array $searchData): QueryBuilder を返す形で、
+ 管理画面用は getQueryBuilderBySearchDataForAdmin() のように分けます(ProductRepository / OrderRepository が実例)。
+
+
+
+ クエリの書き方
+ $qb = $this->createQueryBuilder('p'); // QueryBuilder / DQL を使う(生 SQL 連結はしない)
+$qb->andWhere('p.name LIKE :name') // 検索条件を組み立て
+ ->setParameter('name', '%'.$word.'%'); // 値は必ず setParameter でバインド(SQL インジェクション対策)
+return $qb->orderBy('p.id', 'DESC'); // 一覧はページング用に QueryBuilder を返すに留める
+
+ createQueryBuilder() / DQL を使う。 生 SQL の文字列連結は避けます。
+ - 値は必ず
setParameter() でバインド。 検索語を文字列に直挿ししません。
+ - N+1 を避ける。 必要に応じて
addSelect() / JOIN で関連をまとめて取得します。
+ - ページングはコントローラ/サービス側。 リポジトリは
QueryBuilder を返すに留め、knp_paginator は呼び出し側で適用します。
+
+
+
+
+ 永続化と拡張開発者向け
+ 保存・削除は AbstractRepository が提供する save(AbstractEntity $entity) / delete(AbstractEntity $entity) を使います。
+ これらをオーバーライドする場合は親クラスのシグネチャを厳守します。
+ プロジェクト固有の検索メソッド追加は app/Customize/Repository/ で行います。
+ 実装の書き方・DO/DON'T は repository/SKILL.md が正典です。
+
+
+
+ 注意点開発者向け
+
+ - 業務ロジックを持ち込まない。 金額計算・状態遷移・受注処理は Service / PurchaseFlow の責務です。
+ ServiceEntityRepository を直接継承しない。 必ず AbstractRepository<T> を継承します。
+ - 全件取得を無制限にしない。 画面表示の一覧・関連取得は件数が際限なく増え得るため、ページング(QueryBuilder を返す)か上限を設けます。
+ - 受注一覧は仮受注を除外する。
PROCESSING・PENDING は確定前の仮受注のため、売上集計や一覧では除外します(OrderStatusFilter)。
+
+
+
+
+
diff --git a/src/Eccube/Repository/README.md b/src/Eccube/Repository/README.md
new file mode 100644
index 00000000000..64ac501ffab
--- /dev/null
+++ b/src/Eccube/Repository/README.md
@@ -0,0 +1,17 @@
+# Repository — データアクセス
+
+エンティティの取得・保存・削除を担う Doctrine リポジトリ。DB への問い合わせ(検索・一覧の絞り込み)を
+コントローラやサービスに散らさず、`AbstractRepository` を継承した各リポジトリに集約する。
+**責務はデータアクセスのみ**(業務ロジックは Service / PurchaseFlow へ)。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`repository/SKILL.md`](../../../.claude/skills/repository/SKILL.md)
+- 📚 ドメイン詳細: https://doc4.ec-cube.net/spec_order
+
+## 主要ファイル
+
+- `AbstractRepository.php` — 全リポジトリの基底。`save()` / `delete()` を提供
+- `ProductRepository.php` — 商品検索(`getQueryBuilderBySearchData` / `...ForAdmin`)
+- `OrderRepository.php` — 受注検索・ステータス変更・会員別受注一覧
+- `CustomerRepository.php` — 会員検索・認証まわりの取得
+- `Master/` — マスタ系(`mtb_*`)リポジトリ
diff --git a/src/Eccube/Security/README.html b/src/Eccube/Security/README.html
new file mode 100644
index 00000000000..1aed15e9eb7
--- /dev/null
+++ b/src/Eccube/Security/README.html
@@ -0,0 +1,109 @@
+
+
+
+
+
+Security — 仕様
+
+
+
+
+Security — 認証・認可
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): security/SKILL.md
+
+
+
+ 概要
+ このディレクトリは、EC-CUBE の認証(誰か)・認可(何を許すか)の中核部品を収めます。
+ EC-CUBE のアクセス制御は「ファイアウォール+ロール+Voter」の 3 層モデルで、
+ 個々のアクションに #[IsGranted] を撒くのではなく、パスのプレフィックスで面的に守るのが基本です。
+
+ ファイアウォールと access_control の宣言は app/config/eccube/packages/security.yaml、
+ および src/Eccube/DependencyInjection/EccubeExtension.php(後述の動的生成)にあります。
+ このディレクトリはそこに差し込まれる実装(UserProvider / PasswordHasher / Voter / 各種ハンドラ)を提供します。
+
+
+ | ファイアウォール | 対象パス | 認証する主体 |
+ | admin | ^/%eccube_admin_route%/ | Member(管理者)。CSRF 有効・ログイン試行制限あり |
+ | customer | ^/(サイト全体) | Customer(会員)。remember_me あり |
+ | dev | 静的リソース等 | security: false(認証対象外) |
+
+ 認可判定は unanimous 戦略(allow_if_all_abstain: false)。
+ 1 つでも Voter が DENY すれば拒否、全 Voter が棄権しても拒否(=明示的な許可が必要)です。
+
+
+
+ ディレクトリ構成と役割
+
+ | サブ | クラス | 役割 |
+ | Core/User | CustomerProvider / MemberProvider |
+ ログイン ID からユーザーを解決する UserProviderInterface。会員は email+本会員(REGULAR)、管理者は login_id+有効(Work::ACTIVE)で引く。PasswordUpgraderInterface 実装によりログイン成功時にハッシュを自動再計算・保存する。 |
+ | PasswordHasher | PasswordHasher |
+ パスワードのハッシュ・照合(hash_hmac)。旧バージョン(2.11 未満)からの移行照合も内包。 |
+ | Core/Encoder | PasswordEncoder |
+ ハッシュ生成・照合・salt 生成のヘルパ。eccube_auth_magic 等の設定を参照する。 |
+ | Voter | AuthorityVoter |
+ 管理画面内の URL 単位の権限制御。Member の Authority に紐づく AuthorityRole.deny_url にパスが一致したら ACCESS_DENIED。 |
+ | Http/Authentication | EccubeAuthenticationSuccessHandler / EccubeAuthenticationFailureHandler / EccubeLogoutSuccessHandler |
+ ログイン成功・失敗時のリダイレクト先を絶対 URL からパスへ正規化(オープンリダイレクト対策)。ログアウト時は管理画面のメンテナンス Cookie を破棄。 |
+
+
+
+
+ access_control はどこで決まるか
+ 「どのパスにどのロールを要求するか」は security.yaml に直書きされておらず、
+ EccubeExtension.php が動的に生成して差し込みます。security.yaml だけを見ても全体像は掴めません。
+ ^/%eccube_admin_route%/login IS_AUTHENTICATED_ANONYMOUSLY … ログイン画面は誰でも
+^/%eccube_admin_route%/ ROLE_ADMIN … 管理画面全体(面で保護)
+^/mypage/login IS_AUTHENTICATED_ANONYMOUSLY
+^/mypage/withdraw_complete IS_AUTHENTICATED_ANONYMOUSLY
+^/mypage/change IS_AUTHENTICATED_FULLY … 重要操作は FULLY(remember-me 除外)
+^/mypage/ ROLE_USER … 会員ページ全体
+ 最重要の含意: 新しい管理アクションは、認可の主装置がファイアウォール(パスのプレフィックス)なので、
+ 必ず %eccube_admin_route% 配下のパスに置くこと。配下から外すと無認証で到達できてしまう(よくある重大な事故)。
+
また ^/mypage/ は ROLE_USER 前提なので、その配下のコントローラでは getUser() は非 null が保証される。
+
+
+
+ 拡張ポイント開発者向け
+ 実装の書き方・DO/DON'T は security/SKILL.md が正典です。ここでは全体像だけ示します。
+
+ | やりたいこと | 手段 |
+ | 特定の権限にこの管理画面を見せない | コントローラ改変ではなく dtb_authority_role の deny_url 設定。AuthorityVoter が拾う。 |
+ | 独自の認可ルールを足す | Symfony の Voter を継承(autoconfigure で security.voter タグは自動付与)。対象外は必ず ACCESS_ABSTAIN。 |
+ | ログイン後の遷移・ハンドラを変える | Http/Authentication の各ハンドラを継承/デコレート。 |
+ | フォームを介さない状態変更(削除・Ajax) | コントローラで $this->isTokenValid() を明示的に呼び CSRF を検証(GET 以外は必須)。 |
+
+
+
+
+ 注意点開発者向け
+
+ - unanimous 戦略に注意。独自 Voter で「対象外」を
ACCESS_DENIED で返すと全体が拒否になる。対象外は必ず ACCESS_ABSTAIN。
+ - IDOR: フロントで
{id} から取得したエンティティは getUser() と突き合わせ、他人のものなら AccessDeniedHttpException。
+ - 重要操作は
IS_AUTHENTICATED_FULLY(パスワード変更・退会・購入確定など)。remember-me(盗難 Cookie)を弾く。
+ - パスワードは
PasswordHasher / PasswordEncoder 任せにし、自前ハッシュ・平文比較を書かない。PasswordHasher::needsRehash() が常に true を返すため、ログインのたびに現行アルゴリズムへ再ハッシュされる。
+ AuthorityVoter はリクエストが取得できないとき(テスト等)ACCESS_ABSTAIN を返す。deny_url は正規表現として評価され、誤りがあれば preg_quote でエスケープしてリトライする。
+ - ログイン成功/失敗ハンドラは、リダイレクト先が絶対 URL のときパス部分だけを取り出す(外部ドメインへのオープンリダイレクト対策)。
+
+
+
+
+
diff --git a/src/Eccube/Security/README.md b/src/Eccube/Security/README.md
new file mode 100644
index 00000000000..6a6be906ee3
--- /dev/null
+++ b/src/Eccube/Security/README.md
@@ -0,0 +1,19 @@
+# Security — 認証・認可
+
+EC-CUBE の認証(誰か)・認可(何を許すか)の中核部品。アクセス制御は
+「ファイアウォール+ロール+Voter」の 3 層で、パスのプレフィックスで面的に守るのが基本。
+UserProvider(ユーザー解決)・PasswordHasher(照合)・AuthorityVoter(URL 単位の権限)・
+ログイン/ログアウトの各ハンドラを収める。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`security/SKILL.md`](../../../.claude/skills/security/SKILL.md)
+
+## 主要ファイル
+
+- `Core/User/CustomerProvider.php` / `Core/User/MemberProvider.php` — ログイン ID からユーザーを解決する UserProvider。ログイン成功時にパスワードを現行アルゴリズムへ自動再ハッシュ
+- `PasswordHasher/PasswordHasher.php` — パスワードのハッシュ・照合(旧 2.11 未満からの移行照合を内包)
+- `Voter/AuthorityVoter.php` — 管理画面内の URL 単位の権限制御(`AuthorityRole.deny_url` に一致で拒否)
+- `Http/Authentication/EccubeAuthenticationSuccessHandler.php` — ログイン後リダイレクトを絶対 URL からパスへ正規化(オープンリダイレクト対策)。失敗・ログアウト用ハンドラも同ディレクトリ
+
+> access_control(どのパスにどのロールを要求するか)は security.yaml 直書きではなく
+> `src/Eccube/DependencyInjection/EccubeExtension.php` が動的生成する点に注意。
diff --git a/src/Eccube/Service/AgentCommerce/Catalog/README.html b/src/Eccube/Service/AgentCommerce/Catalog/README.html
new file mode 100644
index 00000000000..4622b8f68ea
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Catalog/README.html
@@ -0,0 +1,84 @@
+
+
+
+
+
+AgentCommerce Catalog — 仕様
+
+
+
+
+AgentCommerce Catalog — カタログ提供
+
+ 親: AgentCommerce README
+ / 索引: README.md
+ / 同梱リソースの出所: Resource/AgentCommerce/README.md
+
+
+
+ 概要
+ EC-CUBE の Product / ProductClass をプロトコル非依存の中立 DTO に写し、
+ そこから ACP Feed(push 型)と UCP Catalog(POST RPC)の両方へ出力するサブドメインです。
+ addressable unit のマッピングは 親 Product = AgentCatalogItemDto / variant ProductClass = AgentCatalogVariantDto。
+ 出力対象は公開中商品(ProductStatus::DISPLAY_SHOW)かつ visible な ProductClass に限ります。
+ 金額は店頭と同じ税込価格を MinorUnitConverter で minor-unit 整数へ変換します(確定金額は CheckoutSession で再計算)。
+
+
+
+ 中立 DTO / enum
+
+ | クラス | 役割 |
+ AgentCatalogItemDto | カタログ商品(親 = Product)。variants[] に variant DTO を持つ。 |
+ AgentCatalogVariantDto | バリエーション(addressable unit = ProductClass)。金額は minor-unit 整数。 |
+ AgentCatalogOptionDto | variant option。EC-CUBE の規格分類(ClassName 名 / ClassCategory 名)に対応。 |
+ AgentCatalogMediaDto | メディア(画像等)。type / 絶対 URL。 |
+ AgentCatalogDescriptionDto | 商品説明。plain / html / markdown を任意保持(html は描画側でサニタイズ前提)。 |
+ AgentCatalogBarcodeDto | バーコード(GTIN/JAN 等)。標準では常に空(EC-CUBE に GTIN 項目が無いため Customize seam)。 |
+ AvailabilityStatus | 在庫状況 enum。標準出力は IN_STOCK / OUT_OF_STOCK の 2 値のみ(他は仕様準拠で定義のみ)。 |
+
+
+
+
+ マッピングと供給
+
+ | クラス | 役割 |
+ CatalogMapper | Product/ProductClass → AgentCatalogItemDto への写像。出力対象判定と availability 判定の中核(対応表は同クラス docblock)。 |
+ CatalogProvider(CatalogProviderInterface) | 公開中 Product を id 昇順で逐次供給。大規模カタログでメモリ枯渇しないよう EntityManager を一定間隔で clear。 |
+ ProductReferenceResolver(ProductReferenceResolverInterface) | エージェントの参照子(sku=ProductClass.code / product_class_id / barcode)を ProductClass へ解決。 |
+
+
+
+
+ プロトコル別の出力開発者向け
+ Acp/ と Ucp/ に、中立 DTO を各プロトコルの本文へ写すシリアライザ等を置きます。
+
+ | クラス | 役割 |
+ ACP AcpFeedGenerator | 全置換成果物(products.jsonl / metadata.json)をストリーム生成(全件をメモリに蓄積しない)。 |
+ ACP AcpFeedProductSerializer | DTO → ACP Feed Product 連想配列(schema.feed.json の $defs/Product に適合。null/空は出さない)。 |
+ ACP AcpFeedValidator | vendored schema.feed.json に対し push 前検証(不正データ送出を防ぐ必須ゲート)。 |
+ ACP AcpFeedClient(AcpFeedClientInterface) | OpenAI ホストの Feed API へ outbound push。Bearer は env bind、ログ/例外に出さない。 |
+ UCP UcpCatalogResponseBuilder | search / lookup / product の各レスポンス本文(ucp ラッパー付き)を組み立て。 |
+ UCP UcpCatalogProductSerializer | DTO → UCP Catalog Product/Variant 連想配列(price_range は variants から算出)。 |
+ UCP UcpCatalogCache | UCP Catalog レスポンスの FS キャッシュ(POST RPC のためボディ hash をキー、LockFactory でスタンピード防止)。 |
+
+ Exception/ に ACP Feed 用の例外(AcpFeedException 基底 / AcpFeedTransportException(HTTP 4xx/5xx)/ AcpFeedValidationException(schema 不適合))を置きます。
+ ACP Feed は push モデル(Agent=OpenAI がホスト、加盟店がクライアント)で、outbound Bearer で認証する。inbound OAuth2(eccube-api4)とは無関係な点に注意。
+
+
+
+
diff --git a/src/Eccube/Service/AgentCommerce/Catalog/README.md b/src/Eccube/Service/AgentCommerce/Catalog/README.md
new file mode 100644
index 00000000000..594dcbb6e9f
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Catalog/README.md
@@ -0,0 +1,19 @@
+# AgentCommerce Catalog — カタログ提供
+
+EC-CUBE の `Product` / `ProductClass` をプロトコル非依存の中立 DTO に写し、
+ACP Feed(push 型)と UCP Catalog(POST RPC)の両方へ出力するサブドメイン。
+親 `Product` = `AgentCatalogItemDto` / variant `ProductClass` = `AgentCatalogVariantDto`。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- ⬆ 親: [AgentCommerce README](../README.md)
+- 📦 同梱リソースの出所: [`Resource/AgentCommerce/README.md`](../../../Resource/AgentCommerce/README.md)
+
+## 主要ファイル
+
+- `CatalogMapper.php` — `Product`/`ProductClass` → 中立 DTO への写像(出力対象・availability 判定の中核)
+- `CatalogProvider.php`(`CatalogProviderInterface.php`)— 公開中 `Product` を逐次供給(大規模カタログ対応)
+- `ProductReferenceResolver.php`(`ProductReferenceResolverInterface.php`)— sku / product_class_id / barcode を `ProductClass` へ解決
+- `AgentCatalog*Dto.php` / `AvailabilityStatus.php` — プロトコル非依存の中立 DTO・enum
+- `Acp/` — ACP Feed の生成(`AcpFeedGenerator`)/ シリアライズ / 検証(`AcpFeedValidator`)/ push(`AcpFeedClient`)
+- `Ucp/` — UCP Catalog のレスポンス組み立て(`UcpCatalogResponseBuilder`)/ シリアライズ / FS キャッシュ
+- `Exception/` — ACP Feed 用例外(`AcpFeedException` / `AcpFeedTransportException` / `AcpFeedValidationException`)
diff --git a/src/Eccube/Service/AgentCommerce/CheckoutSession/README.html b/src/Eccube/Service/AgentCommerce/CheckoutSession/README.html
new file mode 100644
index 00000000000..272861c4789
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/CheckoutSession/README.html
@@ -0,0 +1,66 @@
+
+
+
+
+
+AgentCommerce CheckoutSession — 仕様
+
+
+
+
+AgentCommerce CheckoutSession — チェックアウト
+
+ 親: AgentCommerce README
+ / 索引: README.md
+ / 受注処理の詳細: PurchaseFlow README
+
+
+
+ 概要
+ エージェントチェックアウトの見積〜確定(complete)を、プロトコル非依存の中立表現で束ねるサブドメインです。
+ エージェントはブラウザセッションを持たないため、通常購入のセッションに代わって本サブドメインの DTO がチェックアウトの状態を運びます。
+ 実際の金額再計算・在庫引当は、ルート直下の AgentCheckoutPurchaseFlowAdapter が
+ 通常購入と同一の shopping flow を使って行います(本サブドメインはその入出力と確定の段取りを担当)。
+
+
+
+ 中立 DTO / enum
+
+ | クラス | 役割 |
+ AgentCheckoutRequest | 見積要求。明細(AgentCheckoutLineItem[])と購入者/配送先住所(AgentCheckoutAddress)を保持。 |
+ AgentCheckoutLineItem | 明細 1 行。プロトコル固有 Mapper が variant 識別子を ProductClass.id へ解決して組み立てる。 |
+ AgentCheckoutAddress | 購入者/配送先住所。prefId は mtb_pref.id(1-47)。 |
+ AgentCheckoutResult | 見積/確定の結果。再計算済み Order とビジネス系 messages[]。見積時は Cart も返す。 |
+ AgentCheckoutCompletionResult | complete 状態機械の結果。status(正規化)/ actionData(continue_url 等の原資)/ messages。 |
+ AgentCheckoutMessage / AgentCheckoutMessageLevel | ビジネス系メッセージ(HTTP 200 + messages[])とそのレベル。 |
+
+
+
+
+ オーケストレーションと会員解決開発者向け
+
+ | クラス | 役割 |
+ AgentCheckoutCompletionService | complete を「中断 → 再開」する状態機械として実行するオーケストレータ。追加認証(EMV-3DS)や外部ハンドオフ(escalation)はエラーでなく正常な中間状態として扱う。在庫引当の保持/回収・正規化ステータス遷移・トランザクション境界を core に集約。 |
+ CustomerResolverInterface | セッションに紐づく会員(Customer)を解決する seam。OAuth2 トークン → Customer 解決(eccube-api4 依存)。 |
+ GuestCustomerResolver | 標準実装。会員 ID 連携が landing するまで常に null(ゲスト購入)を返す。 |
+
+ complete は冪等な単発呼び出しではなく複数回呼ばれる状態機械である。prepare で物理引当した在庫は REQUIRES_ACTION/PENDING では rollback せず保持し、FAILED/期限切れで rollback する。StockReduceProcessor の悲観ロックに対応するため各 complete を明示トランザクションで囲む。
+
+
+
+
diff --git a/src/Eccube/Service/AgentCommerce/CheckoutSession/README.md b/src/Eccube/Service/AgentCommerce/CheckoutSession/README.md
new file mode 100644
index 00000000000..d768d0038f0
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/CheckoutSession/README.md
@@ -0,0 +1,17 @@
+# AgentCommerce CheckoutSession — チェックアウト
+
+エージェントチェックアウトの見積〜確定(complete)を、プロトコル非依存の中立表現で束ねるサブドメイン。
+金額再計算・在庫引当はルート直下の `AgentCheckoutPurchaseFlowAdapter` が通常購入と同一の shopping flow で行い、
+本サブドメインはその入出力と確定(中断→再開する状態機械)の段取りを担う。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- ⬆ 親: [AgentCommerce README](../README.md)
+- 📚 受注処理の詳細: [PurchaseFlow README](../../PurchaseFlow/README.md)
+
+## 主要ファイル
+
+- `AgentCheckoutCompletionService.php` — complete を「中断→再開」する状態機械として実行するオーケストレータ
+- `AgentCheckoutRequest.php` / `AgentCheckoutLineItem.php` / `AgentCheckoutAddress.php` — 見積要求の中立 DTO
+- `AgentCheckoutResult.php` / `AgentCheckoutCompletionResult.php` — 見積/確定・complete の中立結果
+- `AgentCheckoutMessage.php` / `AgentCheckoutMessageLevel.php` — ビジネス系メッセージ(HTTP 200 + messages[])
+- `CustomerResolverInterface.php`(`GuestCustomerResolver.php`)— 会員解決 seam(標準はゲスト固定)
diff --git a/src/Eccube/Service/AgentCommerce/Discovery/README.html b/src/Eccube/Service/AgentCommerce/Discovery/README.html
new file mode 100644
index 00000000000..8a2cfc544cc
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Discovery/README.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+AgentCommerce Discovery — 仕様
+
+
+
+
+AgentCommerce Discovery — 機能発見
+
+ 親: AgentCommerce README
+ / 索引: README.md
+ / プロトコル仕様: UCP profile.json
+
+
+
+ 概要
+ エージェントが店舗の対応機能を発見するための UCP discovery profile(/.well-known/ucp)を組み立てるサブドメインです。
+ profile には対応サービス・決済ハンドラ・署名鍵(JWK)が含まれ、これを起点にエージェントはカタログ取得やチェックアウトを行います。
+ { "ucp": { version, services, payment_handlers, capabilities }, "signing_keys": [JWK...] }
+ services / capabilities / payment_handlers のキーは reverse-domain 形式。endpoint(絶対 URL)はパスをハードコードせず UrlGenerator(RequestContext)から動的生成します。
+
+
+
+ 主要クラス
+
+ | クラス | 役割 |
+ UcpProfileBuilder | UCP discovery profile ドキュメントを組み立てる。signing_keys[] は EC 公開鍵 JWK のみ(秘密鍵パラメータ非混入、UcpMessageSigner の戻りをそのまま使う)。 |
+ PaymentHandlerRegistryInterface | profile の payment_handlers(reverse-domain キーのレジストリ)を寄与する口。 |
+ EmptyPaymentHandlerRegistry | 既定実装。tagged service で寄与された各レジストリをマージ。寄与が無ければ空オブジェクト {} を返す。 |
+
+
+
+
+ 注意点開発者向け
+
+ - core 本体は
payment_handlers をカラム化せず、PaymentHandlerRegistryInterface の実装からのみ収集する。決済ハンドラプラグイン(将来の Google Pay 等)が eccube.agent_commerce.payment_handler_registry タグ付きサービスで寄与する。
+ - UCP profile schema 上
payment_handlers は必須のため、寄与が無い場合でも空オブジェクト {} を出す(空配列 [] ではない)。
+
+
+
+
+
diff --git a/src/Eccube/Service/AgentCommerce/Discovery/README.md b/src/Eccube/Service/AgentCommerce/Discovery/README.md
new file mode 100644
index 00000000000..eb16a5ccd1b
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Discovery/README.md
@@ -0,0 +1,15 @@
+# AgentCommerce Discovery — 機能発見
+
+エージェントが店舗の対応機能を発見するための UCP discovery profile(`/.well-known/ucp`)を組み立てるサブドメイン。
+profile には対応サービス・決済ハンドラ・署名鍵(JWK)が含まれる。
+core は `payment_handlers` をカラム化せず、tagged service で寄与された内容のみを収集する。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- ⬆ 親: [AgentCommerce README](../README.md)
+- 📚 プロトコル仕様: [UCP profile.json](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/schemas/profile.json)
+
+## 主要ファイル
+
+- `UcpProfileBuilder.php` — UCP discovery profile ドキュメントの組み立て(endpoint は UrlGenerator で動的生成)
+- `PaymentHandlerRegistryInterface.php` — `payment_handlers`(reverse-domain キー)を寄与する口
+- `EmptyPaymentHandlerRegistry.php` — 既定実装。寄与が無ければ空オブジェクト `{}` を返す
diff --git a/src/Eccube/Service/AgentCommerce/Exception/README.html b/src/Eccube/Service/AgentCommerce/Exception/README.html
new file mode 100644
index 00000000000..ed14a7d9a90
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Exception/README.html
@@ -0,0 +1,59 @@
+
+
+
+
+
+AgentCommerce Exception — 仕様
+
+
+
+
+AgentCommerce Exception — 例外とエラー分類
+
+ 親: AgentCommerce README
+ / 索引: README.md
+
+
+
+ 概要
+ エージェントチェックアウトのプロトコル系エラー(不正要求・処理不能)を分類・表現するサブドメインです。
+ ACP/UCP 共通の「2 系統エラー」のうち、ここで扱うのは HTTP 4xx/5xx + Error に対応する側です。
+ 在庫切れ・販売停止等のビジネス系エラー(HTTP 200 + messages[])は例外ではなく
+ CheckoutSession の AgentCheckoutMessage で表現します。
+
+
+
+ 主要クラス / enum
+
+ | クラス | 役割 |
+ AgentCheckoutErrorCode | プロトコル系エラーのコード分類 enum(空明細・商品未検出・住所不正・数量不正等)。 |
+ AgentCheckoutException | プロトコル系エラーの例外。不正要求・処理不能を表す。 |
+ IdempotencyConflictException | Idempotency-Key の競合(HTTP 409 相当)。Idempotency サブドメインから投げられる。 |
+
+
+
+
+ 注意点開発者向け
+
+ - HTTP ステータスや
messages[] への具体的な変換アダプタはプロトコル層(#6776 / #6574)が担う。本サブドメインは中立なエラー分類・例外型のみを提供する。
+ - ビジネス系(在庫切れ等)をここで例外にしない。混同すると本来 HTTP 200 で返すべき結果を 4xx/5xx で返してしまう。
+
+
+
+
+
diff --git a/src/Eccube/Service/AgentCommerce/Exception/README.md b/src/Eccube/Service/AgentCommerce/Exception/README.md
new file mode 100644
index 00000000000..feae8c5e0b7
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Exception/README.md
@@ -0,0 +1,16 @@
+# AgentCommerce Exception — 例外とエラー分類
+
+エージェントチェックアウトのプロトコル系エラー(不正要求・処理不能=HTTP 4xx/5xx + Error)を分類・表現するサブドメイン。
+在庫切れ・販売停止等のビジネス系エラー(HTTP 200 + messages[])は例外ではなく
+[CheckoutSession](../CheckoutSession/README.md) の `AgentCheckoutMessage` で表現する(混同しない)。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- ⬆ 親: [AgentCommerce README](../README.md)
+
+## 主要ファイル
+
+- `AgentCheckoutErrorCode.php` — プロトコル系エラーのコード分類 enum
+- `AgentCheckoutException.php` — プロトコル系エラーの例外(不正要求・処理不能)
+- `IdempotencyConflictException.php` — Idempotency-Key の競合(HTTP 409 相当)
+
+HTTP ステータスや messages[] への変換はプロトコル層(#6776 / #6574)が担う。
diff --git a/src/Eccube/Service/AgentCommerce/Fulfillment/README.html b/src/Eccube/Service/AgentCommerce/Fulfillment/README.html
new file mode 100644
index 00000000000..94ad486383b
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Fulfillment/README.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+AgentCommerce Fulfillment — 仕様
+
+
+
+
+AgentCommerce Fulfillment — 配送方法の提示
+
+ 親: AgentCommerce README
+ / 索引: README.md
+
+
+
+ 概要
+ エージェントへ提示する配送方法の選択肢を、EC-CUBE の配送マスタから中立表現へ組み立てるサブドメインです。
+ 各選択肢は配送方法(Delivery)に対応し、配送先(Pref)に対する送料・配送日数・利用可能な支払方法を保持します。
+ 金額はすべて minor-unit(整数)で表現します。
+
+
+
+ 主要クラス
+
+ | クラス | 役割 |
+ FulfillmentOption | 配送方法の選択肢(Delivery 1 件)。送料(shippingFeeMinor)・配送日数・支払方法選択肢を保持。 |
+ FulfillmentPaymentOption | 配送方法に紐づく支払方法の選択肢。代引手数料等を chargeMinor(minor-unit)で保持。 |
+ FulfillmentOptionMapperInterface | 明細と配送先から選択肢を組み立てる seam(app/Customize で差し替え可能)。 |
+ StandardFulfillmentOptionMapper | 標準実装。Delivery/DeliveryFee/Payment/DeliveryDuration マスタから選択肢を組み立てる。 |
+
+
+
+
+ 標準実装の判定開発者向け
+
+ - 利用可能な
Delivery は明細の SaleType から DeliveryRepository::getDeliveries() で絞る。
+ - 送料は配送先
Pref と Delivery の DeliveryFee から解決(該当行が無ければ配送不可として除外)。
+ - 手数料(代引等)は
Delivery の PaymentOption → Payment::getCharge() から解決。
+ - 配送日数は明細横断の
DeliveryDuration::getDuration() の最大値(負数=お取り寄せが 1 件でもあれば未確定として null)。
+ - 複数
SaleType をまたぐ注文(複数配送分割)の厳密な表現は標準実装の対象外で、app/Customize での差し替えに委ねる(フラットな一覧を返す)。
+
+
+
+
+
diff --git a/src/Eccube/Service/AgentCommerce/Fulfillment/README.md b/src/Eccube/Service/AgentCommerce/Fulfillment/README.md
new file mode 100644
index 00000000000..a25ed433277
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Fulfillment/README.md
@@ -0,0 +1,14 @@
+# AgentCommerce Fulfillment — 配送方法の提示
+
+エージェントへ提示する配送方法の選択肢を、EC-CUBE の配送マスタ(`Delivery`/`DeliveryFee`/`Payment`/`DeliveryDuration`)
+から中立表現へ組み立てるサブドメイン。各選択肢は送料・配送日数・利用可能な支払方法を minor-unit(整数)で保持する。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- ⬆ 親: [AgentCommerce README](../README.md)
+
+## 主要ファイル
+
+- `StandardFulfillmentOptionMapper.php` — 配送マスタから選択肢を組み立てる標準実装
+- `FulfillmentOptionMapperInterface.php` — 差し替え用 seam
+- `FulfillmentOption.php` — 配送方法の選択肢(送料・配送日数・支払方法)
+- `FulfillmentPaymentOption.php` — 配送方法に紐づく支払方法の選択肢(代引手数料等)
diff --git a/src/Eccube/Service/AgentCommerce/Idempotency/README.html b/src/Eccube/Service/AgentCommerce/Idempotency/README.html
new file mode 100644
index 00000000000..0d20599271e
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Idempotency/README.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+AgentCommerce Idempotency — 仕様
+
+
+
+
+AgentCommerce Idempotency — 冪等性
+
+ 親: AgentCommerce README
+ / 索引: README.md
+
+
+
+ 概要
+ 状態変更操作(complete 等)の二重実行を防ぐためのサブドメインです。
+ リクエストに Idempotency-Key ヘッダが付与された場合、初回はハンドラを実行して結果を保存し、
+ 同一キーの再送はハンドラを再実行せず保存済みレスポンスをリプレイします(副作用の再実行なし)。
+
+
+
+ 主要クラス
+
+ | クラス | 役割 |
+ AgentCheckoutIdempotencyStore | Idempotency-Key 処理の本体。dtb_agent_checkout_idempotency に結果を保存し、再送時にリプレイする。 |
+
+ (idempotency_key, subject) の DB 一意制約で直列化するため、Redis 等の共有キャッシュや分散ロックに依存せず、マルチインスタンス(AWS 等)でも単一の共有 DB だけで並行・越境の二重実行を防ぐ。subject は認証済みエージェント識別子で名前空間化する。
+
+
+
+ 競合の扱い開発者向け
+ 次の場合は IdempotencyConflictException(Exception サブドメイン)を投げ、プロトコル層で HTTP 409 Conflict へ変換する。
+
+ - 同一キーが異なるリクエスト内容で再利用された場合
+ - 同一キーの処理がまだ進行中(並行リクエスト)の場合
+
+
+
+
+
diff --git a/src/Eccube/Service/AgentCommerce/Idempotency/README.md b/src/Eccube/Service/AgentCommerce/Idempotency/README.md
new file mode 100644
index 00000000000..e8ad24e33ee
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Idempotency/README.md
@@ -0,0 +1,14 @@
+# AgentCommerce Idempotency — 冪等性
+
+状態変更操作(complete 等)の二重実行を防ぐサブドメイン。`Idempotency-Key` ヘッダの初回はハンドラを実行して結果を保存し、
+同一キーの再送は保存済みレスポンスをリプレイする(副作用の再実行なし)。
+`(idempotency_key, subject)` の DB 一意制約で直列化し、マルチインスタンスでも共有 DB だけで越境の二重実行を防ぐ。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- ⬆ 親: [AgentCommerce README](../README.md)
+
+## 主要ファイル
+
+- `AgentCheckoutIdempotencyStore.php` — Idempotency-Key 処理(`dtb_agent_checkout_idempotency` へ保存・再送でリプレイ)
+
+競合時(内容不一致の再利用・処理進行中)は `Exception/IdempotencyConflictException`(HTTP 409 相当)を投げる。
diff --git a/src/Eccube/Service/AgentCommerce/Payment/README.html b/src/Eccube/Service/AgentCommerce/Payment/README.html
new file mode 100644
index 00000000000..1147a22e8dc
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Payment/README.html
@@ -0,0 +1,63 @@
+
+
+
+
+
+AgentCommerce Payment — 仕様
+
+
+
+
+AgentCommerce Payment — 決済ハンドラ
+
+ 親: AgentCommerce README
+ / 索引: README.md
+
+
+
+ 概要
+ エージェント注文の決済ハンドラの型と、支払方法の割当・解決を担うサブドメインです。
+ 通常購入では買い手が画面で支払方法を選びますが、エージェントは画面を持たず discovery で広告された決済ハンドラを
+ handler_id で指定します。core は具象決済実装を持たず、決済プラグインがタグ付きサービスとして
+ ハンドラを寄与します(型のみ core が保持)。
+
+
+
+ 主要クラス / enum
+
+ | クラス | 役割 |
+ AgentCheckoutPaymentHandlerInterface | 決済ハンドラの共通基底。プロトコル固有のトークン償還(ACP Shared Payment Token / UCP Payment Token Exchange)は派生 IF が定義。 |
+ AgentCheckoutPaymentHandlerRegistry | agent_commerce.payment_handler タグのハンドラを集約し、Order の支払方法から適切なハンドラを解決。 |
+ AgentPaymentMethodResolverInterface | エージェント注文に割り当てる Payment を解決する seam。 |
+ DefaultAgentPaymentMethodResolver | 標準実装。PaymentRepository::findAllowedPayments() を、登録済みハンドラが扱える Payment へ絞り込む。 |
+ PaymentOutcome / PaymentOutcomeStatus | 決済ハンドラ(authorize/capture)の処理結果(Symfony Response 非依存の中立 DTO)とステータス enum。 |
+
+
+
+
+ 注意点開発者向け
+
+ - sort_no の機械採用は禁止。利用可能な支払方法の先頭(sort_no 最小)を採ると、エージェント決済不可能な方法(銀行振込等)が割り当たりハンドラ未通過のまま確定する「沈黙のすり抜け」を招く。リゾルバは登録済みハンドラが扱える
Payment のみを候補にし、sort_no は候補内のタイブレークとしてのみ使う。
+ DefaultAgentPaymentMethodResolver は methodClass 等のプロトコル固有判定をハンドラ側(supports())に委ね、特定の決済クラスに依存しない(将来 PSP が新 methodClass を出しても無改変で動く)。
+ PaymentOutcomeStatus は追加認証(EMV-3DS)や外部ハンドオフ(escalation)をエラーでなく正常な中間状態として表す。これを受けて CheckoutSession/AgentCheckoutCompletionService がステータス遷移・在庫引当の保持/回収を決める。
+ - 店舗ごとに選択ロジック(PSP 優先順位・通貨別ルーティング等)を変えるには
AgentPaymentMethodResolverInterface を app/Customize で実装し alias を差し替える。
+
+
+
+
+
diff --git a/src/Eccube/Service/AgentCommerce/Payment/README.md b/src/Eccube/Service/AgentCommerce/Payment/README.md
new file mode 100644
index 00000000000..367f39ea4d9
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Payment/README.md
@@ -0,0 +1,15 @@
+# AgentCommerce Payment — 決済ハンドラ
+
+エージェント注文の決済ハンドラの型と、支払方法の割当・解決を担うサブドメイン。
+エージェントは画面を持たず discovery で広告された決済ハンドラを `handler_id` で指定する。
+core は具象決済実装を持たず、決済プラグインがタグ付きサービスでハンドラを寄与する(型のみ core が保持)。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- ⬆ 親: [AgentCommerce README](../README.md)
+
+## 主要ファイル
+
+- `AgentCheckoutPaymentHandlerInterface.php` — 決済ハンドラの共通基底(プロトコル固有 IF が派生)
+- `AgentCheckoutPaymentHandlerRegistry.php` — タグ付きハンドラの集約と Order からの解決
+- `AgentPaymentMethodResolverInterface.php`(`DefaultAgentPaymentMethodResolver.php`)— `Payment` 割当リゾルバ(sort_no 機械採用を避ける)
+- `PaymentOutcome.php` / `PaymentOutcomeStatus.php` — 決済結果の中立 DTO とステータス(追加認証/escalation を中間状態として表す)
diff --git a/src/Eccube/Service/AgentCommerce/README.html b/src/Eccube/Service/AgentCommerce/README.html
new file mode 100644
index 00000000000..c1a09055a31
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/README.html
@@ -0,0 +1,109 @@
+
+
+
+
+
+AgentCommerce — 仕様
+
+
+
+
+AgentCommerce — エージェントコマース基盤
+
+ 索引: README.md
+ / 同梱リソースの出所: Resource/AgentCommerce/README.md
+ / プロトコル仕様: ACP
+ ・ UCP
+
+
+
+ 概要
+ AgentCommerce は、AI エージェント(OpenAI 等)が EC-CUBE の商品を発見し購入できるようにするための
+ エージェントコマース対応の基盤サービス群です。ブラウザセッションを持たないエージェントからの
+ カタログ提供・チェックアウト・決済・署名検証を、コントローラや個別サービスに散らさずこのディレクトリに集約します。
+
+ 対応プロトコルは 2 系統あり、プロトコル非依存の中立表現(DTO・中間結果)を核に置いて、
+ 各プロトコルの入出力へ写す設計です。プロトコル固有のコントローラ/マッパーは別 PR(ACP=#6776 / UCP=#6574)が担い、
+ 本ディレクトリはその共通基盤に徹します。
+
+
+ | プロトコル | 意味 |
+ | ACP | Agentic Commerce Protocol(OpenAI ホスト)。カタログは push 型の Feed API。 |
+ | UCP | Universal Commerce Protocol。カタログは POST RPC、discovery は /.well-known/ucp。 |
+
+
+ 設計の核心(#6777): 新規の購入フローは作らない。
+ エージェントチェックアウトも通常購入と
同一の shopping flow(
PurchaseFlow)を再利用し、
+ 税計算・送料・手数料・ポイント・在庫引当・受注番号採番のロジックを完全共有します。
+ 詳細は
PurchaseFlow の README を参照。
+
+
+
+ 用語
+
+ | 用語 | 意味 |
+ | 中立表現(プロトコル非依存 DTO) | ACP / UCP どちらにも写せる共通の入出力表現。AgentCatalog*Dto / AgentCheckout* 等。プロトコル固有の変換は各プロトコル層が行う。 |
+ | addressable unit | カタログで一意に指せる購入単位。EC-CUBE では親=Product、variant=ProductClass に対応。 |
+ | minor-unit(最小通貨単位) | ACP / UCP の金額表現。通貨の小数桁数(ISO 4217)だけ整数化した値。JPY は ×1、USD は ×100 と通貨ごとに桁数が変わる。MinorUnitConverter が変換。 |
+ | 2 系統エラー | プロトコル系(HTTP 4xx/5xx + Error=不正要求・処理不能)とビジネス系(HTTP 200 + messages[]=在庫切れ・販売停止等)を明確に分ける。 |
+ | discovery | エージェントが店舗の対応機能・決済ハンドラ・署名鍵を発見する仕組み(UCP の /.well-known/ucp profile 等)。 |
+
+
+
+
+ サブドメイン索引
+ 本ディレクトリは 8 つのサブドメインで構成されます。各サブドメインの詳細はそれぞれの README を参照してください。
+
+ | サブドメイン | 担うこと |
+ | Catalog | 商品カタログの中立 DTO 化と ACP Feed / UCP Catalog への出力・検証。 |
+ | CheckoutSession | 見積〜確定(complete)の中立表現と、中断→再開する状態機械オーケストレーション。 |
+ | Discovery | UCP discovery profile の組み立てと決済ハンドラの収集口。 |
+ | Fulfillment | 配送方法・送料・支払方法の選択肢を EC-CUBE マスタから中立表現へ。 |
+ | Idempotency | Idempotency-Key による状態変更操作の二重実行防止(DB 一意制約ベース)。 |
+ | Payment | エージェント決済ハンドラのレジストリと、Order への支払方法割当リゾルバ。 |
+ | Security | インバウンド OAuth2 検証・scope 照合・メッセージ署名(RFC 9421)・鍵ストア。 |
+ | Exception | プロトコル系エラーのコード分類と例外(ビジネス系は CheckoutSession の message で表現)。 |
+
+
+
+
+ ルート直下の共通ユーティリティ開発者向け
+ サブドメインをまたいで使われる、プロトコル非依存の共通部品です。
+
+ | クラス | 役割 |
+ AgentCheckoutPurchaseFlowAdapter | 中立 DTO → Cart/Order を構築し、通常購入と同一の shopping flow で税・送料・在庫を再計算。buildOrder(見積)/ prepare(在庫引当・採番)/ commit / rollback を提供。自身はトランザクションを開始しない(境界は決済オーケストレーション層の責務)。 |
+ AddressMappingService | 住所系エンティティ(Customer / CustomerAddress / Shipping)を ACP/UCP 住所 DTO へ写す。国コードは mtb_country_iso_code で numeric→alpha-2 変換、region は Pref 名。 |
+ MinorUnitConverter | major-unit(表記)と minor-unit(整数)を相互変換。桁数は symfony/intl の ISO 4217 権威データから取得(一律 ×100 ではない)。負数(割引・返金)対応。金額計算は bcmath。 |
+ StorefrontUrlResolver | discovery / checkout レスポンスに必要な店舗 URL(プライバシーポリシー・利用規約・注文 permalink)を UrlGenerator から絶対 URL で実行時生成。BaseInfo にカラム化しない。 |
+
+
+
+
+ 注意点開発者向け
+
+ - 2 系統エラーを混同しない。在庫不足・販売停止・配送制限などのビジネス系は例外を投げず
AgentCheckoutMessage(HTTP 200 + messages[])に写す。不正な商品参照・空明細などのプロトコル系のみ AgentCheckoutException を投げる。
+ - 金額は minor-unit 整数で持ち回る。通貨ごとに桁数が違うため一律 ×100 で組まない(必ず
MinorUnitConverter を通す)。EC-CUBE 本体の金額カラムは 2 桁までである点に注意。
+ - 会員はゲスト購入が基準線。会員 ID 連携は eccube-api4 の Customer authorization_code grant に依存し、標準実装(
GuestCustomerResolver)は常に null(ゲスト)を返す。会員解決は CustomerResolverInterface の差し替えで有効化する。
+ - 支払方法の「沈黙のすり抜け」に注意。利用可能な支払方法の先頭(sort_no 最小)を機械採用するとエージェント決済不可能な方法(銀行振込等)が割り当たる。
AgentPaymentMethodResolverInterface は登録済みハンドラが扱える Payment のみを候補にする。
+ - 拡張は各 Interface の差し替え(seam)で。
ProductReferenceResolverInterface / CustomerResolverInterface / FulfillmentOptionMapperInterface / KeyStoreInterface 等を app/Customize で実装・alias 上書きする。core は具象決済実装を持たず、決済ハンドラは決済プラグインがタグ付きサービスで寄与する。
+ - 本ディレクトリはプロトコル非依存に徹する。HTTP ステータス/各プロトコルのレスポンス形への変換はプロトコル層(ACP=#6776 / UCP=#6574)が担う。ここに HTTP 依存を持ち込まない。
+
+
+
+
+
diff --git a/src/Eccube/Service/AgentCommerce/README.md b/src/Eccube/Service/AgentCommerce/README.md
new file mode 100644
index 00000000000..5b490f3abaf
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/README.md
@@ -0,0 +1,28 @@
+# AgentCommerce — エージェントコマース基盤
+
+AI エージェント(OpenAI 等)が EC-CUBE の商品を発見し購入できるようにするための基盤サービス群。
+ACP(Agentic Commerce Protocol)/ UCP(Universal Commerce Protocol)の 2 系統に対し、
+プロトコル非依存の中立表現を核に置いて共通処理を提供する。
+チェックアウトは新規フローを作らず**通常購入と同一の shopping flow**([PurchaseFlow](../PurchaseFlow/README.md))を再利用する(#6777)。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 📦 同梱リソースの出所: [`Resource/AgentCommerce/README.md`](../../Resource/AgentCommerce/README.md)
+- 📚 プロトコル仕様: [ACP](https://github.com/agentic-commerce-protocol/agentic-commerce-protocol) / [UCP](https://github.com/Universal-Commerce-Protocol/ucp)
+
+## サブドメイン
+
+- [Catalog](./Catalog/README.html) — カタログの中立 DTO 化と ACP Feed / UCP Catalog 出力・検証
+- [CheckoutSession](./CheckoutSession/README.html) — 見積〜確定(complete)の中立表現と状態機械
+- [Discovery](./Discovery/README.html) — UCP discovery profile 組み立てと決済ハンドラ収集口
+- [Fulfillment](./Fulfillment/README.html) — 配送方法・送料・支払方法の選択肢を中立表現へ
+- [Idempotency](./Idempotency/README.html) — Idempotency-Key による二重実行防止(DB 一意制約)
+- [Payment](./Payment/README.html) — 決済ハンドラレジストリと支払方法割当リゾルバ
+- [Security](./Security/README.html) — OAuth2 検証・scope 照合・メッセージ署名・鍵ストア
+- [Exception](./Exception/README.html) — プロトコル系エラーのコード分類と例外
+
+## ルート直下の共通ユーティリティ
+
+- `AgentCheckoutPurchaseFlowAdapter.php` — 中立 DTO → `Cart`/`Order` を構築し shopping flow で再計算(`buildOrder`/`prepare`/`commit`/`rollback`)
+- `AddressMappingService.php` — 住所系エンティティを ACP/UCP 住所 DTO へ写す(国コード numeric→alpha-2、region=`Pref` 名)
+- `MinorUnitConverter.php` — major-unit ⇄ minor-unit 変換(ISO 4217 権威データ・bcmath・負数対応)
+- `StorefrontUrlResolver.php` — 店舗 URL(規約・プライバシー・注文 permalink)を絶対 URL で実行時生成
diff --git a/src/Eccube/Service/AgentCommerce/Security/README.html b/src/Eccube/Service/AgentCommerce/Security/README.html
new file mode 100644
index 00000000000..d937cb6b89e
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Security/README.html
@@ -0,0 +1,69 @@
+
+
+
+
+
+AgentCommerce Security — 仕様
+
+
+
+
+AgentCommerce Security — 認証と署名
+
+ 親: AgentCommerce README
+ / 索引: README.md
+
+
+
+ 概要
+ エージェントコマースのインバウンド認証(OAuth2)・scope 照合・メッセージ署名(RFC 9421)・署名鍵の管理を担うサブドメインです。
+ エージェントはブラウザセッション(form_login)を持たないため、認証は OAuth2 アクセストークン、
+ 応答の真正性は HTTP Message Signatures で担保します。公開鍵は discovery の signing_keys[](JWK)として広告されます。
+
+
+
+ 認証・scope
+
+ | クラス | 役割 |
+ AgentCommerceOAuth2Authenticator | インバウンド OAuth2 トークン検証 + scope×protocol 照合。トークン検証は Symfony 標準の AccessTokenHandlerInterface 経由(実体は eccube-api4 が提供)。 |
+ AgentCommerceScopeRegistry | scope を "<protocol>:<capability>" 形式(例 "ucp:checkout")に正準化。protocol 越境を許可しない。 |
+
+
+
+
+ メッセージ署名・鍵ストア
+
+ | クラス | 役割 |
+ AgentCommerceMessageSignerInterface | メッセージ署名・検証の抽象。アルゴリズムはプロトコルごとに差し替え可能。 |
+ UcpMessageSigner | UCP の RFC 9421 実装。EC P-256 / ES256、署名は raw R‖S(IEEE P1363, 64byte)。公開鍵を EC JWK として広告。 |
+ KeyStoreInterface | 署名鍵の永続化抽象(app/Customize で DB 保管・Vault 連携等に差し替え可能)。 |
+ FilesystemKeyStore | 標準実装。既定パス {projectDir}/app/keystore/agent-commerce/{purpose}.key。鍵ファイルは 0600、ディレクトリは 0700。 |
+
+
+
+
+ 注意点開発者向け
+
+ - OAuth2 の実体(リソースサーバー)は eccube-api4 が提供する想定([EC-CUBE/eccube-api4#188])。eccube-api4 未導入時は handler が null となり 503(ServiceUnavailable)を返すフィーチャーフラグ的フォールバック。firewall への配線は checkout エンドポイントを持つ #6776/#6574 で行う。
+ - 付与 scope は
UserBadge の attributes に handler が載せる前提(scopes: array もしくは scope: 空白区切り string)。
+ signing_keys[] には EC 公開鍵 JWK のみを出す(秘密鍵パラメータ非混入)。秘密鍵が鍵ストアに無ければ生成して永続化する。
+
+
+
+
+
diff --git a/src/Eccube/Service/AgentCommerce/Security/README.md b/src/Eccube/Service/AgentCommerce/Security/README.md
new file mode 100644
index 00000000000..9664bfbb2c1
--- /dev/null
+++ b/src/Eccube/Service/AgentCommerce/Security/README.md
@@ -0,0 +1,15 @@
+# AgentCommerce Security — 認証と署名
+
+エージェントコマースのインバウンド認証(OAuth2)・scope 照合・メッセージ署名(RFC 9421)・署名鍵の管理を担うサブドメイン。
+エージェントはブラウザセッションを持たないため、認証は OAuth2 アクセストークン、応答の真正性は HTTP Message Signatures で担保する。
+公開鍵は discovery の `signing_keys[]`(JWK)として広告される。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- ⬆ 親: [AgentCommerce README](../README.md)
+
+## 主要ファイル
+
+- `AgentCommerceOAuth2Authenticator.php` — インバウンド OAuth2 検証 + scope×protocol 照合(トークン検証は eccube-api4 依存、未導入時は 503)
+- `AgentCommerceScopeRegistry.php` — scope を `:` 形式へ正準化(protocol 越境を禁止)
+- `AgentCommerceMessageSignerInterface.php`(`UcpMessageSigner.php`)— メッセージ署名(RFC 9421 / EC P-256 / ES256)
+- `KeyStoreInterface.php`(`FilesystemKeyStore.php`)— 署名鍵の永続化(標準は FS、0600/0700)
diff --git a/src/Eccube/Service/PurchaseFlow/README.html b/src/Eccube/Service/PurchaseFlow/README.html
new file mode 100644
index 00000000000..72845054d78
--- /dev/null
+++ b/src/Eccube/Service/PurchaseFlow/README.html
@@ -0,0 +1,124 @@
+
+
+
+
+
+PurchaseFlow — 受注処理パイプライン 仕様
+
+
+
+
+PurchaseFlow — 受注処理パイプライン
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): purchase-flow/SKILL.md
+ / ドメイン詳細: doc4「受注」仕様
+
+
+
+ 概要
+ PurchaseFlow は、カート・注文確認・受注確定・受注編集の各場面で使われる
+ 受注処理の中核パイプラインです。送料・手数料・税・値引き・ポイント・在庫引当・注文番号採番といった
+ 「受注に関わる計算・検証・確定」を、コントローラや個別サービスに散らさず、この 1 本のパイプラインに集約します。
+
+ パイプラインは cart / shopping / order の 3 フローぶん存在し、それぞれ別々の
+ 処理部品(Processor / Validator)の組を持ちます。どのフローで実行中かは PurchaseContext が保持します。
+
+
+ | フロー | 使われる場面 |
+ | cart | カート投入・数量変更時の検証と金額再計算 |
+ | shopping | 注文確認画面での検証・送料/手数料/税/ポイントの確定計算 |
+ | order | 管理画面の受注編集など、確定済み受注に対する再計算・差分反映 |
+
+
+
+
+ 用語
+ 受注ドメインの詳細(ER 図・受注ステータスの遷移・税計算の仕様)は複製せず
+ doc4「受注」仕様 を参照してください。ここでは本パイプラインの理解に必要な語だけをまとめます。
+
+ | 用語 | 意味 |
+ | 明細(Item) | OrderItem / CartItem。商品だけでなく送料・手数料・値引き・ポイント・税も「明細の 1 行」として表現される。 |
+ | 受注/カート全体(ItemHolder) | Order / Cart。明細を束ねる入れ物。Order 固有の概念(出荷・ポイント)は Cart には無い。 |
+ | 出荷(Shipping) | 1 受注に複数あり得る出荷単位。送料は Shipping 単位で計算される。 |
+ | 販売種別(SaleType) | 注文を決済単位に分割する区分(通常購入/定期購入など)。詳細は doc4。 |
+
+
+
+
+ 処理フロー
+ 検証・計算は validate() が、受注の確定は prepare() → commit()(失敗時 rollback())が担います。
+ この 2 系統は独立して呼び出されます。
+
+ 検証・計算:validate() の実行順
+ 各段階の合間で金額が再集計(calculateAll():送料 → 手数料 → 値引き → 小計 → 税 → 合計。計算は bcmath)されます。
+ ① 明細検証 ItemValidator … 明細1行ごとの検証(在庫・価格変更 等)
+ ↓ calculateAll
+② 受注検証 ItemHolderValidator … カート/受注全体の検証(空明細・在庫合算 等)
+ ↓ calculateAll
+③ 明細前処理 ItemPreprocessor … 明細1行ごとの調整(コアでは未使用の拡張点)
+ ↓ calculateAll
+④ 受注前処理 ItemHolderPreprocessor … 送料/手数料/税 明細の付与・調整
+ ↓ calculateAll
+⑤ 値引き(削除) DiscountProcessor … 既存の値引き明細をいったん全消し
+ ↓ calculateAll
+⑥ 値引き(追加) DiscountProcessor … 値引き明細を積み直す(合計を超えない範囲で)
+ ↓ calculateAll
+⑦ 最終検証 ItemHolderPostValidator … 全処理後の最終検証・確定値の確定(ポイント付与 等)
+ 値引きは「⑤ いったん全消し → ⑥ 再計算で積み直す」のが大前提です。前処理も同様に、
+ 自分が付けた明細を毎回削除してから付け直すことで何度実行しても結果が変わらない(冪等)ように作られています。
+
+ 確定:prepare() / commit() / rollback()
+ 確定系は PurchaseProcessor のみが担います。在庫引当や注文番号採番など、実際に世界を変える処理です。
+ prepare() 仮確定(在庫引当 等)… 失敗時は例外を投げてフロー中断
+commit() 確定
+rollback() 仮確定の取消(在庫戻し 等)… prepare の逆操作
+
+
+
+ 拡張ポイント開発者向け
+ 受注処理へ独自の検証・計算を差し込むには、対応する部品を拡張して対象フローへ登録します。
+ 実装の書き方・DO/DON'T は purchase-flow/SKILL.md が正典です。ここでは全体像だけ示します。
+
+ | やりたいこと | 拡張する部品(基底) |
+ | 明細1行の検証 | ItemValidator(abstract。常に warning 化) |
+ | カート/受注全体の検証(購入を止めたい) | ItemHolderValidator / ItemHolderPostValidator |
+ | 送料・手数料・税など明細の付与/調整 | ItemHolderPreprocessor |
+ | 値引き | DiscountProcessor |
+ | 在庫引当・採番・ポイント付与など確定処理 | PurchaseProcessor(AbstractPurchaseProcessor 継承) |
+
+ 登録方法は 2 通り(併用しない):
+
+ - コア
app/config/eccube/packages/purchaseflow.yaml でタグ登録。flow_type で対象フロー、priority(降順=大きいほど先)で実行順を指定。
+ - プラグイン / Customize
#[CartFlow] / #[ShoppingFlow] / #[OrderFlow] 属性で対象フローを宣言(src/Eccube/Attribute/)。基底の継承で自動タグ付けされる。順序制御が必要なら YAML タグを使う。
+
+
+
+
+ 注意点開発者向け
+
+ - 金額計算は bcmath(
bcadd/bcsub/bcmul/bccomp)。float で組まない。合計の集計は calculateAll() が行うので、部品側は明細の足し引きに集中する。
+ ItemValidator は常に warning に変換される(購入は止まらない)。購入を中断したい検証は ItemHolderValidator / ItemHolderPostValidator で warning なしの error にする。
+ Cart と Order の違いに注意。Shipping・ポイント・Customer は Order 固有。instanceof Order でガードする。
+ PurchaseProcessor の rollback() 実装漏れに注意(prepare() の逆操作を必ず用意する)。
+ - パイプラインに実際にどの部品がどの順で乗っているかは
PurchaseFlow::dump()(__toString())で確認できる(確定系の PurchaseProcessor は dump に出ない点に注意)。
+
+
+
+
+
diff --git a/src/Eccube/Service/PurchaseFlow/README.md b/src/Eccube/Service/PurchaseFlow/README.md
new file mode 100644
index 00000000000..0824d9ab2ee
--- /dev/null
+++ b/src/Eccube/Service/PurchaseFlow/README.md
@@ -0,0 +1,18 @@
+# PurchaseFlow — 受注処理パイプライン
+
+受注に関わる計算・検証・確定(送料/手数料/税/値引き/ポイント/在庫引当・採番)を、
+コントローラや汎用サービスに散らさず 1 本のパイプラインに集約する EC-CUBE 受注処理の中核。
+cart / shopping / order の 3 フローぶん存在する。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`purchase-flow/SKILL.md`](../../../../.claude/skills/purchase-flow/SKILL.md)
+- 📚 ドメイン詳細: https://doc4.ec-cube.net/spec_order
+
+## 主要ファイル
+
+- `PurchaseFlow.php` — パイプライン本体。7 種の拡張点を保持し `validate()` / `prepare()` / `commit()` / `rollback()` を実行
+- `PurchaseContext.php` — 実行中コンテキスト(フロー種別・実行前の状態を保持)
+- `ProcessResult.php` / `PurchaseFlowResult.php` — 各処理の結果(success/warn/error)とその集約
+- `ItemValidator.php` / `ItemHolderValidator.php` / `ItemHolderPostValidator.php` — 検証系の基底(abstract)
+- `ItemPreprocessor.php` / `ItemHolderPreprocessor.php` / `DiscountProcessor.php` / `PurchaseProcessor.php` — 前処理・値引き・確定の拡張点(interface)
+- `Processor/` — 標準実装(`StockValidator` `TaxProcessor` `PointProcessor` `StockReduceProcessor` 等)
diff --git a/src/Eccube/Service/README.html b/src/Eccube/Service/README.html
new file mode 100644
index 00000000000..feacd0504e0
--- /dev/null
+++ b/src/Eccube/Service/README.html
@@ -0,0 +1,102 @@
+
+
+
+
+
+Service — 仕様
+
+
+
+
+Service — 業務ロジックの置き場所
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): service/SKILL.md
+
+
+
+ 概要
+ src/Eccube/Service/ は、EC-CUBE の業務ロジックの受け皿です。
+ 税の算出、カートの操作、受注ステータスの遷移、プラグインの導入、メール送信・CSV 入出力・PDF 出力といった
+ 「アプリの仕事そのもの」を、コントローラ(画面・HTTP の担当)から切り離してここに集約します。
+
+ Service に集約する狙いは 2 つ。
+ 複数の画面・コマンドから同じ処理を再利用できること、そして
+ 1 つの Service が 1 つの責務だけを持つ(単一責任)ことで見通しとテスト容易性を保つことです。
+ コントローラは「入出力の交通整理」に徹し、実処理は Service に委ねる、という分担になっています。
+
+ 受注に関わる計算・検証・確定は別枠です。
+ 送料・手数料・税・値引き・ポイント・在庫引当・注文番号採番は、汎用 Service ではなく
+
PurchaseFlow パイプライン(同ディレクトリ配下・別途 README あり)が担います。
+ この境界が EC-CUBE の受注処理の核心です。
+
+
+
+ サービスの役割と代表例
+ このディレクトリ直下にある主な Service は次の通りです(実装は各ファイル)。
+
+ | サービス | 役割 |
+ CartService | カートの取得・商品の追加/削除・保存。セッション上のカートと永続化カートの統合も担う。 |
+ TaxRuleService | 税額の算出(getTax() / calcTax())。税込価格の計算と丸め処理。 |
+ OrderHelper | カートから購入処理中(PROCESSING)の受注を組み立てる入口。会員情報の反映・仮注文 ID の発番など。 |
+ OrderStateMachine | 受注ステータスの遷移(apply() / can())。遷移に伴うポイント・在庫の確定/巻き戻しをイベントで実行。 |
+ PluginService | プラグインの install/enable/disable/uninstall。アーカイブ展開・プロキシ生成・スキーマ更新まで。 |
+ OrderPdfService | 受注情報の PDF(納品書等)出力。 |
+ TwoFactorAuthService | 管理者の 2 要素認証(シークレット生成・コード検証・認証済み Cookie 発行)。 |
+ SystemService | システム情報の取得とメンテナンスモードの切り替え。 |
+
+ このほか、計算補助(Calculator/)・決済(Payment/)・Agentic Commerce(AgentCommerce/)など、
+ 用途別のサブディレクトリを持ちます。
+
+
+
+ 関連する仕様(別ディレクトリ・別 Skill)
+ Service 配下でも、次の領域はそれぞれ専用の実装規約(Skill)が正典です。独立配置はせず本 README から案内します。
+
+
+
+
+ 拡張ポイント開発者向け
+ プロジェクト固有の業務ロジックはコアを直接改変せず、app/Customize/Service/ に追加します。
+ コアアップグレードの影響を避けるためです。既存 Service の振る舞いを差し替えたいときは 2 通り。
+
+ | やりたいこと | 手段 |
+ | 新しい業務ロジックを足す | app/Customize/Service/ に新規 Service を置き、コンストラクタ DI で利用する。 |
+ | 既存 Service の一部を差し替える | サービスデコレーション(#[AsDecorator])またはコンパイラパスで置換する。 |
+
+ 実装の書き方・DO/DON'T は service/SKILL.md が正典です(app/Customize 全般は customize Skill)。
+
+
+
+ 注意点開発者向け
+
+ - 依存は一方向(Controller → Service → Repository)。Service が
Eccube\Controller\... を use / 型ヒントするのはレイヤ違反。
+ - HTTP の知識を持ち込まない。
Request / Response を引数で受けず、必要な値(プリミティブやエンティティ)だけを渡す。
+ - 単一責任を保つ。1 つの Service に無関係な責務(例: 商品検索とメール送信)を同居させない。責務が増えたら分割する。
+ - 受注の計算・検証・確定は Service に直書きしない。PurchaseFlow の Processor / Validator を拡張する。
+ - 永続化(
persist() / flush())は Service に置いてよい。ただし flush() をループ内で乱発せず、トランザクション境界を意識してまとめる。
+
+
+
+
+
diff --git a/src/Eccube/Service/README.md b/src/Eccube/Service/README.md
new file mode 100644
index 00000000000..805bec9fcf5
--- /dev/null
+++ b/src/Eccube/Service/README.md
@@ -0,0 +1,18 @@
+# Service — 業務ロジックの置き場所
+
+コントローラ(HTTP・画面)から切り離した業務ロジックの受け皿。税の算出・カート操作・受注ステータス遷移・
+プラグイン導入・メール送信・CSV 入出力・PDF 出力などを、単一責任・HTTP 非依存で集約する。
+受注に関わる計算・検証・確定は汎用 Service ではなく `PurchaseFlow/` が担う。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`service/SKILL.md`](../../../.claude/skills/service/SKILL.md)
+- 📚 関連: 受注処理は [`PurchaseFlow/`](./PurchaseFlow/README.html) / CSV は [`csv/SKILL.md`](../../../.claude/skills/csv/SKILL.md) / メールは [`mail/SKILL.md`](../../../.claude/skills/mail/SKILL.md)
+
+## 主要ファイル
+
+- `CartService.php` — カートの取得・商品追加/削除・保存(セッション/永続化カートの統合)
+- `TaxRuleService.php` — 税額算出(`getTax()` / `calcTax()`)と税込価格・丸め
+- `OrderHelper.php` — カートから購入処理中(`PROCESSING`)受注を組み立てる入口
+- `OrderStateMachine.php` — 受注ステータス遷移(ポイント・在庫の確定/巻き戻しをイベントで実行)
+- `PluginService.php` — プラグインの install/enable/disable/uninstall
+- `PurchaseFlow/` — 受注処理パイプライン(別途 [README](./PurchaseFlow/README.html))
diff --git a/src/Eccube/Twig/Extension/README.html b/src/Eccube/Twig/Extension/README.html
new file mode 100644
index 00000000000..6bb7a44e315
--- /dev/null
+++ b/src/Eccube/Twig/Extension/README.html
@@ -0,0 +1,105 @@
+
+
+
+
+
+Twig/Extension — 仕様
+
+
+
+
+Twig/Extension — Twig 拡張
+
+ 索引: README.md
+ / 実装規約(AI・開発者向け): twig-template/SKILL.md
+
+
+
+ 概要
+ ここは Twig テンプレートから使える独自のフィルタ・関数・テストを定義するディレクトリです。
+ 価格の表示({{ price|price }})、日付整形({{ date|date_format }})、商品取得({{ product(id) }})といった、
+ テンプレート側で頻出する処理を PHP 側にまとめて提供します。
+ 各クラスは Twig 標準の AbstractExtension を継承し、services.yaml の autoconfigure: true で
+ 自動的に Twig 拡張として登録されます(手動タグ不要)。提供できる要素は 3 種類です。
+
+ | 提供メソッド | テンプレートでの使い方 |
+ getFilters() | フィルタ {{ value|filter }} |
+ getFunctions() | 関数 {{ func(args) }} |
+ getTests() | テスト {% if value is test %} |
+
+
+
+
+ 主要な拡張とその役割
+
+ | クラス | 役割 | 提供する主なフィルタ/関数 |
+ EccubeExtension | コアで最も使われる汎用拡張 | price / date_format / ellipsis / file_ext_icon(filter)、product() / has_errors() / active_menus() / class_categories_as_json() / currency_symbol()(function) |
+ TaxExtension | 税に関する判定 | is_reduced_tax_rate()(軽減税率対象か) |
+ CsrfExtension | CSRF トークンの出力 | csrf_token_for_anchor() |
+ RepositoryExtension | テンプレートからリポジトリ参照 | repository() |
+ IntlExtension | 国際化(通貨・地域表記) | Intl 系フィルタ |
+ TemplateEventExtension | テンプレートイベントの差し込み基盤(NodeVisitor) | (フィルタ/関数ではなく描画フックを提供) |
+
+ テンプレートの描画時に、ファイル名をイベント名として TemplateEvent が dispatch されます。
+ プラグイン・カスタマイズはここにアセットやスニペットを差し込みます(見た目の調整に使い、業務ロジックは書かない)。
+ 購読方法は Skill event-subscriber を参照。
+
+
+
+ 使いどころ
+ テンプレートで表示・整形が必要になったら、まず既存のフィルタ/関数で足りるかを確認します。
+ 同じ整形ロジックをテンプレートに散らさないのが原則です。
+
+ | やりたいこと | 使うもの |
+ | 金額を通貨記号・桁区切り付きで表示 | {{ value|price }} |
+ | 日付を EC-CUBE 既定フォーマットで表示 | {{ value|date_format }} |
+ | 長い文字列を省略(…) | {{ value|ellipsis(length) }} |
+ | ID から商品エンティティを取得 | {{ product(id) }} |
+ | グローバル値の参照 | BaseInfo / eccube_config / Layout / Page(TwigInitializeListener が注入) |
+
+
+
+
+ 独自フィルタ/関数の追加方法開発者向け
+ AbstractExtension を継承したクラスを作り、getFilters() / getFunctions() で登録します。
+ 依存はコンストラクタ DI で受けます。実装の DO/DON'T は twig-template/SKILL.md が正典です。
+ new TwigFilter('price', $this->getPriceFilter(...));
+// HTML を返すフィルタ/関数は is_safe を明示する
+new TwigFilter('file_ext_icon', $this->getExtensionIcon(...), ['is_safe' => ['html']]);
+new TwigFunction('product', $this->getProduct(...));
+
+ - コア
src/Eccube/Twig/Extension/ に置く(このディレクトリ)。
+ - プラグイン / Customize 同じ
AbstractExtension 継承で追加でき、autoconfigure で登録される。
+
+
+
+
+ 注意点(XSS・エスケープ)開発者向け
+
+ - Twig は HTML オートエスケープが既定で有効。
{{ value }} は自動でエスケープされる。|raw を付けない限り安全、が大原則。
+ |raw はエスケープ無効化。ユーザー入力・DB 由来の値に付けると XSS。管理画面テンプレートも例外ではない。
+ - コンテキストに応じたエスケープを使う。JavaScript の中に埋めるなら
{{ value|escape('js') }}(HTML エスケープでは JS 文脈の XSS を防げない)。
+ - HTML を返すフィルタ/関数には
['is_safe' => ['html']] を付ける(付けないと二重エスケープ)。ただし付ける=出力責任を負うということ。
+ 中で外部入力を混ぜるなら htmlspecialchars($value, ENT_QUOTES, 'UTF-8') で自前エスケープしてから返す。
+ - ユーザー編集可能なテンプレート文字列(CMS・フリーエリア・メール本文)は Twig サンドボックスを通す
+ (
template_from_string(...) + sandboxed = true)。外すとテンプレートインジェクションになる(過去の脆弱性修正の中心領域)。
+
+
+
+
+
diff --git a/src/Eccube/Twig/Extension/README.md b/src/Eccube/Twig/Extension/README.md
new file mode 100644
index 00000000000..cd352f44706
--- /dev/null
+++ b/src/Eccube/Twig/Extension/README.md
@@ -0,0 +1,16 @@
+# Twig/Extension — Twig 拡張
+
+Twig テンプレートから使える独自のフィルタ・関数・テストを定義するディレクトリ。
+価格表示 `{{ value|price }}`・日付整形 `{{ value|date_format }}`・商品取得 `{{ product(id) }}` など、
+テンプレート頻出の処理を PHP 側に集約する。`AbstractExtension` を継承し `autoconfigure` で自動登録される。
+
+- 📖 仕様(人間向け): [README.html](./README.html)
+- 🛠 実装規約(AI 向け): [`twig-template/SKILL.md`](../../../../.claude/skills/twig-template/SKILL.md)
+
+## 主要ファイル
+
+- `EccubeExtension.php` — コアで最も使う汎用拡張(`price` / `date_format` / `ellipsis` / `product()` / `has_errors()` 等)
+- `TaxExtension.php` — 軽減税率対象かの判定 `is_reduced_tax_rate()`
+- `CsrfExtension.php` — CSRF トークン出力 `csrf_token_for_anchor()`(`is_safe` の付与例)
+- `TemplateEventExtension.php` — テンプレートイベント差し込みの基盤(NodeVisitor)。プラグイン/カスタマイズの拡張点
+- `RepositoryExtension.php` — テンプレートからリポジトリを参照する `repository()`
From d806f4c61efad35e3b1575efd453e9d03b83dd74 Mon Sep 17 00:00:00 2001
From: "takumi.tokoro"
Date: Fri, 10 Jul 2026 10:31:13 +0900
Subject: [PATCH 02/17] =?UTF-8?q?docs(readme):=20=E5=85=A8=20README.html?=
=?UTF-8?q?=20=E3=81=B8=E3=81=AE=E5=85=A5=E5=8F=A3=E3=81=A8=E3=81=AA?=
=?UTF-8?q?=E3=82=8B=E3=83=AB=E3=83=BC=E3=83=88=20README.html=EF=BC=88?=
=?UTF-8?q?=E4=BB=95=E6=A7=98=E6=9B=B8=E3=83=9D=E3=83=BC=E3=82=BF=E3=83=AB?=
=?UTF-8?q?=EF=BC=89=E3=82=92=E8=BF=BD=E5=8A=A0=20(#6906)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
各機能ディレクトリに配置した 27 個の README.html への TOP ページをリポジトリルートへ置く。
自己完結 HTML でビルド不要、ブラウザで開いて目次から各ディレクトリの仕様へ辿れる。
README.md からポータルへのリンクと、AGENTS.md に入口の記述を追加。
Co-Authored-By: Claude Opus 4.8 (1M context)
---
AGENTS.md | 1 +
README.html | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++
README.md | 1 +
3 files changed, 102 insertions(+)
create mode 100644 README.html
diff --git a/AGENTS.md b/AGENTS.md
index 6cc142f8d49..8fe3b31e0cc 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -43,6 +43,7 @@ AI エージェント向けの情報は、この `AGENTS.md` を**正典(ハ
| `.claude/skills//SKILL.md` | AI エージェント | **実装の書き方ルール**(規約・DO/DON'T)。仕様説明は `README.html` に委ね相互リンク |
- **棲み分け**: `README.html`=「機能の仕様(人間向け)」、`SKILL.md`=「コードの書き方(AI 向け)」。読者と目的が異なるため両立する。
+- **入口(TOP ページ)**: リポジトリルートの [`README.html`](./README.html) が全 `README.html` へのポータル(目次)。ビルド不要でブラウザで開ける。新しい `README.html` を配置したら、この目次にも 1 行追加する(漏れは CI `docs-check` が検出)。
- **参照トポロジ**(既存の一方向ルールを維持): `README.md`(索引)→ `README.html`(仕様)/ `SKILL.md`(規約)→ `AGENTS.md`(正典)。
上流(`AGENTS.md`)から個別 README への下向き内容参照は足さない。
- **ドメイン詳細は複製しない**: 受注 ER・ステートマシン・計算仕様など doc4 に既出のものは `README.html` から `https://doc4.ec-cube.net/` へリンクする。
diff --git a/README.html b/README.html
new file mode 100644
index 00000000000..69982b94fe8
--- /dev/null
+++ b/README.html
@@ -0,0 +1,100 @@
+
+
+
+
+
+EC-CUBE 仕様書ポータル
+
+
+
+
+EC-CUBE 仕様書ポータル
+
+ 索引: README.md
+ / 規約ハブ(正典): AGENTS.md
+ / ドメイン詳細: doc4.ec-cube.net
+
+
+
+ このページについて
+ EC-CUBE の主要な機能ディレクトリには、コードと同じ場所へ README.html(人間向けの真の仕様書)と
+ README.md(GitHub 表示用の短い索引)を配置しています(Issue #6906・AGENTS.md §8.1)。
+ このページは、それら各ディレクトリの README.html への入口(TOP ページ)です。
+ 各 README.html は自己完結した HTML 文書なので、リポジトリを取得したあと
+ ビルド不要でそのままブラウザで開けます。このページをブラウザで開き、目的のディレクトリのリンクをたどってください。
+ 実装の書き方ルール(AI・開発者向けの規約 / DO・DON'T)は各 README.html からリンクする
+ .claude/skills/<name>/SKILL.md が正典です。README.html=「機能の仕様」、SKILL.md=「コードの書き方」で棲み分けます。
+
+
+
+
+
+ 高複雑サブシステム
+
+ | ディレクトリ | 役割 | 仕様 |
+ Service/PurchaseFlow/ | 受注処理パイプライン(計算・検証・確定) | README.html |
+ Controller/Admin/Order/ | 管理画面の受注管理 | README.html |
+ Service/AgentCommerce/ | エージェントコマース(ACP)基盤 | README.html |
+
+
+
+
+ AgentCommerce サブドメイン(src/Eccube/Service/AgentCommerce/)
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index 4f7c8402f86..294fd28b2c1 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@
+ 本体開発にあたって不明点などあれば[Issue](https://github.com/EC-CUBE/ec-cube/wiki/Issues%E3%81%AE%E5%88%A9%E7%94%A8%E6%96%B9%E6%B3%95)をご利用下さい。
+ EC-CUBE 3系の保守については、 [EC-CUBE/ec-cube3](https://github.com/EC-CUBE/ec-cube3/)にて開発を行っております。
+ EC-CUBE 2系の保守については、 [EC-CUBE/ec-cube2](https://github.com/EC-CUBE/ec-cube2/)にて開発を行っております。
++ コード近接の仕様書(各機能ディレクトリの `README.html`)の入口は [`README.html`(仕様書ポータル)](README.html) です。ビルド不要でブラウザで開けます。
## インストール
From a1a6bfe00841614c78be93e30011aea5afa0b258 Mon Sep 17 00:00:00 2001
From: "takumi.tokoro"
Date: Fri, 10 Jul 2026 10:47:37 +0900
Subject: [PATCH 03/17] =?UTF-8?q?docs(readme):=20=E4=BB=95=E6=A7=98?=
=?UTF-8?q?=E6=9B=B8=E3=83=9D=E3=83=BC=E3=82=BF=E3=83=AB=E3=81=AE=E8=A1=A8?=
=?UTF-8?q?=E3=82=92=E6=95=B4=E7=90=86=E3=81=97=E3=80=8C=E7=9C=9F=E3=81=AE?=
=?UTF-8?q?=E3=80=8D=E8=A1=A8=E8=A8=98=E3=82=92=E5=89=8A=E9=99=A4=20(#6906?=
=?UTF-8?q?)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 全行同じ「README.html」だった仕様列を廃し、ディレクトリ名自体をリンクに
- overview の「人間向けの真の仕様書」→「人間向けの仕様書」
- 併せてルート README のリンク切れ(bundle.js パス)も修正
Co-Authored-By: Claude Opus 4.8 (1M context)
---
README.html | 66 ++++++++++++++++++++++++++---------------------------
README.md | 2 +-
2 files changed, 34 insertions(+), 34 deletions(-)
diff --git a/README.html b/README.html
index 69982b94fe8..e664310f8f3 100644
--- a/README.html
+++ b/README.html
@@ -32,11 +32,11 @@ EC-CUBE 仕様書ポータル
このページについて
- EC-CUBE の主要な機能ディレクトリには、コードと同じ場所へ README.html(人間向けの真の仕様書)と
+
EC-CUBE の主要な機能ディレクトリには、コードと同じ場所へ README.html(人間向けの仕様書)と
README.md(GitHub 表示用の短い索引)を配置しています(Issue #6906・AGENTS.md §8.1)。
このページは、それら各ディレクトリの README.html への入口(TOP ページ)です。
各 README.html は自己完結した HTML 文書なので、リポジトリを取得したあと
- ビルド不要でそのままブラウザで開けます。このページをブラウザで開き、目的のディレクトリのリンクをたどってください。
+ ビルド不要でそのままブラウザで開けます。下の表のディレクトリ名をクリックすると、そのディレクトリの仕様へ移動します。