Next.js, Expo, Tauri 간에 인증과 결제를 공유하려면?
하나의 ID 모델과 하나의 권한 저장소를 유지할 수 있느냐에 달렸습니다. 웹은 HttpOnly 쿠키 세션, Expo는 딥링크 OAuth 콜백 후 Bearer 토큰을 expo-secure-store에 저장, Tauri는 webview 안에서 웹 세션을 재사용—모두 같은 Better Auth 인스턴스가 검증합니다. 결제도 같은 규칙입니다. 8개 웹 게이트웨이와 RevenueCat 모바일 IAP가 하나의 멱등 webhook 원장으로 흘러, 모든 표면이 읽는 워크스페이스 단위 권한을 기록합니다.
크로스플랫폼 SaaS가 깨지는 두 이음매
웹 SaaS에 모바일 앱을 용접한 팀에게 진짜 아픈 곳을 물으면 답은 항상 UI가 아닙니다. ID와 돈입니다. 브라우저에서는 깔끔하게 돌아오는 OAuth 플로가 앱으로는 딥링크로 돌아와야 하고, 웹에서 결제한 고객은 자기 권한이 즉시 폰에 나타나길 기대합니다.
팀은 보통 두 개의 반쪽짜리 해결책을 출시한 뒤에야 깨닫습니다. WKWebView 안에서 조용히 실패하는 쿠키 로직과, 웹 DB의 Stripe 구독과 싸우는 RevenueCat 권한. 수정은 구조적이며 같은 수정을 두 번 합니다—하나의 ID 모델, 하나의 권한 저장소.
하나의 Better Auth 인스턴스, 세 가지 세션 전송
supastack은 @supastack/auth에 단일 Better Auth 설정을 두고 표면별 진입점을 둡니다. 두 웹 앱(Next.js 16과 TanStack Start)은 HttpOnly, SameSite=Lax 쿠키 세션을 사용—기본 CSRF 안전이며 Server Components와 궁합이 좋습니다. Expo 앱은 딥링크 콜백 끝에서 OAuth나 매직 링크 완료를 Bearer 토큰으로 교환해 expo-secure-store(iOS Keychain / Android Keystore)에 영속화하고 Authorization 헤더로 보내며, 같은 인증 미들웨어가 검증합니다.
Tauri의 데스크톱 셸은 웹 앱을 가리키는 단일 webview이므로 웹 세션을 그대로 상속합니다—관리할 세 번째 인증 플로가 없습니다. 모바일 인증 가이드와 딥링크 가이드에 콜백 계약이 있고, 웹 + 모바일 모노레포 가이드가 공유 클라이언트 코드를 보여줍니다.
// 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.8개 결제 게이트웨이, 하나의 계약
모든 웹 결제 제공자—Stripe, Lemon Squeezy, Polar, Creem, Dodo, Paddle, Whop, PayPal—가 같은 @supastack/billing 계약에 연결되고, 서명된 webhook은 모두 권한을 건드리기 전에 하나의 멱등 webhook_event 원장에 기록됩니다. 제공자의 재시도와 순서 뒤바뀐 배달은 데이터베이스 경계에서 무해한 no-op이 되지, 지원 티켓이 되지 않습니다.
모바일 인앱 결제는 RevenueCat을 통해 같은 원장에 합류합니다. 스토어 영수증은 서버 측에서 검증되고, 권한은 디바이스가 아니라 워크스페이스에 기록됩니다—iOS에서 결제한 사용자는 웹과 데스크톱에서도 즉시 결제 완료 상태입니다. RevenueCat 가이드가 App Store / Play 배선을, 멱등 결제 webhook이 원장이 왜 하중 부품인지를 설명합니다.
동기화를 지루하게 만드는 권한 모델
이 상태가 지루할 수 있는 이유는 권한이 워크스페이스에 있고 서버 측에서 해석되기 때문입니다. 「결제됨」을 저장하는 클라이언트는 없습니다—웹은 세션에, 모바일은 API에, 데스크톱은 웹 앱에 묻고 셋은 같은 답을 얻습니다. 답할 레코드가 하나뿐이기 때문입니다.
새 폰에서의 구매 복원, 웹에서의 Stripe 갱신, 대시보드에서의 Agency 시트 추가—각각 같은 권한 행에 대한 쓰기이며, 각 표면은 다음에 물을 때 그것을 읽습니다. 동기화할 것이 존재하지 않으므로 동기화 엔진을 만들 필요가 없습니다.
| 표면 | 세션 전송 | 결제 경로 | 권한 읽는 곳 |
|---|---|---|---|
| Next.js 16 웹 | HttpOnly 쿠키 | 8개 게이트웨이 중 하나 → webhook 원장 | 공유 Postgres 권한 행 |
| TanStack Start 웹 | HttpOnly 쿠키 | 같은 어댑터, 같은 원장 | 공유 Postgres 권한 행 |
| Expo iOS / Android | expo-secure-store의 Bearer 토큰 | RevenueCat IAP → 서버 검증 영수증 | 같은 행(타입 API 경유) |
| Tauri 2 데스크톱 | webview 안의 웹 세션 | 셸 안의 웹 체크아웃 | 같은 행(웹 앱 경유) |
| MV3 확장 | 워크스페이스 API 키 | —(워크스페이스 데이터 읽기) | 같은 행(/api/v1 경유) |
만들지 않아도 되는 것들
저녁 시간으로 환산하면 이것이 기반과 기능 목록의 차이입니다. 딥링크 인증 복귀, SecureStore 토큰 영속화, 리플레이 안전한 8개 webhook 통합, App Store 영수증 검증, 디바이스 간 모순되지 않는 권한 모델. 모두 출시됐고 테스트됐으며(693+ 테스트가 자격 증명 없이 실행), 문서화돼 있습니다.
$299 Full Bundle은 모든 표면을 한 리포지토리에 담습니다—시트 수와 에이전시 티어는 가격 페이지에서.
FAQ: 크로스플랫폼 인증·결제 공유
통합 스택에 커밋하기 전 팀이 묻는 질문입니다:
- 모바일 사용자는 웹과 별도로 로그인하나요?, 아니요. OAuth나 매직 링크 플로는 딥링크로 앱에 돌아와 하나의 세션을 발급합니다. 발급자가 같은 Better Auth 인스턴스이므로 웹과 모바일이 「당신이 누구인지」 같은 답을 합니다.
- iOS에서 결제하고 웹 앱을 쓰는 사람은?, RevenueCat이 서버 측에서 구매를 전달합니다. 권한은 웹이 읽는 같은 워크스페이스 행에 기록돼, 웹 앱은 다음 요청에서 결제 완료를 표시합니다.
- 웹만으로 시작해 나중에 앱을 추가할 수 있나요?, 가능합니다—인증과 결제가 공유 패키지에 있어 모바일과 데스크톱은 해당 표면을 켜는 순간 상속받습니다.
- Tauri 앱에 자체 체크아웃이 필요한가요?, 필요 없습니다—데스크톱 셸은 webview 안에서 웹 앱의 세션과 체크아웃을 재사용하며, 지켜야 할 두 번째 결제 표면은 없습니다.