@convex-dev/polar

中文文档
0.9.2发布于 2026/06/25Apache-2.0TypeScriptES Module近 30 天下载 4.9万

Convex 的极性组件 .

中文使用文档
文档目录16 节
  1. 先决条件
  2. Convex 应用程序
  3. 极账户
  4. 安装
  5. 使用量
  6. 设置极地网点
  7. 在极地创建产品
  8. 初始化极端客户端
  9. 显示产品和价格
  10. 添加订阅 UI 组件
  11. 处理订阅更改
  12. 访问订阅数据
  13. API 参考
  14. 极端客户端
  15. 反应组件
  16. API 函数

Convex 极地组件 npm version

在您的 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:read
    • products:write
    • subscriptions:read
    • subscriptions:write
    • customers:read
    • customers:write
    • checkouts:read
    • checkouts:write
    • checkout_links:read
    • checkout_links:write
    • customer_portal:read
    • customer_portal:write
    • customer_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保持订阅数据同步. 你会需要的 改为:

  1. 在极地仪表板上创建一则网钩和网钩秘密,使用您 Convex 站点 URL + 键 /polar/events 作为网点。 应该是这样的 https://verb-noun-123.convex.site/polar/events

    启用以下事件 :

    • product.created
    • product.updated
    • subscription.created
    • subscription.updated
  2. 设置您的 Convex 环境中的 Webhook 密钥 :

npx convex env set POLAR_WEBHOOK_SECRET xxxxx
  1. 在您的网页处理器中注册 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);
    },
  },
});
  1. 一定要运行 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:极地产品ID
  • name: 产品名称
  • 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 环境 var
  • webhookSecret: (可选)你的极地网球秘诀. 倒计时 POLAR_WEBHOOK_SECRET 环境 var
  • server: (可选)极地服务器环境:"sandbox"或"生产". 倒计时 POLAR_SERVER 环境 var

反应组件

道具 :

  • polarApi: 含有 generateCheckoutLink 函数
  • productIds: 复选时要显示的产品标识组
  • children: 反应儿童(按钮内容)
  • embed: (可选)是否嵌入出票. 默认为 true。 。 。 .
  • lazy: (可选) 推迟检出 URL 生成直到点击. 有用时 提供许多链接。 默认为 false。 。 。 .
  • theme: (可选)嵌入式取出主题: "dark" 或者说 "light"默认 改为 "dark"。 。 。 .
  • trialInterval: (可选) 审判时间间隔: "day", (中文(简体) ), "week", (中文(简体) ), "month",或 "year"。 。 。 .
  • trialIntervalCount: (备选) 审判间隔次数(例如, 7 7个 (日数).
  • 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);
  },
});

版本与发布记录

@convex-dev/polar 共发布 19 个版本,首次发布于 2025/04/05;过去 12 个月发布了 8 个版本。

版本发布日期距今
0.9.2latest2026/06/254 个月前
0.9.12026/05/046 个月前
0.9.02026/03/247 个月前
0.8.12026/02/158 个月前
0.8.02026/02/158 个月前
0.7.32025/11/2811 个月前

关于 @convex-dev/polar 的常见问题

@convex-dev/polar 是什么?

Convex 的极性组件 .

如何安装 @convex-dev/polar?

在项目目录运行 npm install @convex-dev/polar,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^18 || ^19)、convex(^1.29.0)、react-dom(^18 || ^19)、@polar-sh/sdk(>=0.45.0)、@polar-sh/checkout(>=0.1.10)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@convex-dev/polar 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@convex-dev/polar 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@convex-dev/polar 可以商用吗?

@convex-dev/polar 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@convex-dev/polar 的下载量有多少?

根据 npm 官方下载统计,@convex-dev/polar 在 2026/10/11 – 2026/11/09 这 30 天内被下载 49,304 次,日均约 1,643 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@convex-dev/polar 还在维护吗?

最新版本 0.9.2 发布于 2026/06/25(4 个月前);至今共发布 19 个版本,其中过去 12 个月发布了 8 个;首次发布于 2025/04/05。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。