Blog AI / Agent Payments

Qu'est-ce que l'Agent Payments Protocol (AP2) ? Paiement auto des agents IA, API JSON et commerce intelligent 2026

When you wire an AI shopping or procurement agent, the docs mention A2A, MCP, and a payments protocol called AP2. The first two cover conversation and tools; AP2 covers what authorizes the agent to spend.

Agent Payments Protocol (AP2) is an open protocol Google announced in September 2025 with 60+ payments and technology companies. It adds verifiable authorization, authentic intent, and after-the-fact accountability when an agent pays for a user. The 2026 implementation draft (v0.2) splits a transaction into two verifiable credentials—Checkout Mandate and Payment Mandate—with JSON payloads, typically wrapped as SD-JWT.

This article covers:

Keep this in mind: AP2 is not another checkout UI, and it is not a Stripe replacement. It is the trust layer for agent payments: a cryptographically signed Mandate proves the user allowed this transaction. Below we walk through why it is needed → roles → Mandate JSON → two modes → schema validation → how it stacks with A2A/MCP.

What AP2 is

Google Cloud announcement defines AP2 as a payment-agnostic open protocol: users, merchants, and payment institutions share one language for agent-initiated transactions. It can serve as a payments extension of Agent2Agent (A2A) and Model Context Protocol (MCP), without replacing either.

The 2026 public spec is AP2 Specification v0.2; reference implementations live in google-agentic-commerce/AP2.

The core object is not an “order form.” It is a Mandate: a tamper-evident, verifiable digital contract proving the user gave the agent permission for a purchase or a class of constrained purchases. The spec wraps Mandates in SD-JWT verifiable digital credentials—not a bare JSON POST.

The launch narrative used three credentials—Intent, Cart, and Payment Mandates. The current v0.2 draft converges on two, and distinguishes open vs closed:

Concept v0.2 equivalent What it proves
Intent / open constraints Open Checkout / Open Payment Mandate User-preapproved budget, merchant scope, and expiry
Cart / closed checkout Closed Checkout Mandate This cart’s items, prices, and merchant-signed Checkout JWT
Payment Closed Payment Mandate Amount, payee, and instrument for this Checkout

Don’t get stuck on three vocabularies in older posts: product language is intent → cart → pay; the implementation is open constraints plus closed Checkout/Payment.

Why agent payments need a new protocol

Classic ecommerce assumes a human clicks Buy on a trusted page. Once an agent can place the order, that assumption breaks. AP2 answers three questions:

A prompt like “buy me tickets” has no cryptographic boundary. The model can change quantity, swap merchants, or resubmit. AP2 binds user-signed constraints to the agent-assembled closed order. Verification must run in deterministic code—not another LLM judgment.

The rails stay cards, bank transfers, or stablecoins. AP2 does not clear funds; it only answers whether this agent transaction was authorized. For crypto, the A2A x402 extension is built with Coinbase, the Ethereum Foundation, MetaMask, and others.

Five roles: who signs, who verifies, who gets paid

The spec defines five roles. One entity may play several, but verification rules stack per role:

Role English What it does May be agentic?
Shopping Agent Shopping Agent Discover products, assemble checkout, execute the purchase Expected to be agentic
Credential Provider Credential Provider Issues payment credentials and confirms the agent may use them Optional
Merchant Merchant Provides and completes Checkout; checks inventory and price Optional
Merchant Payment Processor Merchant Payment Processor Processes payment; confirms the credential is bound to this Checkout Optional
Trusted Surface Trusted Surface Shows intent to the user and collects the signature Must not be agentic

Hard rule: the Trusted Surface must be a non-agentic, deterministic UI. User signatures must not be “paraphrased” by another LLM. The Shopping Agent may be non-deterministic, so its output is untrusted by default—Mandate signatures and hashes pin it down.

Mandates: Checkout and Payment

v0.2 defines only two Mandate types. After the shopping agent assembles the task, it obtains signatures via the Trusted Surface and hands them to the merchant and the payment path.

Checkout Mandate

Shown to the merchant: the Shopping Agent is authorized to buy this assembled Checkout. The merchant must first issue a merchant-signed Checkout JWT. The closed Checkout Mandate binds to that JWT’s cryptographic hash (checkout_hash) so the agent cannot change the price and reuse an old authorization.

After accept or reject, the merchant must return a Checkout Receipt.

Payment Mandate

Shown to the credential provider, network, and processor: the Shopping Agent is authorized to pay for this Checkout. Binding is the cryptographic hash of the Checkout JWT. The spec requires a non-deterministic signature such as ECDSA—not Ed25519—to avoid rainbow-table attacks.

After accept or reject, the processor must return a signed Payment Receipt to the agent, the credential provider, and optionally the network. In a dispute, Checkout and Payment Mandates plus Receipts are the evidence pack.

Two modes: human present vs autonomous

Verifiers always receive closed Checkout and Payment Mandates. The difference is who signed the closed credentials and how open constraints are checked.

  1. 1
    Human Present (direct)

    The user sees the closed cart on a trusted surface and signs it. Verifiers check a user credential or a user signature from a trusted Agent Provider list.

  2. 2
    Human Not Present (autonomous)

    The user first signs an open Mandate: amount range, allowed merchants, expiry, plus the agent public key (cnf). When conditions match, the agent signs the closed Mandate with its key. Verifiers must confirm the closed Mandate satisfies the open constraints.

Autonomous mode has two hard rules: the same open Mandate must not authorize another closed checkout until the previous one is rejected; when presenting an open Mandate, disclose only the fields needed to evaluate the closed one—protect user privacy.

Typical cases: buying tickets the instant they go on sale, restocking a missing SKU, booking a flight and hotel inside a trip budget. Constraints are signed first; the closed Mandate is filled in at execution—not an unlimited agent credit card.

JSON APIs: fields, vct, and schema

Day to day, AP2 is JSON. The official SDK keeps protocol objects in code/sdk/python/ap2/schemas/ and generates Pydantic models. Debug them as ordinary JSON objects first, then worry about the SD-JWT wrapper.

Each Mandate declares type and version with vct, for example mandate.payment.1 and mandate.checkout.open.1. Implementations must match the exact string, including the numeric suffix; an incompatible revision becomes .2.

Here is the business payload shape of a closed Payment Mandate from the spec (units depend on the implementation; the sample uses minor currency units):

Closed Payment Mandate payload (illustrative, AP2 v0.2)
{
  "vct": "mandate.payment.1",
  "transaction_id": "NivWhuqfzcvZNapvIEJ2-3tsdQLkiuIcye2g46WVgX8",
  "payee": {
    "id": "merchant_1",
    "name": "Demo Merchant",
    "website": "https://demo-merchant.example"
  },
  "payment_amount": {
    "amount": 19900,
    "currency": "USD"
  },
  "payment_instrument": {
    "id": "stub",
    "type": "card",
    "description": "Card ••••4242"
  }
}

An open Payment Mandate does not freeze one charge; it writes constraints:

Open Payment Mandate constraints (illustrative)
{
  "vct": "mandate.payment.open.1",
  "cnf": {
    "jwk": {
      "kty": "EC",
      "crv": "P-256",
      "x": "QpSyxPQHy38xckypDr54gZ3T42zj9iLtV4koyb5U27c",
      "y": "37HLd7JJinxjJIn8J7HijssoeclbfhdW-gUL7feI9lw"
    }
  },
  "exp": 1781203600,
  "constraints": [
    {
      "type": "mandate.payment.allowed_payees",
      "allowed": [{ "id": "merchant_1" }]
    },
    {
      "type": "mandate.payment.amount_range",
      "currency": "USD",
      "min": 10000,
      "max": 40000
    }
  ]
}

On the wire they are not bare JSON—they are SD-JWTs: selective disclosure plus key binding. When debugging at the business layer, reconstruct the JSON above from disclosures, then validate fields and constraints.

A JSON Schema should at least lock vct, transaction_id, payee, payment_amount, and the instrument type, so the model cannot drop fields or emit amounts as strings:

Payment Mandate business-field JSON Schema (debug subset)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://jsonnote.com/schemas/ap2-payment-mandate-debug.json",
  "title": "AP2 Closed Payment Mandate (debug)",
  "type": "object",
  "additionalProperties": false,
  "required": ["vct", "transaction_id", "payee", "payment_amount", "payment_instrument"],
  "properties": {
    "vct": {
      "type": "string",
      "const": "mandate.payment.1"
    },
    "transaction_id": {
      "type": "string",
      "minLength": 16
    },
    "payee": {
      "type": "object",
      "required": ["id", "name"],
      "properties": {
        "id": { "type": "string", "minLength": 1 },
        "name": { "type": "string", "minLength": 1 },
        "website": { "type": "string", "format": "uri" }
      }
    },
    "payment_amount": {
      "type": "object",
      "required": ["amount", "currency"],
      "properties": {
        "amount": { "type": "integer", "minimum": 1 },
        "currency": { "type": "string", "pattern": "^[A-Z]{3}$" }
      }
    },
    "payment_instrument": {
      "type": "object",
      "required": ["id", "type"],
      "properties": {
        "id": { "type": "string" },
        "type": {
          "type": "string",
          "enum": ["card", "bank_transfer", "stablecoin"]
        },
        "description": { "type": "string" }
      }
    }
  }
}

In production, follow the canonical schemas in the official repo. The subset above is for local JSONNote checks—it does not replace full spec fields or selective-disclosure annotations.

Worked example: validate a Payment Mandate

Agent-side failures are often broken JSON, not failed signatures:

The business layer should parse, then schema-validate, and only then verify the SD-JWT and evaluate constraints:

Validate JSON structure before signatures
import json
from jsonschema import validate, ValidationError

PAYMENT_MANDATE_SCHEMA = {
    "type": "object",
    "required": ["vct", "transaction_id", "payee", "payment_amount"],
    "properties": {
        "vct": {"type": "string", "const": "mandate.payment.1"},
        "transaction_id": {"type": "string", "minLength": 16},
        "payee": {
            "type": "object",
            "required": ["id", "name"],
            "properties": {
                "id": {"type": "string"},
                "name": {"type": "string"},
            },
        },
        "payment_amount": {
            "type": "object",
            "required": ["amount", "currency"],
            "properties": {
                "amount": {"type": "integer", "minimum": 1},
                "currency": {"type": "string", "pattern": "^[A-Z]{3}$"},
            },
        },
    },
    "additionalProperties": True,
}

def parse_payment_mandate(raw: str) -> dict:
    try:
        data = json.loads(raw)
    except json.JSONDecodeError as e:
        raise ValueError(f"invalid mandate JSON: {e}") from e
    try:
        validate(instance=data, schema=PAYMENT_MANDATE_SCHEMA)
    except ValidationError as e:
        raise ValueError(f"mandate schema failed: {e.message}") from e
    return data

# 用法:先过结构,再拿 transaction_id 去对 Checkout JWT 的哈希
mandate = parse_payment_mandate(disclosed_payload)
assert mandate["transaction_id"] == checkout_hash

This is the same discipline as validating tool_calls[].function.arguments: first valid JSON, then contract conformance, then side effects. See AI Agents and JSON Schema.

The smart-commerce path looks like this:

  1. 1
    User signs constraints or a closed Mandate in person

    The Trusted Surface issues the Mandate. The agent must not “backfill” user intent.

  2. 2
    Merchant issues a Checkout JWT

    Price, inventory, and discounts are whatever the merchant signed. The agent cannot edit them and pretend it is the same checkout.

  3. 3
    Closed Mandate binds checkout_hash

    Payment Mandate transaction_id must equal that hash.

  4. 4
    Parties verify and return Receipts

    Failures return a Receipt JWT with an error. Only success proceeds to capture or a push of funds.

AP2 vs traditional payments / A2A / MCP

Layer What it solves Typical objects Relation to AP2
Traditional payment APIs Charge, refund, tokenized cards PaymentIntent, capture AP2 does not replace clearing; a Payment Mandate proves you may call those APIs
A2A Agent-to-agent interoperability Tasks, messages, capability discovery AP2 can extend A2A for payments; x402 covers crypto
MCP Agents calling external tools inputSchema / outputSchema The tool contract describes parameters; paying still needs an AP2 Mandate
UCP and other commerce protocols Catalog, price changes, checkout session Checkout object AP2 is explicitly compatible; it does not browse products
JSON Schema Structural contract type / required / enum Mandates and tool parameters both use it; structure first, then signatures

In one line: MCP lets agents use tools, A2A lets agents collaborate, AP2 lets agents pay inside allowed bounds. Together they are the 2026 smart-commerce stack. How to write MCP tool contracts: AI Agents and JSON Schema; how models emit stable JSON: 2026 Structured Output.

Debug AP2 JSON with JSONNote

The slow part of AP2 is rarely cryptography—it is reconciling payloads. Mandates, Checkout JWT claims, Receipts, and constraint arrays are all JSON. JSONNote runs in the browser; raw transaction data never uploads:

  1. 1
    Check the structure first

    Paste the reconstructed Mandate into JSON Formatter to catch missing commas, trailing commas, or a truncated JWT.

  2. 2
    Pin fields with a schema

    Put the debug schema and instance into JSON Schema to see missing fields, wrong types, and enum violations immediately.

  3. 3
    Compare two transactions

    Open constraints vs a closed Mandate, or two agent assemblies—use JSON Diff to see which fields changed.

  4. 4
    Share a local debug sample

    Never put real card numbers or private keys in a link. Use Hash sharing for redacted sample JSON only. Data stays in the URL fragment and is not uploaded.

FAQ

How does AP2 relate to A2A and MCP?

A2A covers how agents talk to each other; MCP covers how agents call external tools. AP2 only answers who authorized this payment, on what grounds, and how to prove it later. It can extend A2A / MCP; it does not replace them.

Does AP2 only support credit cards?

No. AP2 is payment-instrument agnostic: cards, bank transfers, and stablecoins all work, distinguished by Payment Instrument type. Crypto flows also have the A2A x402 extension.

Does autonomous pay mean the agent can spend freely?

No. In autonomous mode the user first signs an open Mandate on a trusted surface—amount cap, merchant scope, expiry, and the agent public key. The agent may only sign closed Mandates that satisfy those constraints; verifiers must check them.

Why still validate JSON Schema when building AP2?

On the wire a Mandate is an SD-JWT; at the business layer you still see a JSON object. Wrong field names, amount units, or vct versions fail before signature checks even start. Block structural errors with JSON Schema, then do crypto verification.

Do Intent / Cart Mandates from the announcement still count?

The product story is still intent → cart → pay. Implement against the current Checkout / Payment Mandates (open and closed) and official schemas. Do not invent a third set of field names.

Résumé

If agents are going to pay automatically in 2026, what is missing is not another checkout button—it is verifiable authorization.

AP2 pins user intent to a signed JSON contract: Checkout pins what is bought, Payment pins how much and to whom.

Human present: sign the closed Mandate. Human not present: sign constraints first, then let the agent fill the closed Mandate. Validate structure with JSON Schema, then verify the SD-JWT. Debug payloads in JSONNote locally—keep keys and real payment credentials on your machine.

← Retour au blog