@ convex-dev/ stripe (英语)
用于整合面额支付、订阅和计费的Convex组件 输入您的 Convex 应用程序 .
需要节点.js 18 或以后, 匹配 Strede 节点 SDK v22 运行时间 页:1.
特征
- 🛒 退出会话 - 建立一次性付款和订阅结账制度
- 📦 订阅管理 - 创建、更新、取消订阅
- 👥 客户管理 - 自动创建客户和链接
- 💳 客户端门户 - 让用户管理账单
- 🪑 基于座位的定价 - 更新团队账单的订阅数量
- 🔗 用户/操作链接 - 连接用户的付款和订阅 组织
- 🔔 Webhook 处理 - 将条纹数据自动同步到您的 Convex 数据库
- 📊 实时数据 - 实时查询付款、订阅和发票
快速启动
1. 安装组件
npm install @convex-dev/stripe
2. 添加到您的 Convex 应用程序
创建或更新 convex/convex.config.ts数字 :
import { defineApp } from "convex/server";
import stripe from "@convex-dev/stripe/convex.config.js";
const app = defineApp();
app.use(stripe);
export default app;
3. 设置环境变量
把这些添加到您的 凸起板 环境变量:
| 变量 | 说明 |
|---|---|
STRIPE_SECRET_KEY | 您的条纹密钥( E)sk_test_... 或者说 sk_live_...(中文(简体) ) |
STRIPE_WEBHOOK_SECRET | Webhook 签名秘密( Namewhsec_...- 见步骤4 |
4. 配置花纹 Webhooks
- 转到 Frape Dashboard – 开发者 – Webhooks 页面存档备份,存于互联网档案馆
- 点击 “添加终点”
- 输入您的 Webhook URL :
(在Convex仪表板上找到您的部署名称 - 这是之前的部分)https://<your-convex-deployment>.convex.site/stripe/webhook.convex.cloud在您的 URL 中) - 选择这些事件 :
checkout.session.completedcustomer.createdcustomer.updatedcustomer.deletedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.createdinvoice.finalizedinvoice.updatedinvoice.paidinvoice.payment_failedpayment_intent.succeededpayment_intent.payment_failed
- 点击 “添加终点”
- 复制 签名秘密 并添加为
STRIPE_WEBHOOK_SECRET在凸起处
5. 登记Webhook路线
创建 convex/http.ts数字 :
import { httpRouter } from "convex/server";
import { components } from "./_generated/api";
import { registerRoutes } from "@convex-dev/stripe";
const http = httpRouter();
// Register Stripe webhook handler at /stripe/webhook
registerRoutes(http, components.stripe, {
webhookPath: "/stripe/webhook",
apiVersion: "2026-04-22.dahlia", // Optional
});
export default http;
6. 使用本构成部分
创建 convex/stripe.ts数字 :
import { action } from "./_generated/server";
import { components } from "./_generated/api";
import { StripeSubscriptions } from "@convex-dev/stripe";
import { v } from "convex/values";
const stripeClient = new StripeSubscriptions(components.stripe, {});
// Create a checkout session for a subscription
export const createSubscriptionCheckout = action({
args: { priceId: v.string() },
returns: v.object({
sessionId: v.string(),
url: v.union(v.string(), v.null()),
}),
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Not authenticated");
// Get or create a Stripe customer
const customer = await stripeClient.getOrCreateCustomer(ctx, {
userId: identity.subject,
email: identity.email,
name: identity.name,
});
// Create checkout session
return await stripeClient.createCheckoutSession(ctx, {
priceId: args.priceId,
customerId: customer.customerId,
mode: "subscription",
successUrl: "http://localhost:5173/?success=true",
cancelUrl: "http://localhost:5173/?canceled=true",
subscriptionMetadata: { userId: identity.subject },
});
},
});
// Create a checkout session for a one-time payment
export const createPaymentCheckout = action({
args: { priceId: v.string() },
returns: v.object({
sessionId: v.string(),
url: v.union(v.string(), v.null()),
}),
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Not authenticated");
const customer = await stripeClient.getOrCreateCustomer(ctx, {
userId: identity.subject,
email: identity.email,
name: identity.name,
});
return await stripeClient.createCheckoutSession(ctx, {
priceId: args.priceId,
customerId: customer.customerId,
mode: "payment",
successUrl: "http://localhost:5173/?success=true",
cancelUrl: "http://localhost:5173/?canceled=true",
paymentIntentMetadata: { userId: identity.subject },
});
},
});
API 参考
条纹订阅客户端
import { StripeSubscriptions } from "@convex-dev/stripe";
const stripeClient = new StripeSubscriptions(components.stripe, {
STRIPE_SECRET_KEY: "sk_...", // Optional, defaults to process.env.STRIPE_SECRET_KEY
apiVersion: "2026-04-22.dahlia", // Optional Stripe API version
});
方法
| 方法 | 说明 |
|---|---|
createCheckoutSession() | 创建条纹检查会话 |
createCustomerPortalSession() | 生成客户端 URL |
createCustomer() | 创建新条纹客户端 |
getOrCreateCustomer() | 获取已有客户或创建新客户 |
cancelSubscription() | 取消订阅 |
reactivateSubscription() | 重新启动要取消的订阅集 |
updateSubscriptionQuantity() | 更新座位数 |
创建检查会议
await stripeClient.createCheckoutSession(ctx, {
priceId: "price_...",
customerId: "cus_...", // Optional
mode: "subscription", // "subscription" | "payment" | "setup"
successUrl: "https://...",
cancelUrl: "https://...",
quantity: 1, // Optional, default 1
metadata: {}, // Optional, session metadata
subscriptionMetadata: {}, // Optional, attached to subscription
paymentIntentMetadata: {}, // Optional, attached to payment intent
params: {
allow_promotion_codes: true,
ui_mode: "embedded_page",
return_url: "https://...",
}, // Optional Stripe Checkout Session params
});
params 是 Strefe 取出会话字段的打出通过
组件不直接建模。 域数 params 覆盖已构建
默认,但 mode继续由高层控制 mode
参数。 对于非托管的UI 模式,组件省略 successUrl 财务报告和审定财务报表
cancelUrl 从 Strade 请求并期望提供重定向行为
通过条纹支持的参数,例如 return_url。 。 。 .
组件查询
通过组件的公开查询直接访问数据:
import { query } from "./_generated/server";
import { components } from "./_generated/api";
// List subscriptions for a user
export const getUserSubscriptions = query({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) return [];
return await ctx.runQuery(
components.stripe.public.listSubscriptionsByUserId,
{ userId: identity.subject },
);
},
});
// List payments for a user
export const getUserPayments = query({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) return [];
return await ctx.runQuery(components.stripe.public.listPaymentsByUserId, {
userId: identity.subject,
});
},
});
可用的公共查询
| 查询 | 参数 | 说明 |
|---|---|---|
getCustomer | stripeCustomerId | 用花旗身份证明客户 |
listSubscriptions | stripeCustomerId | 为客户订阅的列表 |
listSubscriptionsByUserId | userId | 用户订阅列表 |
getSubscription | stripeSubscriptionId | 由 ID 订阅 |
getSubscriptionByOrgId | orgId | 获取 Org 的订阅 |
getPayment | stripePaymentIntentId | 通过身份证明获得付款 |
listPayments | stripeCustomerId | 客户清单付款 |
listPaymentsByUserId | userId | 列出用户的付款 |
listPaymentsByOrgId | orgId | 列出某矿石的付款 |
listInvoices | stripeCustomerId | 列出客户发票 |
listInvoicesByUserId | userId | 列出用户发票 |
listInvoicesByOrgId | orgId | 列出一矿石的发票 |
Webhook 事件
该组件自动处理这些 Strade webhook 事件 :
| 活动 | 行动 |
|---|---|
customer.created | 创建客户记录 |
customer.updated | 更新客户记录 |
customer.deleted | 刷新客户 PII |
customer.subscription.created | 创建订阅记录 |
customer.subscription.updated | 更新订阅记录 |
customer.subscription.deleted | 将订阅标记为已取消 |
payment_intent.succeeded | 创建付款记录 |
payment_intent.payment_failed | 更新付款状况 |
invoice.created | 创建发票记录 |
invoice.finalized | 发票记录 |
invoice.updated | 镜像发票元数据更改 |
invoice.paid | 预付发票 |
invoice.payment_failed | 标记发票失败 |
checkout.session.completed | 处理已完成的取出会话 |
自定义 Webhook 处理器
向 Webhook 事件添加自定义逻辑 :
import { httpRouter } from "convex/server";
import { components } from "./_generated/api";
import { registerRoutes } from "@convex-dev/stripe";
import type Stripe from "stripe";
const http = httpRouter();
registerRoutes(http, components.stripe, {
events: {
"customer.subscription.updated": async (ctx, event: Stripe.CustomerSubscriptionUpdatedEvent) => {
const subscription = event.data.object;
console.log("Subscription updated:", subscription.id, subscription.status);
// Add custom logic here
},
},
onEvent: async (ctx, event: Stripe.Event) => {
// Called for ALL events - useful for logging/analytics
console.log("Stripe event:", event.type);
},
});
export default http;
数据库Schema
该组件在其命名空间创建这些表格:
客户
| 外地 | 类型 | 说明 |
|---|---|---|
stripeCustomerId | 字符串 | 条纹客户身份 |
email | 绳子? | 客户电子邮件 |
name | 绳子? | 客户名称 |
metadata | 对象? | 自定义元数据 |
订阅
| 外地 | 类型 | 说明 |
|---|---|---|
stripeSubscriptionId | 字符串 | 字条订阅 ID |
stripeCustomerId | 字符串 | 客户身份 |
status | 字符串 | 订阅状态 |
priceId | 字符串 | 价格标识 |
quantity | 号码? | 座位数 |
currentPeriodEnd | 编号 | 期末时间戳 |
cancelAtPeriodEnd | 布尔 | 将在期末取消 |
userId | 绳子? | 链接用户 ID |
orgId | 绳子? | 链接的 Ong ID |
metadata | 对象? | 自定义元数据 |
检查出会话( s)
| 外地 | 类型 | 说明 |
|---|---|---|
stripeCheckoutSessionId | 字符串 | 退出会话编号 |
stripeCustomerId | 绳子? | 客户身份 |
status | 字符串 | 会议状态 |
mode | 字符串 | 会话模式(付费/订阅/设置) |
metadata | 对象? | 自定义元数据 |
付款
| 外地 | 类型 | 说明 |
|---|---|---|
stripePaymentIntentId | 字符串 | 付款意向标识 |
stripeCustomerId | 绳子? | 客户身份 |
amount | 编号 | 数额(美分) |
currency | 字符串 | 货币代码 |
status | 字符串 | 缴款情况 |
created | 编号 | 创建时间戳 |
userId | 绳子? | 链接用户 ID |
orgId | 绳子? | 链接的 Ong ID |
metadata | 对象? | 自定义元数据 |
发票
| 外地 | 类型 | 说明 |
|---|---|---|
stripeInvoiceId | 字符串 | 发票身份证 |
stripeCustomerId | 字符串 | 客户身份 |
stripeSubscriptionId | 绳子? | 订阅编号 |
status | 字符串 | 发票状况 |
amountDue | 编号 | 应付款 |
amountPaid | 编号 | 支付金额 |
created | 编号 | 创建时间戳 |
userId | 绳子? | 链接用户 ID |
orgId | 绳子? | 链接的 Ong ID |
metadata | 对象? | 发票元数据 |
示例应用
请检查url=值 (帮助) example/ 目录 :
git clone https://github.com/get-convex/convex-stripe
cd convex-stripe
npm install
npm run dev
例子包括:
- 有产品展示的登陆页面
- 一次性付款和订阅
- 有命令历史的用户配置文件
- 订阅管理(取消,更新席位)
- 客户门户整合
- 团队/组织账单
认证
此组件与任何 Convex 认证提供者合作 。 示例使用 办事员数字 :
- 在 书记员.com
- 添加
VITE_CLERK_PUBLISHABLE_KEY给您的.env.local - 创建
convex/auth.config.ts数字 :
export default {
providers: [
{
domain: "https://your-clerk-domain.clerk.accounts.dev",
applicationID: "convex",
},
],
};
解决问题
退职后表格为空
确保你已经:
- 设定
STRIPE_SECRET_KEY财务报告和审定财务报表STRIPE_WEBHOOK_SECRET位于 Convex 环境中 变量 - 配置了 Frede 中的 Webhook 端点, 并带有正确的事件
- 已添加
invoice.created财务报告和审定财务报表invoice.finalized事件(不仅仅是invoice.paid(中文(简体) )
“ 未验证” 错误
确保您的认证提供者配置 :
- 创建
convex/auth.config.ts与您的提供者 - 运行
npx convex dev来推动配置 - 在调用动作前验证用户签名
Webhooks 返回 400/500
请检查date=中的日期值 (帮助) Convex日志在您的仪表板中查找出错. 共同问题:
- 失踪
STRIPE_WEBHOOK_SECRET - 错误的 Webhook URL( 应该是)
https://<deployment>.convex.site/stripe/webhook(中文(简体) ) - Webhook 配置中缺失的事件
许可证
阿帕奇 - 2.0