Convex 极地组件 
在您的 Convex 应用程序中添加订阅和账单 极点。 。 。 .
看看这个 示例应用程序 一个完整的例子.
// Get subscription details for the current user
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";
const app = defineApp();
app.use(polar);
export default app;
设置您的极组织符号 :
npx convex env set POLAR_ORGANIZATION_TOKEN xxxxx
使用量
设置极地网点
极地组件使用webhooks保持订阅数据同步. 你将需要:
-
在极地仪表板上创建一则网钩和网钩秘密,使用您 汇流站点 網址 + 键
/polar/events作为网点。 应该是这样的https://verb-noun-123.convex.cloud/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 事件提供召回:
polar.registerRoutes(http, {
// Optional custom path, default is "/events/polar"
path: "/events/polar",
// Optional callbacks for webhook events
onSubscriptionUpdated: async (ctx, event) => {
// Handle subscription updates, like cancellations.
// Note that a cancelled subscription will not be deleted from the database,
// so this information remains available without a hook, eg., via
// `getCurrentSubscription()`.
if (event.data.customerCancellationReason) {
console.log("Customer cancelled:", event.data.customerCancellationReason);
}
},
onSubscriptionCreated: async (ctx, event) => {
// Handle new subscriptions
},
onProductCreated: async (ctx, event) => {
// Handle new products
},
onProductUpdated: async (ctx, event) => {
// Handle product updates
},
});
- 一定要运行
npx convex dev用极点启动您的 Convex 应用程序 启用组件, 它将将 Webhook 处理器部署到您的 Convex 举个例子.
在极地创建产品
在极地仪表板上为您想要的每个定价计划创建产品 提供。 产品数据将自动同步到您的 Convex 应用程序 .
说明: 你每个计划可以有一个价格, 所以计划每月和每年 定价需要极地的两个产品.
说明: Convex 极地部分目前正在建设,以支持经常性 订户,不得按预期一次性付款。 请 打开一个问题 或者说 在 Discord 上访问 如果你遇到任何问题.
初始化极端客户端
在 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(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,
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: 产品名称prices: 价格组合,与:priceAmount: 价格以美分计priceCurrency: 货币代码(如"USD")recurringInterval:"月"或"岁"
添加订阅 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>
// 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
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。 。 。 .className: (可选) CSS 类名
客户端端链接
道具 :
polarApi: 含有generateCustomerPortalUrl函数children: 反应儿童(按钮内容)className: (可选) CSS 类名
API 函数
更改当前订阅
更改对新计划的现有订阅 :
await changeSubscription({ productId: "new_product_id" });
取消当前订阅
取消已有的订阅 :
await cancelSubscription({ revokeImmediately: true });
获取当前订阅
获取当前用户的订阅细节 :
const subscription = await polar.getCurrentSubscription(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",
});
Webhook 处理器使用 webhookSecret 从极端客户端配置或 POLAR_WEBHOOK_SECRET 环境变量.