Stripe Horoscope Agent
This example demonstrates a seller agent that:
- Chats using the Agent Chat Protocol
- Requests a $1 USD Stripe payment using the Agent Payment Protocol (
payment_method="stripe") - After payment, generates a “horoscope of the day” using ASI:One (
model="asi1")
Source code (recommended)
The full runnable example lives in the examples repo:
Keeping code in the examples repo avoids overwhelming docs with large code blocks.
Prerequisites
- Python 3.11+
- ASI:One API key: create one from the ASI:One developer page
- Stripe test API keys (recommended first):
- Use a Stripe sandbox for testing (no real money moves). See Stripe Sandboxes.
- Get your test keys (publishable + secret). See Stripe API keys.
- Copy:
- Secret key (
sk_test_...) →STRIPE_SECRET_KEY - Publishable key (
pk_test_...) →STRIPE_PUBLISHABLE_KEY
- Secret key (
Run locally
git clone https://github.com/fetchai/innovation-lab-examples.git
cd innovation-lab-examples/stripe-horoscope-agent
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
python3 agent.py
Key files (what to read first)
agent.py: tiny entrypoint; loads env and includes the chat + payment protocols. (GitHub)handlers.py: the main state machine (sign → payment → horoscope). (GitHub)stripe_payments.py: creates embedded Stripe Checkout sessions and verifiespayment_status. (GitHub)llm.py: ASI:One calls + prompts (normal reply vs horoscope generation).state.py: short-livedctx.storagestate + zodiac parsing helpers.chat_proto.py/payment_proto.py: protocol wrappers that keep boilerplate out of the handlers.
How payments are enabled (Payment Protocol + Stripe)
This example combines:
- the Agent Payment Protocol (for requesting/committing/completing payments), and
- Stripe embedded Checkout (as the payment rail when
Funds.payment_method == "stripe").
Flow diagram

If you haven’t read the protocol docs yet, start here:
1) Enable the Agent Payment Protocol (seller)
To make an agent “payable”, you include the payment protocol with seller role:
payment_proto.py: wrapspayment_protocol_specwithrole="seller"and routes payment messages to handlers. (GitHub)
from uagents import Context, Protocol
from uagents_core.contrib.protocols.payment import CommitPayment, RejectPayment, payment_protocol_spec
def build_payment_proto(on_commit, on_reject) -> Protocol:
proto = Protocol(spec=payment_protocol_spec, role="seller")
@proto.on_message(CommitPayment)
async def _on_commit(ctx: Context, sender: str, msg: CommitPayment):
await on_commit(ctx, sender, msg)
@proto.on_message(RejectPayment)
async def _on_reject(ctx: Context, sender: str, msg: RejectPayment):
await on_reject(ctx, sender, msg)
return proto
agent.py: includes the payment protocol in the agent. (GitHub)
agent.include(build_chat_proto(on_chat), publish_manifest=True)
agent.include(build_payment_proto(on_commit, on_reject), publish_manifest=True)
In this flow you’ll typically handle:
RequestPayment(seller → buyer/UI): “Here’s what to pay and how.”CommitPayment(buyer/UI → seller): “I paid; here’s the transaction id.”CompletePayment(seller → buyer/UI): “Payment verified and accepted.”RejectPayment(either side): “Unsupported / cancelled / not paid yet.”
2) Add Stripe: create an embedded Checkout Session
Stripe integration in the example lives in stripe_payments.py:
create_embedded_checkout_session(...)- calls
stripe.checkout.Session.create(ui_mode="embedded", ...) - returns a small dict containing the publishable key, client secret, and Checkout Session ID
- calls
def create_embedded_checkout_session(*, user_address: str, chat_session_id: str, description: str) -> dict:
session = stripe.checkout.Session.create(
ui_mode="embedded",
redirect_on_completion="if_required",
mode="payment",
payment_method_types=["card"],
return_url=STRIPE_SUCCESS_URL + "?session_id={CHECKOUT_SESSION_ID}",
line_items=[
{
"price_data": {
"currency": STRIPE_CURRENCY,
"product_data": {"name": STRIPE_PRODUCT_NAME, "description": description},
"unit_amount": STRIPE_AMOUNT_CENTS,
},
"quantity": 1,
}
],
)
return {
"client_secret": session.client_secret,
"checkout_session_id": session.id,
"publishable_key": STRIPE_PUBLISHABLE_KEY,
"currency": STRIPE_CURRENCY,
"amount_cents": STRIPE_AMOUNT_CENTS,
"ui_mode": "embedded",
}
verify_checkout_session_paid(checkout_session_id)- calls
stripe.checkout.Session.retrieve(checkout_session_id) - checks
payment_status == "paid"
- calls
def verify_checkout_session_paid(checkout_session_id: str) -> bool:
session = stripe.checkout.Session.retrieve(checkout_session_id)
return getattr(session, "payment_status", None) == "paid"
See the implementation:
- Stripe session creation:
create_embedded_checkout_session - Stripe payment verification:
verify_checkout_session_paid
Embedded Checkout (what “embedded” means)
ui_mode="embedded"means your UI renders Stripe Checkout inside the page (instead of redirecting to a hosted checkout page).- Stripe returns a
client_secret(for the embedded checkout UI) and a Checkout Session ID (checkout_session_id). - Your UI uses the publishable key + client secret to render the checkout.
- Your agent uses the Checkout Session ID to verify payment status after
CommitPayment.
Dynamic pricing (variable amounts)
There are two common patterns:
- Server-side computed amount (ad-hoc): compute
amount_centsin your agent (based on plan/tier/promo) and pass it intostripe.checkout.Session.create(... unit_amount=amount_cents ...). - Pre-created Stripe Prices: create Prices in Stripe and pass
line_items=[{"price": "price_...", "quantity": 1}](recommended when you have a small set of fixed tiers).
If you implement dynamic amounts, keep it safe by:
- computing price on the seller/agent side (never trust client/UI-supplied price)
- optionally verifying
amount_total/currencyon the retrieved Checkout Session before delivering the paid content.
3) Put Stripe session details into RequestPayment.metadata
The Payment Protocol is rail-agnostic. The “how to render and complete payment” details go into RequestPayment.metadata.
For Stripe embedded Checkout, the convention used in this example is:
Funds(payment_method="stripe")RequestPayment.metadata["stripe"] = <embedded checkout payload>
Payload shape (example):
{
"stripe": {
"ui_mode": "embedded",
"publishable_key": "pk_test_...",
"client_secret": "cs_test_...",
"checkout_session_id": "cs_test_...",
"currency": "usd",
"amount_cents": 100
}
}
This is what lets Agentverse (or any compatible UI) render the embedded checkout for the user.
See where the agent constructs RequestPayment and attaches metadata["stripe"]:
handlers.pypayment request:RequestPayment(..., metadata={"stripe": ...})
checkout = create_embedded_checkout_session(...)
req = RequestPayment(
accepted_funds=[Funds(currency="USD", amount="1.00", payment_method="stripe")],
recipient=str(ctx.agent.address),
description="Pay $1 to receive your horoscope of the day.",
metadata={"stripe": checkout, "service": "daily_horoscope"},
)
await ctx.send(sender, req)
4) Commit → verify → complete (the critical contract)
After the user completes checkout, the buyer/UI sends:
CommitPayment(funds.payment_method="stripe", transaction_id="<checkout_session_id>")
In this example, transaction_id is the Stripe Checkout Session ID (e.g. cs_test_...).
Then the seller agent:
- Verifies with Stripe that
payment_status == "paid" - Sends
CompletePayment(transaction_id=...) - Delivers the paid content (the horoscope)
See the commit handler that verifies, completes, then responds:
handlers.pycommit flow:on_commit
async def on_commit(ctx: Context, sender: str, msg: CommitPayment):
if msg.funds.payment_method != "stripe" or not msg.transaction_id:
await ctx.send(sender, RejectPayment(reason="Unsupported payment method (expected stripe)."))
return
paid = verify_checkout_session_paid(msg.transaction_id)
if not paid:
await ctx.send(sender, RejectPayment(reason="Stripe payment not completed yet. Please finish checkout."))
return
await ctx.send(sender, CompletePayment(transaction_id=msg.transaction_id))
# ... generate + send horoscope ...
End-to-end run + test (Agentverse UI)
- Start the agent locally (see Run locally above).
abhimanyugangani@Abhimanyus-MacBook-Pro stripe-horoscope-agent % python3 agent.py
INFO: [stripe-horoscope-agent]: Starting agent with address: agent1q2t7gnf3wymv6g7mxghm8df5cvfcgncuf057gpngrhgggv82ynxvj0r7alj
INFO: [stripe-horoscope-agent]: Agent inspector available at https://agentverse.ai/inspect/?uri=http%3A//127.0.0.1%3A8012&address=agent1q2t7gnf3wymv6g7mxghm8df5cvfcgncuf057gpngrhgggv82ynxvj0r7alj
INFO: [stripe-horoscope-agent]: Starting server on http://0.0.0.0:8012 (Press CTRL+C to quit)
INFO: [stripe-horoscope-agent]: Starting mailbox client for https://agentverse.ai
INFO: [stripe-horoscope-agent]: Manifest published successfully: AgentChatProtocol
INFO: [stripe-horoscope-agent]: Manifest published successfully: AgentPaymentProtocol
INFO: [uagents.registration]: Registration on Almanac API successful
- Open the Agent inspector link printed in the terminal and click connect.

- In chat:
- Send
give me my horoscope - Reply with a sign (e.g.
Leo)
- Send

- The UI shows a Stripe payment card.
- Use Stripe test card
4242 4242 4242 4242(any future expiry, any CVC, any ZIP). See Stripe test cards. - Click PAY (this triggers
CommitPaymentto the agent).
- Use Stripe test card
- The agent verifies Stripe payment, sends
CompletePayment, and replies with your horoscope.
