Skip to content

[Security Hardening Suggestion]AP2 Human-Present: CartMandate ↔ PaymentMandate Binding and Verification Gap #211

@zesttec

Description

@zesttec

AP2 Human-Present: CartMandate ↔ PaymentMandate Binding and Verification Gap

Description

Hi team, we find a binding and verification gap between CartMandate and PaymentMandate in the AP2 human-present flow.

In the current design, the merchant commits to the cart, the Shopping Agent generates the PaymentMandate, the user approves the {cart, payment} pair, and the main payment execution path primarily consumes the PaymentMandate. If the {cart, payment} pair is already inconsistent at the time of final confirmation (for example, the amount differs) and the user does not notice, the sample payment path can continue based on the PaymentMandate, because the current public spec and sample do not clearly require an executing party to fetch the referenced cart, compare the relevant fields, and reject on mismatch.1234

For example, the merchant-authorized cart says “red jacket, size M, total USD 120,” but a buggy or compromised Shopping-Agent-side component constructs a PaymentMandate whose payment_details_total is USD 160. If the user misses that discrepancy on the trusted surface, the current sample path can still proceed.

The current system lacks a rule-enforced cart↔payment consistency gate. A more robust system should not rely only on the user visually spotting discrepancies on the trusted surface. Instead, before releasing tokens, initiating payment, and generating a receipt, the executing parties should automatically verify that the current PaymentMandate is a valid payment projection of the merchant-authorized cart. The public specification says that PaymentMandate is “bound to Cart/Intent mandate” and that the “payment subset of the Cart Mandate must be conveyed,” but it does not define sufficiently clear rules for field consistency, interoperability, verifiability, and reject-on-mismatch behavior.

At the code level, even if the placeholder comments (for example, hashes and signatures) were implemented, the current object model and workflow would still be insufficient to provide strong binding, strong verification, and a complete consent / audit / dispute closure. 156 A payment could still be executed for an amount that is inconsistent with the cart.

As a result, in the human-present flow, there is no strong consistency verification chain jointly enforced by both the specification and the code across the merchant-authorized cart, the user-confirmed cart/payment pair, and the executed payment. 15

Design Intent Already Expressed by the Protocol

Our understanding: AP2 is not trying to make the cart and the payment two unrelated objects. Instead, it treats them as two views of the same checkout transaction. 1

  • The Cart Mandate is the merchant-side transaction object used for fulfillment; the specification requires the merchant to commit to the cart and, in the human-present flow, to have the user confirm it. 1
  • The PaymentMandate is generated by the Shopping Agent and is described as “bound to Cart/Intent mandate but containing separate information”. Its purpose is to provide additional visibility into the agentic transaction to the payments ecosystem. 1
  • In the human-present flow, the trusted device surface presents CartMandate + PaymentMandate to the user, after which the user confirms and an attestation is created. 1

Protocol-Level Gaps

1. The specification says they are “bound,” but does not define what exactly is bound

The specification states that PaymentMandate is "bound to the Cart/Intent mandate", and it also says that the “payment subset of the Cart Mandate must be conveyed.” However, it does not turn the following into sufficiently clear normative rules: 1

  • what the minimum field set of the payment subset is;
  • which fields between CartMandate and PaymentMandate must match;
  • which differences are allowed and which differences must trigger renewed confirmation;
  • who must reject the transaction when an inconsistency is found.

2. The protocol does not normatively require strong binding validation

The public specification does not clearly state that the Credential Provider, Merchant, and Merchant Payment Processor must recompute and verify the cart↔payment binding before credential release or payment execution, and that any mismatch must be rejected. 1

This weakens the certainty with which the protocol’s goals can be achieved.

AP2 aims to support trust, audit trail, and dispute handling through a cryptographic chain of evidence. However, without clearly defined binding fields, verifier responsibilities, and reject rules, those goals depend more on individual implementations than on guarantees provided by the protocol itself. 1

Code-Level Gaps

1. PaymentMandateContents lacks an explicit cart reference

In the current data model, PaymentMandateContents includes payment_mandate_id, payment_details_id, payment_details_total, payment_response, merchant_agent, and timestamp, but it does not include explicit cart_mandate_id / cart_mandate_hash fields. Although the description of user_authorization says that it signs the hashes of CartMandate and PaymentMandateContents, the binding is primarily embedded in authorization semantics rather than exposed as a first-class field. 5

2. PaymentMandateContents still does not expose the cart reference explicitly

The currently exposed fields of PaymentMandateContents are only:

  • payment_mandate_id
  • payment_details_id
  • payment_details_total
  • payment_response
  • merchant_agent
  • timestamp

There is no explicit cart_mandate_id / cart_mandate_hash field. 5

This means that even if the signature semantics in user_authorization are intended to cover the hashes of CartMandate and PaymentMandateContents, the cart↔payment relationship still remains mostly hidden inside the internal authorization semantics, rather than being surfaced as an explicit, stable, interoperable field-level interface. 5

3. Performing only “signature verification” does not imply “strong binding”

validate_payment_mandate_signature(payment_mandate) currently accepts only a payment_mandate. Even if the commented “full validation logic” were implemented, this function could at most:

  • verify that user_authorization is cryptographically valid;
  • verify that it covers some PaymentMandateContents hash;
  • if the authorization encodes a cart hash, verify that it covers some cart hash. 65

However, because the function signature does not include an explicit cart input, the data model does not expose an explicit cart reference, and the protocol still does not clearly define the equality set, this still would not be enough to:

  • verify that the executing path has retrieved the exact merchant-authorized cart;
  • verify that the cart and payment satisfy a clearly defined set of protocol-level equality rules;
  • verify that the downstream payment object being executed is the same one the user saw at confirmation time. 561

4. The human-present sample workflow lacks a consistency gate

In the human-present sample workflow:

  • the Shopping Agent creates and passes along the PaymentMandate; 7
  • the Merchant continues forwarding the PaymentMandate; 2
  • the MPP continues processing payment based on the payment data inside the PaymentMandate; 4
  • on the CP side, the token is primarily bound to payment_mandate_id. 3

Even if signature verification were added to these steps, without an additional rule requiring them to retrieve the same cart, recompute according to the same field set, compare, and reject on mismatch before execution, the workflow could still remain at the level of “a signed pair of objects” rather than “a strongly enforced guarantee that this pair of objects is fully consistent with what is ultimately executed.” 7243

Impact of the code-level gaps

Therefore, the code-level gaps mean:

  • the cart↔payment binding is not exposed as an explicit object relationship;
  • this binding is not elevated into a mandatory gate enforced by all executing parties;
  • the user-confirmed pair, the merchant-committed cart, and the final executed payment are not forcibly closed into one verification chain within the workflow. 51

This weakens consent integrity, audit trail quality, and dispute handling, because in a dispute there may be multiple signed objects, but not necessarily one protocol-defined, consistently enforced comparison path across them. 1

Recommendations

1. Protocol recommendations

The protocol revisions should focus on object relationships and verifier obligations:

  1. In §4.1.3 Payment Mandate, explicitly require:

    • PaymentMandateContents MUST include cart_mandate_id and cart_mandate_hash;
    • the object over which cart_mandate_hash is computed, and the canonicalization method;
    • the minimum field set of the payment subset;
    • the equality set for cart↔payment consistency. 1
  2. In §5.1 Human Present / Payment Execution, explicitly require:

    • the Credential Provider, Merchant, and Merchant Payment Processor MUST verify the CartMandate referenced by the PaymentMandate before credential release or payment execution;
    • any mismatch MUST be rejected;
    • any change to a chargeable field MUST trigger a new cart and fresh user confirmation. 1

Suggested normative text:

For Human-Present transactions, a PaymentMandate MUST reference exactly one merchant-authorized CartMandate approved by the user. PaymentMandateContents MUST include cart_mandate_id and cart_mandate_hash. Credential Provider, Merchant, and Merchant Payment Processor MUST retrieve, recompute, and verify the referenced CartMandate and the PaymentMandateContents before credential release or payment execution. Any mismatch MUST cause the transaction to be rejected.

2. Code recommendations

Code improvements should focus on the object model and workflow gates:

  1. Add cart_mandate_id / cart_mandate_hash to PaymentMandateContents; 5
  2. Make validation logic explicitly receive or retrieve the corresponding CartMandate, rather than only receiving payment_mandate; 65
  3. Add mandatory verification gates in the CP / Merchant / MPP execution paths;
  4. Make receipt generation, credential release, and payment execution depend on a successful cart↔payment consistency check, rather than depending only on payment_mandate_id or the mere presence of authorization. 324

Summary

Our view is that both the protocol and the code comments already express an intent to bind cart and payment. However, even if the commented logic were implemented, the current object model and workflow still would not automatically produce protocol-grade strong binding, strong verification, and a complete consent / audit / dispute closure.

We therefore recommend formally defining the cart↔payment object relationship, field consistency requirements, verifier obligations, and reject-on-mismatch rules in the protocol, and enforcing them in the workflow. 15

Please let us know how you think about it. Thank you for the work on AP2.


References

Footnotes

  1. AP2 Specification, https://ap2-protocol.org/specification/ 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

  2. samples/python/src/roles/merchant_agent/tools.py, https://raw.githubusercontent.com/google-agentic-commerce/AP2/main/samples/python/src/roles/merchant_agent/tools.py 2 3 4

  3. samples/python/src/roles/credentials_provider_agent/tools.py, https://raw.githubusercontent.com/google-agentic-commerce/AP2/main/samples/python/src/roles/credentials_provider_agent/tools.py 2 3 4

  4. samples/python/src/roles/merchant_payment_processor_agent/tools.py, https://raw.githubusercontent.com/google-agentic-commerce/AP2/main/samples/python/src/roles/merchant_payment_processor_agent/tools.py 2 3 4

  5. src/ap2/types/mandate.py, https://raw.githubusercontent.com/google-agentic-commerce/AP2/main/src/ap2/types/mandate.py 2 3 4 5 6 7 8 9 10 11

  6. samples/python/src/common/validation.py, https://raw.githubusercontent.com/google-agentic-commerce/AP2/main/samples/python/src/common/validation.py 2 3 4

  7. samples/python/src/roles/shopping_agent/tools.py, https://raw.githubusercontent.com/google-agentic-commerce/AP2/main/samples/python/src/roles/shopping_agent/tools.py 2

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions