+
+ {(["monthly", "yearly"] as const).map((option) => (
+ setCycle(option)}
+ aria-pressed={cycle === option}
+ className={`cursor-pointer rounded-md px-4 py-2 text-sm font-medium transition-colors ${cycle === option ? "bg-background shadow-sm" : "text-muted-foreground hover:text-foreground"}`}
+ >
+ {option === "monthly" ? "Monthly" : "Yearly"}
+ {option === "yearly" && Save ~17% }
+
+ ))}
+
+
+ {(["usd", "inr"] as const).map((option) => (
+ setCurrency(option)}
+ aria-pressed={effectiveCurrency === option}
+ className={`cursor-pointer rounded-md px-4 py-2 text-sm font-medium transition-colors ${effectiveCurrency === option ? "bg-background shadow-sm" : "text-muted-foreground hover:text-foreground"}`}
+ >
+ {option === "usd" ? "USD" : "INR"}
+
+ ))}
+
+
{tiers.map((tier) => (
- {tier.price}
- {tier.period && (
- {tier.period}
- )}
+
+ {tier.tier
+ ? `${effectiveCurrency === "inr" ? "₹" : "$"}${paidPlans[tier.tier][cycle][effectiveCurrency]}`
+ : tier.name === "Free"
+ ? effectiveCurrency === "inr" ? "₹0" : "$0"
+ : "Custom"}
+
+ {tier.name !== "Enterprise" && /{cycle === "monthly" ? "mo" : "yr"} }
+ {tier.tier && (
+ <>
+
+ {effectiveCurrency === "inr" ? `≈ $${paidPlans[tier.tier][cycle].usd}/yr` : `India: ₹${paidPlans[tier.tier][cycle].inr}/${cycle === "monthly" ? "mo" : "yr"}`}
+
+ {cycle === "yearly" && (
+
+ Save {discountPercent(tier.tier, effectiveCurrency)}% versus monthly
+
+ )}
+ >
+ )}
{tier.description}
@@ -176,7 +280,7 @@ export default function PricingPage() {
) : (
)}
- {feature.href ? (
+ {"href" in feature && feature.href ? (
feature.label === "Unlimited fair-use traffic" ? (
Unlimited{" "}
@@ -209,9 +313,11 @@ export default function PricingPage() {
startCheckout(tier.tier!) : undefined}
>
- {tier.cta}
+ {tier.tier ? (checkoutTier === tier.tier ? "Opening checkout..." : tier.cta) : {tier.cta}}
diff --git a/apps/web/src/lib/billing-auth.ts b/apps/web/src/lib/billing-auth.ts
new file mode 100644
index 0000000..18de21a
--- /dev/null
+++ b/apps/web/src/lib/billing-auth.ts
@@ -0,0 +1,17 @@
+import { NextRequest } from "next/server"
+import { auth } from "@/lib/auth"
+import { getAccountDevice } from "@/lib/account-device-auth"
+import { db } from "@/lib/db"
+import { user } from "@/lib/db/schema"
+import { eq } from "drizzle-orm"
+
+export async function getBillingUser(req: NextRequest) {
+ const session = await auth.api.getSession({ headers: req.headers })
+ if (session) {
+ return await db.query.user.findFirst({ where: eq(user.id, session.user.id) })
+ }
+
+ const account = await getAccountDevice(req.headers.get("authorization"))
+ if ("error" in account) return null
+ return account.user
+}
diff --git a/apps/web/src/lib/db/schema.ts b/apps/web/src/lib/db/schema.ts
index 95bd830..74991af 100644
--- a/apps/web/src/lib/db/schema.ts
+++ b/apps/web/src/lib/db/schema.ts
@@ -8,10 +8,22 @@ export const user = pgTable("user", {
image: text("image"),
tier: text("tier").notNull().default("free"),
apiKey: text("api_key").unique(),
+ dodoCustomerId: text("dodo_customer_id").unique(),
+ dodoSubscriptionId: text("dodo_subscription_id").unique(),
+ subscriptionStatus: text("subscription_status"),
+ subscriptionProductId: text("subscription_product_id"),
+ subscriptionRenewsAt: timestamp("subscription_renews_at"),
+ subscriptionCancelAtPeriodEnd: boolean("subscription_cancel_at_period_end").notNull().default(false),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().defaultNow(),
})
+export const dodoWebhookEvent = pgTable("dodo_webhook_event", {
+ id: text("id").primaryKey(),
+ type: text("type").notNull(),
+ processedAt: timestamp("processed_at").notNull().defaultNow(),
+})
+
export const session = pgTable("session", {
id: text("id").primaryKey(),
userId: text("user_id")
diff --git a/apps/web/src/lib/dodo.ts b/apps/web/src/lib/dodo.ts
new file mode 100644
index 0000000..e150928
--- /dev/null
+++ b/apps/web/src/lib/dodo.ts
@@ -0,0 +1,32 @@
+import DodoPayments from "dodopayments"
+
+const environment = process.env.DODO_PAYMENTS_ENVIRONMENT === "test_mode"
+ ? "test_mode"
+ : "live_mode"
+
+export function getDodo() {
+ const bearerToken = process.env.DODO_PAYMENTS_API_KEY
+ if (!bearerToken) throw new Error("DODO_PAYMENTS_API_KEY is not configured")
+ return new DodoPayments({
+ bearerToken,
+ environment,
+ webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
+ })
+}
+
+export function getProductId(tier: "starter" | "builder", cycle: "monthly" | "yearly") {
+ const key = `DODO_PRODUCT_${tier.toUpperCase()}_${cycle.toUpperCase()}` as const
+ const productId = process.env[key]
+ if (!productId) throw new Error(`${key} is not configured`)
+ return productId
+}
+
+export function tierFromProductId(productId: string): "starter" | "builder" | null {
+ if (productId === process.env.DODO_PRODUCT_STARTER_MONTHLY || productId === process.env.DODO_PRODUCT_STARTER_YEARLY) return "starter"
+ if (productId === process.env.DODO_PRODUCT_BUILDER_MONTHLY || productId === process.env.DODO_PRODUCT_BUILDER_YEARLY) return "builder"
+ return null
+}
+
+export function appUrl(path: string) {
+ return `${process.env.NEXT_PUBLIC_APP_URL || process.env.BETTER_AUTH_URL || "http://localhost:3000"}${path}`
+}
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index 8cc7745..1894254 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -27,6 +27,8 @@ export type DeviceLinkQrPayload = {
v: number
}
+export * from "./plans"
+
function toBase64(str: string): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
let result = ''
diff --git a/packages/shared/src/plans.ts b/packages/shared/src/plans.ts
new file mode 100644
index 0000000..ddcf82e
--- /dev/null
+++ b/packages/shared/src/plans.ts
@@ -0,0 +1,27 @@
+export type BillingCycle = "monthly" | "yearly"
+export type BillingCurrency = "usd" | "inr"
+export type PaidTier = "starter" | "builder"
+
+export const paidPlans = {
+ starter: {
+ name: "Starter",
+ monthly: { usd: 2, inr: 175 },
+ yearly: { usd: 20, inr: 1899 },
+ },
+ builder: {
+ name: "Builder",
+ monthly: { usd: 5, inr: 475 },
+ yearly: { usd: 50, inr: 4799 },
+ },
+} as const satisfies Record
+
+export const tierTunnelLimits: Record = {
+ free: 1,
+ starter: 1,
+ builder: 5,
+ enterprise: Number.POSITIVE_INFINITY,
+}
diff --git a/packages/tunnel-server/src/db.ts b/packages/tunnel-server/src/db.ts
index 8375614..602cd6b 100644
--- a/packages/tunnel-server/src/db.ts
+++ b/packages/tunnel-server/src/db.ts
@@ -15,7 +15,10 @@ export async function validateApiKey(apiKey: string): Promise<{ userId: string;
logger.debug("Validating API key", { apiKey: apiKey.substring(0, 8) + "..." })
try {
const rows = await sql`
- SELECT id, email, tier FROM "user" WHERE api_key = ${apiKey} LIMIT 1
+ SELECT id, email,
+ CASE WHEN tier IN ('starter', 'builder', 'enterprise') AND COALESCE(subscription_status, '') != 'active'
+ THEN 'free' ELSE tier END AS tier
+ FROM "user" WHERE api_key = ${apiKey} LIMIT 1
`
if (rows.length === 0) {
logger.warn("API key not found in database", { apiKey: apiKey.substring(0, 8) + "..." })
diff --git a/packages/tunnel-server/src/ws-handler.ts b/packages/tunnel-server/src/ws-handler.ts
index 547d35d..2490996 100644
--- a/packages/tunnel-server/src/ws-handler.ts
+++ b/packages/tunnel-server/src/ws-handler.ts
@@ -2,6 +2,7 @@ import { WebSocket } from "ws"
import { validateApiKey } from "./db.js"
import { register, deregister, get, removePendingRequest, countByUserId } from "./registry.js"
import type { TunnelC2S, TunnelS2C } from "@crosscode/shared"
+import { tierTunnelLimits } from "@crosscode/shared"
import crypto from "crypto"
import { logger } from "./logger.js"
@@ -138,10 +139,11 @@ export function handleWebSocket(ws: WebSocket, req: import("http").IncomingMessa
return
}
- const activeTunnels = countByUserId(result.userId)
- if (result.tier === "free" && activeTunnels >= 1) {
- logger.warn("Auth failed: free tier tunnel limit reached", { projectId: projId, userId: result.userId, activeTunnels })
- send(ws, { type: "auth.fail", reason: "Free plan allows only 1 active custom tunnel. Upgrade for more." })
+ const activeTunnels = countByUserId(result.userId)
+ const tunnelLimit = tierTunnelLimits[result.tier] ?? tierTunnelLimits.free
+ if (activeTunnels >= tunnelLimit) {
+ logger.warn("Auth failed: tunnel limit reached", { projectId: projId, userId: result.userId, tier: result.tier, activeTunnels, tunnelLimit })
+ send(ws, { type: "auth.fail", reason: `${result.tier} plan allows ${tunnelLimit} active tunnel${tunnelLimit === 1 ? "" : "s"}. Upgrade for more.` })
ws.close(4003, "Tunnel limit reached")
return
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1a1bd48..9c23031 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -206,7 +206,7 @@ importers:
version: 2.1.1
'@sanity/vision':
specifier: ^6.9.2
- version: 6.9.2(kpxfdzo2ri5s5fzcvhi2vl5hwq)
+ version: 6.9.2(qyrdcirsfetoszvdfcpvmthi6i)
'@tailwindcss/postcss':
specifier: ^4.1.11
version: 4.3.3
@@ -225,6 +225,9 @@ importers:
clsx:
specifier: ^2.1.1
version: 2.1.1
+ dodopayments:
+ specifier: ^2.47.0
+ version: 2.47.0
drizzle-orm:
specifier: ^0.45.2
version: 0.45.2(kysely@0.29.4)(postgres@3.4.9)
@@ -236,7 +239,7 @@ importers:
version: 16.2.12(@babel/core@7.29.7)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next-sanity:
specifier: ^13.3.3
- version: 13.3.3(zr5qijm5tnrieupv4r2xxrsnhu)
+ version: 13.3.3(dixuumzgngq34ckzxgjqk57cde)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -263,7 +266,7 @@ importers:
version: 4.0.1
sanity:
specifier: ^6.9.2
- version: 6.9.2(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@noble/hashes@2.3.0)(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(rolldown@1.2.4)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0)))(@sanity/sdk@2.19.0(@types/react@19.2.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))(xstate@5.32.5))(@types/node@22.20.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(babel-plugin-react-compiler@1.0.0)(esbuild@0.28.1)(jiti@2.7.0)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(rolldown@1.2.4)(styled-components@6.5.3(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(terser@5.48.0)(typescript@5.9.3)
+ version: 6.9.2(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@noble/hashes@2.3.0)(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(rolldown@1.2.4)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0)))(@sanity/sdk@2.19.0(@types/react@19.2.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))(xstate@5.32.5))(@types/node@22.20.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(babel-plugin-react-compiler@1.0.0)(esbuild@0.28.1)(jiti@2.7.0)(prismjs@1.30.0)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(rolldown@1.2.4)(styled-components@6.5.3(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(terser@5.48.0)(typescript@5.9.3)
styled-components:
specifier: ^6.5.3
version: 6.5.3(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
@@ -5084,6 +5087,9 @@ packages:
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
engines: {node: '>=18'}
+ '@stablelib/base64@1.0.1':
+ resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==}
+
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
@@ -6564,6 +6570,10 @@ packages:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
+ dodopayments@2.47.0:
+ resolution: {integrity: sha512-4lmI7/NDJjJOmy2jGJaowt3/heXx01IDwPkiuuHqifcdi4+nFg1d1vrlcE8ULw4r+TO+TA1ZPFyaHxSm8hbjvQ==}
+ hasBin: true
+
dom-accessibility-api@0.5.16:
resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
@@ -7314,6 +7324,9 @@ packages:
fast-levenshtein@3.0.0:
resolution: {integrity: sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==}
+ fast-sha256@1.3.0:
+ resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==}
+
fast-string-truncated-width@3.0.3:
resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==}
@@ -9441,6 +9454,10 @@ packages:
resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==}
engines: {node: '>=18'}
+ prismjs@1.30.0:
+ resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==}
+ engines: {node: '>=6'}
+
proc-log@4.2.0:
resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
@@ -10195,6 +10212,9 @@ packages:
standard-navigation@0.0.7:
resolution: {integrity: sha512-NCGLCNyuXrFOkGHxdNZFnpsehGtiq1oXbPhKl7ZuxFO5J//H2evqqOchmD4YwEUJnkjO4kH9Xp4hQX6hdAYCKQ==}
+ standardwebhooks@1.0.0:
+ resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==}
+
statuses@1.5.0:
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
engines: {node: '>= 0.6'}
@@ -15393,9 +15413,7 @@ snapshots:
metro-runtime: 0.84.4
transitivePeerDependencies:
- '@babel/core'
- - bufferutil
- supports-color
- - utf-8-validate
'@react-native/normalize-colors@0.74.89':
optional: true
@@ -16215,7 +16233,9 @@ snapshots:
'@sanity/client': 7.26.2
'@sanity/uuid': 3.0.3
- '@sanity/prism-groq@1.1.2': {}
+ '@sanity/prism-groq@1.1.2(prismjs@1.30.0)':
+ optionalDependencies:
+ prismjs: 1.30.0
'@sanity/runtime-cli@17.7.0(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@noble/hashes@2.3.0)(@types/node@22.20.1)(@types/react@19.2.17)(babel-plugin-react-compiler@1.0.0)(esbuild@0.28.1)(rolldown@1.2.4)(terser@5.48.0)(tsx@4.23.1)(typescript@5.9.3)(yaml@2.9.0)':
dependencies:
@@ -16378,7 +16398,7 @@ snapshots:
dependencies:
uuid: 11.1.1
- '@sanity/vision@6.9.2(kpxfdzo2ri5s5fzcvhi2vl5hwq)':
+ '@sanity/vision@6.9.2(qyrdcirsfetoszvdfcpvmthi6i)':
dependencies:
'@codemirror/autocomplete': 6.20.3
'@codemirror/commands': 6.11.0
@@ -16406,7 +16426,7 @@ snapshots:
react: 19.2.3
react-rx: 5.1.1(react@19.2.3)(rxjs@7.8.2)
rxjs: 7.8.2
- sanity: 6.9.2(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@noble/hashes@2.3.0)(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(rolldown@1.2.4)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0)))(@sanity/sdk@2.19.0(@types/react@19.2.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))(xstate@5.32.5))(@types/node@22.20.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(babel-plugin-react-compiler@1.0.0)(esbuild@0.28.1)(jiti@2.7.0)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(rolldown@1.2.4)(styled-components@6.5.3(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(terser@5.48.0)(typescript@5.9.3)
+ sanity: 6.9.2(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@noble/hashes@2.3.0)(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(rolldown@1.2.4)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0)))(@sanity/sdk@2.19.0(@types/react@19.2.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))(xstate@5.32.5))(@types/node@22.20.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(babel-plugin-react-compiler@1.0.0)(esbuild@0.28.1)(jiti@2.7.0)(prismjs@1.30.0)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(rolldown@1.2.4)(styled-components@6.5.3(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(terser@5.48.0)(typescript@5.9.3)
use-effect-event: 2.0.3(react@19.2.3)
transitivePeerDependencies:
- '@babel/runtime'
@@ -16598,6 +16618,8 @@ snapshots:
'@sindresorhus/merge-streams@4.0.0': {}
+ '@stablelib/base64@1.0.1': {}
+
'@standard-schema/spec@1.1.0': {}
'@swc/helpers@0.5.15':
@@ -18054,6 +18076,10 @@ snapshots:
dependencies:
esutils: 2.0.3
+ dodopayments@2.47.0:
+ dependencies:
+ standardwebhooks: 1.0.0
+
dom-accessibility-api@0.5.16: {}
dom-accessibility-api@0.6.3: {}
@@ -18403,7 +18429,7 @@ snapshots:
'@next/eslint-plugin-next': 16.2.12
eslint: 9.39.5(jiti@2.7.0)
eslint-import-resolver-node: 0.3.10
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0))
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5(jiti@2.7.0))
eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@2.7.0))
@@ -18426,7 +18452,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)):
+ eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.3(supports-color@8.1.1)
@@ -18441,14 +18467,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.14.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)):
+ eslint-module-utils@2.14.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3)
eslint: 9.39.5(jiti@2.7.0)
eslint-import-resolver-node: 0.3.10
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0))
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0))
transitivePeerDependencies:
- supports-color
@@ -18463,7 +18489,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.39.5(jiti@2.7.0)
eslint-import-resolver-node: 0.3.10
- eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0))
+ eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0))
hasown: 2.0.4
is-core-module: 2.16.2
is-glob: 4.0.3
@@ -19035,6 +19061,8 @@ snapshots:
dependencies:
fastest-levenshtein: 1.0.16
+ fast-sha256@1.3.0: {}
+
fast-string-truncated-width@3.0.3: {}
fast-string-width@3.0.2:
@@ -20983,7 +21011,7 @@ snapshots:
negotiator@1.0.0: {}
- next-sanity@13.3.3(zr5qijm5tnrieupv4r2xxrsnhu):
+ next-sanity@13.3.3(dixuumzgngq34ckzxgjqk57cde):
dependencies:
'@portabletext/react': 7.0.1(react@19.2.3)
'@sanity/client': 7.26.2
@@ -20996,7 +21024,7 @@ snapshots:
next: 16.2.12(@babel/core@7.29.7)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
- sanity: 6.9.2(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@noble/hashes@2.3.0)(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(rolldown@1.2.4)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0)))(@sanity/sdk@2.19.0(@types/react@19.2.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))(xstate@5.32.5))(@types/node@22.20.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(babel-plugin-react-compiler@1.0.0)(esbuild@0.28.1)(jiti@2.7.0)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(rolldown@1.2.4)(styled-components@6.5.3(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(terser@5.48.0)(typescript@5.9.3)
+ sanity: 6.9.2(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@noble/hashes@2.3.0)(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(rolldown@1.2.4)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0)))(@sanity/sdk@2.19.0(@types/react@19.2.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))(xstate@5.32.5))(@types/node@22.20.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(babel-plugin-react-compiler@1.0.0)(esbuild@0.28.1)(jiti@2.7.0)(prismjs@1.30.0)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(rolldown@1.2.4)(styled-components@6.5.3(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(terser@5.48.0)(typescript@5.9.3)
styled-components: 6.5.3(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
transitivePeerDependencies:
- '@sveltejs/kit'
@@ -21452,6 +21480,9 @@ snapshots:
dependencies:
parse-ms: 4.0.0
+ prismjs@1.30.0:
+ optional: true
+
proc-log@4.2.0: {}
process-nextick-args@2.0.1: {}
@@ -22247,7 +22278,7 @@ snapshots:
safer-buffer@2.1.2: {}
- sanity@6.9.2(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@noble/hashes@2.3.0)(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(rolldown@1.2.4)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0)))(@sanity/sdk@2.19.0(@types/react@19.2.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))(xstate@5.32.5))(@types/node@22.20.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(babel-plugin-react-compiler@1.0.0)(esbuild@0.28.1)(jiti@2.7.0)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(rolldown@1.2.4)(styled-components@6.5.3(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(terser@5.48.0)(typescript@5.9.3):
+ sanity@6.9.2(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@noble/hashes@2.3.0)(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(rolldown@1.2.4)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0)))(@sanity/sdk@2.19.0(@types/react@19.2.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))(xstate@5.32.5))(@types/node@22.20.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(babel-plugin-react-compiler@1.0.0)(esbuild@0.28.1)(jiti@2.7.0)(prismjs@1.30.0)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(rolldown@1.2.4)(styled-components@6.5.3(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(terser@5.48.0)(typescript@5.9.3):
dependencies:
'@algorithm.ts/lcs': 4.0.6
'@date-fns/tz': 1.5.0
@@ -22295,7 +22326,7 @@ snapshots:
'@sanity/mutator': 6.9.2(@types/react@19.2.17)
'@sanity/presentation-comlink': 2.2.3(@sanity/types@6.9.2(@types/react@19.2.17))
'@sanity/preview-url-secret': 4.1.4(@sanity/client@7.26.2)
- '@sanity/prism-groq': 1.1.2
+ '@sanity/prism-groq': 1.1.2(prismjs@1.30.0)
'@sanity/schema': 6.9.2(@types/react@19.2.17)
'@sanity/telemetry': 1.1.0(react@19.2.3)
'@sanity/types': 6.9.2(@types/react@19.2.17)
@@ -22626,6 +22657,11 @@ snapshots:
standard-navigation@0.0.7: {}
+ standardwebhooks@1.0.0:
+ dependencies:
+ '@stablelib/base64': 1.0.1
+ fast-sha256: 1.3.0
+
statuses@1.5.0: {}
statuses@2.0.2: {}