接 Stripe 收款
V1 内测期 Hashee 平台还没内建支付(V2 marketplace 走 Stripe Connect)。 本页讲你自己作为 Agent 开发者接 Stripe 收款的标准模式。
用户故事
“我的 Agent 提供 Pro 订阅($10/月,去掉 daily query 限制)。用户在 Hashee 聊天里
/upgrade→ 跳 Stripe Checkout → 付款成功 → Agent 自动启用 Pro 权限。“
架构
用户 /upgrade │ ▼ Agent 创建 Stripe Checkout Session → 返回 url │ ▼ Agent 在 Hashee app 推 artifact 卡(含跳转 URL) │ ▼ 用户点 URL → 浏览器打开 Stripe Checkout │ ▼ 用户付款成功 → Stripe redirect 回你的成功页 │ ▼ Stripe webhook → 你的服务器(订阅创建/续费/失败事件) │ ▼ 你的服务器更新 DB user_subscriptions │ ▼ 业务侧检查 Pro 权限决定行为代码:创建 checkout
import Stripe from "stripe";const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
agent.addMessageHandler(async (msg) => { if (msg.payload?.text === "/upgrade") { const session = await stripe.checkout.sessions.create({ mode: "subscription", line_items: [{ price: process.env.STRIPE_PRO_PRICE_ID!, quantity: 1 }], success_url: `https://my-agent.example.com/stripe/success?session={CHECKOUT_SESSION_ID}`, cancel_url: `https://my-agent.example.com/stripe/cancel`, metadata: { hashee_user_id: msg.sender_id, // 关键:让 webhook 知道是谁 }, client_reference_id: msg.sender_id, });
await agent.sendArtifact(msg.conversation_id, { artifact: { subtype: "info", title: "🚀 Upgrade to Pro", payload: { body: "**$10/月** · 去掉每日查询上限 · 支持优先", actions: [{ id: "pay", label: "去付款", url: session.url ?? "" }], }, }, }); }});Stripe Webhook handler
// app/api/stripe/webhook/route.ts (Next.js Route Handler 例)import Stripe from "stripe";import { Pool } from "pg";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);const db = new Pool({ connectionString: process.env.DATABASE_URL });
export async function POST(req: Request): Promise<Response> { const sig = req.headers.get("stripe-signature")!; const body = await req.text(); let event: Stripe.Event; try { event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!); } catch (err) { return new Response("bad signature", { status: 400 }); }
switch (event.type) { case "checkout.session.completed": { const session = event.data.object as Stripe.Checkout.Session; const userId = session.metadata?.hashee_user_id; const subId = session.subscription as string; if (userId && subId) { await db.query( `INSERT INTO user_subscriptions (hashee_user_id, stripe_sub_id, tier, status, current_period_end) VALUES ($1, $2, 'pro', 'active', NOW() + INTERVAL '30 days') ON CONFLICT (hashee_user_id) DO UPDATE SET stripe_sub_id = $2, tier = 'pro', status = 'active'`, [userId, subId], ); // 通知用户 const convId = await lookupDmConv(userId); if (convId) { await pushToHashee(convId, { type: "text", text: `🎉 Pro 已激活,享受 30 天 unlimited query。", }); } } break; } case "customer.subscription.updated": case "customer.subscription.deleted": { const sub = event.data.object as Stripe.Subscription; const status = sub.status; await db.query( "UPDATE user_subscriptions SET status = $1, current_period_end = to_timestamp($2) WHERE stripe_sub_id = $3", [status, sub.current_period_end, sub.id], ); break; } case "invoice.payment_failed": { // 提醒用户更新支付方式 break; } } return new Response("ok");}业务侧检查 Pro 权限
async function isPro(userId: string): Promise<boolean> { const r = await db.query( `SELECT status, current_period_end FROM user_subscriptions WHERE hashee_user_id = $1`, [userId], ); if (r.rows.length === 0) return false; const sub = r.rows[0]; return sub.status === "active" && new Date(sub.current_period_end) > new Date();}
agent.addMessageHandler(async (msg) => { if (msg.payload?.type !== "text") return; const pro = await isPro(msg.sender_id); if (!pro && msg.payload.text.startsWith("/heavy-query")) { await agent.send(msg.conversation_id, { type: "text", text: "这是 Pro 功能。发 `/upgrade` 升级。", }); return; } // 正常处理...});客户 Portal(自助管理订阅)
agent.addMessageHandler(async (msg) => { if (msg.payload?.text === "/billing") { const r = await db.query("SELECT stripe_customer_id FROM user_subscriptions WHERE hashee_user_id = $1", [msg.sender_id]); if (r.rows.length === 0) { await agent.send(msg.conversation_id, { type: "text", text: "没找到订阅,先发 /upgrade 订阅。" }); return; } const portal = await stripe.billingPortal.sessions.create({ customer: r.rows[0].stripe_customer_id, return_url: "https://my-agent.example.com/back", }); await agent.sendArtifact(msg.conversation_id, { artifact: { subtype: "info", title: "管理订阅", payload: { body: "在 Stripe Portal 里查看 / 更新 / 取消订阅。", actions: [{ id: "portal", label: "打开 Portal", url: portal.url }], }, }, }); }});DB schema
CREATE TABLE user_subscriptions ( hashee_user_id TEXT PRIMARY KEY, stripe_customer_id TEXT, stripe_sub_id TEXT, tier TEXT NOT NULL DEFAULT 'free', status TEXT NOT NULL DEFAULT 'inactive', current_period_end TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW());部署细节
| 维度 | 实施 |
|---|---|
| Stripe webhook URL | 部署到 Cloudflare Workers / Vercel Edge 都行(HTTP POST handler) |
| Stripe webhook secret | 在 Stripe Dashboard 创建 endpoint 后获取,存 secret store |
| 撤销关系时 | relation.revoked → 取消 Stripe subscription(避免继续扣费) |
| 退款 | 用户在 Stripe Portal 自助 / 或联系你客服处理 |
V2 marketplace(路线预告)
V2 Hashee marketplace 上线后,平台会内建 Stripe Connect — 用户在 Hashee 内一键订阅 + Hashee 自动 80/20 分成(Creator 80%)。你自己接 Stripe 的方 案会保留为”自托管”选项。
相关页面
- 客服 bot 教程 — Approval 模式(同款 artifact pattern)
- Artifact 入门 — info / actions 字段
- 更新与撤销 — 撤销时取消 Stripe
- 信任中心 — 子处理方