ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

@polar-sh/nuxt 适配器全解析:在 Nuxt 应用中集成 Polar 支付、Checkout 与 Webhook

@polar-sh/nuxt 适配器全解析:在 Nuxt 应用中集成 Polar 支付、Checkout 与 Webhook polar-sh/nuxt 适配器全解析在 Nuxt 应用中集成 Polar 支付、Checkout 与 Webhook【免费下载链接】polarPolar — A billing platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/po/polarpolar-sh/nuxt 是 Polar 官方为 Nuxt 3 提供的服务端适配器模块将 Polar 的托管结账Checkout、客户门户Customer Portal与 Webhook 验签能力以三个即插即用的 Server Handler 形式封装进 Nuxt 应用。本文以仓库内 CHANGELOG.md 的版本演进为主线结合 README.md 与模块源码、测试用例完整讲解安装配置、Checkout Query 参数、Webhook 细粒度事件处理器与权益Entitlement机制读完即可在 Nuxt 3 应用中落地一套完整的 Polar 收款闭环。模块定位与整体结构polar-sh/nuxt 当前版本为 0.5.8定位是Polar 的 Nuxt 集成适配器见 package.json 的 description它不是一个业务组件库而是一组运行在 Nuxt 服务端Nitro/h3的路由处理器工厂。模块源代码结构如下clients/adapters/nuxt/ ├── src/ │ ├── module.ts # Nuxt 模块定义configKey: polar │ └── runtime/server/ │ ├── checkoutHandler.ts # Checkout 处理器 │ ├── customerPortalHandler.ts # Customer Portal 处理器 │ ├── webhookHandler.ts # Webhook 验签与分发 │ └── index.ts # 统一导出 ├── playground/ # 本地开发调试用 Nuxt 应用 ├── test/ # vitest 单元测试 ├── CHANGELOG.md # 版本演进记录 └── README.md # 使用文档从 module.ts 可以看出模块通过defineNuxtModule注册configKey为polar即nuxt.config.ts中的polar: {}配置块核心动作是调用addServerImportsDir将runtime/server目录下的Checkout、CustomerPortal、Webhooks三个工厂函数注册为 Nuxt 服务端自动导入因此业务代码中无需手动 import。在底层三个处理器都基于 Polar TypeScript SDK源码中引入的是polar-sh/sdk/2026-04这个按 API 版本划分的命名空间通过createPolarCore({ accessToken, environment })创建 SDK 客户端与 h3 的事件对象H3Event协作。安装与模块注册按 README.md 与 package.json 的记录安装要求 Node.js 22使用任意包管理器安装即可pnpm add polar-sh/nuxt然后在nuxt.config.ts中注册模块export default defineNuxtConfig({ modules: [polar-sh/nuxt], })值得注意的一点是仓库内的 playground/nuxt.config.ts 展示了推荐的运行时配置方式——把 Polar 的敏感凭据放进runtimeConfig.private从而避免在业务代码里硬编码密钥export default defineNuxtConfig({ modules: [../src/module], polar: {}, compatibilityDate: 2025-02-25, runtimeConfig: { private: { polarAccessToken: , polarServer: , polarCheckoutSuccessUrl: , polarWebhookSecret: , }, }, })这些私有运行时配置键polarAccessToken、polarServer、polarCheckoutSuccessUrl、polarWebhookSecret会在后续三个 Handler 的示例代码中通过useRuntimeConfig()读取建议在部署时用环境变量注入。Checkout一行代码接入 Polar 托管结账页Polar 的 Checkout 是托管式的你的服务端只需把客户重定向到 Polar 生成的结账 URL。polar-sh/nuxt 的Checkout工厂负责创建结账会话并完成重定向sendRedirect参考 README 的最小实现// server/routes/api/checkout.post.ts export default defineEventHandler((event) { const { private: { polarAccessToken, polarCheckoutSuccessUrl, polarServer }, } useRuntimeConfig() const checkoutHandler Checkout({ accessToken: polarAccessToken, successUrl: polarCheckoutSuccessUrl, returnUrl: https://myapp.com, // 可选结账页显示返回按钮 environment: polarServer as sandbox | production, theme: dark, // 强制深色主题不传则跟随系统主题 }) return checkoutHandler(event) })CheckoutConfig 配置项对照 checkoutHandler.ts 中导出的CheckoutConfig类型可用的配置如下配置项类型说明accessTokenstringPolar 组织访问令牌必填用于创建结账会话successUrlstring支付成功后的跳转地址默认会追加checkout_id参数returnUrlstring可选在结账页渲染返回按钮的回跳地址0.3.12 版本加入includeCheckoutIdboolean是否在successUrl中追加{CHECKOUT_ID}占位符默认trueenvironmentsandbox \| productionPolar 环境对应 SDK 的Environmentthemelight \| dark强制结账页主题0.3.3 版本加入省略则跟随系统偏好底层行为细节从源码实现checkoutHandler.ts可以看到几个关键行为successUrl占位符当includeCheckoutId默认 true时处理器会把{CHECKOUT_ID}占位符写入successUrl的查询参数SDK 创建会话后会用真实的结账 ID 替换方便你在成功页读取checkout_id完成后续订单同步。discount_id与discount_code的优先级discount_id在创建会话时直接传给 API而discount_code是在会话创建成功之后通过clientUpdateCheckouts(result.client_secret, { discount_code })二次调用更新进去的。源码中的if (discountCode !discountId)保证了两者都传时以discount_id为准README 明确标注了这一优先级规则。主题注入theme通过重定向 URL 的theme查询参数传给 Polar 结账页因此测试用例中能看到最终重定向地址形如https://polar.sh/checkout/123?themedark。错误处理任何创建失败都会被包装成 h3 的 500 错误createError({ statusCode: 500, statusMessage: error.message })避免把 Polar 内部错误直接暴露给前端。Checkout Query 参数全解析Polar 结账会话的所有输入都通过请求当前路由的 Query 参数传递README 称之为 Query Params。README 只列举了 7 个常用参数但源码中的 zod 校验模式checkoutHandler.ts实际支持完整参数集下面给出全表参数必填示例说明products是?products123商品 ID可用逗号分隔传多个商品0.3.0 起替代productId/productPriceIdcustomer_id否?customer_idxxx关联已有 Polar 客户external_customer_id否?external_customer_idxxx你自己的系统中的客户 IDcustomer_email否?customer_emailjanedoegmail.com预填客户邮箱customer_name否?customer_nameJane预填客户姓名customer_billing_address否URL 编码的 JSON账单地址对象源码中用JSON.parse解析customer_tax_id否?customer_tax_idTAX1客户税号customer_ip_address否?customer_ip_address10.0.0.1客户 IPcustomer_metadata否URL 编码的 JSON客户自定义元数据JSON.parse解析allow_discount_codes否?allow_discount_codestrue是否允许该结账会话使用折扣码字符串true才会被解析为布尔真discount_id否?discount_iddisc_1预选折扣优先于discount_codediscount_code否?discount_codeSAVE20预填折扣码需在 Polar 后台开启折扣码功能metadata否URL 编码的 JSON结账会话元数据JSON.parse解析seats否?seats5按席位计费的席位数量parseInt解析几点使用提醒products是唯一必填参数且支持逗号分隔多商品——这正是 0.3.0 版本的破坏性变更见下文版本演进小节旧的productId、productPriceId已被移除。所有 JSON 类型参数customer_billing_address、customer_metadata、metadata必须经过 URL 编码处理器内部会调用JSON.parse还原为对象。allow_discount_codes只接受字面量true转小写后比较其余值一律视为false。这些行为在 checkoutHandler.test.ts 中有对应的回归测试覆盖例如?seats5透传为seats: 5、URL 转义保留、JSON 参数解析、discount_code触发二次更新接口等。Customer Portal让客户自助管理订单与订阅客户门户让已购客户自行查看订单和订阅状态。polar-sh/nuxt 的CustomerPortal工厂接收一个getCustomerId回调由你在服务端解析出当前登录客户然后由它创建 Polar 客户会话并重定向// server/routes/api/portal.get.ts export default defineEventHandler((event) { const { private: { polarAccessToken, polarServer }, } useRuntimeConfig() const customerPortalHandler CustomerPortal({ accessToken: polarAccessToken, environment: polarServer as sandbox | production, getCustomerId: (event) { return Promise.resolve(9d89909b-216d-475e-8005-053dba7cff07) }, returnUrl: https://myapp.com, // 可选门户内返回按钮的地址 }) return customerPortalHandler(event) })对照 customerPortalHandler.tsCustomerPortalConfig包含四个字段配置项类型说明accessTokenstringPolar 组织访问令牌environmentsandbox \| productionPolar 环境getCustomerId(event: H3Event) Promisestring从请求中解析当前客户 ID 的异步回调returnUrlstring可选门户页面返回按钮回跳地址源码中的关键逻辑customerPortalHandler.ts先await getCustomerId(event)拿到客户 ID如果回调返回空值直接抛 400 错误customerId not defined并在服务端打印错误日志。拿到 ID 后调用 SDK 的createCustomerSessions创建客户会话returnUrl会经过decodeURI处理后再传给 Polar最后sendRedirect到result.customer_portal_url。会话创建失败同样包装为 h3 500 错误。Webhooks签名验证 细粒度事件分发Polar 通过 Webhook 把订单、订阅、权益等事件推送到你的服务器。polar-sh/nuxt 的Webhooks处理器封装了完整的验签流程——它从请求头读取webhook-id、webhook-timestamp、webhook-signature读取原始请求体readRawBody然后交给 SDK 的webhooks.validateEvent验证签名// server/routes/webhook/polar.post.ts export default defineEventHandler((event) { const { private: { polarWebhookSecret }, } useRuntimeConfig() const webhooksHandler Webhooks({ webhookSecret: polarWebhookSecret, onPayload: async (payload: any) { // 处理所有事件 // 无需返回确认响应 }, }) return webhooksHandler(event) })验签与错误响应语义对照 webhookHandler.ts验签失败时的响应有明确的 HTTP 语义异常类型响应说明PolarWebhookVerificationError403{ received: false }签名验证失败拒绝该请求PolarWebhookUnknownTypeError事件类型为 null 时 400否则 200未知事件类型按已接收或非法处理其他PolarWebhookError400{ received: false }通用 Webhook 错误验签通过200{ received: true }正常处理并确认这里特别值得注意 0.5.4 版本的修复Fix webhook signature verification failing on parsed re-serialized webhook bodies。这解释了为什么处理器必须用readRawBody读取原始请求体再交给validateEvent——如果先用 JSON.parse 再序列化字节内容会与 Polar 签名时使用的原始字节不一致导致验签失败。这一点对自行实现验签的读者同样是重要提示。细粒度 Payload HandlersWebhooks配置除了通用onPayload外还支持按事件类型注册独立处理器README 与 webhooks.ts 中定义的完整列表CheckoutonCheckoutCreated、onCheckoutUpdated、onCheckoutExpiredOrderonOrderCreated、onOrderUpdated、onOrderPaid、onOrderRefundedRefundonRefundCreated、onRefundUpdated0.3.8 / 0.3.9 版本加入SubscriptiononSubscriptionCreated、onSubscriptionUpdated、onSubscriptionActive、onSubscriptionCanceled、onSubscriptionCycled、onSubscriptionPastDue、onSubscriptionPaused、onSubscriptionResumed、onSubscriptionRevoked、onSubscriptionUncanceledProductonProductCreated、onProductUpdatedOrganizationonOrganizationUpdatedBenefitonBenefitCreated、onBenefitUpdatedBenefit GrantonBenefitGrantCreated、onBenefitGrantUpdated、onBenefitGrantRevoked、onBenefitGrantCycledCustomeronCustomerCreated、onCustomerUpdated、onCustomerDeleted、onCustomerStateChanged0.2.2 版本加入客户状态支持Customer SeatonCustomerSeatAssigned、onCustomerSeatClaimed、onCustomerSeatRevokedDiscountonDiscountCreated、onDiscountUpdated、onDiscountDeletedMemberonMemberCreated、onMemberUpdated、onMemberDeleted这些处理器定义在 polar-sh/nuxt 的底层依赖 polar-sh/adapter-utils 中handleWebhookPayload用switch (payload.type)把事件精确分发到对应回调并用Promise.all并发执行所有命中的处理器通用onPayload总会执行事件专属处理器按需执行最后统一await全部完成——这正是 0.1.2 版本Await webhook handlers变更带来的语义处理器函数返回的 Promise 会被完整等待确保异步副作用如写库完成后才返回响应。从 Webhook 到权益Entitlementspolar-sh/nuxt 的Webhooks配置还支持传入entitlements权益分发器用于在benefit_grant.created/benefit_grant.revoked事件发生时自动执行授权/撤销逻辑。机制位于 adapter-utils 的 entitlement 实现import { Entitlements, EntitlementStrategy } from polar-sh/adapter-utils const proStrategy new EntitlementStrategy() .grant(async ({ customer, properties }) { // 授予权益按 properties 为 customer 开通功能 }) .revoke(async ({ customer }) { // 撤销权益 }) Entitlements.use(pro-plan, proStrategy)其设计要点EntitlementStrategy以链式方式注册grant/revoke回调通过handler(slug)生成一个EntitlementHandler。Entitlements.use(slug, strategy)把策略挂载到全局静态handlers数组最终传入Webhooks的entitlements配置。匹配规则回调中拿payload.data.benefit.description slug来判定该事件是否属于该权益策略事件携带的customer与properties会注入到回调上下文EntitlementContext中。权益相关能力从 0.1.11导出 Entitlement 类到 0.1.13导出权益工具逐步完善是适配器收款闭环之外的重要一环——订单支付后自动授权益、退款或订阅取消后自动撤销权益。版本演进从 CHANGELOG 看模块能力的时间线CHANGELOG 完整记录了 polar-sh/nuxt 从 0.1.x 到 0.5.8 的演进按功能域归纳如下0.1.x能力奠基期0.1.1 加入细粒度 Webhook 处理器granular webhook handlers0.1.2 修复 Webhook 处理器未被 await的问题0.1.9 增加productPriceId参数能力0.1.10 保证结账时必须传 price 或 product0.1.11 导出 Entitlement 类0.1.12 落地 entitlements 实现并初始化 Nuxt 接入0.1.13 导出权益工具0.1.14 导出类型0.1.15 修复 URI 解码decode the URI properly。0.2.x模块化与状态支持0.2.1 正式初始化 Nuxt 模块包结构0.2.2 加入客户状态customer state支持0.2.4 改善错误信息0.2.5 加入新订单 Webhook 支持。0.3.xCheckout 能力大版本0.3.0破坏性变更Checkout 端点不再支持productId/productPriceId传商品统一改用可重复的products参数一次结账可传多个商品0.3.1 修复products参数传递问题0.3.3 为 Checkout 配置加入主题支持theme0.3.4 修复 SDK 误解析 Zod v4 的问题0.3.5 修复导入路径0.3.8 / 0.3.9 两连发加入退款 Webhookrefund webhooks0.3.12 加入returnUrl支持结账页/门户返回按钮。0.4.xSDK 升级0.4.0 将 SDK 更新到 0.40.2adapter-utils 同步升至 0.3.0。0.5.x稳定性与 SDK 同步0.5.0 升级 Polar SDK0.5.1 统一升级依赖0.5.2 / 0.5.3 持续跟随 SDK 发布0.5.4 修复解析后重新序列化的 Webhook 请求体导致验签失败——即前文强调的readRawBody原始字节验签0.5.5 ~ 0.5.7 密集跟随 SDK 更新0.46.0 → 0.47.0 区间其间 adapter-utils 同步升级0.5.8 将 adapter-utils 更新到 0.4.7。当前仓库中 package.json 记录的依赖为polar-sh/sdk1.0.0-alpha.20、polar-sh/adapter-utilsworkspace 版本即仓库内 adapter-utilsCHANGELOG 中 0.3.x ~ 0.5.x 的 SDK 版本号属于历史快照二者并不矛盾——这反映的是该适配器长期保持紧跟 SDK 发布节奏的维护策略。测试与验证适配器配有 vitest 单元测试见 test/checkoutHandler.test.ts通过 mock SDK 与sendRedirect对Checkout处理器做行为级断言覆盖了URL 转义保留%2541、%2500、%7Bx%7D等在successUrl/returnUrl中不被二次破坏{CHECKOUT_ID}占位符可正常替换seats 透传?seats5/1/0的透传以及非数字输入按NaN透传与 nextjs 适配器保持一致性多参数组合products逗号分隔、客户字段、allow_discount_codes、discount_id的组合透传JSON 参数解析customer_billing_address、customer_metadata、metadata的JSON.parse行为折扣流程discount_code触发clientUpdateCheckouts二次调用、discount_id存在时不再调用、折扣应用失败时不重定向主题注入themedark追加到重定向 URL错误处理创建结账失败时抛 h3 500 错误。运行测试使用pnpm test # 在 clients/adapters/nuxt 目录下执行小结polar-sh/nuxt 把 Polar 的结账、客户门户与 Webhook 三个核心能力压缩为三个配置型工厂函数开发者只需在 Nuxt 服务端路由中调用Checkout、CustomerPortal、Webhooks即可完成接入。从 CHANGELOG 的演进脉络看模块的能力增长点集中在多商品结账0.3.0products、结账主题与返回链接0.3.3 / 0.3.12、退款事件与客户状态0.3.8 / 0.2.2、原始字节验签修复0.5.4以及贯穿始终的 SDK 同步升级——这为理解如何正确使用该适配器以及自行接入 Polar 时应关注哪些细节提供了清晰的路线图。【免费下载链接】polarPolar — A billing platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/po/polar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表