跳转到内容

接 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 的方 案会保留为”自托管”选项。

相关页面