fix(stripe): use latest version to fix event mismatch issues (#1336)

* fix(stripe): use latest version to fix event mismatch issues

* fix enterprise handling

* cleanup

* update better auth version

* fix overage order of ops

* upgrade better auth version

* fix image typing

* change image type to string | undefined
This commit is contained in:
Vikhyath Mondreti
2025-09-15 15:54:41 -07:00
committed by GitHub
parent ba21d274ec
commit 4ce6bc94c3
7 changed files with 147 additions and 123 deletions

View File

@@ -48,7 +48,7 @@ const validStripeKey = env.STRIPE_SECRET_KEY
let stripeClient = null
if (validStripeKey) {
stripeClient = new Stripe(env.STRIPE_SECRET_KEY || '', {
apiVersion: '2025-02-24.acacia',
apiVersion: '2025-08-27.basil',
})
}
@@ -592,7 +592,6 @@ export const auth = betterAuth({
id: uniqueId,
name: 'Wealthbox User',
email: `${uniqueId.replace(/[^a-zA-Z0-9]/g, '')}@wealthbox.user`,
image: null,
emailVerified: false,
createdAt: now,
updatedAt: now,
@@ -650,7 +649,6 @@ export const auth = betterAuth({
id: uniqueId,
name: 'Supabase User',
email: `${uniqueId.replace(/[^a-zA-Z0-9]/g, '')}@supabase.user`,
image: null,
emailVerified: false,
createdAt: now,
updatedAt: now,
@@ -760,7 +758,7 @@ export const auth = betterAuth({
id: profile.account_id,
name: profile.name || profile.display_name || 'Confluence User',
email: profile.email || `${profile.account_id}@atlassian.com`,
image: profile.picture || null,
image: profile.picture || undefined,
emailVerified: true, // Assume verified since it's an Atlassian account
createdAt: now,
updatedAt: now,
@@ -811,7 +809,7 @@ export const auth = betterAuth({
email: profile.email || `${profile.id}@discord.user`,
image: profile.avatar
? `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.png`
: null,
: undefined,
emailVerified: profile.verified || false,
createdAt: now,
updatedAt: now,
@@ -881,7 +879,7 @@ export const auth = betterAuth({
id: profile.account_id,
name: profile.name || profile.display_name || 'Jira User',
email: profile.email || `${profile.account_id}@atlassian.com`,
image: profile.picture || null,
image: profile.picture || undefined,
emailVerified: true, // Assume verified since it's an Atlassian account
createdAt: now,
updatedAt: now,
@@ -949,7 +947,6 @@ export const auth = betterAuth({
id: profile.bot?.owner?.user?.id || profile.id,
name: profile.name || profile.bot?.owner?.user?.name || 'Notion User',
email: profile.person?.email || `${profile.id}@notion.user`,
image: null, // Notion API doesn't provide profile images
emailVerified: !!profile.person?.email,
createdAt: now,
updatedAt: now,
@@ -1000,7 +997,7 @@ export const auth = betterAuth({
id: data.id,
name: data.name || 'Reddit User',
email: `${data.name}@reddit.user`, // Reddit doesn't provide email in identity scope
image: data.icon_img || null,
image: data.icon_img || undefined,
emailVerified: false,
createdAt: now,
updatedAt: now,
@@ -1075,7 +1072,7 @@ export const auth = betterAuth({
emailVerified: true,
createdAt: new Date(),
updatedAt: new Date(),
image: viewer.avatarUrl || null,
image: viewer.avatarUrl || undefined,
}
} catch (error) {
logger.error('Error in getUserInfo:', error)
@@ -1138,7 +1135,6 @@ export const auth = betterAuth({
id: uniqueId,
name: 'Slack Bot',
email: `${uniqueId.replace(/[^a-zA-Z0-9]/g, '')}@slack.bot`,
image: null,
emailVerified: false,
createdAt: now,
updatedAt: now,

View File

@@ -50,13 +50,12 @@ export async function getUserUsageData(userId: string): Promise<UsageData> {
])
if (userStatsData.length === 0) {
logger.error('User stats not found for userId', { userId })
throw new Error(`User stats not found for userId: ${userId}`)
}
const stats = userStatsData[0]
const currentUsage = Number.parseFloat(
stats.currentPeriodCost?.toString() ?? stats.totalCost.toString()
)
const currentUsage = Number.parseFloat(stats.currentPeriodCost?.toString() ?? '0')
// Determine usage limit based on plan type
let limit: number

View File

@@ -38,7 +38,7 @@ const createStripeClientSingleton = () => {
isInitializing = true
stripeClient = new Stripe(env.STRIPE_SECRET_KEY || '', {
apiVersion: '2025-02-24.acacia',
apiVersion: '2025-08-27.basil',
})
logger.info('Stripe client initialized successfully')

View File

@@ -98,6 +98,9 @@ export async function handleManualEnterpriseSubscription(event: Stripe.Event) {
throw new Error('Enterprise subscription must include valid monthlyPrice in metadata')
}
// Get the first subscription item which contains the period information
const referenceItem = stripeSubscription.items?.data?.[0]
const subscriptionRow = {
id: crypto.randomUUID(),
plan: 'enterprise',
@@ -105,11 +108,11 @@ export async function handleManualEnterpriseSubscription(event: Stripe.Event) {
stripeCustomerId,
stripeSubscriptionId: stripeSubscription.id,
status: stripeSubscription.status || null,
periodStart: stripeSubscription.current_period_start
? new Date(stripeSubscription.current_period_start * 1000)
periodStart: referenceItem?.current_period_start
? new Date(referenceItem.current_period_start * 1000)
: null,
periodEnd: stripeSubscription.current_period_end
? new Date(stripeSubscription.current_period_end * 1000)
periodEnd: referenceItem?.current_period_end
? new Date(referenceItem.current_period_end * 1000)
: null,
cancelAtPeriodEnd: stripeSubscription.cancel_at_period_end ?? null,
seats,

View File

@@ -53,8 +53,14 @@ export async function handleInvoicePaymentSucceeded(event: Stripe.Event) {
try {
const invoice = event.data.object as Stripe.Invoice
if (!invoice.subscription) return
const stripeSubscriptionId = String(invoice.subscription)
const subscription = invoice.parent?.subscription_details?.subscription
const stripeSubscriptionId = typeof subscription === 'string' ? subscription : subscription?.id
if (!stripeSubscriptionId) {
logger.info('No subscription found on invoice; skipping payment succeeded handler', {
invoiceId: invoice.id,
})
return
}
const records = await db
.select()
.from(subscriptionTable)
@@ -156,7 +162,9 @@ export async function handleInvoicePaymentFailed(event: Stripe.Event) {
attemptCount,
})
// Block all users under this customer (org members or individual)
const stripeSubscriptionId = String(invoice.subscription || '')
// Overage invoices are manual invoices without parent.subscription_details
// We store the subscription ID in metadata when creating them
const stripeSubscriptionId = invoice.metadata?.subscriptionId as string | undefined
if (stripeSubscriptionId) {
const records = await db
.select()
@@ -203,10 +211,16 @@ export async function handleInvoiceFinalized(event: Stripe.Event) {
try {
const invoice = event.data.object as Stripe.Invoice
// Only run for subscription renewal invoices (cycle boundary)
if (!invoice.subscription) return
const subscription = invoice.parent?.subscription_details?.subscription
const stripeSubscriptionId = typeof subscription === 'string' ? subscription : subscription?.id
if (!stripeSubscriptionId) {
logger.info('No subscription found on invoice; skipping finalized handler', {
invoiceId: invoice.id,
})
return
}
if (invoice.billing_reason && invoice.billing_reason !== 'subscription_cycle') return
const stripeSubscriptionId = String(invoice.subscription)
const records = await db
.select()
.from(subscriptionTable)
@@ -216,11 +230,9 @@ export async function handleInvoiceFinalized(event: Stripe.Event) {
if (records.length === 0) return
const sub = records[0]
// Always reset usage at cycle end for all plans
await resetUsageForSubscription({ plan: sub.plan, referenceId: sub.referenceId })
// Enterprise plans have no overages - skip overage invoice creation
// Enterprise plans have no overages - reset usage and exit
if (sub.plan === 'enterprise') {
await resetUsageForSubscription({ plan: sub.plan, referenceId: sub.referenceId })
return
}
@@ -229,7 +241,7 @@ export async function handleInvoiceFinalized(event: Stripe.Event) {
invoice.lines?.data?.[0]?.period?.end || invoice.period_end || Math.floor(Date.now() / 1000)
const billingPeriod = new Date(periodEnd * 1000).toISOString().slice(0, 7)
// Compute overage (only for team and pro plans)
// Compute overage (only for team and pro plans), before resetting usage
let totalOverage = 0
if (sub.plan === 'team') {
const members = await db
@@ -254,88 +266,101 @@ export async function handleInvoiceFinalized(event: Stripe.Event) {
totalOverage = Math.max(0, usage.currentUsage - basePrice)
}
if (totalOverage <= 0) return
if (totalOverage > 0) {
const customerId = String(invoice.customer)
const cents = Math.round(totalOverage * 100)
const itemIdemKey = `overage-item:${customerId}:${stripeSubscriptionId}:${billingPeriod}`
const invoiceIdemKey = `overage-invoice:${customerId}:${stripeSubscriptionId}:${billingPeriod}`
const customerId = String(invoice.customer)
const cents = Math.round(totalOverage * 100)
const itemIdemKey = `overage-item:${customerId}:${stripeSubscriptionId}:${billingPeriod}`
const invoiceIdemKey = `overage-invoice:${customerId}:${stripeSubscriptionId}:${billingPeriod}`
// Inherit billing settings from the Stripe subscription/customer for autopay
const getPaymentMethodId = (
pm: string | Stripe.PaymentMethod | null | undefined
): string | undefined => (typeof pm === 'string' ? pm : pm?.id)
// Inherit billing settings from the Stripe subscription/customer for autopay
const getPaymentMethodId = (
pm: string | Stripe.PaymentMethod | null | undefined
): string | undefined => (typeof pm === 'string' ? pm : pm?.id)
let collectionMethod: 'charge_automatically' | 'send_invoice' = 'charge_automatically'
let defaultPaymentMethod: string | undefined
try {
const stripeSub = await stripe.subscriptions.retrieve(stripeSubscriptionId)
if (stripeSub.collection_method === 'send_invoice') {
collectionMethod = 'send_invoice'
let collectionMethod: 'charge_automatically' | 'send_invoice' = 'charge_automatically'
let defaultPaymentMethod: string | undefined
try {
const stripeSub = await stripe.subscriptions.retrieve(stripeSubscriptionId)
if (stripeSub.collection_method === 'send_invoice') {
collectionMethod = 'send_invoice'
}
const subDpm = getPaymentMethodId(stripeSub.default_payment_method)
if (subDpm) {
defaultPaymentMethod = subDpm
} else if (collectionMethod === 'charge_automatically') {
const custObj = await stripe.customers.retrieve(customerId)
if (custObj && !('deleted' in custObj)) {
const cust = custObj as Stripe.Customer
const custDpm = getPaymentMethodId(cust.invoice_settings?.default_payment_method)
if (custDpm) defaultPaymentMethod = custDpm
}
}
} catch (e) {
logger.error('Failed to retrieve subscription or customer', { error: e })
}
const subDpm = getPaymentMethodId(stripeSub.default_payment_method)
if (subDpm) {
defaultPaymentMethod = subDpm
} else if (collectionMethod === 'charge_automatically') {
const custObj = await stripe.customers.retrieve(customerId)
if (custObj && !('deleted' in custObj)) {
const cust = custObj as Stripe.Customer
const custDpm = getPaymentMethodId(cust.invoice_settings?.default_payment_method)
if (custDpm) defaultPaymentMethod = custDpm
// Create a draft invoice first so we can attach the item directly
const overageInvoice = await stripe.invoices.create(
{
customer: customerId,
collection_method: collectionMethod,
auto_advance: false,
...(defaultPaymentMethod ? { default_payment_method: defaultPaymentMethod } : {}),
metadata: {
type: 'overage_billing',
billingPeriod,
subscriptionId: stripeSubscriptionId,
},
},
{ idempotencyKey: invoiceIdemKey }
)
// Attach the item to this invoice
await stripe.invoiceItems.create(
{
customer: customerId,
invoice: overageInvoice.id,
amount: cents,
currency: 'usd',
description: `Usage Based Overage ${billingPeriod}`,
metadata: {
type: 'overage_billing',
billingPeriod,
subscriptionId: stripeSubscriptionId,
},
},
{ idempotencyKey: itemIdemKey }
)
// Finalize to trigger autopay (if charge_automatically and a PM is present)
const draftId = overageInvoice.id
if (typeof draftId !== 'string' || draftId.length === 0) {
logger.error('Stripe created overage invoice without id; aborting finalize')
} else {
const finalized = await stripe.invoices.finalizeInvoice(draftId)
// Some manual invoices may remain open after finalize; ensure we pay immediately when possible
if (collectionMethod === 'charge_automatically' && finalized.status === 'open') {
try {
const payId = finalized.id
if (typeof payId !== 'string' || payId.length === 0) {
logger.error('Finalized invoice missing id')
throw new Error('Finalized invoice missing id')
}
await stripe.invoices.pay(payId, {
payment_method: defaultPaymentMethod,
})
} catch (payError) {
logger.error('Failed to auto-pay overage invoice', {
error: payError,
invoiceId: finalized.id,
})
}
}
}
} catch (e) {
logger.error('Failed to retrieve subscription or customer', { error: e })
}
// Create a draft invoice first so we can attach the item directly
const overageInvoice = await stripe.invoices.create(
{
customer: customerId,
collection_method: collectionMethod,
auto_advance: false,
...(defaultPaymentMethod ? { default_payment_method: defaultPaymentMethod } : {}),
metadata: {
type: 'overage_billing',
billingPeriod,
subscriptionId: stripeSubscriptionId,
},
},
{ idempotencyKey: invoiceIdemKey }
)
// Attach the item to this invoice
await stripe.invoiceItems.create(
{
customer: customerId,
invoice: overageInvoice.id,
amount: cents,
currency: 'usd',
description: `Usage Based Overage ${billingPeriod}`,
metadata: {
type: 'overage_billing',
billingPeriod,
subscriptionId: stripeSubscriptionId,
},
},
{ idempotencyKey: itemIdemKey }
)
// Finalize to trigger autopay (if charge_automatically and a PM is present)
const finalized = await stripe.invoices.finalizeInvoice(overageInvoice.id)
// Some manual invoices may remain open after finalize; ensure we pay immediately when possible
if (collectionMethod === 'charge_automatically' && finalized.status === 'open') {
try {
await stripe.invoices.pay(finalized.id, {
payment_method: defaultPaymentMethod,
})
} catch (payError) {
logger.error('Failed to auto-pay overage invoice', {
error: payError,
invoiceId: finalized.id,
})
}
}
// Finally, reset usage for this subscription after overage handling
await resetUsageForSubscription({ plan: sub.plan, referenceId: sub.referenceId })
} catch (error) {
logger.error('Failed to handle invoice finalized', { error })
throw error

View File

@@ -26,17 +26,17 @@
"test:billing:suite": "bun run scripts/test-billing-suite.ts"
},
"dependencies": {
"@e2b/code-interpreter": "^2.0.0",
"@anthropic-ai/sdk": "^0.39.0",
"@aws-sdk/client-s3": "^3.779.0",
"@aws-sdk/s3-request-presigner": "^3.779.0",
"@azure/communication-email": "1.0.0",
"@azure/storage-blob": "12.27.0",
"@better-auth/stripe": "^1.2.9",
"@better-auth/stripe": "https://pkg.pr.new/better-auth/better-auth/@better-auth/stripe@4683",
"@browserbasehq/stagehand": "^2.0.0",
"@cerebras/cerebras_cloud_sdk": "^1.23.0",
"@chatscope/chat-ui-kit-react": "2.1.1",
"@chatscope/chat-ui-kit-styles": "1.4.0",
"@e2b/code-interpreter": "^2.0.0",
"@hookform/resolvers": "^4.1.3",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/exporter-collector": "^0.25.0",
@@ -75,7 +75,7 @@
"@vercel/og": "^0.6.5",
"@vercel/speed-insights": "^1.2.0",
"ai": "^4.3.2",
"better-auth": "^1.2.9",
"better-auth": "1.3.10",
"browser-image-compression": "^2.0.2",
"cheerio": "1.1.2",
"class-variance-authority": "^0.7.1",
@@ -127,7 +127,7 @@
"rtf-stream-parser": "3.8.0",
"sharp": "0.34.3",
"socket.io": "^4.8.1",
"stripe": "^17.7.0",
"stripe": "18.5.0",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"three": "0.177.0",

View File

@@ -62,7 +62,7 @@
"@aws-sdk/s3-request-presigner": "^3.779.0",
"@azure/communication-email": "1.0.0",
"@azure/storage-blob": "12.27.0",
"@better-auth/stripe": "^1.2.9",
"@better-auth/stripe": "https://pkg.pr.new/better-auth/better-auth/@better-auth/stripe@4683",
"@browserbasehq/stagehand": "^2.0.0",
"@cerebras/cerebras_cloud_sdk": "^1.23.0",
"@chatscope/chat-ui-kit-react": "2.1.1",
@@ -106,7 +106,7 @@
"@vercel/og": "^0.6.5",
"@vercel/speed-insights": "^1.2.0",
"ai": "^4.3.2",
"better-auth": "^1.2.9",
"better-auth": "1.3.10",
"browser-image-compression": "^2.0.2",
"cheerio": "1.1.2",
"class-variance-authority": "^0.7.1",
@@ -123,6 +123,7 @@
"geist": "1.4.2",
"groq-sdk": "^0.15.0",
"html-to-text": "^9.0.5",
"@better-auth/stripe": "https://pkg.pr.new/better-auth/better-auth/@better-auth/stripe@4683",
"iconv-lite": "0.7.0",
"input-otp": "^1.4.2",
"ioredis": "^5.6.0",
@@ -158,7 +159,7 @@
"rtf-stream-parser": "3.8.0",
"sharp": "0.34.3",
"socket.io": "^4.8.1",
"stripe": "^17.7.0",
"stripe": "18.5.0",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"three": "0.177.0",
@@ -439,9 +440,9 @@
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="],
"@better-auth/stripe": ["@better-auth/stripe@1.3.7", "", { "dependencies": { "better-auth": "^1.3.7" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-hmcWJu9RB1IOf+Mb8izQyLOAkRFKes8O0j/VoIFwLR0l2zWTknXKzkLUjadFPeKXHtV3i2n2++prjuVev6F6zw=="],
"@better-auth/stripe": ["@better-auth/stripe@https://pkg.pr.new/better-auth/better-auth/@better-auth/stripe@4683", { "dependencies": { "zod": "^4.1.5" }, "peerDependencies": { "better-auth": "1.3.10", "stripe": "^18" } }],
"@better-auth/utils": ["@better-auth/utils@0.2.6", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-3y/vaL5Ox33dBwgJ6ub3OPkVqr6B5xL2kgxNHG8eHZuryLyG/4JSPGqjbdRSgjuy9kALUZYDFl+ORIAxlWMSuA=="],
"@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="],
"@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="],
@@ -717,9 +718,9 @@
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.1", "", { "os": "win32", "cpu": "x64" }, "sha512-y+wTBxelk2xiNofmDOVU7O5WxTHcvOoL3srOM0kxTzKDjQ57kPU0tpnPJ/BWrRnsOwXEv0+3QSbGR7hY4n9LkQ=="],
"@noble/ciphers": ["@noble/ciphers@0.6.0", "", {}, "sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ=="],
"@noble/ciphers": ["@noble/ciphers@2.0.0", "", {}, "sha512-j/l6jpnpaIBM87cAYPJzi/6TgqmBv9spkqPyCXvRYsu5uxqh6tPJZDnD85yo8VWqzTuTQPgfv7NgT63u7kbwAQ=="],
"@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
"@noble/hashes": ["@noble/hashes@2.0.0", "", {}, "sha512-h8VUBlE8R42+XIDO229cgisD287im3kdY6nbNZJFjc6ZvKIXPYXe6Vc/t+kyjFdMFyt5JpapzTsEg8n63w5/lw=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@@ -1633,9 +1634,9 @@
"bcryptjs": ["bcryptjs@3.0.2", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog=="],
"better-auth": ["better-auth@1.3.7", "", { "dependencies": { "@better-auth/utils": "0.2.6", "@better-fetch/fetch": "^1.1.18", "@noble/ciphers": "^0.6.0", "@noble/hashes": "^1.8.0", "@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/server": "^13.1.2", "better-call": "^1.0.13", "defu": "^6.1.4", "jose": "^5.10.0", "kysely": "^0.28.5", "nanostores": "^0.11.4" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-/1fEyx2SGgJQM5ujozDCh9eJksnVkNU/J7Fk/tG5Y390l8nKbrPvqiFlCjlMM+scR+UABJbQzA6An7HT50LHyQ=="],
"better-auth": ["better-auth@1.3.10", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "^1.1.18", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/server": "^13.1.2", "better-call": "1.0.19", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.1.5" }, "peerDependencies": { "@lynx-js/react": "*", "@sveltejs/kit": "^2.0.0", "next": "^14.0.0 || ^15.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@sveltejs/kit", "next", "react", "react-dom", "solid-js", "svelte", "vue"] }, "sha512-cEdvbqJ2TlTXUSktHKs8V3rHdFYkEG7QmQZpLXGjXZX6F0nYbTk2QPsWXNhxbinFAlE2ca4virzuDsmsQlLIVw=="],
"better-call": ["better-call@1.0.16", "", { "dependencies": { "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-42dgJ1rOtc0anOoxjXPOWuel/Z/4aeO7EJ2SiXNwvlkySSgjXhNjAjTMWa8DL1nt6EXS3jl3VKC3mPsU/lUgVA=="],
"better-call": ["better-call@1.0.19", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-sI3GcA1SCVa3H+CDHl8W8qzhlrckwXOTKhqq3OOPXjgn5aTOMIqGY34zLY/pHA6tRRMjTUC3lz5Mi7EbDA24Kw=="],
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
@@ -2575,7 +2576,7 @@
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"nanostores": ["nanostores@0.11.4", "", {}, "sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ=="],
"nanostores": ["nanostores@1.0.1", "", {}, "sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw=="],
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
@@ -3057,7 +3058,7 @@
"strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="],
"stripe": ["stripe@17.7.0", "", { "dependencies": { "@types/node": ">=8.1.0", "qs": "^6.11.0" } }, "sha512-aT2BU9KkizY9SATf14WhhYVv2uOapBWX0OFWF4xvcj1mPaNotlSc2CsxpS4DS46ZueSppmCF5BX1sNYBtwBvfw=="],
"stripe": ["stripe@18.5.0", "", { "dependencies": { "qs": "^6.11.0" }, "peerDependencies": { "@types/node": ">=12.x.x" }, "optionalPeers": ["@types/node"] }, "sha512-Hp+wFiEQtCB0LlNgcFh5uVyKznpDjzyUZ+CNVEf+I3fhlYvh7rZruIg+jOwzJRCpy0ZTPMjlzm7J2/M2N6d+DA=="],
"strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
@@ -3363,6 +3364,8 @@
"@babel/template/@babel/parser": ["@babel/parser@7.28.3", "", { "dependencies": { "@babel/types": "^7.28.2" }, "bin": "./bin/babel-parser.js" }, "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA=="],
"@better-auth/stripe/zod": ["zod@4.1.5", "", {}, "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg=="],
"@browserbasehq/sdk/@types/node": ["@types/node@18.19.123", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg=="],
"@browserbasehq/sdk/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
@@ -3747,7 +3750,9 @@
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"better-auth/jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="],
"better-auth/jose": ["jose@6.1.0", "", {}, "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA=="],
"better-auth/zod": ["zod@4.1.5", "", {}, "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg=="],
"bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
@@ -3925,8 +3930,6 @@
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"stripe/@types/node": ["@types/node@24.2.1", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ=="],
"sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
"sucrase/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
@@ -4405,8 +4408,6 @@
"sim/tailwindcss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
"stripe/@types/node/undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
"sucrase/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
"sucrase/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],