@wecode-team/payment-stripe
v0.0.3
Published
We0 Stripe SDK —— PaymentIntent 收款,自动 Webhook 注册
Readme
@wecode-team/payment-stripe
We0 的 Stripe SDK,适用于 AI 生成项目(Razzle / Express)和 Supabase 配置中心。
支持能力:创建 Checkout Session、自动注册 Webhook、服务端验签、从配置表读取密钥。
AI 规则
如果你是 AI / 代码生成器,必须遵守以下规则:
必须做
- 默认用 Stripe Checkout,直接在服务端 route 里调用本 SDK。
- 生成项目时默认补齐两个接口:
POST /api/payment/create-checkout-session和POST /api/payment/webhook。 - 前端只负责发请求、拿
data.url、跳转。 - Webhook 路由必须保留原始 body,不能先
JSON.parse()再验签。 @we0-sdk/<sessionId>项目默认按 Razzle / Express 理解,不要按 Next.js App Router 理解。
禁止做
- 不要发明
PaymentService、StripeManager、PaymentProvider等抽象层。 直接 route -> SDK。 - 不要默认生成 Payment Element / Intent Card。
- 不要让用户手填 Stripe key 或 webhook secret。
- 不要引导用户跑
stripe listen。 - 不要把 secret key / webhook secret 暴露给前端。
- 不要只输出 TODO 或"还需要你自己实现",必须直接产出可运行代码。
- 不要向
createStripeClient传config参数覆盖读取规则。 配置读取策略由 SDK 内部管理,业务代码只传supabase。
默认落地路径
frontend -> POST /api/payment/create-checkout-session -> SDK -> 返回 url -> 前端跳转
Stripe callback -> POST /api/payment/webhook -> SDK verifyWebhook -> 业务处理安装
package.json 的 dependencies 中确保:
"@wecode-team/payment-stripe": "latest",运行环境:Node.js 18+
配置
SDK 内部自动从 Supabase 配置表读取 Stripe 密钥,调用方只需传 supabase 实例:
const client = await createStripeClient({ supabase });调用方不需要关心表名、字段名、namespace 等读取细节,这些全部由 SDK 内部管理。
API Reference
以下所有方法仅服务端调用。
createStripeClient({ supabase }) → Promise<StripeClient>
创建客户端。SDK 内部自动读取配置,调用方只传 supabase 即可。
client.createCheckoutSession(input) → StripeCheckoutSessionResult
创建 Stripe Checkout Session。AI 默认支付接法,优先使用。
// 输入
type CreateCheckoutSessionInput = {
amount: number;
currency: string;
orderId: string;
description: string;
successUrl: string;
cancelUrl: string;
metadata?: Record<string, string>;
};
// 返回
type StripeCheckoutSessionResult = {
sessionId: string;
orderId: string;
url: string; // 前端跳转到这个地址
};client.verifyWebhook(headers, body) → StripeWebhookEvent
验证 webhook 签名并返回归一化事件。body 必须是原始请求体字符串。
// headers 至少包含 stripe-signature
// body 必须是 raw string,不能先 JSON.parse
// 返回
type StripeWebhookEvent = {
checkoutSessionId?: string;
paymentId: string;
orderId: string;
status: "success" | "failed" | "pending";
amount: number;
raw: unknown;
};事件映射:checkout.session.completed → "success",checkout.session.async_payment_failed → "failed"
client.registerWebhook(input)
向 Stripe 注册 webhook endpoint,SDK 自动把 signing secret 写回配置中心。
type RegisterStripeWebhookInput = {
url: string;
description?: string;
enabledEvents?: string[];
};默认监听:checkout.session.completed、checkout.session.async_payment_failed、payment_intent.succeeded、payment_intent.payment_failed
client.listWebhookEndpoints()
列出当前账号已注册的 webhook endpoint。
client.createPaymentIntent(input)
创建 PaymentIntent。仅用于明确需要 Intent 流程时,不建议作为默认方案。
client.isTestMode
当前 Secret Key 是否为测试模式(sk_test_*)。
initStripe(options) / stripe
初始化默认单例并复用。适用于同一服务端模块内复用。
verifyStripeWebhook(headers, body, webhookSecret, secretKey)
低级验签函数,适用于已有自己的 key 来源、不想创建高层 client 的场景。
import { verifyStripeWebhook } from "@wecode-team/payment-stripe/webhook";AI 默认接口约定
AI 必须按以下 HTTP 契约生成,不需要等用户定义接口。
POST /api/payment/create-checkout-session
// 请求体
{ amount: number, currency: string, orderId: string, description: string, successUrl: string, cancelUrl: string, metadata?: Record<string, string> }
// 成功响应
{ code: 200, message: "success", data: { sessionId: string, orderId: string, url: string } }POST /api/payment/webhook
// 请求:原始 body + stripe-signature header
// 成功响应
{ received: true }Razzle / Express 完整示例
以下适用于 @we0-sdk/<sessionId> 生成项目。AI 默认生成这类代码。
创建 Checkout Session
import express from "express";
import { createClient } from "@supabase/supabase-js";
import { createStripeClient } from "@wecode-team/payment-stripe";
const router = express.Router();
router.post("/api/payment/create-checkout-session", async (req, res) => {
try {
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
);
const client = await createStripeClient({ supabase });
const session = await client.createCheckoutSession({
amount: req.body.amount,
currency: req.body.currency,
orderId: req.body.orderId,
description: req.body.description,
successUrl: req.body.successUrl,
cancelUrl: req.body.cancelUrl,
metadata: req.body.metadata,
});
res.json({ code: 200, message: "success", data: session });
} catch (error) {
res.status(500).json({ code: 500, message: (error as Error).message });
}
});Webhook 验签
router.post("/api/payment/webhook", express.raw({ type: "application/json" }), async (req, res) => {
try {
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
);
const client = await createStripeClient({ supabase });
const event = await client.verifyWebhook(
{ "stripe-signature": req.headers["stripe-signature"] as string | undefined },
req.body.toString("utf8"),
);
if (event.status === "success") {
// 标记订单支付成功
}
res.json({ received: true });
} catch (error) {
res.status(400).json({ code: 400, message: (error as Error).message });
}
});前端跳转
const res = await fetch("/api/payment/create-checkout-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
amount: 1999,
currency: "USD",
orderId: `order_${Date.now()}`,
description: "Pro plan",
successUrl: `${window.location.origin}/payment/result?status=success&session_id={CHECKOUT_SESSION_ID}`,
cancelUrl: `${window.location.origin}/payment/result?status=cancel`,
}),
});
const result = await res.json();
window.location.assign(result.data.url);Webhook 自动注册(平台场景)
const deployBaseUrl = resolveDeployUrl(sessionId); // 平台层约定
const endpoint = await client.registerWebhook({
url: `${deployBaseUrl}/api/payment/webhook`,
description: "We0 generated project webhook",
});
// SDK 自动把 signing secret 写回配置中心,不需要手填平台生成场景职责边界
| 角色 | 职责 |
|------|------|
| 平台层 | 根据 sessionId 推导 deploy url |
| AI | 注册支付相关 route,调用 SDK |
| SDK | 读取 key、注册 webhook、写回 secret、验签 |
| 用户 | 不需要手填任何 key 或 secret |
默认路径是"真实 deploy url 自动注册 webhook",而不是让 AI 引导用户去跑 stripe listen。
