SuperAPI AI 工具集

polar-integration

编程与开发

查看项目

更新于 10-19 23:42

技能介绍

一、用途 Polar Integration 是一份面向 TypeScript/JavaScript 应用的接入指南,目标是把 Polar 的计费能力直接接入现有应用,核心是使用 @polar-sh/sdk 包调用 Polar 的 HTTP API。指南围绕三个相互独立的功能端点展开:创建结账、打开客户门户、接收并校验 Webhook 事件。用户只需实现自己需要的部分。

二、安装与初始化 安装 SDK: npm install @polar-sh/sdk 也可使用 pnpm / yarn / bun。指南明确提示不要安装 @polar-sh/<framework> 这类框架包(如 @polar-sh/nextjs、@polar-sh/express、@polar-sh/hono 等),因为它们已弃用,这些配方只需要 @polar-sh/sdk。

所需环境变量:

  • POLAR_ACCESS_TOKEN:Polar 后台的组织访问令牌。
  • POLAR_WEBHOOK_SECRET:仅在实现 Webhook 配方时需要。
  • POLAR_SERVER:开发时设为 sandbox,生产环境可省略或设为 production。

初始化一个客户端并复用: import { Polar } from "@polar-sh/sdk"; const polar = new Polar({ accessToken: process.env.POLAR_ACCESS_TOKEN, server: (process.env.POLAR_SERVER as "sandbox" | "production") ?? "production", });

三、Checkout 结账端点 目标是提供一个 HTTP 端点,创建 Polar 结账会话后以 302 跳转到托管的结账页。底层 API 为 POST /v1/checkouts/,SDK 调用为 polar.checkouts.create(...)。

主要字段(SDK 使用 camelCase,HTTP API 使用 snake_case):

  • products(必填):产品 UUID 数组,第一个默认选中。
  • customerId / externalCustomerId:已有 Polar 客户 ID 或自有用户 ID;若未匹配到客户,会以外部 ID 创建新客户。
  • customerEmail、customerName、customerBillingName、customerTaxId、customerIpAddress:客户信息。
  • customerBillingAddress:账单地址对象,设置地址时 country(ISO alpha-2)必填。
  • customerMetadata、metadata:分别复制到客户和订单/订阅。
  • isBusinessCustomer:默认 false,为 true 时需要完整账单地址与名称。
  • customFieldData、discountId、allowDiscountCodes(默认 true)、requireBillingAddress(默认 false,美国客户始终要求)。
  • seats、minSeats / maxSeats:仅基于席位的定价可用。
  • amount:以分为单位,仅用于自定义(随意付费)价格。
  • subscriptionId:升级已有的免费订阅。
  • successUrl、returnUrl、embedOrigin、locale(默认 en-US)、currency。
  • trialInterval / trialIntervalCount、allowTrial(默认 true)、prices(按产品 ID 覆盖目录价格)。

响应包含 { id, url, client_secret, expires_at, status, ... },将用户重定向到 url 即可;client_secret 仅在使用 @polar-sh/checkout 嵌入结账时需要。successUrl 中的 {CHECKOUT_ID} 会在跳转时被替换为实际会话 ID,参数名可自定义。托管结账页支持在 URL 上追加 ?theme=light|dark 设置主题,但这不是 create 的 API 字段。

官方给出的标准实现基于 Request/Response,可直接用于 Deno、Bun、Cloudflare Workers、Supabase Edge Functions、SvelteKit、Remix、TanStack Start、Astro 以及 Next.js Route Handlers。示例处理 GET 请求,从查询参数读取 products 及可选客户信息,调用 polar.checkouts.create,然后 Response.redirect(url, 302)。各框架的差异仅在请求签名、URL 获取和响应构造方式,例如 Next.js 放在 app/api/checkout/route.ts,Express 用 res.redirect,Fastify 用 reply.redirect,Hono 用 c.redirect,Elysia 用 redirect,Nuxt 用 sendRedirect,SvelteKit 用 +server.ts,Astro 用 APIRoute。若前端已由服务端渲染并知道用户信息,应直接从鉴权上下文写入 externalCustomerId,而不是从查询参数读取。

四、Customer Portal 客户门户端点 目标是一个带鉴权的端点:创建 Polar 客户会话后以 302 跳转到托管的客户门户(订阅、发票、支付方式)。底层 API 为 POST /v1/customer-sessions/,SDK 调用为 polar.customerSessions.create(...)。

请求体为联合类型:customerId 与 externalCustomerId 二选一传入。可选 returnUrl(门户返回按钮地址)、memberId 或 externalMemberId(仅对启用 member_model_enabled 的组织有意义)。

响应包含 { id, customer_portal_url, token, expires_at, customer, customer_id, return_url, ... },重定向到 customer_portal_url;若要在自有界面中展示门户数据,可使用 token 和 customer。

标准实现先从鉴权中取用户 ID(如 Cookie、JWT 或会话),未登录返回 401,然后调用 polar.customerSessions.create({ externalCustomerId, returnUrl }),重定向到返回的 customerPortalUrl。框架差异同样只在请求/响应写法,鉴权状态读取方式按框架而定(Next.js 用 cookies(),Express 用 req.user,Hono 用 c.get('user') 等)。

五、Webhooks 事件端点 目标是在 Polar 后台配置的固定 URL 上接收事件、验证签名并分发到各事件处理器。契约:用 @polar-sh/sdk/webhooks 的 validateEvent 校验 webhook-id、webhook-timestamp、webhook-signature 三个请求头,成功返回 200,签名不匹配返回 403。

标准实现读取 request.text() 原始请求体(不能是已解析的 JSON),调用 validateEvent(body, headers, WEBHOOK_SECRET),捕获 WebhookVerificationError 时返回 403,然后按 event.type 分支处理。指南列出的事件包括:checkout.created/updated;order.created/updated/paid/refunded;subscription.created/updated/active/canceled/uncanceled/revoked;refund.created/updated;product.created/updated;benefit.created/updated;benefit_grant.created/updated/revoked;customer.created/updated/deleted/state_changed;organization.updated。event 有完整类型,TypeScript 会在各分支内收窄 event.data。

关键注意点:

  • 必须使用原始请求体做签名校验。若框架已自动解析 JSON(如 Express 的 express.json()、Fastify 默认解析器),需在该路由禁用解析或手动读取原始体;重新序列化的 JSON 会导致校验失败。
  • 快速响应。Polar 对非 2xx 与超时会重试;耗时处理应放入队列/后台任务并立即返回 200。
  • 幂等。Polar 可能重复投递,应按 webhook-id 请求头去重(遵循 Standard Webhooks 规范);不要按 event.data.id 去重,因为同一资源的不同事件共享该 ID。
  • 路由必须公开。Polar 不跟随重定向,若路由被鉴权中间件重定向到登录页,所有投递都会失败;签名即认证。

本地测试:运行开发服务器后,用 Polar CLI 转发并触发事件: polar webhook listen 3000/api/polar/webhook # 保持后台运行 polar webhook trigger order.paid # 处理器失败时退出码为 1 并打印响应 本地 POLAR_WEBHOOK_SECRET 来自 polar webhook listen --print-secret。

框架差异:Next.js 放在 app/api/polar/webhook/route.ts,App Router 不自动解析请求体,request.text() 即可;Express 使用 express.raw({ type: "application/json" }) 仅挂载到该路由;Fastify 需保留原始体的 content-type 解析器或 @fastify/raw-body;Hono 用 await c.req.text();Elysia、SvelteKit、Astro 均用 request.text()。

六、适用场景与限制 适用于需要为 TypeScript/JavaScript 应用快速接入 Polar 结账、客户自助门户和计费事件的开发者。限制包括:只覆盖上述三个端点及相关字段,其他 Polar 能力未在本资料中说明;条款、价格、平台支持范围等信息暂未提供;框架适配只涉及请求/响应写法,不含完整的部署或上线步骤。