DEV Community

Brand Design Ltd.
Brand Design Ltd.

Posted on Edited on AI-assisted

How We Keep an AI Agent's Payment Tied to an Accepted Offer

A supplier page can describe a product. It cannot approve a payment.

Suppose an agent is asked to buy a replacement server for €1,000. The supplier's page contains hidden text telling the agent to add a €500 consulting fee. If the agent submits a €1,500 checkout request, valid JSON is beside the point. The buyer never authorized the extra fee.

This distinction shaped the business-to-agent (B2A) commerce flow we built at Brand Design Ltd., a branding and B2A agency in Varna, Bulgaria. An agent needs room to find and compare offers. The merchant needs a dependable record of the offer, its acceptance, and the buyer's separate permission to spend.

On 22 September, six banks, including Bank of America and NatWest, published principles for trusted agentic commerce. The W3C and GS1 workshop earlier that month included sessions on agent identity, authorization and payment credentials. For a merchant, those concerns have to become checks at each purchase step.

Record the offer before accepting it

Our direct purchase path follows five steps:

catalog.read -> order.create -> order.accept -> payment.start -> order.read
Enter fullscreen mode Exit fullscreen mode
  1. catalog.read gives the agent the current service, price rules, currency and terms.
  2. order.create records a specific offer and an archived copy of the service page. A SHA-256 pageHash identifies those archived bytes.
  3. order.accept checks the offer version and terms hash. If either changed, the agent needs a new offer and acceptance.
  4. payment.start checks the concrete payable amount and currency against the accepted offer and the buyer's authorized payment capability, including its limit and expiry.
  5. order.read returns the resulting order and payment state.

The payment service makes these checks against recorded state. It does not use the model's explanation of a supplier page as evidence that the buyer approved a new price. The full path is described in our reference repository.

Domain control is not spending authority

We use a DNS TXT challenge to check control of a registered agent's declared domain. Current domain verification is required for autonomous shared-token payments by self-registered agents and for authenticated UCP access. It helps us reject an unauthenticated claim to represent that domain.

That check does not identify the buyer or grant the agent a budget. The right to pay comes from a separate buyer-authorized capability. Even an authenticated agent cannot turn a line of untrusted page text into a higher spending limit.

What the hash example actually shows

The repository includes a synthetic order manifest and a short Node.js verifier. It hashes a fixed set of offer fields and compares the result with a trusted hash supplied separately from the manifest. If an attacker changes both the amount and the hash inside the same untrusted JSON file, the separate commitment still fails.

The sample quote_hash is an illustration, not a production wire field or a digital signature. The published implementation uses pageHash, offer versions and terms hashes. HTTP request authentication and signed route evidence are separate controls. None of these hashes, on its own, proves that the buyer consented to pay.

Where ACP, UCP and AP2 fit

Our merchant system has deployed adapters for ACP and two dated versions of UCP. They use the same catalogue and order records as the direct route. Our UCP business profile is published on the merchant's own domain at branddesign.ltd/.well-known/ucp. The route used for a transaction must be recorded and checked; merely having an adapter does not prove that a purchase ran through a platform's native checkout.

Since 9 October, our UCP checkout also accepts AP2 checkout mandates. We sign the checkout, and before any charge we verify the mandate: the issuer's signature, the holder's signature, and whether the amounts and lines match the current checkout. We do not accept AP2 payment mandates or open mandates.

There are platform limits too. OpenAI says building with ACP is open, while Instant Checkout in ChatGPT is currently available to approved partners. The UCP checkout specification calls for a trusted user interface to finalize checkout unless the AP2 Mandates extension is supported. Google Pay is connected to our UCP checkout in test mode until Google approves the integration. We do not treat protocol support as permission to skip either platform onboarding or the buyer's authorization step.

Read-only access for agents

Our MCP server at branddesign.ltd/mcp is listed in the official MCP Registry as ltd.branddesign/commerce. In browsers that support WebMCP, every page of branddesign.ltd registers seven read-only tools for services, prices, the catalogue, evidence and capabilities, and a command-line client gives the same reads. None of these read paths can create an order or start a payment.

What we have proved so far

On 21 September, an external participant gave its agent a neutral task to buy a test service. The agent was not given our name, domain or checkout link. It found 101Ts3t, paid EUR 0.99 with its authorized wallet and completed the order. The public verifier returned PASS for discovery, evidence integrity, transaction evidence and the completed agentic-commerce run. The proof package is in the version 1.3 Zenodo record.

That is evidence for one real purchase. It is not a prompt-injection stress test, a reliability study or a live ACP/UCP checkout test. The version 1.4 record documents the added protocol routes, DNS checks, signed route evidence and synthetic regression checks. It does not report a new route-qualified live purchase. AP2 mandates, the UCP profile on branddesign.ltd and WebMCP on every page came after version 1.4.

Offer integrity also does not prevent a duplicate charge after an ambiguous payment response. That failure mode needs idempotency and payment-status reconciliation. Our published hash example and the version 1.3 PASS do not establish those retry guarantees.

Verify it yourself

Every item above has a public address you can open without an account. Our implementation record lists what runs today, from the direct API to UCP with AP2 mandates, MCP and WebMCP, with a link to check each one, the 21 September test and the DOI records.

The agent can browse and choose. At payment.start, the service still needs an accepted amount, a currency and the buyer's authority for that payment. If those do not match, the charge should not proceed.

Disclosure: This article was prepared with AI assistance and checked against the linked source records.

Top comments (4)

Collapse
 
axiru profile image
Axiru •

Yes. Matching amount and currency to the accepted offer at payment.start is a strong point.

You already called out the hole after an ambiguous payment response.

We hold the next start until status is paid or failed, and we write a receipt either way.

Happy to DM you a free shadow review of your last 90 days of outflows if useful.

Collapse
 
branddesignltd profile image
Brand Design Ltd. •

Thanks for the offer. We’ve also built internal automation that tracks orders and payments, maintains the related financial records, and generates reports we can review ourselves.

The amount-and-currency check is one boundary; payment-state controls and restrictions on repeated starts are another.

Duplicate-payment prevention is enforced against the orders in our system, so a new API request does not bypass that control.

We’re covered for an internal review at this stage, but I appreciate the suggestion.

Collapse
 
axiru profile image
Axiru •

Yes. You already cover amount, currency, payment-state, and repeat-start controls in-house.

Duplicate-payment prevention tied to orders is the hard part most teams skip.

Glad you are covered for internal review right now.

If that ever gets noisy across rails, we are happy to compare notes in DM.

Collapse
 
axiru profile image
Comment marked as low quality/non-constructive by the community. View Code of Conduct
Axiru •

Thanks again for laying out how you split those controls. Tying duplicate checks to your own orders is the part most teams never get to.

One thing we added on our side: each approval covers one exact amount and payee, and it can only be used once. Every call gets allow, hold, or deny before the rail, with a receipt.

If it would help to see that next to your setup, our founder can walk you through it in a 10 minute live demo. DM us or reply and we will set a time.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.