Skip to main content
Version: Next

A2A Cart Store (AP2 + Skyfire)

This example wraps a small A2A/AP2 cart store with SingleA2AAdapter so a uAgent can chat, emit RequestPayment, and complete checkout through Skyfire Pay.

It is not a no-credentials dry run. You need:

  • A Skyfire seller account (account id, service id, API key)
  • An Agentverse mailbox connection (mailbox=True on the adapter)
  • A client that understands uAgents RequestPayment with payment_method="skyfire"ASI:One Chat is the supported UI for Skyfire Pay in this walkthrough

Built on: A2A Outbound Adapter (SingleA2AAdapter + AP2 ↔ Payment Protocol mapping). Package README: uagents-adapter. AP2 mapping source: a2a_outbound/ap2/bridge_mapping.py.

Repo: a2a-cart-store

Overview

  • A2A/AP2 store (JSON-RPC) with: list, add, remove, cart, checkout
  • uAgent adapter (SingleA2AAdapter) forwards chat and bridges payments
  • Payment handshake (uAgents): RequestPaymentCommitPayment / RejectPaymentCompletePayment / CancelPayment
  • Skyfire method is used at checkout so the UI offers Skyfire Pay

AP2 to PaymentProtocol mapping

Prerequisites

  • Python 3.10+ (3.10–3.12 recommended for uagents 0.25.x). On Windows use py -3.10; on macOS/Linux use python3.
  • A Skyfire seller account: Skyfire dashboard, API reference, sandbox vs production. For a first run, use sandbox endpoints from those docs if you do not want production settlement.
  • An Agentverse account so you can Connect → Mailbox from the inspector (mailbox agents).
  • ASI:One (or another uAgents client that surfaces Skyfire Pay on RequestPayment).

Installation

Clone the public examples repo and work only inside a2a-cart-store. There is no parent examples-a2a/requirements.txt on GitHub — install the cart-store file plus the pins below.

clone.sh
git clone https://github.com/fetchai/innovation-lab-examples.git
cd innovation-lab-examples/a2a-cart-store

Create a virtualenv:

venv-unix.sh
python3 -m venv .venv
source .venv/bin/activate
venv-windows.ps1
py -3.10 -m venv .venv
.\.venv\Scripts\Activate.ps1

Install pins that match Innovation Lab uagents==0.25.5 and a pre-1.0 A2A SDK. Uncapped a2a-sdk[http-server] resolves to 1.x, which removed a2a.server.apps.A2AStarletteApplication and breaks SingleA2AAdapter (same import failure as #290).

The copy of requirements.txt in the example repo may still list uagents==0.22.10 and uagents-adapter==0.6.0. Do not use those pins. Install the stack below instead (it also pulls the other cart-store libraries from that file):

install.sh
python3 -m pip install \
"uagents==0.25.5" \
"uagents-adapter[a2a-outbound]==0.6.2" \
"a2a-sdk[http-server]==0.3.26" \
"python-dotenv>=1.0.1" \
"httpx>=0.27.2" \
"python-jose[cryptography]>=3.3.0" \
"aiohttp>=3.9.5"

On Windows: py -3.10 -m pip install with the same package list.

Quick import check before running:

import-check.sh
python3 -c "from uagents_adapter.a2a_outbound import SingleA2AAdapter; from a2a.server.apps import A2AStarletteApplication; print('ok')"

Environment

Put .env in a2a-cart-store/ (next to av_adapter.py). av_adapter.py loads parents[1]/.env first (repo root of innovation-lab-examples), then the local folder with override=True, so the file beside av_adapter.py wins. Do not rely on a private examples-a2a layout.

Use this cart-store–scoped file (ports + Skyfire + mailbox). Skip the public .env.example if it still mixes SELLER_A2A_PORT, dummy ASI_API_KEY=ASI_API_KEY, or STORE_A2A_PORT=10030 — those are leftover from a multi-example env. Canonical A2A port is 10031 (matches av_adapter.py).

.env
# Ports (must match av_adapter.py defaults)
STORE_A2A_PORT=10031
STORE_UAGENT_PORT=8230

# Agentverse mailbox (required: adapter uses mailbox=True)
# Create at https://agentverse.ai/profile/api-keys
AGENTVERSE_API_KEY=YOUR_AGENTVERSE_API_KEY

# Skyfire seller (sandbox or production — keep JWKS/issuer/charge URL on the same env)
JWKS_URL=https://app.skyfire.xyz/.well-known/jwks.json
JWT_ISSUER=https://app.skyfire.xyz
SELLER_ACCOUNT_ID=YOUR_SKYFIRE_ACCOUNT_ID
SELLER_SERVICE_ID=YOUR_SKYFIRE_SERVICE_ID
SKYFIRE_TOKENS_CHARGE_API_URL=https://api.skyfire.xyz/api/v1/tokens/charge
SELLER_SKYFIRE_API_KEY=YOUR_SKYFIRE_API_KEY

PowerShell does not use export. Prefer the .env file above (python-dotenv loads it). To set a variable in the current session only:

env-windows.ps1
$env:STORE_A2A_PORT = "10031"
$env:STORE_UAGENT_PORT = "8230"

Unique agent name

The sample hardcodes name="demo_store_uagent" in av_adapter.py. The default seed pattern yields the same address for every reader (agent1qtsnjt3j2cg9ldezxjh4jqmwkw30trfwqhvcw9qku2nq94nerwqjxgyexjm). Change name (and optionally pass a unique seed) before you register:

av_adapter.py
adapter = SingleA2AAdapter(
agent_executor=executor,
name="demo_store_uagent", # change to a unique name, e.g. store_uagent_<your_handle>
description="Store agent with cart + payment",
port=uagent_port,
a2a_port=a2a_port,
mailbox=True,
)

Optional: read the name from the environment if you patch the constructor, for example name=os.getenv("STORE_UAGENT_NAME", "demo_store_uagent").

Run

From a2a-cart-store/ with the venv active:

run-unix.sh
python3 av_adapter.py
run-windows.ps1
py -3.10 av_adapter.py

Expected startup

You should see the A2A server on 10031, the uAgent on 8230, and an inspector URL after python3 av_adapter.py.

Open the inspector while logged into Agentverse and choose Connect → Mailbox if the mailbox token is not acquired automatically. See mailbox agents.

Commands on ASI:One or another uAgents client

Catalog prices are 0.001–0.002 USDC. SKUs include book, pen, watch, plus shoe, laptop, phone, headphones, and others. Start with list or catalog before add so you see live SKUs.

Suggested sequence (matches the screenshots below):

  1. list or catalog
  2. add book 1 (or add book 1 pen 2 watch 1)
  3. cart
  4. checkout — then Skyfire Pay in the client → CompletePayment

Other commands: remove <sku>, reset / clear, help.

ASI chat listing catalog and adding items

ASI chat showing the cart totals in USDC

Expected payment output

After checkout the adapter returns a RequestPayment with payment_method="skyfire" and the client should show Skyfire Pay. On approval the adapter emits CompletePayment; on failure it emits CancelPayment.

ASI checkout with Skyfire Pay

How it works (AP2 ↔ Payment Protocol mapping)

  • CartMandate → RequestPayment (amount/currency from AP2 total; payment_method from AP2 method_data; include cart_hash)
  • CommitPayment → PaymentMandate (token from transaction_id; total from funds)
  • PaymentSuccess → CompletePayment; PaymentFailure → CancelPayment; RejectPayment → DenyCartMandate

Files

  • av_adapter.py: boots SingleA2AAdapter, loads .env, forwards chat, bridges payments.
  • store_executor.py: store logic; builds AP2 CartMandate on checkout; validates/charges on PaymentMandate.
  • skyfire_payment.py: Skyfire JWT verify (JWKS) + charge helper; returns a transaction_id on success.
  • requirements.txt: example pins — override with uagents==0.25.5, uagents-adapter[a2a-outbound]==0.6.2, and a2a-sdk[http-server]==0.3.26 as in Installation.

Troubleshooting

SymptomWhat to check
ImportError: a2a.server.apps / A2AStarletteApplicationYou installed a2a-sdk 1.x. Reinstall a2a-sdk[http-server]==0.3.26 (#290).
Skyfire errors at checkoutConfirm seller env values, JWKS/charge reachability, and that sandbox vs production URLs match. Docs: charge token.
No Skyfire Pay buttonClient must handle RequestPayment with Skyfire; use ASI:One. Ensure payment_request.method_data is non-empty (supported_methods="skyfire").
Mailbox / not discoverablemailbox=True requires Agentverse inspector Connect → Mailbox and a valid AGENTVERSE_API_KEY.
Wrong A2A portUse STORE_A2A_PORT=10031. Ignore 10030 if you copied an old .env.example.
Address collisionsChange name= in av_adapter.py (see Unique agent name).