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=Trueon the adapter) - A client that understands uAgents
RequestPaymentwithpayment_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):
RequestPayment→CommitPayment/RejectPayment→CompletePayment/CancelPayment - Skyfire method is used at checkout so the UI offers Skyfire Pay

Prerequisites
- Python 3.10+ (3.10–3.12 recommended for
uagents0.25.x). On Windows usepy -3.10; on macOS/Linux usepython3. - 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.
git clone https://github.com/fetchai/innovation-lab-examples.git
cd innovation-lab-examples/a2a-cart-store
Create a virtualenv:
python3 -m venv .venv
source .venv/bin/activate
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):
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:
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).
# 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: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:
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:
python3 av_adapter.py
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.
- A2A / AP2 store: http://localhost:10031 (override with
STORE_A2A_PORT) - Agent card: http://localhost:10031/.well-known/agent.json
- uAgent: http://localhost:8230 (override with
STORE_UAGENT_PORT) - Inspector template:
https://agentverse.ai/inspect/?uri=http%3A//127.0.0.1%3A8230&address=<YOUR_AGENT_ADDRESS>
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):
listorcatalogadd book 1(oradd book 1 pen 2 watch 1)cartcheckout— then Skyfire Pay in the client →CompletePayment
Other commands: remove <sku>, reset / clear, help.


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.

How it works (AP2 ↔ Payment Protocol mapping)
- CartMandate → RequestPayment (amount/currency from AP2 total;
payment_methodfrom AP2method_data; includecart_hash) - CommitPayment → PaymentMandate (token from
transaction_id; total from funds) - PaymentSuccess → CompletePayment; PaymentFailure → CancelPayment; RejectPayment → DenyCartMandate
Files
av_adapter.py: bootsSingleA2AAdapter, loads.env, forwards chat, bridges payments.store_executor.py: store logic; builds AP2CartMandateoncheckout; validates/charges onPaymentMandate.skyfire_payment.py: Skyfire JWT verify (JWKS) + charge helper; returns atransaction_idon success.requirements.txt: example pins — override withuagents==0.25.5,uagents-adapter[a2a-outbound]==0.6.2, anda2a-sdk[http-server]==0.3.26as in Installation.
Troubleshooting
| Symptom | What to check |
|---|---|
ImportError: a2a.server.apps / A2AStarletteApplication | You installed a2a-sdk 1.x. Reinstall a2a-sdk[http-server]==0.3.26 (#290). |
| Skyfire errors at checkout | Confirm seller env values, JWKS/charge reachability, and that sandbox vs production URLs match. Docs: charge token. |
| No Skyfire Pay button | Client must handle RequestPayment with Skyfire; use ASI:One. Ensure payment_request.method_data is non-empty (supported_methods="skyfire"). |
| Mailbox / not discoverable | mailbox=True requires Agentverse inspector Connect → Mailbox and a valid AGENTVERSE_API_KEY. |
| Wrong A2A port | Use STORE_A2A_PORT=10031. Ignore 10030 if you copied an old .env.example. |
| Address collisions | Change name= in av_adapter.py (see Unique agent name). |