はじめに

初めまして。 「DApps開発入門」という本や色々記事を書いているかるでねです。

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!

今回は「Commerce Payments Protocol」についてまとめていきます。

この記事は以下の方法でベースを作成しています。

従来の決済フローとAuth & Capture

クレジットカード決済を利用したことがある方なら、「Authorization」と「Capture」という用語を聞いたことがあるかもしれません。 これは従来の金融システムで確立された2フェーズの決済パターンです。

まず、本記事で使用する主要な用語を整理します。

用語 説明
Authorization 与信枠の確保。顧客の支払い能力を確認し、指定金額を「ホールド(保留)」する処理。実際の資金移動は発生しない
Capture 売上確定。Authorizationでホールドされた金額を実際にMerchantへ移動させる処理
Escrow 第三者預託。取引の安全性を確保するため、信頼できる第三者が資金を一時的に保管する仕組み
Payer 支払者。決済において資金を支払う側の当事者
Receiver/Merchant 受取人。決済において資金を受け取る側の当事者(売り手)
Operator 決済フローを管理・実行する主体。ガス代を負担し、トランザクションを送信する
sequenceDiagram
    participant Customer as Customer
    participant Merchant as Merchant
    participant Acquirer as Acquirer
    participant Issuer as Issuer

    Customer->>Merchant: 購入リクエスト
    Merchant->>Acquirer: Authorization要求
    Acquirer->>Issuer: 与信枠確認
    Issuer-->>Acquirer: 承認(与信枠確保)
    Acquirer-->>Merchant: Authorization完了
    Note over Merchant: 商品発送・サービス提供
    Merchant->>Acquirer: Capture要求
    Acquirer->>Issuer: 売上確定
    Issuer-->>Acquirer: 決済完了
    Acquirer-->>Merchant: 入金

従来のクレジットカード決済におけるAuth & Captureフロー

この2フェーズが必要な理由は、実際のコマースでは即時決済だけでは対応できない様々なケースが存在するためです。

シナリオ 説明
在庫切れ 注文後に在庫がないことが判明し、キャンセルが必要になる
返金リクエスト 顧客が商品に不満を持ち、返金を要求する
分割配送 複数の商品を別々に発送し、発送ごとに請求する
税金計算の確定 配送先が確定するまで正確な税額が計算できない
価格調整 プロモーション適用や価格変更による金額修正

Authorizationでは、顧客の支払い能力を確認し、その金額を「ホールド(保留)」します。 この時点では実際の資金移動は発生しませんが、Merchantは支払いが確実に行われることを保証されます。

Captureでは、ホールドされた金額の一部または全部を実際にMerchantへ移動させ、決済を確定します。

この「支払い保証の中間状態」が、スケーラブルなコマース決済の基盤となっています。

オンチェーン決済の課題

ブロックチェーン上の決済は、P2P(ピアツーピア)取引には非常に適しています。 AさんからBさんへトークンを送るという単純な取引は、スマートコントラクトで即座に実行できます。

しかし、ECサイトのような複雑なコマース決済では、従来のオンチェーン決済には以下の課題がありました。

graph TB
    subgraph Problem["従来の課題"]
        P1[即時・最終的な送金のみ]
        P2[資金のホールド機能なし]
        P3[返金は別トランザクション]
        P4[部分Capture不可]
    end

    subgraph Impact["ビジネス影響"]
        I1[在庫切れ時の対応困難]
        I2[Merchantのリスク増大]
        I3[複雑なオーダー管理]
        I4[顧客体験の低下]
    end

    P1 --> I1
    P2 --> I2
    P3 --> I3
    P4 --> I4

    style Problem fill:#ffcccc
    style Impact fill:#ffffcc

従来のオンチェーン決済がコマースに不向きだった理由

つまり、オンチェーンには「今すぐ支払うわけではないが、将来の支払いを保証する」という中間状態が存在しなかったのです。

Commerce Payments Protocolの概要

Commerce Payments Protocolは、Baseチェーン上で従来の金融システムと同等のAuth & Captureパターンを実現するプロトコルです。

このプロトコルは以下の設計思想に基づいています。

Permissionless 誰でもOperatorとしてプロトコルを利用でき、特定の管理者による承認は不要です。 Merchant自身がOperatorになることも、サードパーティの決済プロバイダーを利用することもできます。

Immutable デプロイ後にプロトコルのコードを変更することはできません。 これにより、参加者は将来にわたってルールが変わらないことを信頼できます。

Trust-Minimized Operatorは決済フローを管理しますが、プロトコルの暗号学的制約により資金を盗むことはできません。 最悪のケースでも、Operatorができることは限定されています。

graph LR
    subgraph Traditional["従来型"]
        T1[Payer] -->|即時送金| T2[Receiver]
    end

    subgraph CPP["CPP"]
        C1[Payer] -->|Authorize| C2[Escrow]
        C2 -->|Capture| C3[Receiver]
        C2 -.->|Void/Reclaim| C1
    end

    style Traditional fill:#ffcccc
    style CPP fill:#ccffcc

従来のオンチェーン決済とCommerce Payments Protocolの比較

プロトコルの核心は「Escrow方式」です。 Authorizeの時、資金はPayerからEscrowコントラクトに移動します。 Captureの時、資金はEscrowからReceiverに移動します。 この2ステップにより、従来の金融システムと同等の「支払い保証の中間状態」をオンチェーンで実現しています。

アーキテクチャと主要コンポーネント

Commerce Payments Protocolは、3つの主要コンポーネントで構成されています。

graph TB
    subgraph External["外部参加者"]
        Operator[Operator]
        Payer[Payer]
        Receiver[Receiver/Merchant]
    end

    subgraph Protocol["CPP Core"]
        ACE[AuthCaptureEscrow]
        TC[Token Collectors]
        TS[TokenStore]
    end

    Operator -->|決済フロー管理| ACE
    Payer -->|署名| TC
    TC -->|トークン収集| ACE
    ACE -->|資金保管| TS
    ACE -->|支払い| Receiver

    style ACE fill:#4169E1,color:#fff
    style TC fill:#32CD32,color:#fff
    style TS fill:#FF8C00,color:#fff

Commerce Payments Protocolのアーキテクチャ

AuthCaptureEscrow

メインのEscrowコントラクトで、決済のライフサイクル全体を管理します。

このコントラクトは以下の責務を持ちます。

  • 決済パラメータとタイミング制約の検証
  • 決済状態の管理(authorized、captured、refunded)
  • 手数料の分配
  • リエントランシー攻撃からの保護

すべての決済は PaymentInfo 構造体で定義され、この構造体のハッシュが決済を一意に識別します。

solidity
struct PaymentInfo {
    address operator;           // 決済フローを管理するエンティティ
    address payer;              // 支払者のウォレットアドレス
    address receiver;           // Merchantの受取アドレス
    address token;              // 支払いトークンのコントラクトアドレス
    uint120 maxAmount;          // Authorization可能な最大金額
    uint48 preApprovalExpiry;   // 署名の有効期限
    uint48 authorizationExpiry; // Authorizationの有効期限(この後はCapture不可)
    uint48 refundExpiry;        // 返金の有効期限
    uint16 minFeeBps;           // 最小手数料(基準点)
    uint16 maxFeeBps;           // 最大手数料(基準点)
    address feeReceiver;        // 手数料受取人(0なら動的に指定可能)
    uint256 salt;               // 一意性を保証するためのエントロピー
}

TokenStore

Operatorごとに作成される資金保管庫です。

このコンポーネントの重要な特徴は「流動性の分離」です。 すべてのEscrow資金を単一のコントラクトに保管すると、万が一の脆弱性発見時に全資金が危険にさらされます。 TokenStoreはOperatorごとに独立して作成されるため、あるOperatorの資金が他のOperatorの資金に影響を与えることはありません。

graph TB
    ACE[AuthCaptureEscrow]

    subgraph Stores["Operator別TokenStore"]
        TS1[TokenStore A]
        TS2[TokenStore B]
        TS3[TokenStore C]
    end

    ACE --> TS1
    ACE --> TS2
    ACE --> TS3

    style ACE fill:#4169E1,color:#fff
    style TS1 fill:#FF8C00,color:#fff
    style TS2 fill:#FF8C00,color:#fff
    style TS3 fill:#FF8C00,color:#fff

Operatorごとに分離されたTokenStore

TokenStoreは以下の特徴を持ちます。

  • CREATE2を使用した決定論的デプロイ(Operatorアドレスから一意に導出)
  • Minimal Proxyパターンによるガス効率の最適化
  • AuthCaptureEscrowからのみ呼び出し可能

Token Collector

トークンの承認方法を抽象化するモジュラーなコンポーネントです。

ブロックチェーンには複数のトークン承認標準が存在し、それぞれに特徴があります。 Token Collectorはこれらの違いを吸収し、プロトコルのコア部分を変更することなく様々な承認方法に対応できるようにします。

詳細は後述の「Token Collectorによる承認方法の抽象化」セクションで説明します。

6つの決済オペレーション

Commerce Payments Protocolは、決済ライフサイクル全体をカバーする6つのオペレーションを提供します。

stateDiagram-v2
    [*] --> Pending: 決済作成

    Pending --> Authorized: authorize()
    Pending --> Captured: charge()

    Authorized --> Captured: capture()
    Authorized --> Voided: void()
    Authorized --> Reclaimed: reclaim()

    Captured --> Refunded: refund()
    Captured --> [*]: 決済完了

    Voided --> [*]: キャンセル完了
    Reclaimed --> [*]: 回収完了
    Refunded --> [*]: 返金完了

決済状態の遷移図

Authorize

Payerの資金をEscrowにホールドし、将来のCaptureを保証します。

solidity
function authorize(
    PaymentInfo calldata paymentInfo,
    uint256 amount,
    address tokenCollector,
    bytes calldata collectorData
) external

この関数が実行されると、指定された金額がPayerからTokenStoreに移動します。 Merchantはこの時点で、支払いが確実に行われることを保証されます。

主な検証項目

  • 金額が maxAmount を超えていないこと
  • 現在時刻が preApprovalExpiry より前であること
  • 同じ決済がまだAuthorizeされていないこと

Capture

Authorizeされた資金をEscrowからMerchantに移動し、決済を確定します。

solidity
function capture(
    PaymentInfo calldata paymentInfo,
    uint256 amount,
    uint16 feeBps,
    address feeReceiver
) external

Captureは複数回に分けて実行できます(部分Capture)。 例えば、1,000 USDCのAuthorizationに対して、最初に600 USDC、次に400 USDCをCaptureすることが可能です。

部分Captureの例

text
初期状態: capturableAmount = 1000 USDC

1回目のCapture(600 USDC):
  → capturableAmount = 400 USDC
  → refundableAmount = 600 USDC

2回目のCapture(400 USDC):
  → capturableAmount = 0 USDC
  → refundableAmount = 1000 USDC

Charge

AuthorizationとCaptureを1つのトランザクションで実行します。

solidity
function charge(
    PaymentInfo calldata paymentInfo,
    uint256 amount,
    address tokenCollector,
    bytes calldata collectorData,
    uint16 feeBps,
    address feeReceiver
) external

即時決済が適切なユースケース(デジタルコンテンツの購入など)では、2つのトランザクションを分ける必要がありません。 Chargeを使用することで、ガスコストを削減し、シンプルな決済フローを実現できます。

Void

オペレーターがAuthorizationをキャンセルし、Escrow資金をPayerに返還します。

solidity
function void(PaymentInfo calldata paymentInfo) external

VoidはOperatorのみが実行でき、いつでも呼び出し可能です。 在庫切れや注文キャンセルなど、Merchant側の理由で決済を中止する時に使用します。

Reclaim

Authorization有効期限後に、Payerが自分の資金を回収します。

solidity
function reclaim(PaymentInfo calldata paymentInfo) external

Reclaimは authorizationExpiry を過ぎた後にのみ、Payer自身が実行できます。 これは重要な安全メカニズムで、Operatorが非アクティブになったり、何らかの理由でCaptureもVoidも行われなかった場合に、Payerが資金を取り戻せることを保証します。

Refund

Capture済みの資金をPayerに返還します。

solidity
function refund(
    PaymentInfo calldata paymentInfo,
    uint256 amount,
    address tokenCollector,
    bytes calldata collectorData
) external

返金には別途、返金用の流動性を調達する必要があります。 これは OperatorRefundCollector などのRefund Collectorを通じて行われます。

返金可能期間

返金は refundExpiry までしか実行できません。 これにより、決済に最終的な確定性が生まれ、Merchantは一定期間後に資金が確定することを保証されます。

OperatorモデルとSecurity

Commerce Payments Protocolの特徴的な設計の1つが「Operatorモデル」です。

Operatorの役割

Operatorは決済フローを管理する主体です。 具体的には、以下の責務を持ちます。

  • オンチェーントランザクションの送信(ガス代の負担)
  • 決済の状態遷移のトリガー(authorize、capture、void、refund)
  • PayerやMerchantとブロックチェーンの間の技術的な橋渡し

Operatorになれるのは、決済プロセッサー(Shopifyなど)、独立したサービスプロバイダー、Merchant自身、あるいはカスタムビジネスロジックを実装したスマートコントラクトなど、誰でも可能です。

暗号学的制約

Operatorは強力な役割を持ちますが、プロトコルの暗号学的制約により、その能力は厳しく制限されています。

graph TB
    subgraph Can["Operatorができること"]
        C1[決済フローのトリガー]
        C2[手数料の設定(範囲内)]
        C3[決済の無効化]
    end

    subgraph Cannot["Operatorができないこと"]
        N1[資金の窃取]
        N2[決済条件の変更]
        N3[署名の再利用]
        N4[他Operatorの決済操作]
    end

    style Can fill:#ccffcc
    style Cannot fill:#ffcccc

Operatorの能力と制約

Payerが決済を承認する時、以下の情報すべてに暗号学的署名を行います。

  • 正確な金額と受取人
  • 手数料の範囲
  • 各種有効期限
  • 指定されたOperator

この署名により、Operatorが条件を変更したり、署名を別の決済に再利用することは不可能になります。

Operator妥協時のリスク

万が一Operatorが悪意を持った場合や、秘密鍵が漏洩した場合でも、被害は限定的です。

リスク 説明
検閲 決済のCaptureを拒否したり、不当にVoidすることが可能
手数料の最大化 maxFeeBps までの手数料を徴収可能
手数料受取人の変更 feeReceiver が未設定の場合、任意のアドレスを指定可能

重要なのは、Operatorは資金を盗むことができないという点です。 資金は常にPayerかReceiverのいずれかに移動し、それ以外の場所には送れません。

時間ベースの保護

プロトコルは3つの有効期限を使用して、決済が「スタック」状態になることを防ぎます。

preApprovalExpiry 署名の有効期限です。 この時刻を過ぎると、Payerの署名は無効になり、新たなAuthorizationはできなくなります。

authorizationExpiry Authorizationの有効期限です。 この時刻を過ぎると、OperatorはCaptureできなくなり、代わりにPayerがReclaimで資金を回収できるようになります。

refundExpiry 返金の有効期限です。 この時刻を過ぎると、返金はできなくなり、決済が最終確定します。

gantt
    title 決済の時間制約
    dateFormat X
    axisFormat %s

    section 期間
    署名有効期間 :active, 0, 100
    Authorization有効期間 :active, 0, 200
    返金可能期間 :active, 0, 300

    section イベント
    preApprovalExpiry :milestone, 100, 100
    authorizationExpiry :milestone, 200, 200
    refundExpiry :milestone, 300, 300

3つの有効期限の関係

Token Collectorによる承認方法の抽象化

ブロックチェーンには複数のトークン承認標準が存在し、それぞれに長所と短所があります。

標準 特徴 課題
ERC-3009 シングル署名UX、USDCなどが対応 対応トークンが限定的
Permit2 任意のERC-20に対応 初回セットアップが必要
ERC-20 Approval 全トークンで利用可能 別トランザクションが必要
Spend Permissions 最高のUX(Coinbase Smart Wallet) 対応ウォレットが限定的

Commerce Payments Protocolは、「Token Collector」という抽象化レイヤーを導入することで、この断片化問題を解決しています。

graph TB
    ACE[AuthCaptureEscrow]

    subgraph Collectors["Token Collectors"]
        ERC3009[ERC3009]
        Permit2[Permit2]
        PreApproval[PreApproval]
        SpendPerm[SpendPermission]
        OpRefund[OperatorRefund]
    end

    ACE --> ERC3009
    ACE --> Permit2
    ACE --> PreApproval
    ACE --> SpendPerm
    ACE --> OpRefund

    ERC3009 --> |USDC等| Payer1[Payer]
    Permit2 --> |任意のERC-20| Payer2[Payer]
    PreApproval --> |従来型承認| Payer3[Payer]
    SpendPerm --> |Coinbase SW| Payer4[Payer]
    OpRefund --> |返金用| Operator[Operator]

    style ACE fill:#4169E1,color:#fff

Token Collectorのアーキテクチャ

Token Collectorの仕組み

AuthCaptureEscrowは、トークンの収集方法を知りません。 代わりに、Token Collectorに対して「この金額をこのTokenStoreに移動してください」と依頼し、結果として残高が正しく増加したかを検証します。

solidity
abstract contract TokenCollector {
    enum CollectorType { Payment, Refund }

    function collectTokens(
        AuthCaptureEscrow.PaymentInfo calldata paymentInfo,
        address tokenStore,
        uint256 amount,
        bytes calldata collectorData
    ) external;

    function collectorType() external view virtual returns (CollectorType);
}

この設計により、以下のメリットが生まれます。

  • 新しい承認標準が登場しても、新しいCollectorを追加するだけでコアプロトコルの変更は不要
  • オペレーターはPayerのウォレットとトークンの組み合わせに最適なCollectorを選択できる
  • Payment CollectorとRefund Collectorを分離することで、残存する承認を返金に流用することを防止

実装されているCollector

プロトコルには以下のCollectorが実装されています。

ERC3009PaymentCollector USDCなどERC-3009対応トークン用です。 receiveWithAuthorization を使用したガスレス署名転送に対応しています。 ERC-6492によるスマートコントラクトウォレット署名もサポートしています。

Permit2PaymentCollector 任意のERC-20トークンに対応します。 Permit2による署名ベースの転送を使用しますが、ユーザーは事前にPermit2コントラクトへのApproveが必要です。

PreApprovalPaymentCollector 従来型のERC-20 Approvalフローに対応します。 Payerが事前にCollectorにApproveし、その後 transferFrom で転送します。

SpendPermissionPaymentCollector Coinbase Smart Wallet専用の最適化されたCollectorです。 サブスクリプションのような定期的な支払いにも対応可能です。

OperatorRefundCollector Operatorの資金から返金を行うCollectorです。 OperatorがCollectorにApproveし、返金時にOperatorの残高から資金を調達します。

手数料システム

Commerce Payments Protocolは、柔軟な手数料システムを実装しています。 手数料は「基準点(Basis Points, bps)」で指定され、10,000 bps = 100% となります。

手数料パラメータ

PaymentInfo 構造体には3つの手数料関連フィールドがあります。

フィールド 説明
minFeeBps 最小手数料率
maxFeeBps 最大手数料率
feeReceiver 手数料受取人(0なら動的に指定可能)

手数料の設定パターン

固定レート minFeeBps と maxFeeBps を同じ値に設定します。 例えば、両方を250(2.5%)に設定すると、Operatorは必ず2.5%の手数料を徴収します。

可変レート minFeeBps を100(1%)、maxFeeBps を500(5%)に設定すると、Operatorは1%〜5%の範囲で手数料を設定できます。 マーケットプレイスで取引量に応じた階層型手数料を実現する時などに有用です。

ゼロ手数料 両方を0に設定すると、手数料を徴収できません。

手数料受取人の設定

固定受取人 feeReceiver に特定のアドレスを設定すると、手数料は必ずそのアドレスに送られます。

動的受取人 feeReceiver を address(0) に設定すると、OperatorがCapture時に任意の受取人を指定できます。 これはUSDCのデニーリスト問題への対策としても有用で、指定した受取人がブロックされた場合に別のアドレスを指定できます。

デプロイ情報とセキュリティ監査

Commerce Payments ProtocolはBase MainnetとBase Sepoliaにデプロイされています。

コントラクト アドレス
AuthCaptureEscrow 0xBdEA0D1bcC5966192B070Fdf62aB4EF5b4420cff
ERC3009PaymentCollector 0x0E3dF9510de65469C4518D7843919c0b8C7A7757
Permit2PaymentCollector 0x992476B9Ee81d52a5BdA0622C333938D0Af0aB26
PreApprovalPaymentCollector 0x1b77ABd71FCD21fbe2398AE821Aa27D1E6B94bC6
SpendPermissionPaymentCollector 0x8d9F34934dc9619e5DC3Df27D0A40b4A744E7eAa
OperatorRefundCollector 0x934907bffd0901b6A21e398B9C53A4A38F02fa5d

セキュリティ監査

プロトコルはSpearbitとCoinbase Protocol Securityによる複数回の監査を受けています。

監査 日付
Coinbase Protocol Security audit 1 2025年3月19日
Coinbase Protocol Security audit 2 2025年3月26日
Spearbit audit 1 2025年4月1日
Coinbase Protocol Security audit 3 2025年4月15日
Spearbit audit 2 2025年4月22日

監査レポートはGitHubリポジトリの audits/ ディレクトリで公開されています。

最後に

今回は「Commerce Payments Protocol」についてまとめてきました。

このプロトコルは、従来の金融システムで確立されたAuth & Captureパターンをオンチェーンで実現することで、ブロックチェーン決済をコマーススケールに拡張します。 パーミッションレスで不変のプロトコル設計、暗号学的に制約されたオペレーターモデル、モジュラー式Token Collectorによる柔軟な承認方法のサポートにより、マーチャントとPayerの両方に安全で使いやすい決済インフラを提供しています。