Skip to content

feat(agent-commerce): ACP checkout (#6776) のコア実装#6843

Open
nanasess wants to merge 28 commits into
EC-CUBE:4.4from
nanasess:feature/agentic-commerce-acp
Open

feat(agent-commerce): ACP checkout (#6776) のコア実装#6843
nanasess wants to merge 28 commits into
EC-CUBE:4.4from
nanasess:feature/agentic-commerce-acp

Conversation

@nanasess

@nanasess nanasess commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

概要(Overview・Refs Issue)

AI エージェント経由購入の ACP (Agentic Commerce Protocol, 2026-04-17) checkout を EC-CUBE 本体へ実装します。Refs #6776 / 共通基盤 #6777

Phase 1b 共通基盤 (CheckoutSession / AgentCheckoutPurchaseFlowAdapter / 状態機械 / DB Idempotency、#6825) の上に積むスタック PR です。#6825 着地後に 4.4 へ rebase 予定で、それまで本 PR の diff には #6825 の変更が含まれます。本ブランチは upstream/4.4 を取り込み済み (kana nullable #6809 含む)。

  • AcpCheckoutController: 5 エンドポイント (POST /acp/checkout_sessions / POST /{id} / GET /{id} / POST .../complete / POST .../cancel)
  • インバウンド認証 = OAuth2 Bearer (acp:checkout scope)。eccube-api4 のリソースサーバー (api4#188) に依存し、未導入時は 503
  • 決済具象は決済プラグインが寄与 (本 PR は AcpPaymentHandlerInterface の契約のみ)

方針(Policy)

実装に関する補足(Appendix)

テスト(Test)

AgentCommerce スイート 143 tests / 813 assertions / 0 失敗 / incomplete 4 (意図的)

  • Layer 0 仕様適合性 (AcpCheckoutConformanceTest / AcpDiscoveryControllerTest): MUST を 1:1 トレース (Idempotency 400/422/replay、minor unit 整数、business=200+messages[]、requires_3ds、cancel 405、markdown raw HTML 拒否、discovery no-auth/merchant_id 非混入/Cache-Control)。docblock に出典 URL (2026-04-17 pin)
  • Layer 1 純ロジック: AcpStatusMapper / AcpMessageMapper (raw HTML 拒否) / AcpMessageSigner (Merchant-Signature 形式・HMAC・timestamp 窓)
  • Layer 2 エンドポイント単体 (AcpCheckoutControllerTest): create→get→complete ハッピーパス、住所未確定 not_ready_for_payment、404 (他セッション遮断)、Idempotency 必須/replay/422、cancel/cancel完了済405、OAuth2 (欠落401/scope不足403)、フラグ無効 404、InMemoryAccessTokenHandler スタブ (eccube-api4 非依存)

検証 (直接的根拠):

  • PHPUnit: AgentCommerce 143 tests 0 失敗 / PurchaseFlow 回帰 256 tests 0 失敗
  • PHPStan level 6 (src 全体): No errors
  • php-cs-fixer / rector: 0 件

PostgreSQL/MySQL マトリクスは CI で確認 (ローカルは SQLite)。

相談(Discussion)

マイナーバージョン互換性保持のための制限事項チェックリスト

  • 既存機能の仕様変更はありません
  • フックポイントの呼び出しタイミングの変更はありません
  • フックポイントのパラメータの削除・データ型の変更はありません
  • twigファイルに渡しているパラメータの削除・データ型の変更はありません
  • Serviceクラスの公開関数の、引数の削除・データ型の変更はありません
  • 入出力ファイル(CSVなど)のフォーマット変更はありません

補足: IdempotencyConflictException のコンストラクタに reason 引数を追加しましたが、デフォルト値付きで後方互換です (未リリースの agent-commerce 機能内)。

レビュワー確認項目

  • 動作確認
  • コードレビュー
  • E2E/Unit テスト確認(テストの追加・変更が必要かどうか)
  • 互換性が保持されているか
  • セキュリティ上の問題がないか
    • 権限を超えた操作が可能にならないか
    • 不要なファイルアップロードがないか
    • 外部へ公開されるファイルや機能の追加ではないか
    • テンプレートでのエスケープ漏れがないか

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Agent Commerce のACP checkoutエンドポイント(作成/更新/取得/完了/キャンセル)と、認証不要のACP discovery(/.well-known/acp.json)を追加。
    • Idempotency-Key 対応、ステータス/メッセージ仕様、OAuth2 Bearer認証(スコープ制御)を提供。
    • 管理画面にACP/UCPチェックアウト有効化トグルと、エージェント在庫エスカレーション期限設定を追加。
  • Bug Fixes
    • エージェント起因注文ではプリオーダー整合チェックをスキップするよう修正。
  • Chores
    • 追加マイグレーション、マスタデータ取込、関連のテスト追加および署名鍵/keystoreアクセス制御を更新。

nanasess and others added 27 commits June 4, 2026 16:07
ACP/UCP 対応の共通基盤のうち CheckoutSession 非依存の先行スライスを実装。
トラック A (Product Feed / Discovery) を解放する最小集合。

- MinorUnitConverter: 通貨 minor unit 変換 (bcmath / ゼロデシマル / 負数)
- AddressMappingService: 住所マッピング・国コード numeric→alpha-2 (全249件網羅)
- AgentCommerceScopeRegistry: <protocol>:<capability> scope 照合
- KeyStoreInterface / FilesystemKeyStore: 鍵保管 (env パス上書き→既定ファイル、EC-CUBE#6797 雛形)
- AgentCommerceMessageSignerInterface / UcpMessageSigner: RFC 9421 EC P-256 / JWK 公開鍵 / 鍵ローテーション grace
- BaseInfo にフラグ5 (acp/ucp 有効化等、default false) + google_pay_merchant_id + migration
- 秘密鍵は dtb_baseinfo でなく app/keystore/ に保管 (EC-CUBE#6797、.htaccess/.gitignore 多重防御)

プライバシー/利用規約 URL はカラム化せず標準ページ (help_privacy/help_agreement) から自動生成する方針。
検証: PHPUnit 53 tests / 611 assertions / 0 失敗、PHPStan level6 No errors、php-cs-fixer 0件。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
minor-unit は通貨の小数桁数だけ桁が増える (一律 ×100 ではない) ことが
一目で分かるよう、docblock の例を JPY (×1) / USD (×100) の 2 桁までに統一。
3 桁通貨 (BHD 等) の 4 桁例は紛らわしいため削除。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ACP/UCP 用の 6 カラムは BaseInfo エンティティに #[ORM\Column] で定義済みのため、
公式アップデート手順 (doctrine:schema:update --force) で自動反映される。
カラム追加に ALTER TABLE マイグレーションを書かないのが EC-CUBE の慣例
(前例 PR EC-CUBE#4912: カラム追加に ALTER マイグレーション無し、INSERT のみ)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI の rector ジョブ (PR EC-CUBE#6802) で検出された 8 ファイルの指摘を vendor/bin/rector で適用。
- AddressMappingService: 冗長な (int) キャストと三項を null 合体演算子に簡約
- UcpMessageSigner: コンストラクタプロパティ昇格
- テスト各種: self:: → $this->、final class 化、strict_types 宣言等

AgentCommerce テスト 53 件すべて成功を確認。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
決済はプラグイン化方針 (Stripe 同様) のため、Google Pay の merchant_id は
core BaseInfo に持たせない。UCP discovery の payment_handlers は決済ハンドラ
プラグインが寄与する設計とする。あわせて rector 適用後の php-cs-fixer 整形
(ライセンスヘッダ直後の空行) をテストへ反映。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AddressMappingService の numeric->alpha-2 ハードコード const (249件) を廃止し、
新規マスタ mtb_country_iso_code で管理する。PR EC-CUBE#6802 レビュー指摘 (マスタテーブル化) に対応。

- 新規マスタ CountryIsoCode / CountryIsoCodeRepository を追加
- mtb_* の固定スキーマ (id/name/sort_no/discriminator_type) に準拠し、id=ISO numeric / name=alpha-2 を格納 (discriminator=countryisocode)
- 新規インストールは import_csv (definition.yml 登録)、既存環境は INSERT データ migration で backfill (mtb_country は改変しない)
- AddressMappingService はリポジトリ経由解決へ変更、テストを Layer 2 化

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CI rector ジョブ (ContainerGetNameToTypeInTestsRector / AssertFuncCallToPHPUnitAssertRector)
の指摘に対応。get('doctrine') + ManagerRegistry 手動構築をやめ、services_test.yaml で
AddressMappingService を public 化しコンテナから FQCN 取得する方式へ変更。

- services_test.yaml: AddressMappingService を public 化 (consumer 未実装で private では除去されるため)
- AddressMappingServiceTest: self::getContainer()->get(AddressMappingService::class) に簡素化

検証: PHPUnit 52/608、PHPStan level6 No errors、php-cs-fixer 0、rector dry-run クリーン。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BaseInfo の有効化フラグを「checkout の有無を制御する」意味へ整理する。
discovery / catalog は公開して害がないため常時公開とし (ゲート撤去は EC-CUBE#6794)、
ACP feed push は認証情報の有無で実質ガードされるためフラグ不要とする。

- 改名: acp_enabled → acp_checkout_enabled / ucp_enabled → ucp_checkout_enabled
  (getter は isAcpCheckoutEnabled / isUcpCheckoutEnabled)
- 削除: acp_feed_enabled (push は base URL + API key の有無でガード) /
  ucp_catalog_api_enabled (UCP Catalog は常時公開)
- 維持: ucp_catalog_requires_auth (catalog の OAuth 必須モードを api4 着手時に実装)
- dtb_base_info.csv (ja/en) ヘッダを 3 フラグへ更新
- 店舗設定 (ShopMasterType / @admin/Setting/Shop/shop_master.twig) に
  acp_checkout_enabled / ucp_checkout_enabled のトグルを追加 (checkout は日本未提供の注記つき)
- BaseInfoAgentCommerceFlagsTest を 3 フラグへ更新

PHPStan level 6 No errors / php-cs-fixer 0 / 関連テスト green。
カラム変更は schema:update 方式 (ALTER マイグレーションは書かない)。

Refs EC-CUBE#6777
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- MinorUnitConverter: 不正な金額文字列 (abc / 1,000 / 1..2 等) を
  BCMath 呼び出し前に正規表現で弾き、ValueError を防止 (仕様の「不正は 0」契約遵守)
- FilesystemKeyStore: 鍵書き込み時の mkdir / file_put_contents 失敗を
  RuntimeException で検知し、サイレント失敗を防止
- Version20260604120000: down() を不可逆 migration として明示し、
  新規インストール (up() が no-op) 環境での import_csv 由来データ巻き添え削除を回避
- AddressMappingServiceTest: fopen 後に assertIsResource を追加し診断性を向上
- MinorUnitConverterTest: 不正・空入力の回帰テスト 8 ケースを追加

CodeRabbit 指摘のうち COUNT(*) 判定と (bool) キャストの 2 件は非妥当のため非対応。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MinorUnitConverter::toMinorUnits の入力検証を、正規表現での形式チェックから
bcmul/bcadd の ValueError 捕捉へ変更する。

- BCMath が「受け付ける数値文字列形式」の唯一の権威であり、正規表現で再実装すると
  仕様の二重管理・他箇所への正規表現の拡散を招くため、判定を BCMath 自身へ委譲する
- 空文字 '' / '.' は BCMath が 0 として扱うため明示チェックも不要になり簡素化
- is_numeric は指数表記 (1e3) を true と判定するが BCMath では ValueError になり
  不適 (実証済み)。ValueError 捕捉なら指数表記・hex も正しく 0 に倒せる
- 回帰テストに指数表記 (1e3 / 1.5e3) と hex (0x1A) ケースを追加

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
FilesystemKeyStore::write が file_put_contents で umask 既定 (通常 0644) の
パーミッションでファイルを作成し、その後 chmod(0600) するため、その間だけ
秘密鍵が group/other から読める瞬間が生じていた。書き込みの間だけ
umask(0077) に切り替え、作成時点から 0600 になるようにする。あわせて
chmod の戻り値を検査し失敗時に例外を投げる。

CodeRabbit レビュー指摘 (PR EC-CUBE#6825 経由・本ファイルは EC-CUBE#6802 由来) 対応。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
エージェントコマース (ACP/UCP) checkout の前提となる、プロトコル非依存の
CheckoutSession 中核を実装する。checkout エンドポイント本体は EC-CUBE#6776/EC-CUBE#6574 に委ね、
本コミットは再利用可能なエンティティ・サービス・認証部品とテストを提供する。

- CheckoutSession エンティティ + Repository を新規追加 (Cart をセッションレスに
  束ねる上位層・status 正規化(canceled 含む)・json 列・Cart/Order/Customer 関連)
- Order に agent_protocol / agent_id を NULL 許容で追加 (通常購入は両 NULL)
- AgentCheckoutPurchaseFlowAdapter: 中立 DTO → Cart(永続) → OrderHelper →
  既存 shopping flow 再利用で税・送料・在庫を再計算 (新規フローは作らない)
- FulfillmentOptionMapper: 送料(DeliveryFee×Pref)・代引手数料(PaymentOption→
  Payment::charge)・配送日数(DeliveryDuration の明細横断 max) を解決
- CustomerResolverInterface + GuestCustomerResolver (標準はゲスト=null。会員 ID
  連携は eccube-api4#189 landing 後に差し替える seam)
- AgentCommerceOAuth2Authenticator: Symfony 標準 AccessTokenHandlerInterface 経由で
  トークン検証 + scope×protocol 照合。eccube-api4 具象に依存せず、未導入時 503
- AgentCheckoutException + ErrorCode enum、AgentCheckoutPaymentHandlerInterface 共通基底
- カラム追加・新規テーブルは migration を書かず schema:update 方式 (既存規約に準拠)

テスト: Layer 0 (仕様適合) / Layer 2 (Doctrine) / Layer 3 (PurchaseFlow 連携) /
Layer 4a (OAuth2 ユニット・api4 不要)。AgentCommerce スイート 92 tests 0 失敗、
PHPStan level6 No errors。Layer 4b 統合は eccube-api4#188 landing 後。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ull 引数除去)

CI の rector ジョブ指摘を反映:
- DTO/値オブジェクトを readonly class 化 (ReadOnlyAnonymousClassRector ほか)
- テストの self::assert* を $this->assert* に統一 (PreferPHPUnitThisCallRector)
- null デフォルト引数への明示 null 渡しを除去 (RemoveNullArgOnNullDefaultParamRector)

PHPStan level6 No errors / AgentCommerce 92 tests 0 失敗を維持。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CheckoutSession.status / protocol および Order.agent_protocol を、文字列カラム +
クラス文字列定数から、Order/OrderStatus と同じ「マスタ + INTEGER ManyToOne」方式へ変更。
区分値を文字列で保存しない EC-CUBE コア慣習に統一する (protocol は CheckoutSession と
Order の双方で参照するため特にマスタ化が必須)。

- 新規マスタ mtb_checkout_session_status (CheckoutSessionStatus・INCOMPLETE=1..EXPIRED=5)
- 新規マスタ mtb_agent_protocol (AgentProtocol・ACP=1/UCP=2)
- 各 Repository、import_csv (ja/en・name は正準値で同一)、definition.yml 登録
- 既存インストール向け backfill の INSERT migration (冪等・down 非対応)
- CheckoutSession.status/protocol、Order.agent_protocol を ManyToOne 関連へ
- AgentCheckoutRequest.protocol(string) → protocolId(int)、Adapter で AgentProtocol 解決
- テストをマスタ参照へ更新 (status()/PHPUnit final 衝突回避で findStatus にリネーム)

PHPStan level6 No errors / AgentCommerce 93 tests 0 失敗 / Order・PurchaseFlow 回帰 green。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CheckoutSession が参照する Cart が、ログイン会員の Web カート解決
(CartService::getPersistedCarts は customer_id で全カートを取得) に混入し、
マージ・再計算・購入完了時の削除等で操作されてしまう問題を防ぐ。
Web 側の Cart 再生成に CheckoutSession を追従させるのは侵襲的で脆いため、
エージェント所有カートを Web から不可視・操作不可に隔離する方針とする。

- Cart に agent_owned (boolean, default false) を追加
- CartService::getPersistedCarts/getSessionCarts の検索条件に agent_owned=false を追加し、
  エージェント所有カートを Web カート解決から除外 (既存カートは全て false で無影響)
- Adapter はカートに agent_owned=true をセットし、customer_id は常に NULL に保つ
  (会員帰属は Order 側に持たせ、会員 ID 連携時もカート混入を防ぐ多層防御)
- agent_owned カラム追加は schema:update 方式 (migration 不要)
- 隔離の回帰テストを追加

PHPStan level6 No errors / AgentCommerce 85 tests 0 失敗 / CartService 回帰 green。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
expires_at と現在時刻が同値の瞬間だけ未失効になっていた `<` 判定を
`<=` に変更し、境界を期限切れ扱いにする。境界値の回帰テストを追加。

CodeRabbit レビュー指摘 (PR EC-CUBE#6825) 対応。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OAuth2 Authenticator テストのスタブ handler が、rector の
ReadOnlyAnonymousClassRector により `new readonly class` (匿名 readonly
クラス) へ変換されていた。匿名 readonly クラスは PHP 8.3+ 専用で、
サポート下限 (PHP 8.2) の CI で `syntax error, unexpected token "readonly"`
となり PHPUnit が失敗していた。

名前付き `final readonly class ScopeStubAccessTokenHandler` (8.2 で有効・
rector の匿名クラスルール対象外) へ切り出して恒久的に解消する。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CheckoutSessionTest::testJsonColumnsRoundTrip が MySQL CI で失敗していた。
MySQL のネイティブ JSON 型は格納時にオブジェクトのキー順序を保持しない
(PostgreSQL/SQLite は保持) ため、多キーの buyer_data に対する assertSame
(順序厳密) が順序差で落ちていた。buyer_data はキーで参照する map で順序に
意味はないため、assertEqualsCanonicalizing で順序非依存に検証する。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AgentCheckoutPurchaseFlowAdapter::complete() は validate→prepare→commit を一括実行して
いたが、EC-CUBE 標準の決済フロー (在庫引当・採番=prepare → 決済オーソリ → commit/rollback,
PaymentMethod が駆動) と順序が異なり、在庫検証前に決済しうる問題があった (sample-payment-plugin
CreditCard の apply/checkout と乖離)。

- complete() を廃止し prepare()/commit()/rollback() を個別公開。順序とトランザクション境界の
  制御は決済ハンドラ (PaymentMethod 相当) の責務とする。決済処理全体を DB トランザクションで
  囲まない (EMV-3DS 等の外部サイト遷移はリクエストを跨ぐため単一トランザクションで囲えない)。
- AgentCheckoutResult に紐付け元 Cart を追加 (CheckoutSession の cart_id 設定・
  pre_order_id 整合性チェック用)。
- PreOrderIdValidator::rollback はセッションレスなエージェント受注 (agent_protocol!=null) を
  適用外とする。操作中カートと受注の越境防止は CheckoutSession (推測不能な session_id +
  protocol 照合 + cart/order の pre_order_id 整合) が controller 層で担保する。

通常購入 (agent_protocol=null) は従来どおり。ローカル検証: AgentCommerce 95 tests / PurchaseFlow
256 tests / 0 失敗、PHPStan level6 No errors、php-cs-fixer 0 件。PG/MySQL マトリクス・E2E は CI。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tionService)

追加認証 (EMV-3DS) / escalation はエラーでなく complete が返す正常な中間状態であり、
complete は冪等な単発呼び出しでなく状態に応じて複数回呼ばれる状態機械である (EC-CUBE#6777)。

- 決済ハンドラ結果型 PaymentOutcome (COMPLETED/REQUIRES_ACTION/PENDING/FAILED) を新設し、
  AgentCheckoutPaymentHandlerInterface の authorize/capture 戻り値を void から PaymentOutcome へ改訂
- AgentCheckoutCompletionService (状態機械オーケストレータ) を新設。冪等性・初回/再開フロー・
  在庫引当の保持/回収・トランザクション境界を core に集約 (prepare 成功 + authorize COMPLETED 後にのみ
  capture する順序を保証)
- 正規化ステータスマスタに requires_action(6)/in_progress(7) を追加 (CSV + 既存環境向け INSERT migration)。
  findExpired は終端 3 値のみ除外のため両者を自動的に回収対象に含む
- 在庫確保期限を eccube.yaml の eccube_agent_checkout_escalation_expire (既定 15 分・EMV-3DS の 10 分より大) で設定
- AgentCheckoutPaymentHandlerRegistry (core 中立・resolveForOrder) を新設

検証: AgentCommerce+CheckoutSession 104 tests 0 失敗 (incomplete 3 意図的) /
PHPStan level6 src No errors / php-cs-fixer 0 / PurchaseFlow・OrderHelper 回帰 green

Refs EC-CUBE#6777 EC-CUBE#6825

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Idempotency 基盤をプロトコル非依存のコア (EC-CUBE#6777) として実装する。従来の cache+Symfony Lock 方式は
ストアがノードローカル (SemaphoreStore/FlockStore・filesystem cache) でマルチインスタンス (AWS 等) では
越境・並行の二重実行を防げないため、DB の一意制約方式へ作り替える。

- Entity `AgentCheckoutIdempotency` (`dtb_agent_checkout_idempotency`・unique(idempotency_key, subject))
  + Repository。テーブルは空始動のため schema:update 方式 (migration ファイル不要)
- `AgentCheckoutIdempotencyStore` を DB ベースに実装: 予約 INSERT を一意制約で直列化し、
  並行時は処理中=409 / 完了済=リプレイ / 異パラメータ=409 を判定。compute 失敗時は予約を削除し再試行可能に。
  Symfony Lock は撤去 (ロック TTL 問題も解消)。subject (認証エージェント) で名前空間化し越境リプレイ防止
- `IdempotencyConflictException` をコアへ配置 (UCP/ACP 双方が利用)
- 単一の共有 DB だけで動作し、Redis 等の共有キャッシュ・分散ロックに非依存 (共有レンサバ〜AWS 全形態で正しい)

検証: AgentCommerce 110 tests 0 失敗 (incomplete 3 意図的) / PHPStan level6 No errors / php-cs-fixer 0

Refs EC-CUBE#6777

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI rector が指摘した nullable instanceof アサート化・self→$this 呼び出し・
readonly プロパティ化を適用 (PreferPHPUnitThisCall /
AssertEmptyNullableObjectToAssertInstanceof /
AddInstanceofAssertForNullableInstance / ReadOnlyProperty)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ACP / UCP 両 checkout のレスポンス (注文 permalink・プライバシーポリシー /
利用規約リンク) で共用するプロトコル中立な店舗 URL 生成ユーティリティを共通基盤に置く。
標準ページ (help_privacy / help_agreement / mypage_history) から RequestContext
ベースの絶対 URL を実行時生成し、app/Customize で decoration 可能。

スタックする checkout PR (EC-CUBE#6574 UCP / EC-CUBE#6776 ACP) が重複実装せず本サービスを consume する。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ACP / UCP の postal address (region/state) から EC-CUBE の Pref を解決する
getPrefFromRegion() を共通基盤へ追加。Pref 名で逆引きし、ISO 3166-2:JP コード等は
app/Customize の拡張余地とする。スタックする checkout PR (EC-CUBE#6574 / EC-CUBE#6776) が共用する。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ACP (Agentic Commerce Protocol, 2026-04-17) の checkout を共通基盤 (EC-CUBE#6825) の上に実装。

- AcpCheckoutController: 5 エンドポイント (create/update/get/complete/cancel)。
  正準パス /acp/checkout_sessions、ベースパスは RequestContext から生成
- AcpCheckoutSessionMapper / AcpStatusMapper / AcpMessageMapper:
  ACP JSON ⇔ 中立 DTO / Order レスポンス。totals は display_text 付き、金額は minor unit
- 認証は OAuth2 Bearer (acp:checkout scope、AcpAuthenticationSubscriber)。eccube-api4 未導入時 503
- Idempotency-Key は全 POST で必須 (欠落=400 / 異内容=422 / 処理中=409)。
  共通 IdempotencyConflictException に reason を付与して 422/409 を出し分け
- complete は CompletionService へ委譲。3DS は authentication_required の中間状態、
  authentication_result 欠落時は 400 requires_3ds
- 2 系統エラー (プロトコル=4xx/5xx flat Error / ビジネス=200+messages[])
- AcpDiscoveryController + AcpDiscoveryDocumentBuilder: /.well-known/acp.json
  (no-auth、merchant_id 非混入、Cache-Control public max-age=3600)
- AcpMessageSigner: Webhook の Merchant-Signature (HMAC-SHA256) 署名基盤
  (outbound 送出は後続 PR)
- AcpPaymentHandlerInterface: SPT redeem の契約 (具象は決済プラグイン)

テスト: Layer 1 (StatusMapper/MessageMapper/MessageSigner)、Layer 2 (Controller)、
OAuth2 スタブ (InMemoryAccessTokenHandler、eccube-api4 非依存)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- AcpCheckoutConformanceTest: Idempotency (必須/422/replay)、minor unit 整数、
  business=200+messages[]、requires_3ds、cancel 405、markdown raw HTML 拒否を
  MUST 文+出典 URL 付きでトレース。Webhook outbound は markTestIncomplete
- AcpDiscoveryControllerTest: no-auth / merchant_id 非混入 / Cache-Control public / フラグ無効 404
- AcpDiscoveryController: セッションリスナーの no-cache 強制を抑止し public キャッシュを維持

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 86344ef7-bcdb-4847-9eb0-74926a0c7967

📥 Commits

Reviewing files that changed from the base of the PR and between f24dbfb and 08e9ed0.

📒 Files selected for processing (7)
  • src/Eccube/Controller/AgentCommerce/AcpCheckoutController.php
  • src/Eccube/Service/AgentCommerce/Acp/AcpCheckoutSessionMapper.php
  • src/Eccube/Service/AgentCommerce/Acp/AcpMessageMapper.php
  • tests/Eccube/Tests/Service/AgentCommerce/Acp/AcpMessageMapperTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Stub/InMemoryAccessTokenHandler.php
  • tests/Eccube/Tests/Web/AgentCommerce/AcpCheckoutControllerTest.php
  • tests/Eccube/Tests/Web/AgentCommerce/AcpDiscoveryControllerTest.php
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/Eccube/Tests/Service/AgentCommerce/Stub/InMemoryAccessTokenHandler.php
  • tests/Eccube/Tests/Web/AgentCommerce/AcpDiscoveryControllerTest.php
  • src/Eccube/Service/AgentCommerce/Acp/AcpMessageMapper.php
  • src/Eccube/Service/AgentCommerce/Acp/AcpCheckoutSessionMapper.php
  • src/Eccube/Controller/AgentCommerce/AcpCheckoutController.php

📝 Walkthrough

Walkthrough

Agent Commerce の基盤として永続化モデル、設定、購入フロー連携、決済状態機械、OAuth2 認証、署名、ACP discovery と checkout API、冪等性処理、管理画面設定、保護設定、各層の単体・統合・Web テストが追加されています。

Changes

Agent Commerce 基盤と ACP チェックアウト

Layer / File(s) Summary
永続化モデルと管理設定
app/DoctrineMigrations/*, src/Eccube/Entity/..., src/Eccube/Repository/..., src/Eccube/Form/Type/Admin/ShopMasterType.php, src/Eccube/Resource/..., src/Eccube/Service/CartService.php, app/keystore/.htaccess, .gitignore, .htaccess, tests/Eccube/Tests/Entity/*, tests/Eccube/Tests/Service/AgentCommerce/BaseInfoAgentCommerceFlagsTest.php
CheckoutSession・関連マスタ・注文/カート拡張・管理フラグ・管理画面 UI・CSV 定義・保護設定・基礎永続化テストが追加されています。
共有 DTO・変換・購入フロー
src/Eccube/Service/AgentCommerce/CheckoutSession/*, src/Eccube/Service/AgentCommerce/AddressMappingService.php, src/Eccube/Service/AgentCommerce/MinorUnitConverter.php, src/Eccube/Service/AgentCommerce/Fulfillment/*, src/Eccube/Service/AgentCommerce/AgentCheckoutPurchaseFlowAdapter.php, src/Eccube/Service/PurchaseFlow/Processor/PreOrderIdValidator.php, tests/Eccube/Tests/Service/AgentCommerce/{AddressMappingServiceTest,MinorUnitConverterTest,AgentCheckoutPurchaseFlowAdapterTest,Fulfillment/*}, tests/Eccube/Tests/Service/AgentCommerce/Conformance/{AgentCheckoutCoreConformanceTest,AgentCommerceBaseConformanceTest}
住所・金額・配送選択肢・購入リクエスト DTO・購入フロー変換・例外型が追加され、エージェント起点の注文構築とカート分離がテストされています。
冪等性・決済契約・完了状態機械
src/Eccube/Entity/AgentCheckoutIdempotency.php, src/Eccube/Repository/AgentCheckoutIdempotencyRepository.php, src/Eccube/Service/AgentCommerce/Idempotency/*, src/Eccube/Service/AgentCommerce/Payment/*, src/Eccube/Service/AgentCommerce/CheckoutSession/AgentCheckoutCompletion*.php, app/config/eccube/packages/eccube.yaml, app/config/eccube/services_test.yaml, tests/Eccube/Tests/Service/AgentCommerce/Idempotency/*, tests/Eccube/Tests/Service/AgentCommerce/CheckoutSession/AgentCheckoutCompletionServiceTest.php
Idempotency-Key 保存と再生、決済ハンドラ契約、complete 状態機械、期限延長設定が追加され、並行・再実行・決済結果別の状態遷移がテストされています。
認証・署名・鍵管理
src/Eccube/Service/AgentCommerce/Security/*, src/Eccube/Service/AgentCommerce/Acp/AcpAuthenticationSubscriber.php, src/Eccube/Service/AgentCommerce/Acp/AcpMessageSigner.php, app/config/eccube/services.yaml, app/config/eccube/services_test.yaml, tests/Eccube/Tests/Service/AgentCommerce/Security/*, tests/Eccube/Tests/Service/AgentCommerce/Acp/AcpMessageSignerTest.php, tests/Eccube/Tests/Service/AgentCommerce/Stub/InMemoryAccessTokenHandler.php
OAuth2 scope 検証、ACP ルート認証、ファイルベース鍵保管、ACP Webhook 署名、UCP 署名/JWKS 公開、テスト用トークンハンドラと関連テストが追加されています。
ACP API・discovery・HTTP 検証
src/Eccube/Controller/AgentCommerce/*, src/Eccube/Service/AgentCommerce/Acp/*, src/Eccube/Service/AgentCommerce/StorefrontUrlResolver.php, tests/Eccube/Tests/Web/AgentCommerce/*, tests/Eccube/Tests/Service/AgentCommerce/Acp/*, tests/Eccube/Tests/Service/AgentCommerce/Conformance/AcpCheckoutConformanceTest.php
ACP discovery 文書生成、メッセージ/状態/セッション応答マッピング、checkout REST エンドポイント、HTTP レベルの認証・冪等性・3DS・cancel・discovery テストが追加されています。

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Poem

🐇 ぴょこんと鍵をしまいこみ
新しい道へ API が跳ねる
状態はくるり、在庫はきちん
署名も認証も耳をすませて
春草みたいに機能がのびた 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.06% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR タイトル「feat(agent-commerce): ACP checkout (#6776) のコア実装」は、このPRの主な変更であるエージェントコマース(Agent Commerce Protocol)のACP checkoutコア実装を明確に説明しており、技術的かつ具体的で、ノイズのない適切なタイトルになっています。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Eccube/Controller/AgentCommerce/AcpCheckoutController.php`:
- Around line 155-168: The decodeBody() call at line 155 is outside the
try-catch block, so when it throws an AgentCheckoutException for invalid JSON,
it won't be caught and results in a 500 error instead of a proper 400 protocol
error. Move the $payload = $this->decodeBody($request) statement inside the
existing try block (before the isAuthenticationPending check) so that any
AgentCheckoutException thrown by decodeBody will be caught by the
catch(AgentCheckoutException $e) handler and properly converted to a protocol
error response, making the error handling consistent with the create and update
methods.
- Around line 312-320: The findSession() method in AcpCheckoutController is only
validating the protocol but not verifying that the session owner (agent_id or
acp_client_id) matches the current requesting agent, which creates a security
vulnerability where unauthorized agents could access other agents' sessions. Add
an additional authorization check after the protocol validation to verify that
the session's agent_id matches the current agent's identifier, and throw a
NotFoundHttpException or AccessDeniedHttpException if they do not match,
ensuring that principal mismatch is properly rejected.

In `@src/Eccube/Service/AgentCommerce/Acp/AcpCheckoutSessionMapper.php`:
- Around line 61-77: In the toCheckoutRequest method, the current implementation
on lines 75-76 allows invalid values to be accepted - non-numeric itemId becomes
0 and negative quantity values pass through unchecked. Modify the logic to
validate that itemId and quantity are both positive integers only. After
extracting itemId and quantity from the rawLineItem, add validation checks that
throw AgentCheckoutException with an appropriate error message if either value
is not a positive integer (must be greater than 0). Only create the
AgentCheckoutLineItem instance if both values pass validation, preventing
invalid line items from being added to the lineItems array.

In `@src/Eccube/Service/AgentCommerce/Acp/AcpMessageMapper.php`:
- Around line 39-40: The RAW_HTML_PATTERN regular expression currently only
matches a specific whitelist of HTML tags (script, div, img, iframe, style,
object, embed, link, meta, form, input, svg, a, span, table), which allows other
tags like <p>, <h1>, and HTML comments to bypass detection. Update the
RAW_HTML_PATTERN to comprehensively match any HTML tag (opening or closing) and
HTML comments instead of limiting detection to specific tags, ensuring the
assertNoRawHtml() method catches all raw HTML content as required by the
specification.

In
`@src/Eccube/Service/AgentCommerce/CheckoutSession/AgentCheckoutCompletionService.php`:
- Around line 127-135: In the COMPLETED case of the switch statement in
AgentCheckoutCompletionService, only the capture result's metadata is being
merged via the mergePaymentData method call, but the authorize result's metadata
from the $outcome variable is not being preserved. Before the existing
mergePaymentData call that handles $capture->metadata, add another
mergePaymentData call to merge $outcome->metadata first, ensuring that both the
authorize and capture metadata are saved to maintain complete PSP reference
information.

In
`@src/Eccube/Service/AgentCommerce/Idempotency/AgentCheckoutIdempotencyStore.php`:
- Around line 104-109: The hashRequest() method in AgentCheckoutIdempotencyStore
only sorts keys at the top level using ksort($payload), but does not recursively
normalize nested associative arrays. This causes identical requests with
different key ordering in nested structures to produce different hashes,
resulting in false conflicts. Create a recursive helper method that sorts keys
at all nesting levels of the payload array, then apply this helper to the entire
payload before calling json_encode() instead of only using ksort() on the top
level.
- Around line 55-64: The execute method accepts a nullable $subject parameter
that silently defaults to an empty string when null, creating a shared namespace
risk across all subjects. Additionally, the hashRequest() method only applies
ksort() at the top level, failing to canonicalize nested associative arrays,
which causes identical payloads with different key orders to generate different
hashes and break idempotency detection. Fix these issues by: (1) making the
$subject parameter required (remove nullable type and default value) in the
execute method signature to enforce explicit subject provision, and (2)
modifying the hashRequest() method to apply recursive key sorting to all nested
arrays and objects to ensure consistent canonicalization of payloads regardless
of key order.

In `@src/Eccube/Service/AgentCommerce/Security/FilesystemKeyStore.php`:
- Around line 54-76: In the write method of the FilesystemKeyStore class, the
file_put_contents call that writes the private key lacks exclusive file locking,
which can cause data corruption when multiple processes attempt to write to the
same key file simultaneously. Add the FILE_LOCK_EX flag as the third parameter
to the file_put_contents call to ensure exclusive locking and serialize
concurrent write operations.

In `@src/Eccube/Service/AgentCommerce/Security/UcpMessageSigner.php`:
- Around line 158-167: The collectPublicKeys() method does not handle exceptions
thrown by PublicKeyLoader::load() when it encounters invalid or malformed PEM
keys in the gracePublicKeyPems collection. Wrap the PublicKeyLoader::load($pem)
call in a try-catch block to catch any exceptions thrown during key loading, and
use continue to skip over invalid keys and proceed to the next iteration of the
foreach loop. This allows the method to gracefully skip malformed legacy grace
period keys and only collect valid public keys that can be converted to JWK
format.

In
`@tests/Eccube/Tests/Service/AgentCommerce/Stub/InMemoryAccessTokenHandler.php`:
- Around line 1-13: The test file InMemoryAccessTokenHandler.php is missing the
strict types declaration that is required in all test files under the tests/
directory. Add the line declare(strict_types=1); immediately after the opening
<?php tag and before the EC-CUBE license header comment block. This ensures
consistency with the project's coding standards where the order should be:
opening PHP tag, declare statement, then the license header.

In `@tests/Eccube/Tests/Web/AgentCommerce/AcpDiscoveryControllerTest.php`:
- Around line 76-79: The test is checking for an exact string match of
`max-age=3600` using assertStringContainsString, but the specification requires
that max-age be at least 3600 seconds (>= 3600). This means responses with
compliant higher values like `max-age=7200` would incorrectly fail. Replace the
exact string match assertion with logic that extracts the numeric max-age value
from the Cache-Control header in $cacheControl, parses it, and asserts that the
integer value is greater than or equal to 3600.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 699231ee-da11-4701-9222-195e85679352

📥 Commits

Reviewing files that changed from the base of the PR and between 5777408 and f24dbfb.

⛔ Files ignored due to path filters (8)
  • src/Eccube/Resource/doctrine/import_csv/en/dtb_base_info.csv is excluded by !**/*.csv
  • src/Eccube/Resource/doctrine/import_csv/en/mtb_agent_protocol.csv is excluded by !**/*.csv
  • src/Eccube/Resource/doctrine/import_csv/en/mtb_checkout_session_status.csv is excluded by !**/*.csv
  • src/Eccube/Resource/doctrine/import_csv/en/mtb_country_iso_code.csv is excluded by !**/*.csv
  • src/Eccube/Resource/doctrine/import_csv/ja/dtb_base_info.csv is excluded by !**/*.csv
  • src/Eccube/Resource/doctrine/import_csv/ja/mtb_agent_protocol.csv is excluded by !**/*.csv
  • src/Eccube/Resource/doctrine/import_csv/ja/mtb_checkout_session_status.csv is excluded by !**/*.csv
  • src/Eccube/Resource/doctrine/import_csv/ja/mtb_country_iso_code.csv is excluded by !**/*.csv
📒 Files selected for processing (92)
  • .gitignore
  • .htaccess
  • app/DoctrineMigrations/Version20260604120000.php
  • app/DoctrineMigrations/Version20260611120000.php
  • app/DoctrineMigrations/Version20260617120000.php
  • app/config/eccube/packages/eccube.yaml
  • app/config/eccube/services.yaml
  • app/config/eccube/services_test.yaml
  • app/keystore/.gitkeep
  • app/keystore/.htaccess
  • src/Eccube/Controller/AgentCommerce/AcpCheckoutController.php
  • src/Eccube/Controller/AgentCommerce/AcpDiscoveryController.php
  • src/Eccube/Entity/AgentCheckoutIdempotency.php
  • src/Eccube/Entity/BaseInfo.php
  • src/Eccube/Entity/Cart.php
  • src/Eccube/Entity/CheckoutSession.php
  • src/Eccube/Entity/Master/AgentProtocol.php
  • src/Eccube/Entity/Master/CheckoutSessionStatus.php
  • src/Eccube/Entity/Master/CountryIsoCode.php
  • src/Eccube/Entity/Order.php
  • src/Eccube/Form/Type/Admin/ShopMasterType.php
  • src/Eccube/Repository/AgentCheckoutIdempotencyRepository.php
  • src/Eccube/Repository/CheckoutSessionRepository.php
  • src/Eccube/Repository/Master/AgentProtocolRepository.php
  • src/Eccube/Repository/Master/CheckoutSessionStatusRepository.php
  • src/Eccube/Repository/Master/CountryIsoCodeRepository.php
  • src/Eccube/Resource/doctrine/import_csv/en/definition.yml
  • src/Eccube/Resource/doctrine/import_csv/ja/definition.yml
  • src/Eccube/Resource/locale/messages.en.yaml
  • src/Eccube/Resource/locale/messages.ja.yaml
  • src/Eccube/Resource/template/admin/Setting/Shop/shop_master.twig
  • src/Eccube/Service/AgentCommerce/Acp/AcpAuthenticationSubscriber.php
  • src/Eccube/Service/AgentCommerce/Acp/AcpCheckoutSessionMapper.php
  • src/Eccube/Service/AgentCommerce/Acp/AcpDiscoveryDocumentBuilder.php
  • src/Eccube/Service/AgentCommerce/Acp/AcpMessageMapper.php
  • src/Eccube/Service/AgentCommerce/Acp/AcpMessageSigner.php
  • src/Eccube/Service/AgentCommerce/Acp/AcpStatusMapper.php
  • src/Eccube/Service/AgentCommerce/AddressMappingService.php
  • src/Eccube/Service/AgentCommerce/AgentCheckoutPurchaseFlowAdapter.php
  • src/Eccube/Service/AgentCommerce/CheckoutSession/AgentCheckoutAddress.php
  • src/Eccube/Service/AgentCommerce/CheckoutSession/AgentCheckoutCompletionResult.php
  • src/Eccube/Service/AgentCommerce/CheckoutSession/AgentCheckoutCompletionService.php
  • src/Eccube/Service/AgentCommerce/CheckoutSession/AgentCheckoutLineItem.php
  • src/Eccube/Service/AgentCommerce/CheckoutSession/AgentCheckoutMessage.php
  • src/Eccube/Service/AgentCommerce/CheckoutSession/AgentCheckoutMessageLevel.php
  • src/Eccube/Service/AgentCommerce/CheckoutSession/AgentCheckoutRequest.php
  • src/Eccube/Service/AgentCommerce/CheckoutSession/AgentCheckoutResult.php
  • src/Eccube/Service/AgentCommerce/CheckoutSession/CustomerResolverInterface.php
  • src/Eccube/Service/AgentCommerce/CheckoutSession/GuestCustomerResolver.php
  • src/Eccube/Service/AgentCommerce/Exception/AgentCheckoutErrorCode.php
  • src/Eccube/Service/AgentCommerce/Exception/AgentCheckoutException.php
  • src/Eccube/Service/AgentCommerce/Exception/IdempotencyConflictException.php
  • src/Eccube/Service/AgentCommerce/Fulfillment/FulfillmentOption.php
  • src/Eccube/Service/AgentCommerce/Fulfillment/FulfillmentOptionMapperInterface.php
  • src/Eccube/Service/AgentCommerce/Fulfillment/FulfillmentPaymentOption.php
  • src/Eccube/Service/AgentCommerce/Fulfillment/StandardFulfillmentOptionMapper.php
  • src/Eccube/Service/AgentCommerce/Idempotency/AgentCheckoutIdempotencyStore.php
  • src/Eccube/Service/AgentCommerce/MinorUnitConverter.php
  • src/Eccube/Service/AgentCommerce/Payment/AcpPaymentHandlerInterface.php
  • src/Eccube/Service/AgentCommerce/Payment/AgentCheckoutPaymentHandlerInterface.php
  • src/Eccube/Service/AgentCommerce/Payment/AgentCheckoutPaymentHandlerRegistry.php
  • src/Eccube/Service/AgentCommerce/Payment/PaymentOutcome.php
  • src/Eccube/Service/AgentCommerce/Payment/PaymentOutcomeStatus.php
  • src/Eccube/Service/AgentCommerce/Security/AgentCommerceMessageSignerInterface.php
  • src/Eccube/Service/AgentCommerce/Security/AgentCommerceOAuth2Authenticator.php
  • src/Eccube/Service/AgentCommerce/Security/AgentCommerceScopeRegistry.php
  • src/Eccube/Service/AgentCommerce/Security/FilesystemKeyStore.php
  • src/Eccube/Service/AgentCommerce/Security/KeyStoreInterface.php
  • src/Eccube/Service/AgentCommerce/Security/UcpMessageSigner.php
  • src/Eccube/Service/AgentCommerce/StorefrontUrlResolver.php
  • src/Eccube/Service/CartService.php
  • src/Eccube/Service/PurchaseFlow/Processor/PreOrderIdValidator.php
  • tests/Eccube/Tests/Entity/CheckoutSessionTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Acp/AcpMessageMapperTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Acp/AcpMessageSignerTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Acp/AcpStatusMapperTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/AddressMappingServiceTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/AgentCheckoutPurchaseFlowAdapterTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/BaseInfoAgentCommerceFlagsTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/CheckoutSession/AgentCheckoutCompletionServiceTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Conformance/AcpCheckoutConformanceTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Conformance/AgentCheckoutCoreConformanceTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Conformance/AgentCommerceBaseConformanceTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Fulfillment/StandardFulfillmentOptionMapperTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Idempotency/AgentCheckoutIdempotencyStoreTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/MinorUnitConverterTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Security/AgentCommerceOAuth2AuthenticatorTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Security/AgentCommerceScopeRegistryTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Security/UcpMessageSignerTest.php
  • tests/Eccube/Tests/Service/AgentCommerce/Stub/InMemoryAccessTokenHandler.php
  • tests/Eccube/Tests/Web/AgentCommerce/AcpCheckoutControllerTest.php
  • tests/Eccube/Tests/Web/AgentCommerce/AcpDiscoveryControllerTest.php

Comment thread src/Eccube/Controller/AgentCommerce/AcpCheckoutController.php Outdated
Comment thread src/Eccube/Controller/AgentCommerce/AcpCheckoutController.php Outdated
Comment thread src/Eccube/Service/AgentCommerce/Acp/AcpCheckoutSessionMapper.php Outdated
Comment thread src/Eccube/Service/AgentCommerce/Acp/AcpMessageMapper.php Outdated
Comment thread src/Eccube/Service/AgentCommerce/Security/FilesystemKeyStore.php
Comment thread src/Eccube/Service/AgentCommerce/Security/UcpMessageSigner.php
Comment thread tests/Eccube/Tests/Web/AgentCommerce/AcpDiscoveryControllerTest.php Outdated
PR EC-CUBE#6843 のレビュー指摘のうち ACP 層所管の 6 件を修正:

- complete の decodeBody() を try 内へ移動。不正 JSON が 500 ではなく
  400 プロトコルエラーに変換され、create/update と一貫する
- findSession にセッション所有者 (agent_id) 照合を追加。他エージェントの
  セッションへの越境アクセスを存在秘匿で 404 拒否 (多層防御)
- line_items の id/quantity を正の整数のみ許可。非数値→0・負数の素通りを
  防ぎ、不正値は AgentCheckoutException → 400 に寄せる
- AcpMessageMapper の raw HTML 検知を包括化。任意タグ・HTML コメントを
  検知しつつ CommonMark の autolink は誤検知しない
- Discovery テストの max-age を固定値一致から >= 3600 の下限判定へ
- テスト Stub に declare(strict_types=1) を追加

越境遮断・不正値・不正 JSON・raw HTML 包括検知の回帰テストを追加。
B 群 (EC-CUBE#6825 checkout-core) / C 群 (EC-CUBE#6802 base) 所管の指摘は各 PR 側で対応。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant