Skip to content

Add Crossmint Agent Checkouts as an optional way to buy - #34

Open
aadopii wants to merge 3 commits into
browserbase:mainfrom
aadopii:feat/crossmint-agent-checkouts
Open

aadopii wants to merge 3 commits into
browserbase:mainfrom
aadopii:feat/crossmint-agent-checkouts

Conversation

@aadopii

@aadopii aadopii commented Oct 28, 2026 •

Copy link
Copy Markdown

Adds Crossmint Agent Checkouts to Bell as an optional way to buy. One call takes a product URL, a sentence and a spending cap. Crossmint picks the fastest path for that store (merchant API, agent protocol, or its own checkout browser) and returns a run with status snapshots. Bell answers the human questions and gets a receipt. It works at any store, with the user's own card, worldwide, and needs no browser plan because the checkout runs on Crossmint's side.

Nothing in the Link integration, the browser runtime or the hooks changes. Unset the variables and both tools answer not_configured.

Two tools

crossmint_checkout starts a run, follows it, answers forms, declines, cancels. While the run works it returns at each new stage, a fixed phrase such as "Filling in the delivery details", so Bell keeps the person posted instead of sitting on "Working" for minutes. crossmint_pay pays.

Payment runs through Eve's approval gate, approval: always(). The person reads "Pay 16.90 EUR at Zara (payments.zara.com) with your saved card?". After the yes, the server fetches the live request from Crossmint and compares amount, currency, merchant, store name and the cap the run started with. Any difference pays nothing and returns the reason. Payments need an authenticated sender, and only the person who started the run can approve.

Setup page

Money comes from an order intent: a saved card, an amount, a merchant, an expiry. Crossmint creates order intents only for a signed-in user (a client key with the user's JWT), so Bell signs a ten-minute operator token and serves its public key at /api/crossmint/jwks.

/crossmint/setup, behind Bell's operator credentials, does the rest in Crossmint's styling. It checks the connection live and shows the two values to paste in the Crossmint console when Crossmint rejects Bell's token. It saves delivery details as a buyer profile, so checkouts stop asking for addresses. It lists budgets with what is left and their card rails, and approves new ones in Crossmint's card form after a confirmation. It registers the card with the operator's email first, which Crossmint requires and its card component does not send. A card without a Visa or Mastercard agent rail still pays through Crossmint's encrypted card.

Policy and boundary

CROSSMINT_ALLOWED_MERCHANTS and CROSSMINT_MAX_COST are checked before a run exists, because Crossmint bills a run that ends blocked.

Key, buyer id, profile and order intent id never enter tool arguments or output. Snapshots are projected to an allowlist before Eve stores them, and Crossmint's own progress text is never passed on. protected fields cannot be answered by the model. The setup page is operator-only: the proxy asks for credentials, refuses cross-origin writes and framing, and every action checks again.

Tested live

On a Zara Spain order, Bell reached the payment step at 1:51 and the order was placed at 3:03, paid with a non-US Visa. The same request on Bell as shipped did not reach payment. That run used this flow before the progress updates, the approval wording and the setup page in this revision; those are covered by the tests below and not yet run live.

Tests

394 pass on Node 24 with typecheck, lint and the full build. The new tests never call Crossmint. They pin request bodies to the documented API, prove no provider body leaks, run the payment cross-check through every refusal reason, cover progress stages and polling, and cover the setup page's access rules, token, budget approval and card registration.

🤖 Generated with Claude Code

Two tools let Bell buy through Crossmint Agent Checkouts: crossmint_checkout
starts a run from a product URL, a plain-language task and a spending cap,
answers form questions and declines or cancels; crossmint_pay settles the
payment request with the operator's order intent.

Payment goes through Eve's tool approval rather than a confirmation in chat.
crossmint_pay is declared with approval: always(), the prompt shows the
amount, currency and merchant the model put in the call, and after approval
the server compares those with the live Crossmint request and with the cap
the run was created under before paying. Payments need an authenticated
sender, and only the requesting principal can approve.

Operator policy is checked before a run is created, so a refused request is
never billed: an optional per-checkout ceiling (CROSSMINT_MAX_COST) and an
optional merchant allowlist (CROSSMINT_ALLOWED_MERCHANTS). A saved buyer
profile (CROSSMINT_BUYER_PROFILE_ID) lets checkouts skip shipping and
contact questions.

The API key, buyer id, buyer profile and order intent id stay on the server.
Each snapshot is projected to a metadata allowlist before Eve can persist it,
and a conversation can only act on runs it started in this process. Link,
the browser runtime and the hooks are unchanged; with the Crossmint variables
unset both tools return not_configured.
One tool starts a checkout at any store with a product URL, a sentence and a
spending cap, then answers the forms. A second tool pays after the person
approves the exact total in Eve, and the server checks that total, the
currency, the merchant and the cap against Crossmint's live request before
paying. The card number never reaches Bell or the model. Nothing changes
unless the Crossmint variables are set.
Agent Checkouts could not pay on its own: the order intent it needs takes a client key with the
user's JWT, and only the card branch could sign one. Move the operator token, the JWKS route, the
operator-only proxy, and the Crossmint React SDK here, and add /crossmint/setup, an operator page in
Crossmint's styling. It checks the connection live and shows the JWKS endpoint and issuer to paste
when Crossmint rejects Bell's token, saves delivery details as a buyer profile, lists budgets with
what is left and their card rails, cancels them, and approves new ones in Crossmint's card form
after an explicit confirmation, with one idempotency key per draft. It registers the card with the
operator's email before creating the order intent, because Crossmint refuses an empty registration
and its card component sends one. A card without a Visa or Mastercard agent rail still pays through
Crossmint's encrypted card, and the page says so. The proxy forbids framing, and every action checks
the operator's credentials and origin again.

While a run works, crossmint_checkout returns at each new stage, a fixed phrase chosen from
Crossmint's activity kinds, so Bell tells the person what is happening instead of showing "Working"
for minutes. Crossmint's own progress text is never passed on, since it can name the buyer and is
written while reading merchant pages. Progress reads stay within the wait and stop on cancel.

The approval names the payee in plain words, "Pay 16.90 EUR at Zara (payments.zara.com) with your
saved card?", and the server cross-check now also refuses a store name that is not Crossmint's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​crossmint/​client-sdk-react-ui@​4.12.18810088100100

View full report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant