我们是如何实现无后端计费的 (Backendless Billing)
我们是如何实现无后端计费的 (Backendless Billing)
Ayush, Autumn 联合创始人
我们认为在前端处理支付能提供更好的开发者体验。
通常,计费是一项后端任务,需要用到 webhooks、状态同步,然后将数据传递到前端。我们希望在处理支付链接、付费墙以及升级/降级流程等事情时,提供更“开箱即用”的体验,并花费大量时间思考如何在不需要“往返”后端的情况下执行敏感操作。
这是一篇简短的文章,记录了我们对这个问题的探索以及我们最终放弃的原因。
第一部分:可发布密钥 (Publishable Key)
当我们刚推出产品时,我们有一个密钥 (secret key),可以像 Stripe 一样从后端安全地使用。我们的许多早期用户实际上从未设置过 Stripe,并立即告诉我们,他们希望可以直接从前端完成设置。
我们的第一个解决方案是创建一个“可发布密钥 (publishable key)”,让开发者可以直接从前端以不受保护的方式获取支付链接并检查功能访问权限(例如,我的用户是否还有剩余积分)。这些功能本身不会被滥用。
最初的反馈很好,人们很乐意用它来做初始设置。但是我们很快遇到了几个问题:
- 它只适用于某些 endpoints(例如,跟踪可计费的使用事件必须通过密钥 (secret key) 完成),最终让开发者对哪些 endpoints 可以使用哪些密钥感到困惑。
- 大多数软件计费流程允许您在之前购买过商品的情况下自动购买某些商品。这种自动购买(例如用于升级)肯定不能用公钥 (public key) 完成。
- 虽然它帮助人们快速启动了一个示例集成,但无论如何都必须很快将其删除,所以最终变得毫无意义。
这个东西现在还在我们的文档中,并且一直困扰着人们。迫不及待地想摆脱它。
第二部分:服务器 Actions (Server Actions)
当我们推出我们的 Next.js 库时,我们很高兴能使用服务器 Actions (server actions)。DX (开发者体验) 感觉很神奇,因为用户可以:
- 像任何普通函数一样从前端调用它们
- 这些函数在服务器上运行,并且可以访问我们存储为 ENV 变量的密钥 (secret key)
- 无需设置路由,并且请求是安全的——太棒了!
不幸的是,我们很快发现我们的方法是有缺陷的。服务器 Actions (server actions) 是公共的、未经身份验证的路由,我们的 API 调用根据 customer_id
字段更新资源(例如,升级/降级请求,跟踪某个功能的使用情况等)。
所以,如果你得到了别人的 customer ID,你就可以像那个客户一样向公共服务器 Actions (server actions) 发出请求——这使得这种方法不安全。
第三部分:服务器 Actions (Server Actions) + 加密
尽管如此,我们真的非常喜欢服务器 Actions (server actions) 的 DX (开发者体验),所以我们必须集思广益,找到一种方法来克服 customer ID 在服务器 Action (server action) 路由中暴露的问题。
我们想到了几个选项,比如使用中间件,或者注册一个身份验证函数,但我们认为最干净、最简单的方法是简单地加密 customer ID:
以下是它的工作原理:
- 我们的 Provider 是一个服务器组件,因此它将接收一个 customer ID/user ID(服务器端),使用他们的 API 密钥对其进行加密,并将其传递给客户端的上下文(见下图)
- 我们用一个客户端函数包装每个服务器 Action (server action),该函数从上下文中获取
encryptedCustomerId
并将其传递给服务器 Action (server action)。这些都通过一个 hook——useAutumn
导出 - 每个服务器 Action (server action) 首先解密 customer ID,然后调用 Autumn API
本质上,我们在服务器 Actions (server actions) 中嵌入了我们自己的身份验证层,这就是我们今天的 Next.js 库的工作方式。我还没有看到其他人使用这种方法,但它基本上允许我们完全处理安全的支付、升级、降级、基于使用量的计费事件——所有这些都来自前端。
我们仍然不完全满意,因为这只适用于支持服务器 Actions (server actions) 的框架,并且 SPA / vite 有点卷土重来。这也使得跨框架的实现有所不同。
未来
最终,我认为我们会达到放弃这种方法的地步,并转向一种更与框架无关的方法。与其试图放弃后端路由设置,不如让它变得容易。以 better-auth 为例,它们只需几行代码即可生成后端路由——它们标准化了后端和前端的安装,并且很难出错。
像 Polar 这样的其他公司也有类似的方法,并因此受到高度赞扬。我们可能不需要重新发明轮子(尽管这很有趣)。