Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Facet Purchase Flow · Boson Protocol
Skip to content

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:

#WhatUsed forHow to get it
1A funded buyer wallet (an EVM key)Pays USDC into escrow; also signs the payment authorizationsAny Ethereum key. Fund its address with USDC on the store's network. No ETH needed (gasless).
2Your agent identity: an ES256 key + a published UCP profileSigns the checkout requests (create / commit / redeem)You generate it and publish the public half — see below.
3A KYA token (Know-Your-Agent)Reading the store catalogIssued 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-known path.
  • 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 its kid, 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/client library, 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 from payment_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 (the X-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}/complete with the boson_commit_authorization credential.

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", … }), then POST /ucp/v1/checkout-sessions/redeem with { exchange_id, signed_payload }.

What you must implement

  1. Generate an ES256 key and publish a UCP profile with its public half (once).
  2. Implement RFC 9421 request signing for the three checkout calls — sign @method, @authority, @path, the UCP-Agent header, an idempotency key, and the body digest.
  3. Obtain a KYA token for catalog reads (store-specific).
  4. Sign the payments — use @bosonprotocol/x402-client rather than hand-rolling this. handle402 produces the local ERC-3009 spend authorization; signAction signs the Boson redeem meta-transaction.
  5. Fund a wallet with USDC on the store's network.
  6. Call the three checkout endpoints in order: create → complete → redeem.

Example source code

The complete, working reference implementation for this flow lives in bosonprotocol/x402Bexamples/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.example and a README.md with full setup instructions (pnpm buy --init, dry run, then SETTLE=1 pnpm buy to 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.