Facet Purchase Flow
What this is
This page explains how a software agent — an autonomous buyer, or any app acting on a shopper's behalf — can complete a full purchase on a facet.llc-powered store: browse the catalog, choose a product, pay, and take delivery, with the money held safely in escrow until the order is fulfilled.
Payment settles through Boson Protocol escrow using the x402B scheme: the buyer authorizes a stablecoin (USDC) payment that is locked in an on-chain escrow contract. The seller is paid once the buyer's voucher is redeemed (on fulfillment) or the dispute window expires — until then the buyer is protected, and a dispute can be raised and settled by a registered dispute resolver. The store never takes custody of the funds, and the on-chain transactions are gasless for the buyer (the store's facilitator pays the gas).
The full working example behind this walkthrough lives in examples/facet-store on GitHub — see Example source code below.
The big picture
Requirements
Three things the agent needs:
| # | What | Used for | How to get it |
|---|---|---|---|
| 1 | A funded buyer wallet (an EVM key) | Pays USDC into escrow; also signs the payment authorizations | Any Ethereum key. Fund its address with USDC on the store's network. No ETH needed (gasless). |
| 2 | Your agent identity: an ES256 key + a published UCP profile | Signs the checkout requests (create / commit / redeem) | You generate it and publish the public half — see below. |
| 3 | A KYA token (Know-Your-Agent) | Reading the store catalog | Issued by the store's KYA issuer. The sandbox mints test tokens from a helper endpoint. |
About the agent identity
The checkout endpoints aren't protected by a store-issued password or API key. Instead, you sign each request with your own key, and the store verifies it:
- You generate an ES256 (P-256) key pair — once.
- You publish the public key in a small JSON document, a UCP profile (
ucp-profile.json), hosted at any public HTTPS URL you control. A GitHub Gist "raw" URL is enough — it does not go on the store's website, and you do not need a special/.well-knownpath. - On every checkout request you send the header
UCP-Agent: profile="https://example.com/ucp-profile.json"together with an RFC 9421 signature. The store fetches your profile, finds your public key by itskid, and verifies the signature.
In short: for checkout, the store issues you nothing — your identity is your own key, and the profile URL is simply where the store fetches your public key to check your signature. (This is the UCP "platform" model; see ucp.dev.)
The profile is just this:
{
"ucp_version": "1.0",
"name": "Agent buyer",
"signing_keys": [
{ "kid": "agent-1a2b3c4d", "kty": "EC", "crv": "P-256", "x": "…", "y": "…", "use": "sig", "alg": "ES256" }
]
}The purchase flow
Each step lists what happens, what it requires, and where it is in the example script.
1 · Browse the catalog and choose a product
- What happens: the agent lists the store's products and selects one.
- Requires: a KYA token (catalog reads are authenticated).
- In the example script:
client.search(...)via the@facet-llc/clientlibrary, which attaches a freshly-minted KYA token, then picks a product — e.g. the cheapest.
2 · Create a checkout session
- What happens: the agent asks the store to reserve and price the item (goods + shipping + tax) for a shipping address. The store returns a seller-signed escrow offer — the network, the USDC amount, and the escrow contract address.
- Requires: your RFC 9421 signature (this is a checkout endpoint).
- In the example script:
POST /ucp/v1/checkout-sessions; the offer is read frompayment_handlers["llc.facet.boson_escrow"][0].config.offer.
3 · Authorize the payment locally
- What happens: the agent's wallet signs a spend authorization — an ERC-3009 "transfer with authorization" — for the exact amount. Nothing is broadcast — no money moves yet.
- Requires: the buyer wallet key.
- In the example script:
x402b.handle402(requirements)returns the signed authorization (theX-PAYMENT), which is echoed back verbatim in the commit body.
4 · Commit — fund the escrow
- What happens: the agent submits the signed authorization; the store's facilitator relays it on-chain, locking the USDC in the Boson escrow. The response returns the committed exchange id and state
COMMITTED. Gasless for the buyer. - Requires: your RFC 9421 signature, and enough USDC in the wallet.
- In the example script:
POST /ucp/v1/checkout-sessions/{id}/completewith theboson_commit_authorizationcredential.
5 · Redeem — release on fulfillment
- What happens: the agent signs a redeem for the exchange and hands it to the store. The store stores it and submits it on-chain once the order is fulfilled — that's what releases the escrowed funds to the seller. The buyer stays protected until fulfillment.
- Requires: your RFC 9421 signature, and the exchange id from step 4.
- In the example script:
x402b.signAction({ actionId: "boson-redeem", … }), thenPOST /ucp/v1/checkout-sessions/redeemwith{ exchange_id, signed_payload }.
What you must implement
- Generate an ES256 key and publish a UCP profile with its public half (once).
- Implement RFC 9421 request signing for the three checkout calls — sign
@method,@authority,@path, theUCP-Agentheader, an idempotency key, and the body digest. - Obtain a KYA token for catalog reads (store-specific).
- Sign the payments — use
@bosonprotocol/x402-clientrather than hand-rolling this.handle402produces the local ERC-3009 spend authorization;signActionsigns the Boson redeem meta-transaction. - Fund a wallet with USDC on the store's network.
- Call the three checkout endpoints in order: create → complete → redeem.
Example source code
The complete, working reference implementation for this flow lives in bosonprotocol/x402B → examples/facet-store. It includes:
src/purchase.ts— the whole purchase flow in one self-contained script, paired with this walkthrough.src/purchase-ucp.ts— the same flow, instrumented and configurable (verbose logging, env-selectable product/network, on-chain confirmation polling) — useful for debugging.src/agent.ts— a catalog-only agent (discovery + search, no purchasing)..env.exampleand aREADME.mdwith full setup instructions (pnpm buy --init, dry run, thenSETTLE=1 pnpm buyto buy for real).
Going to production
- Point the store URL at the live store (e.g.
https://<store>.facet.llc). - Switch the chain + USDC address to the store's production network (e.g. Base mainnet,
eip155:8453), and fund the wallet with real USDC. - Obtain a real KYA token from the store's issuer instead of the sandbox test helper.
- Keep the machine clock accurate — a skewed clock makes both the request signature and the payment authorization expire ("signature stale" / "validBefore expired").
Good to know
- The store never holds your money. Funds sit in the Boson escrow contract; the seller is paid only when the redeem is submitted (on fulfillment). A dispute window protects the buyer.
- Gasless: the buyer needs USDC but no ETH; the facilitator pays gas.
- Safety: a production implementation should refuse to spend above a configured cap, check the wallet balance before committing, and default to a dry run — see the example script for a reference implementation of these guards.