Merge pull request #29 from living-ip/codex/leo-realistic-payment-card-20260701
Some checks are pending
CI / lint-and-test (push) Waiting to run

Render Leo x402 checkout as Telegram button card
This commit is contained in:
twentyOne2x 2026-07-01 01:05:51 +02:00 committed by GitHub
commit 5bdd62d312
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 50 additions and 4 deletions

View file

@ -35,7 +35,7 @@ from pathlib import Path
# Add pipeline lib to path for shared modules
sys.path.insert(0, "/opt/teleo-eval/pipeline")
from telegram import Update
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import (
Application,
CommandHandler,
@ -198,11 +198,17 @@ async def _reply_checkout_qr_photo(msg, proxy_body: dict | None, *, do_quote: bo
card = extract_checkout_qr_photo_message(proxy_body)
if not card:
return False
reply_markup = None
if card.get("button_url"):
reply_markup = InlineKeyboardMarkup(
[[InlineKeyboardButton(card.get("button_text") or "Pay with x402", url=card["button_url"])]]
)
try:
await msg.reply_photo(
photo=card["photo_url"],
caption=card["caption_html"],
parse_mode=card.get("parse_mode") or "HTML",
reply_markup=reply_markup,
do_quote=do_quote,
)
return True

View file

@ -193,6 +193,28 @@ def extract_checkout_qr_url(payload: dict[str, Any] | None) -> str | None:
return url
def extract_checkout_url(payload: dict[str, Any] | None) -> str | None:
"""Return a Telegram-safe Leo checkout URL for an inline payment button."""
if not isinstance(payload, dict):
return None
checkout = payload.get("checkout")
if not isinstance(checkout, dict):
return None
telegram = checkout.get("telegram")
raw_url = None
if isinstance(telegram, dict):
raw_url = telegram.get("buttonUrl") or telegram.get("button_url") or telegram.get("checkoutUrl")
raw_url = raw_url or checkout.get("checkoutUrl") or checkout.get("checkout_url")
if not isinstance(raw_url, str):
return None
url = raw_url.strip()
if len(url) > 2000:
return None
if not re.match(r"^https://leo\.livingip\.xyz/agents/leo/research/checkout\?", url):
return None
return url
def extract_checkout_qr_photo_message(payload: dict[str, Any] | None) -> dict[str, str] | None:
"""Return a compact Telegram photo+caption payload for a Leo paid-research quote."""
qr_url = extract_checkout_qr_url(payload)
@ -205,10 +227,14 @@ def extract_checkout_qr_photo_message(payload: dict[str, Any] | None) -> dict[st
telegram = checkout.get("telegram")
caption_html = None
button_text = "Pay with x402"
if isinstance(telegram, dict):
raw_caption = telegram.get("captionHtml") or telegram.get("caption_html")
if isinstance(raw_caption, str) and 1 <= len(raw_caption) <= 1024:
caption_html = raw_caption.strip()
raw_button_text = telegram.get("buttonText") or telegram.get("button_text")
if isinstance(raw_button_text, str) and 1 <= len(raw_button_text.strip()) <= 40:
button_text = raw_button_text.strip()
if not caption_html:
price = str(checkout.get("priceUsd") or checkout.get("price_usd") or "0.07").strip()
@ -237,6 +263,8 @@ def extract_checkout_qr_photo_message(payload: dict[str, Any] | None) -> dict[st
"photo_url": qr_url,
"caption_html": caption_html,
"parse_mode": "HTML",
"button_text": button_text,
"button_url": extract_checkout_url(payload) or "",
}

View file

@ -16,6 +16,7 @@ from http_chat_proxy import ( # noqa: E402
classify_smart_research_auto_resume_response,
extract_auto_smart_research_followup_goal,
extract_auto_smart_research_goal,
extract_checkout_url,
extract_checkout_qr_photo_message,
extract_checkout_qr_url,
extract_paid_work_order_id,
@ -308,8 +309,10 @@ def test_extract_checkout_qr_url(payload, expected):
def test_extract_checkout_qr_photo_message_prefers_structured_telegram_card():
checkout_url = "https://leo.livingip.xyz/agents/leo/research/checkout?q=test"
payload = {
"checkout": {
"checkoutUrl": checkout_url,
"checkoutQrUrl": (
"https://leo.livingip.xyz/api/agents/leo/research/checkout-qr?"
"url=https%3A%2F%2Fleo.livingip.xyz%2Fagents%2Fleo%2Fresearch%2Fcheckout%3Fq%3Dtest"
@ -319,10 +322,11 @@ def test_extract_checkout_qr_photo_message_prefers_structured_telegram_card():
"captionHtml": (
"<b>Paid Leo research</b>\n"
"0.07 USDC on Solana mainnet.\n"
'<a href="https://leo.livingip.xyz/agents/leo/research/checkout?q=test">'
"Pay with x402</a>\n"
"Pay with the button below or scan the QR.\n"
"Recipient: <code>8EgACpZ16XWEt7YjJPsh1ZheVRZUGmmwQ8nJdmA1o5w4</code>"
)
),
"buttonText": "Pay with x402",
"buttonUrl": checkout_url,
},
}
}
@ -333,7 +337,10 @@ def test_extract_checkout_qr_photo_message_prefers_structured_telegram_card():
"photo_url": payload["checkout"]["checkoutQrUrl"],
"caption_html": payload["checkout"]["telegram"]["captionHtml"],
"parse_mode": "HTML",
"button_text": "Pay with x402",
"button_url": checkout_url,
}
assert checkout_url not in card["caption_html"]
assert "npx agentcash" not in card["caption_html"]
assert "Human checkout:" not in card["caption_html"]
assert "QR checkout:" not in card["caption_html"]
@ -354,6 +361,11 @@ def test_extract_checkout_qr_photo_message_builds_address_fallback_caption():
assert card["photo_url"] == payload["checkout"]["checkoutQrUrl"]
assert "0.07 USDC on Solana mainnet" in card["caption_html"]
assert "<code>8EgACpZ16XWEt7YjJPsh1ZheVRZUGmmwQ8nJdmA1o5w4</code>" in card["caption_html"]
assert card["button_url"] == ""
def test_extract_checkout_url_rejects_non_leo_checkout_url():
assert extract_checkout_url({"checkout": {"checkoutUrl": "https://example.com/pay"}}) is None
def test_extract_checkout_qr_photo_message_rejects_non_leo_qr_url():