Image Agent Payment Protocol Example
This example demonstrates a seller agent that requests a small payment (USDC via Skyfire) before generating an image and sending it back as a resource using the chat protocol. It is adapted from your image-agent-payment-protocol
project.
What it shows
- Minimal payment workflow using
AgentPaymentProtocol
with roles. - Skyfire token verification and charge flow.
- Image generation via Pollinations.
- Returning the image through the chat protocol as a resource.
Project Structure
Key files used in this example:
agent.py
– Agent setup; includes chat and payment protocolschat_proto.py
– Chat protocol and message handlerspayment_proto.py
– Seller-side payment logic (request, verify, charge)skyfire.py
– JWT verification and charge against Skyfire API
Payment Protocol (imports)
from uagents_core.contrib.protocols.payment import (
Funds,
RequestPayment,
RejectPayment,
CommitPayment,
CancelPayment,
CompletePayment,
payment_protocol_spec,
)
Skyfire helpers (pseudocode)
Click here to get the full file.
skyfire.py (pseudocode)
# Env inputs: SKYFIRE_API_KEY, SKYFIRE_SERVICE_ID, SELLER_ACCOUNT_ID or JWT_AUDIENCE,
# JWKS_URL, JWT_ISSUER, SKYFIRE_TOKENS_API_URL
async def verify_token_claims(token, logger) -> bool:
# 1) Fetch JWKS from JWKS_URL
# 2) Decode JWT with issuer=JWT_ISSUER and audience=JWT_AUDIENCE or SELLER_ACCOUNT_ID
# 3) Ensure claim ssi == SKYFIRE_SERVICE_ID
# Return True on success, else False
pass
async def charge_token(token, amount_usdc, logger) -> bool:
# POST SKYFIRE_TOKENS_API_URL
# headers: { "skyfire-api-key": SKYFIRE_API_KEY, "skyfire-api-version": "2" }
# body: { "token": token, "chargeAmount": amount_usdc }
# Return True on 2xx, else False
pass
async def verify_and_charge(token, amount_usdc, logger) -> bool:
# Ensure required env vars exist
# If verify_token_claims(token): return charge_token(token, amount_usdc)
# Else return False
pass
def get_skyfire_service_id() -> str | None:
# Return SKYFIRE_SERVICE_ID from env
pass
Payment Logic (seller)
payment_proto.py
import os
from uuid import uuid4
from uagents import Context, Protocol
from uagents_core.contrib.protocols.payment import (
Funds,
RequestPayment,
RejectPayment,
CommitPayment,
CancelPayment,
CompletePayment,
payment_protocol_spec,
)
from skyfire import verify_and_charge, get_skyfire_service_id
from chat_proto import create_text_chat
from urllib.parse import quote
_agent_wallet = None
def set_agent_wallet(wallet):
global _agent_wallet
_agent_wallet = wallet
payment_proto = Protocol(spec=payment_protocol_spec, role="seller")
USDC_FUNDS = Funds(currency="USDC", amount="0.001", payment_method="skyfire")
async def request_payment_from_user(ctx: Context, user_address: str):
accepted_funds = [USDC_FUNDS]
skyfire_service_id = get_skyfire_service_id()
metadata = {}
if skyfire_service_id:
metadata["skyfire_service_id"] = skyfire_service_id
if _agent_wallet:
metadata["provider_agent_wallet"] = str(_agent_wallet.address())
payment_request = RequestPayment(
accepted_funds=accepted_funds,
recipient=ctx.agent.address,
deadline_seconds=300,
reference=str(uuid4()),
description="ASI1 Image Gen: after payment, send your image prompt (one image per payment)",
metadata=metadata,
)
await ctx.send(user_address, payment_request)
@payment_proto.on_message(CommitPayment)
async def handle_commit_payment(ctx: Context, sender: str, msg: CommitPayment):
payment_verified = False
if msg.funds.payment_method == "skyfire" and msg.funds.currency == "USDC":
payment_verified = await verify_and_charge(msg.transaction_id, "0.001", ctx.logger)
if payment_verified:
session_id = str(ctx.session)
ctx.storage.set(f"{sender}:{session_id}:awaiting_prompt", True)
ctx.storage.set(f"{sender}:{session_id}:verified_payment", True)
await ctx.send(sender, CompletePayment(transaction_id=msg.transaction_id))
await ctx.send(sender, create_text_chat("Payment verified. Please send your image prompt."))
else:
await ctx.send(sender, RejectPayment(reason="Payment verification failed"))
Chat Protocol integration
chat_proto.py
from datetime import datetime, timezone
from uuid import uuid4
from uagents import Context, Protocol
from uagents_core.contrib.protocols.chat import (
AgentContent,
ChatAcknowledgement,
ChatMessage,
EndSessionContent,
TextContent,
chat_protocol_spec,
)
def create_text_chat(text: str, end_session: bool = False) -> ChatMessage:
content: list[AgentContent] = [TextContent(type="text", text=text)]
if end_session:
content.append(EndSessionContent(type="end-session"))
return ChatMessage(
timestamp=datetime.now(timezone.utc),
msg_id=uuid4(),
content=content,
)
chat_proto = Protocol(spec=chat_protocol_spec)
@chat_proto.on_message(ChatMessage)
async def handle_message(ctx: Context, sender: str, msg: ChatMessage):
from payment_proto import request_payment_from_user
for item in msg.content:
if isinstance(item, TextContent):
await ctx.send(sender, create_text_chat("Please complete a small payment first."))
await request_payment_from_user(ctx, sender)
Agent setup
agent.py
import os
import dotenv
from uagents import Agent, Context
dotenv.load_dotenv()
from chat_proto import chat_proto
from payment_proto import payment_proto, set_agent_wallet
agent = Agent(
name="ASI1ImageAgent",
port=8021,
mailbox=True,
agentverse=os.getenv("AGENTVERSE_URL", "https://agentverse.ai"),
)
set_agent_wallet(agent.wallet)
@agent.on_event("startup")
async def startup(ctx: Context):
ctx.logger.info(f"ASI1 Image Agent started: {agent.wallet.address()}")
agent.include(chat_proto, publish_manifest=True)
agent.include(payment_proto, publish_manifest=True)
if __name__ == "__main__":
agent.run()
Environment
Prepare a .env
with your Skyfire credentials and Agentverse settings:
AGENTVERSE_URL=https://agentverse.ai
SKYFIRE_API_KEY=
SKYFIRE_SERVICE_ID=
SELLER_ACCOUNT_ID=
JWKS_URL=https://app.skyfire.xyz/.well-known/jwks.json
JWT_ISSUER=https://app.skyfire.xyz/
SKYFIRE_TOKENS_API_URL=https://api.skyfire.xyz/api/v1/tokens/charge
Run locally
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python3 agent.py
Send a short chat message to the agent; it will respond with a payment request. After committing payment, it will prompt for the image description and then return the generated image as a resource.
Enabling Skyfire payments
- Place
skyfire.py
next to youragent.py
andpayment_proto.py
. - Set environment variables in
.env
:SKYFIRE_API_KEY
,SKYFIRE_SERVICE_ID
, and eitherSELLER_ACCOUNT_ID
orJWT_AUDIENCE
- Optional overrides:
JWKS_URL
,JWT_ISSUER
,SKYFIRE_TOKENS_API_URL
- In
payment_proto.py
, keepUSDC_FUNDS = Funds(currency="USDC", amount="0.001", payment_method="skyfire")
and surfaceskyfire_service_id
inRequestPayment.metadata
. - On
CommitPayment
, callverify_and_charge(msg.transaction_id, "0.001", ctx.logger)
and only proceed toCompletePayment
on success.
note
Get the full example with implementation here.