Convex 极地组件 
在您的 Convex 应用程序中添加订阅和账单 极点。 。 。 .
看看这个 示例应用程序 一个完整的例子.
// Get subscription details for the current user
// Note: getCurrentSubscription is for apps that only allow one active
// subscription per user. If you need to support multiple active
// subscriptions, use listUserSubscriptions instead.
const {
productKey,
status,
currentPeriodEnd,
currentPeriodStart,
...
} = await polar.getCurrentSubscription(ctx, {
userId: user._id,
});
// Show available plans
<CheckoutLink
polarApi={api.example}
productIds={[products.premiumMonthly.id, products.premiumYearly.id]}
// Optional: turn off embedding to link to a checkout page
embed={false}
>
Upgrade to Premium
</CheckoutLink>
// Manage existing subscriptions
<CustomerPortalLink polarApi={api.example}>
Manage Subscription
</CustomerPortalLink>
先决条件
Convex 应用程序
你需要一个Convex App来使用组件. 跟着任何 Convex 快速启动 来设置一个.
极账户
- 创建极账户
- 创建一个组织并创建一个组织标志,并有权限:
products:readproducts:writesubscriptions:readsubscriptions:writecustomers:readcustomers:writecheckouts:readcheckouts:writecheckout_links:readcheckout_links:writecustomer_portal:readcustomer_portal:writecustomer_sessions:write
安装
安装组件包 :
npm install @convex-dev/polar
创建一个 convex.config.ts 应用程序中的文件 convex/ 文件夹并安装
通过调用 app.use数字 :
// convex/convex.config.ts
import { defineApp } from "convex/server";
import polar from "@convex-dev/polar/convex.config.js";
const app = defineApp();
app.use(polar);
export default app;
设置您的极组织符号 :
npx convex env set POLAR_ORGANIZATION_TOKEN xxxxx
使用量
设置极地网点
极地组件使用webhooks保持订阅数据同步. 你会需要的 改为:
-
在极地仪表板上创建一则网钩和网钩秘密,使用您 Convex 站点 URL + 键
/polar/events作为网点。 应该是这样的https://verb-noun-123.convex.site/polar/events启用以下事件 :
product.createdproduct.updatedsubscription.createdsubscription.updated
-
设置您的 Convex 环境中的 Webhook 密钥 :
npx convex env set POLAR_WEBHOOK_SECRET xxxxx
- 在您的网页处理器中注册
convex/http.ts数字 :
import { httpRouter } from "convex/server";
import { polar } from "./example";
const http = httpRouter();
// Register the webhook handler at /polar/events
polar.registerRoutes(http as any);
export default http;
您也可以通过该程序为任何极地 Webhook 事件提供类型安全处理器
events 选项。 手提箱接收基于以下特性的已打出的全部事件有效载荷:
事件密钥 :
polar.registerRoutes(http, {
// Optional custom path, default is "/polar/events"
path: "/polar/events",
events: {
"subscription.updated": async (ctx, event) => {
// event.data is typed as Subscription
if (event.data.customerCancellationReason) {
console.log("Customer cancelled:", event.data.customerCancellationReason);
}
},
"order.created": async (ctx, event) => {
// event.data is typed as Order
console.log("New order:", event.data.id);
},
},
});
- 一定要运行
npx convex dev用极点启动您的 Convex 应用程序 启用组件, 它将将 Webhook 处理器部署到您的 Convex 举个例子.
在极地创建产品
在极地仪表板上为您想要的每个定价计划创建产品 提供。 产品数据将自动同步到您的 Convex 应用程序 .
该组件支持所有极地价格类型:固定、免费、自定义 (付费 -什么 -你需要), 座椅,和计量吸入器。 产品还可包括: 在极地仪表板或 离职时间.
使用此组件前创建的产品需要与 Convex 同步
使用 syncProducts 函数.
初始化极端客户端
在 Convex 后端创建极端客户端 :
// convex/example.ts
import { Polar } from "@convex-dev/polar";
import { api, components } from "./_generated/api";
import { DataModel } from "./_generated/dataModel";
export const polar = new Polar<DataModel>(components.polar, {
// Required: provide a function the component can use to get the current user's ID and
// email - this will be used for retrieving the correct subscription data for the
// current user. The function should return an object with `userId` and `email`
// properties.
getUserInfo: async (ctx) => {
const user = await ctx.runQuery(api.example.getCurrentUser);
return {
userId: user._id,
email: user.email,
};
},
// Optional: Configure static keys for referencing your products.
// Alternatively you can use the `listAllProducts` function to get
// the product data and sort it out in your UI however you like
// (eg., by price, name, recurrence, etc.).
// Map your product keys to Polar product IDs (you can also use env vars for this)
// Replace these keys with whatever is useful for your app (eg., "pro", "proMonthly",
// whatever you want), and replace the values with the actual product IDs from your
// Polar dashboard
products: {
premiumMonthly: "product_id_from_polar",
premiumYearly: "product_id_from_polar",
premiumPlusMonthly: "product_id_from_polar",
premiumPlusYearly: "product_id_from_polar",
},
// Optional: Set Polar configuration directly in code
organizationToken: "your_organization_token", // Defaults to POLAR_ORGANIZATION_TOKEN env var
webhookSecret: "your_webhook_secret", // Defaults to POLAR_WEBHOOK_SECRET env var
server: "sandbox", // Optional: "sandbox" or "production", defaults to POLAR_SERVER env var
});
// Export API functions from the Polar client
export const {
changeCurrentSubscription,
cancelCurrentSubscription,
getConfiguredProducts,
listAllProducts,
listAllSubscriptions,
generateCheckoutLink,
generateCustomerPortalUrl,
} = polar.api();
显示产品和价格
使用导出 getConfiguredProducts 或者说 listAllProducts 要显示的函数
您的产品及其价格:
getConfiguredProducts
// Simple example of displaying products and prices if you've configured
// products by key in the Polar constructor
function PricingTable() {
const products = useQuery(api.example.getConfiguredProducts);
if (!products) return null;
return (
<div>
{products.premiumMonthly && (
<div>
<h3>{products.premiumMonthly.name}</h3>
<p>
${(products.premiumMonthly.prices[0].priceAmount ?? 0) / 100}/month
</p>
</div>
)}
{products.premiumYearly && (
<div>
<h3>{products.premiumYearly.name}</h3>
<p>
${(products.premiumYearly.prices[0].priceAmount ?? 0) / 100}/year
</p>
</div>
)}
</div>
);
}
listAllProducts
// Simple example of displaying products and prices if you haven't configured
// products by key in the Polar constructor
function PricingTable() {
const products = useQuery(api.example.listAllProducts);
if (!products) return null;
// You can sort through products in the client as below, or you can use
// `polar.listAllProducts` in your own Convex query and return your desired
// products to display in the UI.
const proMonthly = products.find(
(p) => p.prices[0].recurringInterval === "month",
);
const proYearly = products.find(
(p) => p.prices[0].recurringInterval === "year",
);
return (
<div>
{proMonthly && (
<div>
<h3>{proMonthly.name}</h3>
<p>
${(proMonthly.prices[0].priceAmount ?? 0) / 100}/
{proMonthly.prices[0].recurringInterval}
</p>
</div>
)}
{proYearly && (
<div>
<h3>{proYearly.name}</h3>
<p>${(proYearly.prices[0].priceAmount ?? 0) / 100}/year</p>
</div>
)}
</div>
);
}
每种产品包括:
id:极地产品IDname: 产品名称description: 产品说明isRecurring: 产品是否为订阅recurringInterval:计费间隔(如"月份","年份"等)trialInterval: 试验时间间隔(如果配置)trialIntervalCount: 审判间隔次数(如果配置)benefits: 产品效益矩阵(说明、类型等)prices: 价格矩阵,每组都有amountType确定可用字段:- 已固定数字 :
priceAmount(以美分计),priceCurrency - 免费: 无量字段
- 自定义数字 :
minimumAmount, (中文(简体) ),maximumAmount, (中文(简体) ),presetAmount, (中文(简体) ),priceCurrency - 座标( b)数字 :
seatTiers(数目{ minSeats, maxSeats, pricePerSeat }, (中文(简体) ),priceCurrency - 计算单位数字 :
unitAmount, (中文(简体) ),capAmount, (中文(简体) ),meterId, (中文(简体) ),meter(单位:千美元){ id, name }, (中文(简体) ),priceCurrency
- 已固定数字 :
添加订阅 UI 组件
使用提供的 React 组件为您的应用程序添加订阅功能 :
import { CheckoutLink, CustomerPortalLink } from "@convex-dev/polar/react";
import { api } from "../convex/_generated/api";
// For new subscriptions
<CheckoutLink
// For our example, the api.example object includes the generateCheckoutLink
// function. You can also pass any object that includes this function.
polarApi={api.example}
productIds={[products.premiumMonthly.id, products.premiumYearly.id]}
// Optional: turn off embedding to link to a checkout page
embed={false}
>
Upgrade to Premium
</CheckoutLink>
// With a free trial
<CheckoutLink
polarApi={api.example}
productIds={[products.premiumMonthly.id]}
trialInterval="day"
trialIntervalCount={7}
>
Start 7-day free trial
</CheckoutLink>
// Lazy mode: generates checkout URL on click instead of on mount.
// Useful when rendering many checkout links to avoid rate limits.
<CheckoutLink
polarApi={api.example}
productIds={[products.premiumMonthly.id]}
lazy
>
Subscribe
</CheckoutLink>
// For managing existing subscriptions
<CustomerPortalLink
polarApi={{
generateCustomerPortalUrl: api.example.generateCustomerPortalUrl,
}}
>
Manage Subscription
</CustomerPortalLink>
处理订阅更改
极地组件提供处理用户更改的功能 当前用户.
说明: 强烈建议在 改变他们的订阅方式!
// Change subscription
const changeSubscription = useAction(api.example.changeCurrentSubscription);
await changeSubscription({ productId: "new_product_id" });
// Cancel subscription (works for both active and trialing subscriptions)
const cancelSubscription = useAction(api.example.cancelCurrentSubscription);
await cancelSubscription({ revokeImmediately: true });
访问订阅数据
在您的应用程序中查询订阅信息 :
// convex/example.ts
// A query that returns a user with their subscription details
export const getCurrentUser = query({
handler: async (ctx) => {
const user = await ctx.db.query("users").first();
if (!user) throw new Error("No user found");
const subscription = await polar.getCurrentSubscription(ctx, {
userId: user._id,
});
return {
...user,
subscription,
isFree: !subscription,
isPremium:
subscription?.productKey === "premiumMonthly" ||
subscription?.productKey === "premiumYearly",
};
},
});
API 参考
极端客户端
这个 Polar 类 接受配置对象 :
getUserInfo: 获取当前用户ID和电子邮件的函数products: (可选) 任意命名的极地产品ID密钥地图organizationToken: (可选)你的极地组织标志. 倒计时POLAR_ORGANIZATION_TOKEN环境 varwebhookSecret: (可选)你的极地网球秘诀. 倒计时POLAR_WEBHOOK_SECRET环境 varserver: (可选)极地服务器环境:"sandbox"或"生产". 倒计时POLAR_SERVER环境 var
反应组件
退出链接
道具 :
polarApi: 含有generateCheckoutLink函数productIds: 复选时要显示的产品标识组children: 反应儿童(按钮内容)embed: (可选)是否嵌入出票. 默认为true。 。 。 .lazy: (可选) 推迟检出 URL 生成直到点击. 有用时 提供许多链接。 默认为false。 。 。 .theme: (可选)嵌入式取出主题:"dark"或者说"light"默认 改为"dark"。 。 。 .trialInterval: (可选) 审判时间间隔:"day", (中文(简体) ),"week", (中文(简体) ),"month",或"year"。 。 。 .trialIntervalCount: (备选) 审判间隔次数(例如,77个 (日数).className: (可选) CSS 类名subscriptionId: (可选)订阅升级的ID. 这一定是在 免费定价.metadata: (可选) 离职附加信息的元数据locale: (可选) 退出页面的语言(例如,),"fr", (中文(简体) ),"de"(中文(简体) )
客户端端链接
道具 :
polarApi: 含有generateCustomerPortalUrl函数children: 反应儿童(按钮内容)className: (可选) CSS 类名
API 函数
下面的功能是: Polar 实例,用于您
Convex后端代码. 相应的对应名称(通过
polar.api())显示于 初始化部分
见上文.
更改当前订阅
更改对新计划的现有订阅 :
await changeSubscription({ productId: "new_product_id" });
取消当前订阅
取消正在或正在试验的订阅 :
await cancelSubscription({ revokeImmediately: true });
获取当前订阅
获取当前用户的活动订阅 。 回返 null 如果没有活动
订阅已存在 。 过期的审判不包括在内.
const subscription = await polar.getCurrentSubscription(ctx, { userId });
列表用户订阅
对于支持每个用户多个活动订阅的应用程序. 返回活动 订阅,不包括已终止的订阅和已到期的审判.
const subscriptions = await polar.listUserSubscriptions(ctx, { userId });
列表 全部用户订阅
返回用户的所有订阅,包括已结束和已过期的试验。 有用 用于显示订阅历史或区分“从未订阅” 和"审判期满".
const allSubscriptions = await polar.listAllUserSubscriptions(ctx, { userId });
生产
以极地产品ID获得单一产品:
const product = await polar.getProduct(ctx, { productId });
获取客户端
为用户获取极地客户记录 :
const customer = await polar.getCustomerByUserId(ctx, userId);
产品
列出所有现有产品及其价格:
const products = await polar.listProducts(ctx);
登记册
登记极地组件的 Webhook 处理器 :
polar.registerRoutes(http, {
// Optional: customize the webhook endpoint path (defaults to "/polar/events")
path: "/custom/webhook/path",
// Optional: typesafe handlers for any Polar webhook event
events: {
"order.created": async (ctx, event) => {
// event.data is typed as Order
},
},
});
这个 events 选项为 30+ 中的任意一个接受处理器 极地网点事件
类型对事件有效载荷有完整的TypeScript推断。 内置装卸
(长期订阅和产品)总是自动运行,无论
其中你处理的事件.
Webhook 处理器使用 webhookSecret 从极端客户端配置
或 POLAR_WEBHOOK_SECRET 环境变量.
同步产品
同步来自极地的现有产品( 必须在动作内运行) :
export const syncProducts = action({
args: {},
handler: async (ctx) => {
await polar.syncProducts(ctx);
},
});