はじめに
AIエージェントがユーザーに代わって商品を購入する場合、販売者は「そのAgentに購入権限がある」ことをどう確認すればよいのでしょうか。
この課題に取り組む仕組みの一つが、Googleなどが公開している AP2(Agentic Payment Protocol) です。
私はAP2の購入委任をRubyで扱うOSS、ap2_mandate_verifierを開発しています。
2026年11月1日、v1.0.0をRubyGemsに公開しました。
前回の記事では初期バージョンを紹介しましたが、現在は検証だけでなく、対応するCheckout Mandateの発行・提示・検証・Receipt確認まで扱えるようになりました。
今回は、v1.0.0でできることと、Rubyだけで試せる実行例を紹介します。
1. v0.1.0から何が変わったのか?
最初のv0.1.0は、対応するAP2 Checkout Mandateの検証を中心としたGemでした。
現在のv1.0.0では、次の機能を提供しています。
| 機能 | v0.1.0 | v1.0.0 |
|---|---|---|
| Checkout Mandateの検証 | ✅ | ✅ |
| Checkout JWTの署名・検証 | — | ✅ |
| Direct方式のMandate発行・提示 | — | ✅ |
| 条件付きMandateの発行・提示 | — | ✅ |
| 複数商品・数量の検証 | — | ✅ |
| Checkout Receiptの署名・検証 | — | ✅ |
| 検証入力の組み立て支援 | — | ✅ |
| 失敗時の診断情報 | — | ✅ |
Gem名はVerifierですが、現在は対応するAP2 Checkoutの一連の処理を提供する、フレームワーク非依存のRuby SDKになっています。
なお、これらの機能がすべてv1.0.0で新規追加されたわけではありません。
- v0.5.0:Checkout JWT・Receipt関連の機能
- v0.7.0:Direct方式
- v0.8.0:条件付き購入委任
- v1.0.0:公開API・互換性・保守方針の安定化
1.0.0は、これまで実装した機能を継続して利用できるよう、公開契約を明確にしたリリースです。
2. AP2の2種類の購入委任
AP2では、ユーザーの承認方法によって大きく2つの流れがあります。
Direct方式
ユーザーが具体的な注文内容を見て、その場で承認する方式です。
例えば、
商品Aを1個、3,000円で購入する。
という注文に対して、ユーザーが承認します。
SDKでは、Buyer.issue_closed、Buyer.present_closed、Buyer.verifyなどのAPIを利用できます。
条件付き購入委任(Autonomous方式)
ユーザーがあらかじめ購入条件を承認し、AIエージェントがその条件に従って購入内容を決める方式です。
例えば、
商品AまたはBを1個、さらに商品Cを2個購入してほしい。
この場合、購入する商品が最初から完全に決まっているわけではありません。
ユーザーが承認した条件に対して、Agentが実際の注文を組み立てます。
AP2では、この流れで次のMandateを利用します。
- Open Mandate:事前承認された購入条件を表す
- Closed Mandate:実際の注文内容に結び付けられたMandate
今回はこちらをRubyで試してみます。
3. Rubyで条件付き購入委任を試す
v1.0.0には、条件付き購入委任の一連の処理を実行できるサンプルを同梱しています。
このサンプルはRuby単体で実行でき、Railsは不要です。
正式な対応環境はMRI Ruby 3.4.12/4.0.9です。
インストール
gem install ap2_mandate_verifier -v 1.0.0
サンプル実行
インストールされたGem内のサンプルを実行します。
ruby "$(ruby -e 'puts Gem::Specification.find_by_name("ap2_mandate_verifier", "1.0.0").full_gem_path')/examples/conditional_checkout.rb"
このサンプルには、検証用の合成データと鍵生成処理が含まれています。
実際のユーザー認証や決済サービスは必要ありません。
サンプルの購入条件
例えば、承認条件は次のようなRubyのHashで表現されます。
requirements = [
{
"id" => "choice",
"quantity" => 1,
"acceptable_items" => [
{ "id" => "A", "title" => "Product A" },
{ "id" => "B", "title" => "Product B" }
]
},
{
"id" => "required",
"quantity" => 2,
"acceptable_items" => [
{ "id" => "C", "title" => "Product C" }
]
}
]
意味は次のとおりです。
- 商品AまたはBを1個
- 商品Cを2個
サンプルでは、商品Bを1個と商品Cを2個選びます。
対応する複数明細profileでは、単一販売者・最大8明細まで扱えます。
処理の流れ
サンプルでは次の処理を行います。
- 承認条件からOpen Mandateを発行する
- 販売者がCheckout JWTに署名する
- AgentがOpen Mandateの条件に従いClosed Mandateを生成する
- AgentがMandateを提示する
- 販売者側が署名・委任・購入条件などを検証する
- 販売者側のアプリケーション判断に基づいてCheckout Receiptを署名・検証する
これらは実際のGem APIによる処理です。
4. 実行結果
サンプルの主な出力は次のとおりです。
Open -> Agent Closed -> merchant evidence: verified=true; rows=3; requirements=2
Receipt Success: authentic=true; purchase_succeeded=true
Receipt Error: authentic=true; purchase_succeeded=false
この結果から確認できることは3つです。
① OpenからClosedへの購入委任を検証できた
verified=trueは、対応profileについて、必要な検証を通過したことを意味します。
② Success Receiptの真正性を確認できた
authentic=trueかつpurchase_succeeded=trueとなっています。
③ Error Receiptの真正性も確認できた
Receipt Error: authentic=true; purchase_succeeded=false
ここが重要です。
authentic=trueは、Receiptが真正なものであることを示します。
しかし、purchase_succeeded=falseなので購入成功を意味しません。
つまり、「正当な販売者が拒否した」という結果も、署名付きReceiptで確認できるわけです。
なお、SuccessとErrorはサンプル内の独立した合成ケースです。同じ購入を2回実行しているわけではありません。
また、いずれも実際の決済完了を示す結果ではありません。
5. 主要API
用途ごとにAPIを整理すると、次のようになります。
| API | 用途 |
|---|---|
CheckoutJWT.sign |
販売者によるCheckout JWTの署名 |
CheckoutJWT.verify |
Checkout JWTの署名検証 |
Buyer.issue_closed |
Direct方式のClosed Mandate発行 |
Buyer.present_closed |
Direct方式のMandate提示 |
Buyer.verify |
Direct方式の検証 |
ConditionalCheckout.issue_open |
条件付きOpen Mandate発行 |
ConditionalCheckout.issue_closed |
AgentによるClosed Mandate生成 |
ConditionalCheckout.present |
条件付きMandateの提示 |
InputBuilder.verify |
業務情報から入力を組み立てて完全検証 |
CheckoutReceipt.sign |
Checkout Receiptの署名 |
CheckoutReceipt.verify |
Checkout Receiptの署名・参照検証 |
いずれもAP2MandateVerifier配下のAPIです。
特にInputBuilderは、アプリケーションが保持する注文・信頼情報・Challengeなどから、検証に必要な入力を組み立てるためのものです。
検証に必要な信頼情報をGemが自動取得するわけではありません。アプリケーション側で独立して用意する必要があります。
詳しい引数や利用例は、公開APIドキュメントとサンプルコードを参照してください。
6. 検証成功と購入許可は別
このGemを利用するうえで、特に重要な設計上の境界があります。
Mandateの検証が成功しても、それだけで購入を実行してはいけません。
SDKとアプリケーションには、それぞれ責任があります。
| SDKの責任 | アプリケーションの責任 |
|---|---|
| 対応する署名・委任の検証 | 信頼するIssuer・鍵の管理 |
| 購入条件との整合性検証 | 最新の注文状態の確認 |
| 対応する制約・期限の検査 | nonceの原子的消費・重複防止 |
| Evidenceの生成 | 最終的な購入可否の判断 |
| Checkout Receiptの真正性確認 | 注文処理・決済実行 |
例えば、署名が正しくても、同じ購入要求がすでに処理されているかもしれません。
そのため、アプリケーション側で最新の注文状態やnonceを確認し、重複実行を防ぐ必要があります。
また、AgentがOpen Mandateを使う際、承認サービスの秘密鍵をAgentに渡さない設計にしています。
承認サービスとAgentの責務を分離し、Agentは登録された自分の鍵を使ってClosed Mandateを生成します。
現在の対応範囲
v1.0.0は、AP2 v0.2の対応するCheckout profileを対象としています。
以下は提供範囲に含まれません。
- Payment Mandateの発行・検証
- 任意の予算制約や全AP2 profileへの対応
- 実際の注文確定・決済処理
- 実Issuerとの一般的な相互運用保証
- 本番環境での全面的な安全性保証
v1.0.0の公開は、AP2公式認定やAP2全体への準拠保証を意味しません。
対応するprofileを明確にし、未対応の形式を安易に受理しないことを重視しています。
7. おわりに
AP2はまだ発展途上の規格です。
今後、AIエージェントがユーザーの代わりに取引を行う仕組みが広がると、購入委任の発行・検証は重要な共通機能になると考えています。
その際、Ruby開発者が暗号署名やMandateの処理をゼロから実装することなく、利用できるSDKを提供したいと考えています。
まずは、今回紹介したRuby単体のサンプルを試していただけるとうれしいです。
改善提案や対応してほしいユースケースがあれば、GitHub Issuesでフィードバックをいただけると助かります。
関連リンク
- GitHub — ap2-mandate-verifier
- RubyGems — ap2_mandate_verifier v1.0.0
- v1.0.0 Release Notes
- 条件付き購入委任のサンプルコード
- AP2公式リポジトリ
- 前回のQiita記事
通常の不具合や改善提案はGitHub Issuesへ、脆弱性の疑いは非公開の報告窓口をご利用ください。秘密鍵・実際のMandate・個人情報は公開しないようお願いします。