跳转到内容
Supastack
CN
主题
登录获取终身访问权
全部文章

在 Next.js、Expo 移动端与 Tauri v2 之间共享认证与 8 种支付网关

跨端 SaaS 最难的两条缝:移动端的 Deep Link 登录回调,以及让 Web 支付、RevenueCat 内购与桌面收款共用一份权益。一个 Monorepo 如何同时解决两者。

Noah Zixin Zhang约 4 分钟阅读

如何在 Next.js、Expo 与 Tauri 之间共享认证和支付?

取决于能否保持同一身份模型与同一权益存储。Web 用 HttpOnly Cookie 会话;Expo 在 Deep Link 的 OAuth 回调后把 Bearer Token 存入 expo-secure-store;Tauri 在 Webview 内直接复用 Web 会话——三者由同一个 Better Auth 实例校验。支付同理:8 家 Web 网关与 RevenueCat 移动内购汇入同一个幂等 webhook 账本,写入每个端面都能读取的工作区级权益。

跨端 SaaS 断裂的两条缝

问任何一个把移动应用焊接到 Web SaaS 上的团队真正疼在哪,答案永远不是 UI。是身份和钱:在浏览器里干净回跳的 OAuth 流程,必须通过 Deep Link 跳回应用内;而在 Web 上付过款的客户,期望权益立刻出现在手机里。

团队通常在上线过两个半成品之后才发现这一点:在 WKWebView 里悄悄失效的 Cookie 逻辑,以及与 Web 数据库里 Stripe 订阅互相打架的 RevenueCat 权益。修复是架构层面的,而且是同一个修复用两次——一个身份模型,一个权益存储。

一个 Better Auth 实例,三种会话传输

supastack 在 @supastack/auth 中保留单一的 Better Auth 配置,为每个端面提供不同入口。两个 Web 应用(Next.js 16 与 TanStack Start)使用 HttpOnly、SameSite=Lax 的 Cookie 会话——默认防 CSRF 且与 Server Components 天然契合。Expo 应用在 Deep Link 回调结束时把 OAuth 或魔法链接的完成态换成 Bearer Token,持久化到 expo-secure-store(iOS Keychain / Android Keystore),随后以 Authorization 头发送,由同一个认证中间件校验。

Tauri 的桌面壳是一个指向 Web 应用的单 Webview,因此直接继承 Web 会话——没有第三条认证流程要维护。移动端认证指南与 Deep Link 指南 记载了回调契约;我们的 Web + 移动 Monorepo 指南演示了共享客户端代码。

// packages/auth/src/client.ts — one client, two storages
import { createAuthClient } from "better-auth/react";
import { expoClient } from "@better-auth/expo/client";

export function createCrossPlatformAuthClient(baseURL: string, storage?: any) {
  return createAuthClient({
    baseURL,
    plugins: storage ? [expoClient({ scheme: "myapp", storage })] : [],
  });
}

// Web: cookie session.  Mobile: bearer token in SecureStore.
// Desktop: the web app's own session inside the Tauri webview.

八家支付网关,同一份契约

每一家 Web 支付商——Stripe、Lemon Squeezy、Polar、Creem、Dodo、Paddle、Whop 与 PayPal——都接入同一个 @supastack/billing 契约,每一条签名 webhook 都先落进幂等的 webhook_event 账本,才被允许触碰权益。服务商重试与乱序送达在数据库边界变成无害空操作,而不是客服工单。

移动端内购通过 RevenueCat 汇入同一本账:商店收据在服务端校验,权益写到工作区而非设备上——在 iOS 付过款的用户,在 Web 与桌面端立刻同样是已付费。RevenueCat 指南讲 App Store / Play 的接线;幂等计费 webhook 解释为什么账本才是承重件。

让同步变得无聊的权益模型

这件事之所以能维持无聊,是因为权益放在工作区上、由服务端解析。没有任何客户端存储「已付费」——Web 问会话、移动端问 API、桌面端问 Web 应用——三者拿到同一个答案,因为只有一份记录可答。

新手机上的恢复购买、Web 上的 Stripe 续费、后台里新增的 Agency 席位——每一笔都是写进同一些权益行,每个端面下次询问时读到它。没有同步引擎可建,因为根本没有东西需要同步。

端面会话传输支付路径权益读取自
Next.js 16 WebHttpOnly Cookie8 家网关任一 → webhook 账本共享 Postgres 权益行
TanStack Start WebHttpOnly Cookie同一批适配器,同一本账共享 Postgres 权益行
Expo iOS / Androidexpo-secure-store 中的 Bearer TokenRevenueCat 内购 → 服务端校验收据同一批行(经类型化 API)
Tauri 2 桌面端Webview 内的 Web 会话壳内直接走 Web 收银台同一批行(经 Web 应用)
MV3 插件工作区 API 密钥—(读取工作区数据)同一批行(经 /api/v1)

你省下的开发量

按业余晚上的时间折算,这就是「地基」与「功能清单」的差别:Deep Link 登录回跳、SecureStore 令牌持久化、八个带重放安全的 webhook 集成、App Store 收据校验,以及一个跨设备永不自相矛盾的权益模型。每一项都已交付、已测试(693+ 测试免凭证可跑)、已写进文档。

$299 的 Full Bundle 在同一个仓库里包含全部端面——席位与外包档位见 定价页。

常见问题:跨平台共享认证与支付

团队在押注统一技术栈之前常问的问题:

  • 移动端用户要脱离 Web 单独登录吗?, 不用。OAuth 或魔法链接流程通过 Deep Link 回到应用并签发同一个会话;签发方是同一个 Better Auth 实例,所以 Web 与移动端对「你是谁」答案一致。
  • 有人在 iOS 上付款却用 Web 端怎么办?, RevenueCat 在服务端送达这笔购买;权益写进 Web 读取的同一批工作区行,Web 端下一次请求就显示已付费。
  • 能先只做 Web、之后再上应用吗?, 能——认证与计费都在共享包里,移动端和桌面端应用在你开启这些端面时直接继承它们。
  • Tauri 应用需要自己的收银台吗?, 不需要——桌面壳在 Webview 内复用 Web 应用的会话与收银台,没有第二个支付端面需要设防。

下一个产品,从这里开始。

一次付费,持续打造盈利的产品。