@better-auth/stripe 1.5.1-beta.1 → 1.5.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as stripe, s as StripePlan } from "./index-Dnto1ft4.mjs";
1
+ import { n as stripe, s as StripePlan } from "./index-KDjkA2hK.mjs";
2
2
  import * as better_auth0 from "better-auth";
3
3
 
4
4
  //#region src/error-codes.d.ts
@@ -1,7 +1,7 @@
1
1
  import * as better_auth0 from "better-auth";
2
2
  import { GenericEndpointContext, InferOptionSchema, Session, User } from "better-auth";
3
- import * as better_call0 from "better-call";
4
3
  import * as zod from "zod";
4
+ import * as better_call0 from "better-call";
5
5
  import { Organization } from "better-auth/plugins/organization";
6
6
  import Stripe from "stripe";
7
7
 
@@ -990,4 +990,4 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
990
990
  type StripePlugin<O extends StripeOptions> = ReturnType<typeof stripe<O>>;
991
991
  //#endregion
992
992
  export { StripeCtxSession as a, Subscription as c, WithStripeCustomerId as d, CustomerType as i, SubscriptionOptions as l, stripe as n, StripeOptions as o, AuthorizeReferenceAction as r, StripePlan as s, StripePlugin as t, WithActiveOrganizationId as u };
993
- //# sourceMappingURL=index-Dnto1ft4.d.mts.map
993
+ //# sourceMappingURL=index-KDjkA2hK.d.mts.map
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as StripeCtxSession, c as Subscription, d as WithStripeCustomerId, i as CustomerType, l as SubscriptionOptions, n as stripe, o as StripeOptions, r as AuthorizeReferenceAction, s as StripePlan, t as StripePlugin, u as WithActiveOrganizationId } from "./index-Dnto1ft4.mjs";
1
+ import { a as StripeCtxSession, c as Subscription, d as WithStripeCustomerId, i as CustomerType, l as SubscriptionOptions, n as stripe, o as StripeOptions, r as AuthorizeReferenceAction, s as StripePlan, t as StripePlugin, u as WithActiveOrganizationId } from "./index-KDjkA2hK.mjs";
2
2
  export { AuthorizeReferenceAction, CustomerType, StripeCtxSession, StripeOptions, StripePlan, StripePlugin, Subscription, SubscriptionOptions, WithActiveOrganizationId, WithStripeCustomerId, stripe };
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import { defu } from "defu";
4
4
  import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
5
5
  import { APIError as APIError$1 } from "@better-auth/core/error";
6
6
  import { getSessionFromCtx, originCheck, sessionMiddleware } from "better-auth/api";
7
- import * as z from "zod/v4";
7
+ import * as z from "zod";
8
8
  import { mergeSchema } from "better-auth/db";
9
9
 
10
10
  //#region src/metadata.ts
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["APIError","APIError"],"sources":["../src/metadata.ts","../src/utils.ts","../src/hooks.ts","../src/middleware.ts","../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { defu } from \"defu\";\nimport type Stripe from \"stripe\";\n\n/**\n * Internal metadata fields for Stripe Customer.\n */\ntype CustomerInternalMetadata =\n\t| { customerType: \"user\"; userId: string }\n\t| { customerType: \"organization\"; organizationId: string };\n\n/**\n * Internal metadata fields for Stripe Subscription/Checkout.\n */\ntype SubscriptionInternalMetadata = {\n\tuserId: string;\n\tsubscriptionId: string;\n\treferenceId: string;\n};\n\n/**\n * Customer metadata - set internal fields and extract typed fields.\n */\nexport const customerMetadata = {\n\t/**\n\t * Internal metadata keys for type-safe access.\n\t */\n\tkeys: {\n\t\tuserId: \"userId\",\n\t\torganizationId: \"organizationId\",\n\t\tcustomerType: \"customerType\",\n\t} as const,\n\n\t/**\n\t * Create metadata with internal fields that cannot be overridden by user metadata.\n\t * Uses `defu` which prioritizes the first argument.\n\t */\n\tset(\n\t\tinternalFields: CustomerInternalMetadata,\n\t\t...userMetadata: (Stripe.Emptyable<Stripe.MetadataParam> | undefined)[]\n\t): Stripe.MetadataParam {\n\t\treturn defu(internalFields, ...userMetadata.filter(Boolean));\n\t},\n\n\t/**\n\t * Extract internal fields from Stripe metadata.\n\t * Provides type-safe access to internal metadata keys.\n\t */\n\tget(metadata: Stripe.Metadata | null | undefined) {\n\t\treturn {\n\t\t\tuserId: metadata?.userId,\n\t\t\torganizationId: metadata?.organizationId,\n\t\t\tcustomerType: metadata?.customerType as\n\t\t\t\t| CustomerInternalMetadata[\"customerType\"]\n\t\t\t\t| undefined,\n\t\t};\n\t},\n};\n\n/**\n * Subscription/Checkout metadata - set internal fields and extract typed fields.\n */\nexport const subscriptionMetadata = {\n\t/**\n\t * Internal metadata keys for type-safe access.\n\t */\n\tkeys: {\n\t\tuserId: \"userId\",\n\t\tsubscriptionId: \"subscriptionId\",\n\t\treferenceId: \"referenceId\",\n\t} as const,\n\n\t/**\n\t * Create metadata with internal fields that cannot be overridden by user metadata.\n\t * Uses `defu` which prioritizes the first argument.\n\t */\n\tset(\n\t\tinternalFields: SubscriptionInternalMetadata,\n\t\t...userMetadata: (Stripe.Emptyable<Stripe.MetadataParam> | undefined)[]\n\t): Stripe.MetadataParam {\n\t\treturn defu(internalFields, ...userMetadata.filter(Boolean));\n\t},\n\n\t/**\n\t * Extract internal fields from Stripe metadata.\n\t * Provides type-safe access to internal metadata keys.\n\t */\n\tget(metadata: Stripe.Metadata | null | undefined) {\n\t\treturn {\n\t\t\tuserId: metadata?.userId,\n\t\t\tsubscriptionId: metadata?.subscriptionId,\n\t\t\treferenceId: metadata?.referenceId,\n\t\t};\n\t},\n};\n","import type Stripe from \"stripe\";\nimport type { StripeOptions, StripePlan, Subscription } from \"./types\";\n\nexport async function getPlans(\n\tsubscriptionOptions: StripeOptions[\"subscription\"],\n) {\n\tif (subscriptionOptions?.enabled) {\n\t\treturn typeof subscriptionOptions.plans === \"function\"\n\t\t\t? await subscriptionOptions.plans()\n\t\t\t: subscriptionOptions.plans;\n\t}\n\tthrow new Error(\"Subscriptions are not enabled in the Stripe options.\");\n}\n\nexport async function getPlanByName(options: StripeOptions, name: string) {\n\treturn await getPlans(options.subscription).then((res) =>\n\t\tres?.find((plan) => plan.name.toLowerCase() === name.toLowerCase()),\n\t);\n}\n\n/**\n * Checks if a subscription is in an available state (active or trialing)\n */\nexport function isActiveOrTrialing(\n\tsub: Subscription | Stripe.Subscription,\n): boolean {\n\treturn sub.status === \"active\" || sub.status === \"trialing\";\n}\n\n/**\n * Check if a subscription is scheduled to be canceled (DB subscription object)\n */\nexport function isPendingCancel(sub: Subscription): boolean {\n\treturn !!(sub.cancelAtPeriodEnd || sub.cancelAt);\n}\n\n/**\n * Check if a Stripe subscription is scheduled to be canceled (Stripe API response)\n */\nexport function isStripePendingCancel(stripeSub: Stripe.Subscription): boolean {\n\treturn !!(stripeSub.cancel_at_period_end || stripeSub.cancel_at);\n}\n\n/**\n * Escapes a value for use in Stripe search queries.\n * Stripe search query uses double quotes for string values,\n * and double quotes within the value need to be escaped with backslash.\n *\n * @see https://docs.stripe.com/search#search-query-language\n */\nexport function escapeStripeSearchValue(value: string): string {\n\treturn value.replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Resolve the quantity for a subscription by checking the seat item first,\n * then falling back to the plan item's quantity.\n */\nexport function resolveQuantity(\n\titems: Stripe.SubscriptionItem[],\n\tplanItem: Stripe.SubscriptionItem,\n\tseatPriceId?: string,\n): number {\n\tif (seatPriceId) {\n\t\tconst seatItem = items.find((item) => item.price.id === seatPriceId);\n\t\tif (seatItem) return seatItem.quantity ?? 1;\n\t}\n\treturn planItem.quantity ?? 1;\n}\n\n/**\n * Resolve the plan-matching subscription item and its plan config\n * from a (possibly multi-item) Stripe subscription.\n *\n * - Iterates items to find one whose price matches a configured plan.\n * - For single-item subscriptions, returns the item even without a plan match.\n */\nexport async function resolvePlanItem(\n\toptions: StripeOptions,\n\titems: Stripe.SubscriptionItem[],\n): Promise<\n\t{ item: Stripe.SubscriptionItem; plan: StripePlan | undefined } | undefined\n> {\n\tconst first = items[0];\n\tif (!first) return undefined;\n\tconst plans = await getPlans(options.subscription);\n\tfor (const item of items) {\n\t\tconst plan = plans?.find(\n\t\t\t(p) =>\n\t\t\t\tp.priceId === item.price.id ||\n\t\t\t\tp.annualDiscountPriceId === item.price.id ||\n\t\t\t\t(item.price.lookup_key &&\n\t\t\t\t\t(p.lookupKey === item.price.lookup_key ||\n\t\t\t\t\t\tp.annualDiscountLookupKey === item.price.lookup_key)),\n\t\t);\n\t\tif (plan) return { item, plan };\n\t}\n\treturn items.length === 1 ? { item: first, plan: undefined } : undefined;\n}\n","import type { GenericEndpointContext } from \"@better-auth/core\";\nimport type { User } from \"@better-auth/core/db\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport type Stripe from \"stripe\";\nimport { subscriptionMetadata } from \"./metadata\";\nimport type { CustomerType, StripeOptions, Subscription } from \"./types\";\nimport {\n\tisActiveOrTrialing,\n\tisPendingCancel,\n\tisStripePendingCancel,\n\tresolvePlanItem,\n\tresolveQuantity,\n} from \"./utils\";\n\n/**\n * Find organization or user by stripeCustomerId.\n * @internal\n */\nasync function findReferenceByStripeCustomerId(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tstripeCustomerId: string,\n): Promise<{ customerType: CustomerType; referenceId: string } | null> {\n\tif (options.organization?.enabled) {\n\t\tconst org = await ctx.context.adapter.findOne<Organization>({\n\t\t\tmodel: \"organization\",\n\t\t\twhere: [{ field: \"stripeCustomerId\", value: stripeCustomerId }],\n\t\t});\n\t\tif (org) return { customerType: \"organization\", referenceId: org.id };\n\t}\n\n\tconst user = await ctx.context.adapter.findOne<User>({\n\t\tmodel: \"user\",\n\t\twhere: [{ field: \"stripeCustomerId\", value: stripeCustomerId }],\n\t});\n\tif (user) return { customerType: \"user\", referenceId: user.id };\n\n\treturn null;\n}\n\nexport async function onCheckoutSessionCompleted(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\ttry {\n\t\tconst client = options.stripeClient;\n\t\tconst checkoutSession = event.data.object as Stripe.Checkout.Session;\n\t\tif (checkoutSession.mode === \"setup\" || !options.subscription?.enabled) {\n\t\t\treturn;\n\t\t}\n\t\tconst subscription = await client.subscriptions.retrieve(\n\t\t\tcheckoutSession.subscription as string,\n\t\t);\n\t\tconst resolved = await resolvePlanItem(options, subscription.items.data);\n\t\tif (!resolved) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscription.id} has no items matching a configured plan`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { item: subscriptionItem, plan } = resolved;\n\t\tif (plan) {\n\t\t\tconst checkoutMeta = subscriptionMetadata.get(checkoutSession?.metadata);\n\t\t\tconst referenceId =\n\t\t\t\tcheckoutSession?.client_reference_id || checkoutMeta.referenceId;\n\t\t\tconst { subscriptionId } = checkoutMeta;\n\t\t\tconst seats = resolveQuantity(\n\t\t\t\tsubscription.items.data,\n\t\t\t\tsubscriptionItem,\n\t\t\t\tplan.seatPriceId,\n\t\t\t);\n\t\t\tif (referenceId && subscriptionId) {\n\t\t\t\tconst trial =\n\t\t\t\t\tsubscription.trial_start && subscription.trial_end\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\ttrialStart: new Date(subscription.trial_start * 1000),\n\t\t\t\t\t\t\t\ttrialEnd: new Date(subscription.trial_end * 1000),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {};\n\n\t\t\t\tlet dbSubscription = await ctx.context.adapter.update<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\t...trial,\n\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\tstatus: subscription.status,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\tperiodStart: new Date(subscriptionItem.current_period_start * 1000),\n\t\t\t\t\t\tperiodEnd: new Date(subscriptionItem.current_period_end * 1000),\n\t\t\t\t\t\tstripeSubscriptionId: checkoutSession.subscription as string,\n\t\t\t\t\t\tcancelAtPeriodEnd: subscription.cancel_at_period_end,\n\t\t\t\t\t\tcancelAt: subscription.cancel_at\n\t\t\t\t\t\t\t? new Date(subscription.cancel_at * 1000)\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tcanceledAt: subscription.canceled_at\n\t\t\t\t\t\t\t? new Date(subscription.canceled_at * 1000)\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tendedAt: subscription.ended_at\n\t\t\t\t\t\t\t? new Date(subscription.ended_at * 1000)\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tseats: seats,\n\t\t\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t\t\t},\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\tif (trial.trialStart && plan.freeTrial?.onTrialStart) {\n\t\t\t\t\tawait plan.freeTrial.onTrialStart(dbSubscription as Subscription);\n\t\t\t\t}\n\n\t\t\t\tif (!dbSubscription) {\n\t\t\t\t\tdbSubscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tawait options.subscription?.onSubscriptionComplete?.(\n\t\t\t\t\t{\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\tsubscription: dbSubscription as Subscription,\n\t\t\t\t\t\tstripeSubscription: subscription,\n\t\t\t\t\t\tplan,\n\t\t\t\t\t},\n\t\t\t\t\tctx,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t} catch (e: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${e.message}`);\n\t}\n}\n\nexport async function onSubscriptionCreated(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\ttry {\n\t\tif (!options.subscription?.enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst subscriptionCreated = event.data.object as Stripe.Subscription;\n\t\tconst stripeCustomerId = subscriptionCreated.customer?.toString();\n\t\tif (!stripeCustomerId) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: customer.subscription.created event received without customer ID`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if subscription already exists in database\n\t\tconst { subscriptionId } = subscriptionMetadata.get(\n\t\t\tsubscriptionCreated.metadata,\n\t\t);\n\t\tconst existingSubscription =\n\t\t\tawait ctx.context.adapter.findOne<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: subscriptionId\n\t\t\t\t\t? [{ field: \"id\", value: subscriptionId }]\n\t\t\t\t\t: [{ field: \"stripeSubscriptionId\", value: subscriptionCreated.id }], // Probably won't match since it's not set yet\n\t\t\t});\n\t\tif (existingSubscription) {\n\t\t\tctx.context.logger.info(\n\t\t\t\t`Stripe webhook: Subscription already exists in database (id: ${existingSubscription.id}), skipping creation`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Find reference\n\t\tconst reference = await findReferenceByStripeCustomerId(\n\t\t\tctx,\n\t\t\toptions,\n\t\t\tstripeCustomerId,\n\t\t);\n\t\tif (!reference) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: No user or organization found with stripeCustomerId: ${stripeCustomerId}`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst { referenceId, customerType } = reference;\n\n\t\tconst resolved = await resolvePlanItem(\n\t\t\toptions,\n\t\t\tsubscriptionCreated.items.data,\n\t\t);\n\t\tif (!resolved) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscriptionCreated.id} has no items matching a configured plan`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { item: subscriptionItem, plan } = resolved;\n\t\tif (!plan) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: No matching plan found for priceId: ${subscriptionItem.price.id}`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst seats = resolveQuantity(\n\t\t\tsubscriptionCreated.items.data,\n\t\t\tsubscriptionItem,\n\t\t\tplan.seatPriceId,\n\t\t);\n\t\tconst periodStart = new Date(subscriptionItem.current_period_start * 1000);\n\t\tconst periodEnd = new Date(subscriptionItem.current_period_end * 1000);\n\n\t\tconst trial =\n\t\t\tsubscriptionCreated.trial_start && subscriptionCreated.trial_end\n\t\t\t\t? {\n\t\t\t\t\t\ttrialStart: new Date(subscriptionCreated.trial_start * 1000),\n\t\t\t\t\t\ttrialEnd: new Date(subscriptionCreated.trial_end * 1000),\n\t\t\t\t\t}\n\t\t\t\t: {};\n\n\t\t// Create the subscription in the database\n\t\tconst newSubscription = await ctx.context.adapter.create<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\tdata: {\n\t\t\t\t...trial,\n\t\t\t\t...(plan.limits ? { limits: plan.limits } : {}),\n\t\t\t\treferenceId,\n\t\t\t\tstripeCustomerId,\n\t\t\t\tstripeSubscriptionId: subscriptionCreated.id,\n\t\t\t\tstatus: subscriptionCreated.status,\n\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\tperiodStart,\n\t\t\t\tperiodEnd,\n\t\t\t\tseats,\n\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t},\n\t\t});\n\n\t\tctx.context.logger.info(\n\t\t\t`Stripe webhook: Created subscription ${subscriptionCreated.id} for ${customerType} ${referenceId} from dashboard`,\n\t\t);\n\n\t\tawait options.subscription.onSubscriptionCreated?.({\n\t\t\tevent,\n\t\t\tsubscription: newSubscription,\n\t\t\tstripeSubscription: subscriptionCreated,\n\t\t\tplan,\n\t\t});\n\t} catch (error: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);\n\t}\n}\n\nexport async function onSubscriptionUpdated(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\ttry {\n\t\tif (!options.subscription?.enabled) {\n\t\t\treturn;\n\t\t}\n\t\tconst subscriptionUpdated = event.data.object as Stripe.Subscription;\n\t\tconst resolved = await resolvePlanItem(\n\t\t\toptions,\n\t\t\tsubscriptionUpdated.items.data,\n\t\t);\n\t\tif (!resolved) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscriptionUpdated.id} has no items matching a configured plan`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { item: subscriptionItem, plan } = resolved;\n\n\t\tconst { subscriptionId } = subscriptionMetadata.get(\n\t\t\tsubscriptionUpdated.metadata,\n\t\t);\n\t\tconst customerId = subscriptionUpdated.customer?.toString();\n\t\tlet subscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\twhere: subscriptionId\n\t\t\t\t? [{ field: \"id\", value: subscriptionId }]\n\t\t\t\t: [{ field: \"stripeSubscriptionId\", value: subscriptionUpdated.id }],\n\t\t});\n\t\tif (!subscription) {\n\t\t\tconst subs = await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [{ field: \"stripeCustomerId\", value: customerId }],\n\t\t\t});\n\t\t\tif (subs.length > 1) {\n\t\t\t\tconst activeSub = subs.find((sub: Subscription) =>\n\t\t\t\t\tisActiveOrTrialing(sub),\n\t\t\t\t);\n\t\t\t\tif (!activeSub) {\n\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t`Stripe webhook error: Multiple subscriptions found for customerId: ${customerId} and no active subscription is found`,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsubscription = activeSub;\n\t\t\t} else {\n\t\t\t\tsubscription = subs[0]!;\n\t\t\t}\n\t\t}\n\n\t\tconst seats = plan\n\t\t\t? resolveQuantity(\n\t\t\t\t\tsubscriptionUpdated.items.data,\n\t\t\t\t\tsubscriptionItem,\n\t\t\t\t\tplan.seatPriceId,\n\t\t\t\t)\n\t\t\t: subscriptionItem.quantity;\n\n\t\tconst trial =\n\t\t\tsubscriptionUpdated.trial_start && subscriptionUpdated.trial_end\n\t\t\t\t? {\n\t\t\t\t\t\ttrialStart: new Date(subscriptionUpdated.trial_start * 1000),\n\t\t\t\t\t\ttrialEnd: new Date(subscriptionUpdated.trial_end * 1000),\n\t\t\t\t\t}\n\t\t\t\t: {};\n\n\t\tconst updatedSubscription = await ctx.context.adapter.update<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\tupdate: {\n\t\t\t\t...trial,\n\t\t\t\t...(plan\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\t\tlimits: plan.limits,\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\tstatus: subscriptionUpdated.status,\n\t\t\t\tperiodStart: new Date(subscriptionItem.current_period_start * 1000),\n\t\t\t\tperiodEnd: new Date(subscriptionItem.current_period_end * 1000),\n\t\t\t\tcancelAtPeriodEnd: subscriptionUpdated.cancel_at_period_end,\n\t\t\t\tcancelAt: subscriptionUpdated.cancel_at\n\t\t\t\t\t? new Date(subscriptionUpdated.cancel_at * 1000)\n\t\t\t\t\t: null,\n\t\t\t\tcanceledAt: subscriptionUpdated.canceled_at\n\t\t\t\t\t? new Date(subscriptionUpdated.canceled_at * 1000)\n\t\t\t\t\t: null,\n\t\t\t\tendedAt: subscriptionUpdated.ended_at\n\t\t\t\t\t? new Date(subscriptionUpdated.ended_at * 1000)\n\t\t\t\t\t: null,\n\t\t\t\tseats,\n\t\t\t\tstripeSubscriptionId: subscriptionUpdated.id,\n\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t\tstripeScheduleId: subscriptionUpdated.schedule\n\t\t\t\t\t? typeof subscriptionUpdated.schedule === \"string\"\n\t\t\t\t\t\t? subscriptionUpdated.schedule\n\t\t\t\t\t\t: subscriptionUpdated.schedule.id\n\t\t\t\t\t: null,\n\t\t\t},\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"id\",\n\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tconst isNewCancellation =\n\t\t\tsubscriptionUpdated.status === \"active\" &&\n\t\t\tisStripePendingCancel(subscriptionUpdated) &&\n\t\t\t!isPendingCancel(subscription);\n\t\tif (isNewCancellation) {\n\t\t\tawait options.subscription.onSubscriptionCancel?.({\n\t\t\t\tsubscription,\n\t\t\t\tcancellationDetails:\n\t\t\t\t\tsubscriptionUpdated.cancellation_details || undefined,\n\t\t\t\tstripeSubscription: subscriptionUpdated,\n\t\t\t\tevent,\n\t\t\t});\n\t\t}\n\t\tawait options.subscription.onSubscriptionUpdate?.({\n\t\t\tevent,\n\t\t\tsubscription: updatedSubscription || subscription,\n\t\t});\n\t\tif (plan) {\n\t\t\tif (\n\t\t\t\tsubscriptionUpdated.status === \"active\" &&\n\t\t\t\tsubscription.status === \"trialing\" &&\n\t\t\t\tplan.freeTrial?.onTrialEnd\n\t\t\t) {\n\t\t\t\tawait plan.freeTrial.onTrialEnd({ subscription }, ctx);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tsubscriptionUpdated.status === \"incomplete_expired\" &&\n\t\t\t\tsubscription.status === \"trialing\" &&\n\t\t\t\tplan.freeTrial?.onTrialExpired\n\t\t\t) {\n\t\t\t\tawait plan.freeTrial.onTrialExpired(subscription, ctx);\n\t\t\t}\n\t\t}\n\t} catch (error: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);\n\t}\n}\n\nexport async function onSubscriptionDeleted(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\tif (!options.subscription?.enabled) {\n\t\treturn;\n\t}\n\ttry {\n\t\tconst subscriptionDeleted = event.data.object as Stripe.Subscription;\n\t\tconst subscriptionId = subscriptionDeleted.id;\n\t\tconst subscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tif (subscription) {\n\t\t\tconst trial =\n\t\t\t\tsubscriptionDeleted.trial_start && subscriptionDeleted.trial_end\n\t\t\t\t\t? {\n\t\t\t\t\t\t\ttrialStart: new Date(subscriptionDeleted.trial_start * 1000),\n\t\t\t\t\t\t\ttrialEnd: new Date(subscriptionDeleted.trial_end * 1000),\n\t\t\t\t\t\t}\n\t\t\t\t\t: {};\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tupdate: {\n\t\t\t\t\t...trial,\n\t\t\t\t\tstatus: \"canceled\",\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\tcancelAtPeriodEnd: subscriptionDeleted.cancel_at_period_end,\n\t\t\t\t\tcancelAt: subscriptionDeleted.cancel_at\n\t\t\t\t\t\t? new Date(subscriptionDeleted.cancel_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tcanceledAt: subscriptionDeleted.canceled_at\n\t\t\t\t\t\t? new Date(subscriptionDeleted.canceled_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tendedAt: subscriptionDeleted.ended_at\n\t\t\t\t\t\t? new Date(subscriptionDeleted.ended_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tstripeScheduleId: null,\n\t\t\t\t},\n\t\t\t});\n\t\t\tawait options.subscription.onSubscriptionDeleted?.({\n\t\t\t\tevent,\n\t\t\t\tstripeSubscription: subscriptionDeleted,\n\t\t\t\tsubscription,\n\t\t\t});\n\t\t} else {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook error: Subscription not found for subscriptionId: ${subscriptionId}`,\n\t\t\t);\n\t\t}\n\t} catch (error: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);\n\t}\n}\n","import { createAuthMiddleware } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport { sessionMiddleware } from \"better-auth/api\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport type {\n\tAuthorizeReferenceAction,\n\tCustomerType,\n\tStripeCtxSession,\n\tSubscriptionOptions,\n} from \"./types\";\n\nexport const stripeSessionMiddleware = createAuthMiddleware(\n\t{\n\t\tuse: [sessionMiddleware],\n\t},\n\tasync (ctx) => {\n\t\tconst session = ctx.context.session as StripeCtxSession;\n\t\treturn {\n\t\t\tsession,\n\t\t};\n\t},\n);\n\nexport const referenceMiddleware = (\n\tsubscriptionOptions: SubscriptionOptions,\n\taction: AuthorizeReferenceAction,\n) =>\n\tcreateAuthMiddleware(async (ctx) => {\n\t\tconst ctxSession = ctx.context.session as StripeCtxSession;\n\t\tif (!ctxSession) {\n\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t}\n\n\t\tconst customerType: CustomerType =\n\t\t\tctx.body?.customerType || ctx.query?.customerType;\n\t\tconst explicitReferenceId = ctx.body?.referenceId || ctx.query?.referenceId;\n\n\t\tif (customerType === \"organization\") {\n\t\t\t// Organization subscriptions always require authorizeReference\n\t\t\tif (!subscriptionOptions.authorizeReference) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t`Organization subscriptions require authorizeReference to be defined in your stripe plugin config.`,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.AUTHORIZE_REFERENCE_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst referenceId =\n\t\t\t\texplicitReferenceId || ctxSession.session.activeOrganizationId;\n\t\t\tif (!referenceId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_REFERENCE_ID_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isAuthorized = await subscriptionOptions.authorizeReference(\n\t\t\t\t{\n\t\t\t\t\tuser: ctxSession.user,\n\t\t\t\t\tsession: ctxSession.session,\n\t\t\t\t\treferenceId,\n\t\t\t\t\taction,\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t);\n\t\t\tif (!isAuthorized) {\n\t\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// User subscriptions - pass if no explicit referenceId\n\t\tif (!explicitReferenceId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass if referenceId is user id\n\t\tif (explicitReferenceId === ctxSession.user.id) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!subscriptionOptions.authorizeReference) {\n\t\t\tctx.context.logger.error(\n\t\t\t\t`Passing referenceId into a subscription action isn't allowed if subscription.authorizeReference isn't defined in your stripe plugin config.`,\n\t\t\t);\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.REFERENCE_ID_NOT_ALLOWED,\n\t\t\t);\n\t\t}\n\t\tconst isAuthorized = await subscriptionOptions.authorizeReference(\n\t\t\t{\n\t\t\t\tuser: ctxSession.user,\n\t\t\t\tsession: ctxSession.session,\n\t\t\t\treferenceId: explicitReferenceId,\n\t\t\t\taction,\n\t\t\t},\n\t\t\tctx,\n\t\t);\n\t\tif (!isAuthorized) {\n\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t}\n\t});\n","import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport type { GenericEndpointContext, User } from \"better-auth\";\nimport { HIDE_METADATA } from \"better-auth\";\nimport { getSessionFromCtx, originCheck } from \"better-auth/api\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport { defu } from \"defu\";\nimport type Stripe from \"stripe\";\nimport type { Stripe as StripeType } from \"stripe\";\nimport * as z from \"zod/v4\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport {\n\tonCheckoutSessionCompleted,\n\tonSubscriptionCreated,\n\tonSubscriptionDeleted,\n\tonSubscriptionUpdated,\n} from \"./hooks\";\nimport { customerMetadata, subscriptionMetadata } from \"./metadata\";\nimport { referenceMiddleware, stripeSessionMiddleware } from \"./middleware\";\nimport type {\n\tCustomerType,\n\tStripeCtxSession,\n\tStripeOptions,\n\tSubscription,\n\tSubscriptionOptions,\n\tWithStripeCustomerId,\n} from \"./types\";\nimport {\n\tescapeStripeSearchValue,\n\tgetPlanByName,\n\tgetPlans,\n\tisActiveOrTrialing,\n\tisPendingCancel,\n\tresolvePlanItem,\n\tresolveQuantity,\n} from \"./utils\";\n\n/**\n * Converts a relative URL to an absolute URL using baseURL.\n * @internal\n */\nfunction getUrl(ctx: GenericEndpointContext, url: string) {\n\tif (/^[a-zA-Z][a-zA-Z0-9+\\-.]*:/.test(url)) {\n\t\treturn url;\n\t}\n\treturn `${ctx.context.options.baseURL}${\n\t\turl.startsWith(\"/\") ? url : `/${url}`\n\t}`;\n}\n\n/**\n * Resolves a Stripe price ID from a lookup key.\n * @internal\n */\nasync function resolvePriceIdFromLookupKey(\n\tstripeClient: Stripe,\n\tlookupKey: string,\n): Promise<string | undefined> {\n\tif (!lookupKey) return undefined;\n\tconst prices = await stripeClient.prices.list({\n\t\tlookup_keys: [lookupKey],\n\t\tactive: true,\n\t\tlimit: 1,\n\t});\n\treturn prices.data[0]?.id;\n}\n\n/**\n * Determines the reference ID based on customer type.\n * - `user` (default): uses userId\n * - `organization`: uses activeOrganizationId from session\n * @internal\n */\nfunction getReferenceId(\n\tctxSession: StripeCtxSession,\n\tcustomerType: CustomerType | undefined,\n\toptions: StripeOptions,\n): string {\n\tconst { user, session } = ctxSession;\n\tconst type = customerType || \"user\";\n\n\tif (type === \"organization\") {\n\t\tif (!options.organization?.enabled) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_SUBSCRIPTION_NOT_ENABLED,\n\t\t\t);\n\t\t}\n\n\t\tif (!session.activeOrganizationId) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t);\n\t\t}\n\t\treturn session.activeOrganizationId;\n\t}\n\n\treturn user.id;\n}\n\nconst upgradeSubscriptionBodySchema = z.object({\n\t/**\n\t * The name of the plan to subscribe\n\t */\n\tplan: z.string().meta({\n\t\tdescription: 'The name of the plan to upgrade to. Eg: \"pro\"',\n\t}),\n\t/**\n\t * If annual plan should be applied.\n\t */\n\tannual: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription: \"Whether to upgrade to an annual plan. Eg: true\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Reference ID for the subscription based on customerType:\n\t * - `user`: defaults to `user.id`\n\t * - `organization`: defaults to `session.activeOrganizationId`\n\t */\n\treferenceId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: 'Reference ID for the subscription. Eg: \"org_123\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The Stripe subscription ID to upgrade.\n\t * If provided and not found, it'll throw an error.\n\t */\n\tsubscriptionId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'The Stripe subscription ID to upgrade. Eg: \"sub_1ABC2DEF3GHI4JKL\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription (requires referenceId)\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Additional metadata to store with the subscription.\n\t */\n\tmetadata: z.record(z.string(), z.any()).optional(),\n\t/**\n\t * Number of seats for subscriptions.\n\t */\n\tseats: z\n\t\t.number()\n\t\t.meta({\n\t\t\tdescription: \"Number of seats to upgrade to (if applicable). Eg: 1\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The IETF language tag of the locale Checkout is displayed in.\n\t * If not provided or set to `auto`, the browser's locale is used.\n\t */\n\tlocale: z\n\t\t.custom<StripeType.Checkout.Session.Locale>((localization) => {\n\t\t\treturn typeof localization === \"string\";\n\t\t})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The locale to display Checkout in. Eg: 'en', 'ko'. If not provided or set to `auto`, the browser's locale is used.\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The URL to which Stripe should send customers when payment or setup is complete.\n\t */\n\tsuccessUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Callback URL to redirect back after successful subscription. Eg: \"https://example.com/success\"',\n\t\t})\n\t\t.default(\"/\"),\n\t/**\n\t * If set, checkout shows a back button and customers will be directed here if they cancel payment.\n\t */\n\tcancelUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'If set, checkout shows a back button and customers will be directed here if they cancel payment. Eg: \"https://example.com/pricing\"',\n\t\t})\n\t\t.default(\"/\"),\n\t/**\n\t * The URL to return to from the Billing Portal (used when upgrading existing subscriptions)\n\t */\n\treturnUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'URL to take customers to when they click on the billing portal’s link to return to your website. Eg: \"https://example.com/dashboard\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Schedule the plan change at the end of the current billing period instead of applying immediately.\n\t */\n\tscheduleAtPeriodEnd: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Schedule the plan change at the end of the current billing period instead of applying immediately.\",\n\t\t})\n\t\t.default(false),\n\t/**\n\t * Disable Redirect\n\t */\n\tdisableRedirect: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription: \"Disable redirect after successful subscription. Eg: true\",\n\t\t})\n\t\t.default(false),\n});\n\n/**\n * ### Endpoint\n *\n * POST `/subscription/upgrade`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.upgradeSubscription`\n *\n * **client:**\n * `authClient.subscription.upgrade`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-upgrade)\n */\nexport const upgradeSubscription = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\n\treturn createAuthEndpoint(\n\t\t\"/subscription/upgrade\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: upgradeSubscriptionBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"upgradeSubscription\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"upgrade-subscription\"),\n\t\t\t\toriginCheck((c) => {\n\t\t\t\t\treturn [c.body.successUrl as string, c.body.cancelUrl as string];\n\t\t\t\t}),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { user, session } = ctx.context.session;\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tif (!user.emailVerified && subscriptionOptions.requireEmailVerification) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst plan = await getPlanByName(options, ctx.body.plan);\n\t\t\tif (!plan) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// If subscriptionId is provided, find that specific subscription.\n\t\t\t// Otherwise, active subscription will be resolved by referenceId later.\n\t\t\tconst subscriptionToUpdate = ctx.body.subscriptionId\n\t\t\t\t? await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\t\tvalue: ctx.body.subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t: null;\n\t\t\tif (ctx.body.subscriptionId && !subscriptionToUpdate) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tctx.body.subscriptionId &&\n\t\t\t\tsubscriptionToUpdate &&\n\t\t\t\tsubscriptionToUpdate.referenceId !== referenceId\n\t\t\t) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Determine customer id\n\t\t\tlet customerId: string | undefined;\n\t\t\tif (customerType === \"organization\") {\n\t\t\t\t// Organization subscription - get customer ID from organization\n\t\t\t\tcustomerId = subscriptionToUpdate?.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\tconst org = await ctx.context.adapter.findOne<\n\t\t\t\t\t\tOrganization & WithStripeCustomerId\n\t\t\t\t\t>({\n\t\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tif (!org) {\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tcustomerId = org.stripeCustomerId;\n\n\t\t\t\t\t// If org doesn't have a customer ID, create one\n\t\t\t\t\tif (!customerId) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Find existing organization customer by organizationId metadata\n\t\t\t\t\t\t\tlet stripeCustomer: Stripe.Customer | undefined;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst result = await client.customers.search({\n\t\t\t\t\t\t\t\t\tquery: `metadata[\"${customerMetadata.keys.organizationId}\"]:\"${org.id}\"`,\n\t\t\t\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tstripeCustomer = result.data[0];\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Search API unavailable in some regions, so fall back to paginated list\n\t\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\t\"Stripe customers.search failed, falling back to customers.list\",\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tfor await (const customer of client.customers.list({\n\t\t\t\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t\t\t\t})) {\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\tcustomer.metadata?.[\n\t\t\t\t\t\t\t\t\t\t\tcustomerMetadata.keys.organizationId\n\t\t\t\t\t\t\t\t\t\t] === org.id\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\tstripeCustomer = customer;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!stripeCustomer) {\n\t\t\t\t\t\t\t\t// Get custom params if provided\n\t\t\t\t\t\t\t\tlet extraCreateParams: Partial<StripeType.CustomerCreateParams> =\n\t\t\t\t\t\t\t\t\t{};\n\t\t\t\t\t\t\t\tif (options.organization?.getCustomerCreateParams) {\n\t\t\t\t\t\t\t\t\textraCreateParams =\n\t\t\t\t\t\t\t\t\t\tawait options.organization.getCustomerCreateParams(\n\t\t\t\t\t\t\t\t\t\t\torg,\n\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Create Stripe customer for organization\n\t\t\t\t\t\t\t\t// Email can be set via getCustomerCreateParams or updated in billing portal\n\t\t\t\t\t\t\t\t// Use defu to merge params (first argument takes priority)\n\t\t\t\t\t\t\t\tconst customerParams = defu(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tname: org.name,\n\t\t\t\t\t\t\t\t\t\tmetadata: customerMetadata.set(\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\torganizationId: org.id,\n\t\t\t\t\t\t\t\t\t\t\t\tcustomerType: \"organization\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\textraCreateParams,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tstripeCustomer = await client.customers.create(customerParams);\n\n\t\t\t\t\t\t\t\t// Call onCustomerCreate callback only for newly created customers\n\t\t\t\t\t\t\t\tawait options.organization?.onCustomerCreate?.(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstripeCustomer,\n\t\t\t\t\t\t\t\t\t\torganization: {\n\t\t\t\t\t\t\t\t\t\t\t...org,\n\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\t\tvalue: org.id,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tcustomerId = stripeCustomer.id;\n\t\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t\tctx.context.logger.error(e);\n\t\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_CUSTOMER,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// User subscription - get customer ID from user\n\t\t\t\tcustomerId =\n\t\t\t\t\tsubscriptionToUpdate?.stripeCustomerId || user.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Find existing user Stripe customer by email\n\t\t\t\t\t\tlet stripeCustomer: Stripe.Customer | undefined;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst result = await client.customers.search({\n\t\t\t\t\t\t\t\tquery: `email:\"${escapeStripeSearchValue(user.email)}\" AND -metadata[\"${customerMetadata.keys.customerType}\"]:\"organization\"`,\n\t\t\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tstripeCustomer = result.data[0];\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Search API unavailable in some regions, so fall back to paginated list\n\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\"Stripe customers.search failed, falling back to customers.list\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tfor await (const customer of client.customers.list({\n\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t\t\t})) {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tcustomer.metadata?.[customerMetadata.keys.customerType] !==\n\t\t\t\t\t\t\t\t\t\"organization\"\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tstripeCustomer = customer;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!stripeCustomer) {\n\t\t\t\t\t\t\tstripeCustomer = await client.customers.create({\n\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\t\t\tmetadata: customerMetadata.set(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\t\t\tcustomerType: \"user\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Update local DB with Stripe customer ID\n\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tcustomerId = stripeCustomer.id;\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.context.logger.error(e);\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_CUSTOMER,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst subscriptions = subscriptionToUpdate\n\t\t\t\t? [subscriptionToUpdate]\n\t\t\t\t: await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\n\t\t\tconst activeOrTrialingSubscription = subscriptions.find((sub) =>\n\t\t\t\tisActiveOrTrialing(sub),\n\t\t\t);\n\n\t\t\tconst activeSubscriptions = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: customerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub)));\n\n\t\t\tconst activeSubscription = activeSubscriptions.find((sub) => {\n\t\t\t\t// If we have a specific subscription to update, match by ID\n\t\t\t\tif (\n\t\t\t\t\tsubscriptionToUpdate?.stripeSubscriptionId ||\n\t\t\t\t\tctx.body.subscriptionId\n\t\t\t\t) {\n\t\t\t\t\treturn (\n\t\t\t\t\t\tsub.id === subscriptionToUpdate?.stripeSubscriptionId ||\n\t\t\t\t\t\tsub.id === ctx.body.subscriptionId\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// Only find subscription for the same referenceId to avoid mixing personal and org subscriptions\n\t\t\t\tif (activeOrTrialingSubscription?.stripeSubscriptionId) {\n\t\t\t\t\treturn sub.id === activeOrTrialingSubscription.stripeSubscriptionId;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\n\t\t\t// Get the current price ID from the active Stripe subscription\n\t\t\tconst resolvedPlan = activeSubscription\n\t\t\t\t? await resolvePlanItem(options, activeSubscription.items.data)\n\t\t\t\t: undefined;\n\t\t\tconst planItem = resolvedPlan?.item;\n\t\t\tconst stripeSubscriptionPriceId = planItem?.price.id;\n\n\t\t\t// Also find any incomplete subscription that we can reuse\n\t\t\tconst incompleteSubscription = subscriptions.find(\n\t\t\t\t(sub) => sub.status === \"incomplete\",\n\t\t\t);\n\n\t\t\tconst priceId = ctx.body.annual\n\t\t\t\t? plan.annualDiscountPriceId\n\t\t\t\t: plan.priceId;\n\t\t\tconst lookupKey = ctx.body.annual\n\t\t\t\t? plan.annualDiscountLookupKey\n\t\t\t\t: plan.lookupKey;\n\t\t\tconst resolvedPriceId = lookupKey\n\t\t\t\t? await resolvePriceIdFromLookupKey(client, lookupKey)\n\t\t\t\t: undefined;\n\n\t\t\tconst priceIdToUse = priceId || resolvedPriceId;\n\t\t\tif (!priceIdToUse) {\n\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Price ID not found for the selected plan\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For org subscriptions with seat-based billing, seats are auto-managed.\n\t\t\t// Quantity = memberCount; use Stripe graduated pricing for free tiers.\n\t\t\tconst isAutoManagedSeats = !!(\n\t\t\t\tplan.seatPriceId && customerType === \"organization\"\n\t\t\t);\n\t\t\tlet memberCount = 0;\n\t\t\tif (isAutoManagedSeats) {\n\t\t\t\tmemberCount = await ctx.context.adapter.count({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [{ field: \"organizationId\", value: referenceId }],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst isSamePlan = activeOrTrialingSubscription?.plan === ctx.body.plan;\n\t\t\tconst isSameSeats = isAutoManagedSeats\n\t\t\t\t? true // seats are auto-managed, don't block upgrade\n\t\t\t\t: activeOrTrialingSubscription?.seats === (ctx.body.seats || 1);\n\t\t\tconst isSamePriceId = stripeSubscriptionPriceId === priceIdToUse;\n\t\t\tconst isSubscriptionStillValid =\n\t\t\t\t!activeOrTrialingSubscription?.periodEnd ||\n\t\t\t\tactiveOrTrialingSubscription.periodEnd > new Date();\n\t\t\tconst isSeatOnlyPlan =\n\t\t\t\tisAutoManagedSeats && plan.seatPriceId === plan.priceId;\n\n\t\t\tconst isAlreadySubscribed =\n\t\t\t\tactiveOrTrialingSubscription?.status === \"active\" &&\n\t\t\t\tisSamePlan &&\n\t\t\t\tisSameSeats &&\n\t\t\t\tisSamePriceId &&\n\t\t\t\tisSubscriptionStillValid;\n\t\t\tif (isAlreadySubscribed) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.ALREADY_SUBSCRIBED_PLAN,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (activeSubscription && customerId) {\n\t\t\t\t// Find the corresponding database subscription for this Stripe subscription\n\t\t\t\tlet dbSubscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\tvalue: activeSubscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\t// If no database record exists for this Stripe subscription, update the existing one\n\t\t\t\tif (!dbSubscription && activeOrTrialingSubscription) {\n\t\t\t\t\tawait ctx.context.adapter.update<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\tstripeSubscriptionId: activeSubscription.id,\n\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: activeOrTrialingSubscription.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tdbSubscription = activeOrTrialingSubscription;\n\t\t\t\t}\n\n\t\t\t\tif (!planItem) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"NOT_FOUND\",\n\t\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Release any existing plugin-created subscription schedule.\n\t\t\t\t// Only check when a schedule is attached to avoid unnecessary API calls.\n\t\t\t\tif (activeSubscription.schedule) {\n\t\t\t\t\tconst { data: existingSchedules } =\n\t\t\t\t\t\tawait client.subscriptionSchedules.list({\n\t\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\t});\n\t\t\t\t\tconst existingSchedule = existingSchedules.find(\n\t\t\t\t\t\t(s) =>\n\t\t\t\t\t\t\t(typeof s.subscription === \"string\"\n\t\t\t\t\t\t\t\t? s.subscription\n\t\t\t\t\t\t\t\t: s.subscription?.id) === activeSubscription.id &&\n\t\t\t\t\t\t\ts.status === \"active\",\n\t\t\t\t\t);\n\t\t\t\t\tif (\n\t\t\t\t\t\texistingSchedule &&\n\t\t\t\t\t\texistingSchedule.metadata?.source === \"@better-auth/stripe\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tawait client.subscriptionSchedules.release(existingSchedule.id);\n\t\t\t\t\t\tif (dbSubscription) {\n\t\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\t\tstripeScheduleId: null,\n\t\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\t\tvalue: dbSubscription.id,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst oldPlan = activeOrTrialingSubscription\n\t\t\t\t\t? await getPlanByName(options, activeOrTrialingSubscription.plan)\n\t\t\t\t\t: undefined;\n\n\t\t\t\t// Build a price replacement map:\n\t\t\t\t// oldPriceId -> { newPrice, quantity? }\n\t\t\t\t// This covers base plan, seat, and line item (usage) price changes.\n\t\t\t\tconst priceMap = new Map<\n\t\t\t\t\tstring,\n\t\t\t\t\t{ newPrice: string; quantity?: number }\n\t\t\t\t>();\n\n\t\t\t\tif (isAutoManagedSeats && plan.seatPriceId) {\n\t\t\t\t\tif (\n\t\t\t\t\t\toldPlan?.seatPriceId &&\n\t\t\t\t\t\toldPlan.seatPriceId !== plan.seatPriceId\n\t\t\t\t\t) {\n\t\t\t\t\t\tpriceMap.set(oldPlan.seatPriceId, {\n\t\t\t\t\t\t\tnewPrice: plan.seatPriceId,\n\t\t\t\t\t\t\tquantity: memberCount,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Multiset diff of line item prices.\n\t\t\t\t// old -> -1, new -> +1.\n\t\t\t\t// delta < 0 = remove, delta > 0 = add.\n\t\t\t\tconst lineItemDelta = new Map<string, number>();\n\t\t\t\tfor (const li of oldPlan?.lineItems ?? []) {\n\t\t\t\t\tif (typeof li.price === \"string\")\n\t\t\t\t\t\tlineItemDelta.set(li.price, (lineItemDelta.get(li.price) ?? 0) - 1);\n\t\t\t\t}\n\t\t\t\tfor (const li of plan.lineItems ?? []) {\n\t\t\t\t\tif (typeof li.price === \"string\")\n\t\t\t\t\t\tlineItemDelta.set(li.price, (lineItemDelta.get(li.price) ?? 0) + 1);\n\t\t\t\t}\n\t\t\t\tfor (const [price, delta] of lineItemDelta) {\n\t\t\t\t\tif (delta === 0) lineItemDelta.delete(price);\n\t\t\t\t}\n\n\t\t\t\tlet upgradeUrl: string;\n\t\t\t\tif (ctx.body.scheduleAtPeriodEnd) {\n\t\t\t\t\t// Deferred change:\n\t\t\t\t\t// schedule at billing period end via Subscription Schedules\n\t\t\t\t\tconst schedule = await client.subscriptionSchedules\n\t\t\t\t\t\t.create({\n\t\t\t\t\t\t\tfrom_subscription: activeSubscription.id,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\tconst currentPhase = schedule.phases[0];\n\t\t\t\t\tif (!currentPhase) {\n\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\tmessage: \"Subscription schedule has no phases\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst removeQuota = new Map<string, number>();\n\t\t\t\t\tfor (const [p, d] of lineItemDelta) {\n\t\t\t\t\t\tif (d < 0) removeQuota.set(p, -d);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst newPhaseItems: Array<{\n\t\t\t\t\t\tprice: string;\n\t\t\t\t\t\tquantity?: number;\n\t\t\t\t\t}> = [];\n\t\t\t\t\tfor (const item of currentPhase.items) {\n\t\t\t\t\t\tconst itemPriceId =\n\t\t\t\t\t\t\ttypeof item.price === \"string\" ? item.price : item.price.id;\n\n\t\t\t\t\t\t// Remove items the new plan no longer needs\n\t\t\t\t\t\tconst quota = removeQuota.get(itemPriceId) ?? 0;\n\t\t\t\t\t\tif (quota > 0) {\n\t\t\t\t\t\t\tremoveQuota.set(itemPriceId, quota - 1);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// priceMap takes priority\n\t\t\t\t\t\t// which handles seat-only plans\n\t\t\t\t\t\t// where base price === seat price\n\t\t\t\t\t\tconst replacement = priceMap.get(itemPriceId);\n\t\t\t\t\t\tif (replacement) {\n\t\t\t\t\t\t\tnewPhaseItems.push({\n\t\t\t\t\t\t\t\tprice: replacement.newPrice,\n\t\t\t\t\t\t\t\tquantity: replacement.quantity ?? item.quantity,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Replace base plan price\n\t\t\t\t\t\tif (itemPriceId === stripeSubscriptionPriceId) {\n\t\t\t\t\t\t\tnewPhaseItems.push({\n\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\tquantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Keep as-is\n\t\t\t\t\t\tnewPhaseItems.push({\n\t\t\t\t\t\t\tprice: itemPriceId,\n\t\t\t\t\t\t\tquantity: item.quantity,\n\t\t\t\t\t\t});\n\t\t\t\t\t\t// Consume positive delta to prevent duplicate addition\n\t\t\t\t\t\tconst d = lineItemDelta.get(itemPriceId);\n\t\t\t\t\t\tif (d !== undefined && d > 0) {\n\t\t\t\t\t\t\tif (d === 1) lineItemDelta.delete(itemPriceId);\n\t\t\t\t\t\t\telse lineItemDelta.set(itemPriceId, d - 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Add line items the new plan introduces\n\t\t\t\t\tfor (const [price, delta] of lineItemDelta) {\n\t\t\t\t\t\tfor (let i = 0; i < delta; i++) newPhaseItems.push({ price });\n\t\t\t\t\t}\n\n\t\t\t\t\tawait client.subscriptionSchedules\n\t\t\t\t\t\t.update(schedule.id, {\n\t\t\t\t\t\t\tmetadata: { source: \"@better-auth/stripe\" },\n\t\t\t\t\t\t\tend_behavior: \"release\",\n\t\t\t\t\t\t\tphases: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\titems: currentPhase.items.map((item) => ({\n\t\t\t\t\t\t\t\t\t\tprice:\n\t\t\t\t\t\t\t\t\t\t\ttypeof item.price === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t? item.price\n\t\t\t\t\t\t\t\t\t\t\t\t: item.price.id,\n\t\t\t\t\t\t\t\t\t\tquantity: item.quantity,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t\tstart_date: currentPhase.start_date,\n\t\t\t\t\t\t\t\t\tend_date: currentPhase.end_date,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\titems: newPhaseItems,\n\t\t\t\t\t\t\t\t\tstart_date: currentPhase.end_date,\n\t\t\t\t\t\t\t\t\tproration_behavior: \"none\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\t// Store schedule ID so clients can detect pending plan changes\n\t\t\t\t\tif (dbSubscription) {\n\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tstripeScheduleId: schedule.id,\n\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\tvalue: dbSubscription.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tupgradeUrl = getUrl(ctx, ctx.body.returnUrl || \"/\");\n\t\t\t\t} else if (priceMap.size > 0 || lineItemDelta.size > 0) {\n\t\t\t\t\t// Immediate change with multi-item updates: use direct API.\n\t\t\t\t\t// Billing Portal supports only 1 item update at a time,\n\t\t\t\t\t// so when multiple prices change between plans we call\n\t\t\t\t\t// subscriptions.update directly.\n\t\t\t\t\tconst removeQuota = new Map<string, number>();\n\t\t\t\t\tfor (const [p, d] of lineItemDelta) {\n\t\t\t\t\t\tif (d < 0) removeQuota.set(p, -d);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst itemUpdates: Array<Record<string, unknown>> = [];\n\t\t\t\t\tfor (const si of activeSubscription.items.data) {\n\t\t\t\t\t\t// Remove items the new plan no longer needs\n\t\t\t\t\t\tconst quota = removeQuota.get(si.price.id) ?? 0;\n\t\t\t\t\t\tif (quota > 0) {\n\t\t\t\t\t\t\tremoveQuota.set(si.price.id, quota - 1);\n\t\t\t\t\t\t\titemUpdates.push({ id: si.id, deleted: true });\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// priceMap takes priority (handles seat-only plans\n\t\t\t\t\t\t// where base price === seat price)\n\t\t\t\t\t\tconst replacement = priceMap.get(si.price.id);\n\t\t\t\t\t\tif (replacement) {\n\t\t\t\t\t\t\titemUpdates.push({\n\t\t\t\t\t\t\t\tid: si.id,\n\t\t\t\t\t\t\t\tprice: replacement.newPrice,\n\t\t\t\t\t\t\t\tquantity: replacement.quantity,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (si.price.id === stripeSubscriptionPriceId) {\n\t\t\t\t\t\t\titemUpdates.push({\n\t\t\t\t\t\t\t\tid: si.id,\n\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\tquantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Consume positive delta to prevent duplicate addition\n\t\t\t\t\t\tconst d = lineItemDelta.get(si.price.id);\n\t\t\t\t\t\tif (d !== undefined && d > 0) {\n\t\t\t\t\t\t\tif (d === 1) lineItemDelta.delete(si.price.id);\n\t\t\t\t\t\t\telse lineItemDelta.set(si.price.id, d - 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Add line items the new plan introduces\n\t\t\t\t\tfor (const [price, delta] of lineItemDelta) {\n\t\t\t\t\t\tfor (let i = 0; i < delta; i++) itemUpdates.push({ price });\n\t\t\t\t\t}\n\t\t\t\t\tawait client.subscriptions\n\t\t\t\t\t\t.update(activeSubscription.id, {\n\t\t\t\t\t\t\titems: itemUpdates,\n\t\t\t\t\t\t\tproration_behavior: \"create_prorations\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\tif (dbSubscription) {\n\t\t\t\t\t\tawait ctx.context.adapter.update<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\t\t\tseats: memberCount,\n\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\twhere: [{ field: \"id\", value: dbSubscription.id }],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tupgradeUrl = getUrl(ctx, ctx.body.returnUrl || \"/\");\n\t\t\t\t} else {\n\t\t\t\t\t// Immediate change via Billing Portal\n\t\t\t\t\t({ url: upgradeUrl } = await client.billingPortal.sessions\n\t\t\t\t\t\t.create({\n\t\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl || \"/\"),\n\t\t\t\t\t\t\tflow_data: {\n\t\t\t\t\t\t\t\ttype: \"subscription_update_confirm\",\n\t\t\t\t\t\t\t\tafter_completion: {\n\t\t\t\t\t\t\t\t\ttype: \"redirect\",\n\t\t\t\t\t\t\t\t\tredirect: {\n\t\t\t\t\t\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl || \"/\"),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tsubscription_update_confirm: {\n\t\t\t\t\t\t\t\t\tsubscription: activeSubscription.id,\n\t\t\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tid: planItem.id,\n\t\t\t\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\t\t\t\t...(isAutoManagedSeats\n\t\t\t\t\t\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t\t\t\t\t\t: { quantity: ctx.body.seats || 1 }),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}));\n\t\t\t\t}\n\t\t\t\treturn ctx.json({\n\t\t\t\t\turl: upgradeUrl,\n\t\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet subscription: Subscription | undefined =\n\t\t\t\tactiveOrTrialingSubscription || incompleteSubscription;\n\n\t\t\tif (incompleteSubscription && !activeOrTrialingSubscription) {\n\t\t\t\tconst updated = await ctx.context.adapter.update<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\tseats: isAutoManagedSeats ? memberCount : ctx.body.seats || 1,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t},\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: incompleteSubscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tsubscription = (updated as Subscription) || incompleteSubscription;\n\t\t\t}\n\n\t\t\tif (!subscription) {\n\t\t\t\tsubscription = await ctx.context.adapter.create<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\tstripeCustomerId: customerId,\n\t\t\t\t\t\tstatus: \"incomplete\",\n\t\t\t\t\t\treferenceId,\n\t\t\t\t\t\tseats: isAutoManagedSeats ? memberCount : ctx.body.seats || 1,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!subscription) {\n\t\t\t\tctx.context.logger.error(\"Subscription ID not found\");\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"NOT_FOUND\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = await subscriptionOptions.getCheckoutSessionParams?.(\n\t\t\t\t{\n\t\t\t\t\tuser,\n\t\t\t\t\tsession,\n\t\t\t\t\tplan,\n\t\t\t\t\tsubscription,\n\t\t\t\t},\n\t\t\t\tctx.request,\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tconst allSubscriptions = await ctx.context.adapter.findMany<Subscription>(\n\t\t\t\t{\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [{ field: \"referenceId\", value: referenceId }],\n\t\t\t\t},\n\t\t\t);\n\t\t\tconst hasEverTrialed = allSubscriptions.some((s) => {\n\t\t\t\t// Check if user has ever had a trial for any plan (not just the same plan)\n\t\t\t\t// This prevents users from getting multiple trials by switching plans\n\t\t\t\tconst hadTrial =\n\t\t\t\t\t!!(s.trialStart || s.trialEnd) || s.status === \"trialing\";\n\t\t\t\treturn hadTrial;\n\t\t\t});\n\n\t\t\tconst freeTrial =\n\t\t\t\t!hasEverTrialed && plan.freeTrial\n\t\t\t\t\t? { trial_period_days: plan.freeTrial.days }\n\t\t\t\t\t: undefined;\n\n\t\t\tconst checkoutSession = await client.checkout.sessions\n\t\t\t\t.create(\n\t\t\t\t\t{\n\t\t\t\t\t\t...(customerId\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\t\t\t\tcustomer_update:\n\t\t\t\t\t\t\t\t\t\tcustomerType !== \"user\"\n\t\t\t\t\t\t\t\t\t\t\t? ({ address: \"auto\" } as const)\n\t\t\t\t\t\t\t\t\t\t\t: ({ name: \"auto\", address: \"auto\" } as const), // The customer name is automatically set only for users\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\tcustomer_email: user.email,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tlocale: ctx.body.locale,\n\t\t\t\t\t\tsuccess_url: getUrl(\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t`${\n\t\t\t\t\t\t\t\tctx.context.baseURL\n\t\t\t\t\t\t\t}/subscription/success?callbackURL=${encodeURIComponent(\n\t\t\t\t\t\t\t\tctx.body.successUrl,\n\t\t\t\t\t\t\t\t// {CHECKOUT_SESSION_ID} is a string literal; do not change it!\n\t\t\t\t\t\t\t\t// the actual Session ID is returned in the query parameter when your customer\n\t\t\t\t\t\t\t\t// is redirected to the success page.\n\t\t\t\t\t\t\t)}&checkoutSessionId={CHECKOUT_SESSION_ID}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tcancel_url: getUrl(ctx, ctx.body.cancelUrl),\n\t\t\t\t\t\tline_items: [\n\t\t\t\t\t\t\t// Base price\n\t\t\t\t\t\t\t...(!isSeatOnlyPlan\n\t\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\t\t\t\tquantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t// Per-seat\n\t\t\t\t\t\t\t...(isAutoManagedSeats\n\t\t\t\t\t\t\t\t? [{ price: plan.seatPriceId, quantity: memberCount }]\n\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t// Additional line items (metered prices, add-ons, etc.)\n\t\t\t\t\t\t\t...(plan.lineItems ?? []),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tsubscription_data: {\n\t\t\t\t\t\t\t...freeTrial,\n\t\t\t\t\t\t\tmetadata: subscriptionMetadata.set(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\t\tsubscriptionId: subscription.id,\n\t\t\t\t\t\t\t\t\treferenceId,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\t\tparams?.params?.subscription_data?.metadata,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmode: \"subscription\",\n\t\t\t\t\t\tclient_reference_id: referenceId,\n\t\t\t\t\t\t...params?.params,\n\t\t\t\t\t\t// metadata should come after spread to protect internal fields\n\t\t\t\t\t\tmetadata: subscriptionMetadata.set(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\tsubscriptionId: subscription.id,\n\t\t\t\t\t\t\t\treferenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\tparams?.params?.metadata,\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\tparams?.options,\n\t\t\t\t)\n\t\t\t\t.catch(async (e) => {\n\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\treturn ctx.json({\n\t\t\t\t...checkoutSession,\n\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t});\n\t\t},\n\t);\n};\n\nconst cancelSubscriptionBodySchema = z.object({\n\treferenceId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: \"Reference id of the subscription to cancel. Eg: '123'\",\n\t\t})\n\t\t.optional(),\n\tsubscriptionId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The Stripe subscription ID to cancel. Eg: 'sub_1ABC2DEF3GHI4JKL'\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n\treturnUrl: z.string().meta({\n\t\tdescription:\n\t\t\t'URL to take customers to when they click on the billing portal\\'s link to return to your website. Eg: \"/account\"',\n\t}),\n\t/**\n\t * Disable Redirect\n\t */\n\tdisableRedirect: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Disable redirect after successful subscription cancellation. Eg: true\",\n\t\t})\n\t\t.default(false),\n});\n\n/**\n * ### Endpoint\n *\n * POST `/subscription/cancel`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.cancelSubscription`\n *\n * **client:**\n * `authClient.subscription.cancel`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-cancel)\n */\nexport const cancelSubscription = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/cancel\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: cancelSubscriptionBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"cancelSubscription\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"cancel-subscription\"),\n\t\t\t\toriginCheck((ctx) => ctx.body.returnUrl),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tlet subscription = ctx.body.subscriptionId\n\t\t\t\t? await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\t\tvalue: ctx.body.subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t: await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [{ field: \"referenceId\", value: referenceId }],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\t\t\tif (\n\t\t\t\tctx.body.subscriptionId &&\n\t\t\t\tsubscription &&\n\t\t\t\tsubscription.referenceId !== referenceId\n\t\t\t) {\n\t\t\t\tsubscription = undefined;\n\t\t\t}\n\n\t\t\tif (!subscription || !subscription.stripeCustomerId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst activeSubscriptions = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub)));\n\t\t\tif (!activeSubscriptions.length) {\n\t\t\t\t/**\n\t\t\t\t * If the subscription is not found, we need to delete the subscription\n\t\t\t\t * from the database. This is a rare case and should not happen.\n\t\t\t\t */\n\t\t\t\tawait ctx.context.adapter.deleteMany({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst activeSubscription = activeSubscriptions.find(\n\t\t\t\t(sub) => sub.id === subscription.stripeSubscriptionId,\n\t\t\t);\n\t\t\tif (!activeSubscription) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst { url } = await client.billingPortal.sessions\n\t\t\t\t.create({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t\treturn_url: getUrl(ctx, ctx.body?.returnUrl || \"/\"),\n\t\t\t\t\tflow_data: {\n\t\t\t\t\t\ttype: \"subscription_cancel\",\n\t\t\t\t\t\tsubscription_cancel: {\n\t\t\t\t\t\t\tsubscription: activeSubscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\t.catch(async (e) => {\n\t\t\t\t\tif (e.message?.includes(\"already set to be canceled\")) {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * in-case we missed the event from stripe, we sync the actual state\n\t\t\t\t\t\t * this is a rare case and should not happen\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (!isPendingCancel(subscription)) {\n\t\t\t\t\t\t\tconst stripeSub = await client.subscriptions.retrieve(\n\t\t\t\t\t\t\t\tactiveSubscription.id,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\t\tcancelAtPeriodEnd: stripeSub.cancel_at_period_end,\n\t\t\t\t\t\t\t\t\tcancelAt: stripeSub.cancel_at\n\t\t\t\t\t\t\t\t\t\t? new Date(stripeSub.cancel_at * 1000)\n\t\t\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t\t\t\tcanceledAt: stripeSub.canceled_at\n\t\t\t\t\t\t\t\t\t\t? new Date(stripeSub.canceled_at * 1000)\n\t\t\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\treturn ctx.json({\n\t\t\t\turl,\n\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t});\n\t\t},\n\t);\n};\n\nconst restoreSubscriptionBodySchema = z.object({\n\treferenceId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: \"Reference id of the subscription to restore. Eg: '123'\",\n\t\t})\n\t\t.optional(),\n\tsubscriptionId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The Stripe subscription ID to restore. Eg: 'sub_1ABC2DEF3GHI4JKL'\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n});\n\nexport const restoreSubscription = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/restore\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: restoreSubscriptionBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"restoreSubscription\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"restore-subscription\"),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tlet subscription = ctx.body.subscriptionId\n\t\t\t\t? await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\t\tvalue: ctx.body.subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t: await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\t\t\tif (\n\t\t\t\tctx.body.subscriptionId &&\n\t\t\t\tsubscription &&\n\t\t\t\tsubscription.referenceId !== referenceId\n\t\t\t) {\n\t\t\t\tsubscription = undefined;\n\t\t\t}\n\t\t\tif (!subscription || !subscription.stripeCustomerId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!isActiveOrTrialing(subscription)) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_ACTIVE,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst hasPendingCancel = isPendingCancel(subscription);\n\t\t\tconst { stripeScheduleId } = subscription;\n\n\t\t\tif (!hasPendingCancel && !stripeScheduleId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_PENDING_CHANGE,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Pending cancel and pending schedule are mutually exclusive in Stripe.\n\t\t\tif (stripeScheduleId) {\n\t\t\t\tif (!subscription.stripeSubscriptionId) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst schedule = await client.subscriptionSchedules\n\t\t\t\t\t.retrieve(stripeScheduleId)\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\n\t\t\t\tif (schedule.status === \"active\") {\n\t\t\t\t\tawait client.subscriptionSchedules\n\t\t\t\t\t\t.release(stripeScheduleId)\n\t\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\tstripeScheduleId: null,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t},\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tconst releasedSub = await client.subscriptions.retrieve(\n\t\t\t\t\tsubscription.stripeSubscriptionId,\n\t\t\t\t);\n\t\t\t\treturn ctx.json(releasedSub);\n\t\t\t}\n\n\t\t\t// Handle pending cancellation\n\t\t\tconst activeSubscription = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub))[0]);\n\t\t\tif (!activeSubscription) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Clear scheduled cancellation based on Stripe subscription state\n\t\t\t// Note: Stripe doesn't accept both `cancel_at` and `cancel_at_period_end` simultaneously\n\t\t\tconst updateParams: Stripe.SubscriptionUpdateParams = {};\n\t\t\tif (activeSubscription.cancel_at) {\n\t\t\t\tupdateParams.cancel_at = \"\";\n\t\t\t} else if (activeSubscription.cancel_at_period_end) {\n\t\t\t\tupdateParams.cancel_at_period_end = false;\n\t\t\t}\n\n\t\t\tconst newSub = await client.subscriptions\n\t\t\t\t.update(activeSubscription.id, updateParams)\n\t\t\t\t.catch((e) => {\n\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\tupdate: {\n\t\t\t\t\tcancelAtPeriodEnd: false,\n\t\t\t\t\tcancelAt: null,\n\t\t\t\t\tcanceledAt: null,\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t},\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\treturn ctx.json(newSub);\n\t\t},\n\t);\n};\n\nconst listActiveSubscriptionsQuerySchema = z.optional(\n\tz.object({\n\t\treferenceId: z\n\t\t\t.string()\n\t\t\t.meta({\n\t\t\t\tdescription: \"Reference id of the subscription to list. Eg: '123'\",\n\t\t\t})\n\t\t\t.optional(),\n\t\t/**\n\t\t * Customer type for the subscription.\n\t\t * - `user`: User owns the subscription (default)\n\t\t * - `organization`: Organization owns the subscription\n\t\t */\n\t\tcustomerType: z\n\t\t\t.enum([\"user\", \"organization\"])\n\t\t\t.meta({\n\t\t\t\tdescription:\n\t\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t\t})\n\t\t\t.optional(),\n\t}),\n);\n/**\n * ### Endpoint\n *\n * GET `/subscription/list`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.listActiveSubscriptions`\n *\n * **client:**\n * `authClient.subscription.list`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-list)\n */\nexport const listActiveSubscriptions = (options: StripeOptions) => {\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/list\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: listActiveSubscriptionsQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"listActiveSubscriptions\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"list-subscription\"),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst customerType = ctx.query?.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.query?.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tconst subscriptions = await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!subscriptions.length) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tconst plans = await getPlans(options.subscription);\n\t\t\tif (!plans) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tconst subs = subscriptions\n\t\t\t\t.map((sub) => {\n\t\t\t\t\tconst plan = plans.find(\n\t\t\t\t\t\t(p) => p.name.toLowerCase() === sub.plan.toLowerCase(),\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...sub,\n\t\t\t\t\t\tlimits: plan?.limits,\n\t\t\t\t\t\tpriceId: plan?.priceId,\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((sub) => isActiveOrTrialing(sub));\n\t\t\treturn ctx.json(subs);\n\t\t},\n\t);\n};\n\nconst subscriptionSuccessQuerySchema = z.record(z.string(), z.any()).optional();\n\nexport const subscriptionSuccess = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/success\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: subscriptionSuccessQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleSubscriptionSuccess\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [originCheck((ctx) => ctx.query.callbackURL)],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst callbackURL = ctx.query?.callbackURL || \"/\";\n\n\t\t\tconst session = await getSessionFromCtx<User & WithStripeCustomerId>(ctx);\n\t\t\tif (!session) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\t// checkoutSessionId is substituted by Stripe from the {CHECKOUT_SESSION_ID}\n\t\t\t// template variable in success_url when redirecting after checkout.\n\t\t\tif (!ctx.query?.checkoutSessionId) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\t// Resolve subscriptionId from Stripe checkout session metadata.\n\t\t\t// The metadata is set server-side when creating the checkout session,\n\t\t\t// so it cannot be tampered with — no ownership check needed.\n\t\t\tconst checkoutSession = await client.checkout.sessions\n\t\t\t\t.retrieve(ctx.query.checkoutSessionId)\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\"Error retrieving checkout session from Stripe\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t\treturn null;\n\t\t\t\t});\n\t\t\tif (!checkoutSession) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst { subscriptionId } = subscriptionMetadata.get(\n\t\t\t\tcheckoutSession.metadata,\n\t\t\t);\n\t\t\tif (!subscriptionId) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`No subscriptionId in checkout session metadata: ${checkoutSession.id}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst subscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!subscription) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`Subscription record not found for subscriptionId: ${subscriptionId}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\t// Already active or trialing, no need to update\n\t\t\tif (isActiveOrTrialing(subscription)) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst customerId =\n\t\t\t\tsubscription.stripeCustomerId || session.user.stripeCustomerId;\n\t\t\tif (!customerId) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst stripeSubscription = await client.subscriptions\n\t\t\t\t.list({ customer: customerId, status: \"active\" })\n\t\t\t\t.then((res) => res.data[0])\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\"Error fetching subscription from Stripe\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t\t});\n\t\t\tif (!stripeSubscription) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst resolved = await resolvePlanItem(\n\t\t\t\toptions,\n\t\t\t\tstripeSubscription.items.data,\n\t\t\t);\n\t\t\tif (!resolved) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`No subscription items found for Stripe subscription ${stripeSubscription.id}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst { item: subscriptionItem, plan } = resolved;\n\t\t\tif (!plan) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`Plan not found for price ${subscriptionItem.price.id}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst seats =\n\t\t\t\tresolveQuantity(\n\t\t\t\t\tstripeSubscription.items.data,\n\t\t\t\t\tsubscriptionItem,\n\t\t\t\t\tplan.seatPriceId,\n\t\t\t\t) || 1;\n\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\tupdate: {\n\t\t\t\t\t...(stripeSubscription.trial_start && stripeSubscription.trial_end\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\ttrialStart: new Date(stripeSubscription.trial_start * 1000),\n\t\t\t\t\t\t\t\ttrialEnd: new Date(stripeSubscription.trial_end * 1000),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t\tstatus: stripeSubscription.status,\n\t\t\t\t\tseats,\n\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t\t\tperiodEnd: new Date(subscriptionItem.current_period_end * 1000),\n\t\t\t\t\tperiodStart: new Date(subscriptionItem.current_period_start * 1000),\n\t\t\t\t\tstripeSubscriptionId: stripeSubscription.id,\n\t\t\t\t\tcancelAtPeriodEnd: stripeSubscription.cancel_at_period_end,\n\t\t\t\t\tcancelAt: stripeSubscription.cancel_at\n\t\t\t\t\t\t? new Date(stripeSubscription.cancel_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tcanceledAt: stripeSubscription.canceled_at\n\t\t\t\t\t\t? new Date(stripeSubscription.canceled_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t},\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t},\n\t);\n};\n\nconst createBillingPortalBodySchema = z.object({\n\t/**\n\t * The IETF language tag of the locale Customer Portal is displayed in.\n\t * If not provided or set to `auto`, the browser's locale is used.\n\t */\n\tlocale: z\n\t\t.custom<StripeType.Checkout.Session.Locale>((localization) => {\n\t\t\treturn typeof localization === \"string\";\n\t\t})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The IETF language tag of the locale Customer Portal is displayed in. Eg: 'en', 'ko'. If not provided or set to `auto`, the browser's locale is used.\",\n\t\t})\n\t\t.optional(),\n\treferenceId: z.string().optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n\treturnUrl: z.string().default(\"/\"),\n\t/**\n\t * Disable Redirect\n\t */\n\tdisableRedirect: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Disable redirect after creating billing portal session. Eg: true\",\n\t\t})\n\t\t.default(false),\n});\n\nexport const createBillingPortal = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/billing-portal\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: createBillingPortalBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"createBillingPortal\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"billing-portal\"),\n\t\t\t\toriginCheck((ctx) => ctx.body.returnUrl),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { user } = ctx.context.session;\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tlet customerId: string | undefined;\n\n\t\t\tif (customerType === \"organization\") {\n\t\t\t\t// Organization billing portal - get customer ID from organization\n\t\t\t\tconst org = await ctx.context.adapter.findOne<\n\t\t\t\t\tOrganization & WithStripeCustomerId\n\t\t\t\t>({\n\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\twhere: [{ field: \"id\", value: referenceId }],\n\t\t\t\t});\n\t\t\t\tcustomerId = org?.stripeCustomerId;\n\n\t\t\t\tif (!customerId) {\n\t\t\t\t\t// Fallback to subscription's stripeCustomerId\n\t\t\t\t\tconst subscription = await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [{ field: \"referenceId\", value: referenceId }],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\t\t\t\t\tcustomerId = subscription?.stripeCustomerId;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// User billing portal\n\t\t\t\tcustomerId = user.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\tconst subscription = await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\n\t\t\t\t\tcustomerId = subscription?.stripeCustomerId;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!customerId) {\n\t\t\t\tthrow APIError.from(\"NOT_FOUND\", STRIPE_ERROR_CODES.CUSTOMER_NOT_FOUND);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { url } = await client.billingPortal.sessions.create({\n\t\t\t\t\tlocale: ctx.body.locale,\n\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl),\n\t\t\t\t});\n\n\t\t\t\treturn ctx.json({\n\t\t\t\t\turl,\n\t\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t\t});\n\t\t\t} catch (error: any) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"Error creating billing portal session\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"INTERNAL_SERVER_ERROR\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_BILLING_PORTAL,\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n};\n\nexport const stripeWebhook = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\treturn createAuthEndpoint(\n\t\t\"/stripe/webhook\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tmetadata: {\n\t\t\t\t...HIDE_METADATA,\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleStripeWebhook\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tcloneRequest: true,\n\t\t\tdisableBody: true, // Don't parse the body\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (!ctx.request?.body) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.INVALID_REQUEST_BODY,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst sig = ctx.request.headers.get(\"stripe-signature\");\n\t\t\tif (!sig) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_SIGNATURE_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst webhookSecret = options.stripeWebhookSecret;\n\t\t\tif (!webhookSecret) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"INTERNAL_SERVER_ERROR\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_WEBHOOK_SECRET_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst payload = await ctx.request.text();\n\n\t\t\tlet event: Stripe.Event;\n\t\t\ttry {\n\t\t\t\t// Support both Stripe v18 (constructEvent) and v19+ (constructEventAsync)\n\t\t\t\tif (typeof client.webhooks.constructEventAsync === \"function\") {\n\t\t\t\t\t// Stripe v19+ - use async method\n\t\t\t\t\tevent = await client.webhooks.constructEventAsync(\n\t\t\t\t\t\tpayload,\n\t\t\t\t\t\tsig,\n\t\t\t\t\t\twebhookSecret,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\t// Stripe v18 - use sync method\n\t\t\t\t\tevent = client.webhooks.constructEvent(payload, sig, webhookSecret);\n\t\t\t\t}\n\t\t\t} catch (err: any) {\n\t\t\t\tctx.context.logger.error(`${err.message}`);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.FAILED_TO_CONSTRUCT_STRIPE_EVENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!event) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.FAILED_TO_CONSTRUCT_STRIPE_EVENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tswitch (event.type) {\n\t\t\t\t\tcase \"checkout.session.completed\":\n\t\t\t\t\t\tawait onCheckoutSessionCompleted(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.created\":\n\t\t\t\t\t\tawait onSubscriptionCreated(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.updated\":\n\t\t\t\t\t\tawait onSubscriptionUpdated(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.deleted\":\n\t\t\t\t\t\tawait onSubscriptionDeleted(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch (e: any) {\n\t\t\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${e.message}`);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_WEBHOOK_ERROR,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ctx.json({ success: true });\n\t\t},\n\t);\n};\n","import type { BetterAuthPluginDBSchema } from \"@better-auth/core/db\";\nimport { mergeSchema } from \"better-auth/db\";\nimport type { StripeOptions } from \"./types\";\n\nexport const subscriptions = {\n\tsubscription: {\n\t\tfields: {\n\t\t\tplan: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\treferenceId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstripeSubscriptionId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstatus: {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdefaultValue: \"incomplete\",\n\t\t\t},\n\t\t\tperiodStart: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tperiodEnd: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\ttrialStart: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\ttrialEnd: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tcancelAtPeriodEnd: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\trequired: false,\n\t\t\t\tdefaultValue: false,\n\t\t\t},\n\t\t\tcancelAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tcanceledAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tendedAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tseats: {\n\t\t\t\ttype: \"number\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tbillingInterval: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstripeScheduleId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\nexport const user = {\n\tuser: {\n\t\tfields: {\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\nexport const organization = {\n\torganization: {\n\t\tfields: {\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\ntype GetSchemaResult<O extends StripeOptions> = typeof user &\n\t(O[\"subscription\"] extends { enabled: true } ? typeof subscriptions : {}) &\n\t(O[\"organization\"] extends { enabled: true } ? typeof organization : {});\n\nexport const getSchema = <O extends StripeOptions>(\n\toptions: O,\n): GetSchemaResult<O> => {\n\tlet baseSchema: BetterAuthPluginDBSchema = {};\n\n\tif (options.subscription?.enabled) {\n\t\tbaseSchema = {\n\t\t\t...subscriptions,\n\t\t\t...user,\n\t\t};\n\t} else {\n\t\tbaseSchema = {\n\t\t\t...user,\n\t\t};\n\t}\n\n\tif (options.organization?.enabled) {\n\t\tbaseSchema = {\n\t\t\t...baseSchema,\n\t\t\t...organization,\n\t\t};\n\t}\n\n\tif (\n\t\toptions.schema &&\n\t\t!options.subscription?.enabled &&\n\t\t\"subscription\" in options.schema\n\t) {\n\t\tconst { subscription: _subscription, ...restSchema } = options.schema;\n\t\treturn mergeSchema(baseSchema, restSchema) as GetSchemaResult<O>;\n\t}\n\n\treturn mergeSchema(baseSchema, options.schema) as GetSchemaResult<O>;\n};\n","import type { BetterAuthPlugin, User } from \"better-auth\";\nimport { APIError } from \"better-auth\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport { defu } from \"defu\";\nimport type Stripe from \"stripe\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport { customerMetadata } from \"./metadata\";\nimport {\n\tcancelSubscription,\n\tcreateBillingPortal,\n\tlistActiveSubscriptions,\n\trestoreSubscription,\n\tstripeWebhook,\n\tsubscriptionSuccess,\n\tupgradeSubscription,\n} from \"./routes\";\nimport { getSchema } from \"./schema\";\nimport type {\n\tStripeOptions,\n\tStripePlan,\n\tSubscription,\n\tWithStripeCustomerId,\n} from \"./types\";\nimport { escapeStripeSearchValue, getPlans, isActiveOrTrialing } from \"./utils\";\n\ndeclare module \"@better-auth/core\" {\n\tinterface BetterAuthPluginRegistry<AuthOptions, Options> {\n\t\tstripe: {\n\t\t\tcreator: typeof stripe;\n\t\t};\n\t}\n}\n\nexport const stripe = <O extends StripeOptions>(options: O) => {\n\tconst client = options.stripeClient;\n\n\tconst subscriptionEndpoints = {\n\t\tupgradeSubscription: upgradeSubscription(options),\n\t\tcancelSubscription: cancelSubscription(options),\n\t\trestoreSubscription: restoreSubscription(options),\n\t\tlistActiveSubscriptions: listActiveSubscriptions(options),\n\t\tsubscriptionSuccess: subscriptionSuccess(options),\n\t\tcreateBillingPortal: createBillingPortal(options),\n\t};\n\n\treturn {\n\t\tid: \"stripe\",\n\t\tendpoints: {\n\t\t\tstripeWebhook: stripeWebhook(options),\n\t\t\t...((options.subscription?.enabled\n\t\t\t\t? subscriptionEndpoints\n\t\t\t\t: {}) as O[\"subscription\"] extends {\n\t\t\t\tenabled: true;\n\t\t\t}\n\t\t\t\t? typeof subscriptionEndpoints\n\t\t\t\t: {}),\n\t\t},\n\t\tinit(ctx) {\n\t\t\t// Validate: seatPriceId requires organization to be enabled\n\t\t\tif (options.subscription?.enabled && !options.organization?.enabled) {\n\t\t\t\tconst warnIfSeatPricing = (plans: StripePlan[]) => {\n\t\t\t\t\tif (plans.some((p) => p.seatPriceId)) {\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t\"seatPriceId is configured on a plan but stripe organization option is not enabled. \" +\n\t\t\t\t\t\t\t\t\"Seat-based billing requires `organization: { enabled: true }` in stripe plugin options.\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst { plans } = options.subscription;\n\t\t\t\tif (typeof plans === \"function\") {\n\t\t\t\t\tvoid Promise.resolve(plans())\n\t\t\t\t\t\t.then(warnIfSeatPricing)\n\t\t\t\t\t\t.catch((e: any) => {\n\t\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t\t`Failed to resolve plans for seat pricing validation: ${e.message}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\twarnIfSeatPricing(plans);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.organization?.enabled) {\n\t\t\t\tconst orgPlugin = ctx.getPlugin(\"organization\");\n\t\t\t\tif (!orgPlugin) {\n\t\t\t\t\tctx.logger.error(`Organization plugin not found`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst existingHooks = orgPlugin.options.organizationHooks ?? {};\n\n\t\t\t\t/**\n\t\t\t\t * Sync organization name to Stripe customer\n\t\t\t\t */\n\t\t\t\tconst afterUpdateStripeOrg = async (data: {\n\t\t\t\t\torganization: (Organization & WithStripeCustomerId) | null;\n\t\t\t\t\tuser: User;\n\t\t\t\t}) => {\n\t\t\t\t\tconst { organization } = data;\n\t\t\t\t\tif (!organization?.stripeCustomerId) return;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst stripeCustomer = await client.customers.retrieve(\n\t\t\t\t\t\t\torganization.stripeCustomerId,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (stripeCustomer.deleted) {\n\t\t\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t\t\t`Stripe customer ${organization.stripeCustomerId} was deleted`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Update Stripe customer if name changed\n\t\t\t\t\t\tif (organization.name !== stripeCustomer.name) {\n\t\t\t\t\t\t\tawait client.customers.update(organization.stripeCustomerId, {\n\t\t\t\t\t\t\t\tname: organization.name,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tctx.logger.info(\n\t\t\t\t\t\t\t\t`Synced organization name to Stripe: \"${stripeCustomer.name}\" → \"${organization.name}\"`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t`Failed to sync organization to Stripe: ${e.message}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t/**\n\t\t\t\t * Block deletion if organization has active subscriptions\n\t\t\t\t */\n\t\t\t\tconst beforeDeleteStripeOrg = async (data: {\n\t\t\t\t\torganization: Organization & WithStripeCustomerId;\n\t\t\t\t\tuser: User;\n\t\t\t\t}) => {\n\t\t\t\t\tconst { organization } = data;\n\t\t\t\t\tif (!organization.stripeCustomerId) return;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Check if organization has any active subscriptions\n\t\t\t\t\t\tconst subscriptions = await client.subscriptions.list({\n\t\t\t\t\t\t\tcustomer: organization.stripeCustomerId,\n\t\t\t\t\t\t\tstatus: \"all\",\n\t\t\t\t\t\t\tlimit: 100, // 1 ~ 100\n\t\t\t\t\t\t});\n\t\t\t\t\t\tfor (const sub of subscriptions.data) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tsub.status !== \"canceled\" &&\n\t\t\t\t\t\t\t\tsub.status !== \"incomplete\" &&\n\t\t\t\t\t\t\t\tsub.status !== \"incomplete_expired\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\tif (error instanceof APIError) {\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t`Failed to check organization subscriptions: ${error.message}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t/**\n\t\t\t\t * Sync seat quantity to Stripe when organization members change.\n\t\t\t\t * quantity = memberCount; Stripe graduated pricing handles free tiers.\n\t\t\t\t */\n\t\t\t\tconst syncSeatsAfterMemberChange = async (data: {\n\t\t\t\t\torganization: Organization & WithStripeCustomerId;\n\t\t\t\t}) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!options.subscription?.enabled ||\n\t\t\t\t\t\t!data.organization?.stripeCustomerId\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst memberCount = await ctx.adapter.count({\n\t\t\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"organizationId\",\n\t\t\t\t\t\t\t\t\tvalue: data.organization.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst plans = await getPlans(options.subscription);\n\t\t\t\t\t\tconst seatPlans = plans.filter((p) => p.seatPriceId);\n\t\t\t\t\t\tif (seatPlans.length === 0) return;\n\n\t\t\t\t\t\tconst seatPlanNames = new Set(\n\t\t\t\t\t\t\tseatPlans.map((p) => p.name.toLowerCase()),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst dbSub = await ctx.adapter.findOne<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\t\tvalue: data.organization.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!dbSub?.stripeSubscriptionId ||\n\t\t\t\t\t\t\t!isActiveOrTrialing(dbSub) ||\n\t\t\t\t\t\t\t!seatPlanNames.has(dbSub.plan)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst plan = seatPlans.find(\n\t\t\t\t\t\t\t(p) => p.name.toLowerCase() === dbSub.plan,\n\t\t\t\t\t\t)!;\n\t\t\t\t\t\tconst { seatPriceId } = plan;\n\n\t\t\t\t\t\tconst stripeSub = await client.subscriptions.retrieve(\n\t\t\t\t\t\t\tdbSub.stripeSubscriptionId,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (!isActiveOrTrialing(stripeSub)) return;\n\n\t\t\t\t\t\tconst seatItem = stripeSub.items.data.find(\n\t\t\t\t\t\t\t(item) => item.price.id === seatPriceId,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Skip if no change needed\n\t\t\t\t\t\tif (seatItem?.quantity === memberCount) return;\n\n\t\t\t\t\t\tconst items = seatItem\n\t\t\t\t\t\t\t? [{ id: seatItem.id, quantity: memberCount }]\n\t\t\t\t\t\t\t: [{ price: seatPriceId, quantity: memberCount }];\n\n\t\t\t\t\t\tawait client.subscriptions.update(stripeSub.id, {\n\t\t\t\t\t\t\titems,\n\t\t\t\t\t\t\tproration_behavior: \"create_prorations\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tawait ctx.adapter.update({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\tupdate: { seats: memberCount },\n\t\t\t\t\t\t\twhere: [{ field: \"id\", value: dbSub.id }],\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.logger.error(`Failed to sync seats to Stripe: ${e.message}`);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\torgPlugin.options.organizationHooks = {\n\t\t\t\t\t...existingHooks,\n\t\t\t\t\tafterUpdateOrganization: existingHooks.afterUpdateOrganization\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterUpdateOrganization!(data);\n\t\t\t\t\t\t\t\tawait afterUpdateStripeOrg(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: afterUpdateStripeOrg,\n\t\t\t\t\tbeforeDeleteOrganization: existingHooks.beforeDeleteOrganization\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.beforeDeleteOrganization!(data);\n\t\t\t\t\t\t\t\tawait beforeDeleteStripeOrg(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: beforeDeleteStripeOrg,\n\t\t\t\t\tafterAddMember: existingHooks.afterAddMember\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterAddMember!(data);\n\t\t\t\t\t\t\t\tawait syncSeatsAfterMemberChange(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: syncSeatsAfterMemberChange,\n\t\t\t\t\tafterRemoveMember: existingHooks.afterRemoveMember\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterRemoveMember!(data);\n\t\t\t\t\t\t\t\tawait syncSeatsAfterMemberChange(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: syncSeatsAfterMemberChange,\n\t\t\t\t\tafterAcceptInvitation: existingHooks.afterAcceptInvitation\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterAcceptInvitation!(data);\n\t\t\t\t\t\t\t\tawait syncSeatsAfterMemberChange(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: syncSeatsAfterMemberChange,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\toptions: {\n\t\t\t\t\tdatabaseHooks: {\n\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\tcreate: {\n\t\t\t\t\t\t\t\tasync after(user: User & WithStripeCustomerId, ctx) {\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t!ctx ||\n\t\t\t\t\t\t\t\t\t\t!options.createCustomerOnSignUp ||\n\t\t\t\t\t\t\t\t\t\tuser.stripeCustomerId // Skip if user already has a Stripe customer ID\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t// Check if user customer already exists in Stripe by email\n\t\t\t\t\t\t\t\t\t\tlet stripeCustomer: Stripe.Customer | undefined;\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tconst result = await client.customers.search({\n\t\t\t\t\t\t\t\t\t\t\t\tquery: `email:\"${escapeStripeSearchValue(user.email)}\" AND -metadata[\"${customerMetadata.keys.customerType}\"]:\"organization\"`,\n\t\t\t\t\t\t\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\tstripeCustomer = result.data[0];\n\t\t\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t\t\t// Search API unavailable in some regions, so fall back to paginated list\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\t\t\t\t\"Stripe customers.search failed, falling back to customers.list\",\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\tfor await (const customer of client.customers.list({\n\t\t\t\t\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t\t\t\t\t\t\t})) {\n\t\t\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\t\t\tcustomer.metadata?.[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcustomerMetadata.keys.customerType\n\t\t\t\t\t\t\t\t\t\t\t\t\t] !== \"organization\"\n\t\t\t\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomer = customer;\n\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// If user customer exists, link it to prevent duplicate creation\n\t\t\t\t\t\t\t\t\t\tif (stripeCustomer) {\n\t\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.updateUser(user.id, {\n\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\tawait options.onCustomerCreate?.(\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomer,\n\t\t\t\t\t\t\t\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t...user,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t\t\t\t\t\t\t\t`Linked existing Stripe customer ${stripeCustomer.id} to user ${user.id}`,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// Create new Stripe customer\n\t\t\t\t\t\t\t\t\t\tlet extraCreateParams: Partial<Stripe.CustomerCreateParams> =\n\t\t\t\t\t\t\t\t\t\t\t{};\n\t\t\t\t\t\t\t\t\t\tif (options.getCustomerCreateParams) {\n\t\t\t\t\t\t\t\t\t\t\textraCreateParams = await options.getCustomerCreateParams(\n\t\t\t\t\t\t\t\t\t\t\t\tuser,\n\t\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tconst params = defu(\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\t\t\t\t\t\t\tmetadata: customerMetadata.set(\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcustomerType: \"user\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\textraCreateParams?.metadata,\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\textraCreateParams,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tstripeCustomer = await client.customers.create(params);\n\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.updateUser(user.id, {\n\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\tawait options.onCustomerCreate?.(\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomer,\n\t\t\t\t\t\t\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t...user,\n\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t\t\t\t\t\t\t`Created new Stripe customer ${stripeCustomer.id} for user ${user.id}`,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\t\t\t\t`Failed to create or link Stripe customer: ${e.message}`,\n\t\t\t\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tasync after(user: User & WithStripeCustomerId, ctx) {\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t!ctx ||\n\t\t\t\t\t\t\t\t\t\t!user.stripeCustomerId // Only proceed if user has a Stripe customer ID\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t// Get the user from the database to check if email actually changed\n\t\t\t\t\t\t\t\t\t\t// The 'user' parameter here is the freshly updated user\n\t\t\t\t\t\t\t\t\t\t// We need to check if the Stripe customer's email matches\n\t\t\t\t\t\t\t\t\t\tconst stripeCustomer = await client.customers.retrieve(\n\t\t\t\t\t\t\t\t\t\t\tuser.stripeCustomerId,\n\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\t\t// Check if customer was deleted\n\t\t\t\t\t\t\t\t\t\tif (stripeCustomer.deleted) {\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\t\t\t\t`Stripe customer ${user.stripeCustomerId} was deleted, cannot update email`,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// If Stripe customer email doesn't match the user's current email, update it\n\t\t\t\t\t\t\t\t\t\tif (stripeCustomer.email !== user.email) {\n\t\t\t\t\t\t\t\t\t\t\tawait client.customers.update(user.stripeCustomerId, {\n\t\t\t\t\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t\t\t\t\t\t\t\t`Updated Stripe customer email from ${stripeCustomer.email} to ${user.email}`,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t\t\t\t\t// Ignore errors - this is a best-effort sync\n\t\t\t\t\t\t\t\t\t\t// Email might have been deleted or Stripe customer might not exist\n\t\t\t\t\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\t\t\t\t`Failed to sync email to Stripe customer: ${e.message}`,\n\t\t\t\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\tschema: getSchema(options),\n\t\toptions: options as NoInfer<O>,\n\t\t$ERROR_CODES: STRIPE_ERROR_CODES,\n\t} satisfies BetterAuthPlugin;\n};\n\nexport type StripePlugin<O extends StripeOptions> = ReturnType<\n\ttypeof stripe<O>\n>;\n\nexport type * from \"./types\";\n"],"mappings":";;;;;;;;;;;;;AAsBA,MAAa,mBAAmB;CAI/B,MAAM;EACL,QAAQ;EACR,gBAAgB;EAChB,cAAc;EACd;CAMD,IACC,gBACA,GAAG,cACoB;AACvB,SAAO,KAAK,gBAAgB,GAAG,aAAa,OAAO,QAAQ,CAAC;;CAO7D,IAAI,UAA8C;AACjD,SAAO;GACN,QAAQ,UAAU;GAClB,gBAAgB,UAAU;GAC1B,cAAc,UAAU;GAGxB;;CAEF;;;;AAKD,MAAa,uBAAuB;CAInC,MAAM;EACL,QAAQ;EACR,gBAAgB;EAChB,aAAa;EACb;CAMD,IACC,gBACA,GAAG,cACoB;AACvB,SAAO,KAAK,gBAAgB,GAAG,aAAa,OAAO,QAAQ,CAAC;;CAO7D,IAAI,UAA8C;AACjD,SAAO;GACN,QAAQ,UAAU;GAClB,gBAAgB,UAAU;GAC1B,aAAa,UAAU;GACvB;;CAEF;;;;AC1FD,eAAsB,SACrB,qBACC;AACD,KAAI,qBAAqB,QACxB,QAAO,OAAO,oBAAoB,UAAU,aACzC,MAAM,oBAAoB,OAAO,GACjC,oBAAoB;AAExB,OAAM,IAAI,MAAM,uDAAuD;;AAGxE,eAAsB,cAAc,SAAwB,MAAc;AACzE,QAAO,MAAM,SAAS,QAAQ,aAAa,CAAC,MAAM,QACjD,KAAK,MAAM,SAAS,KAAK,KAAK,aAAa,KAAK,KAAK,aAAa,CAAC,CACnE;;;;;AAMF,SAAgB,mBACf,KACU;AACV,QAAO,IAAI,WAAW,YAAY,IAAI,WAAW;;;;;AAMlD,SAAgB,gBAAgB,KAA4B;AAC3D,QAAO,CAAC,EAAE,IAAI,qBAAqB,IAAI;;;;;AAMxC,SAAgB,sBAAsB,WAAyC;AAC9E,QAAO,CAAC,EAAE,UAAU,wBAAwB,UAAU;;;;;;;;;AAUvD,SAAgB,wBAAwB,OAAuB;AAC9D,QAAO,MAAM,QAAQ,MAAM,OAAM;;;;;;AAOlC,SAAgB,gBACf,OACA,UACA,aACS;AACT,KAAI,aAAa;EAChB,MAAM,WAAW,MAAM,MAAM,SAAS,KAAK,MAAM,OAAO,YAAY;AACpE,MAAI,SAAU,QAAO,SAAS,YAAY;;AAE3C,QAAO,SAAS,YAAY;;;;;;;;;AAU7B,eAAsB,gBACrB,SACA,OAGC;CACD,MAAM,QAAQ,MAAM;AACpB,KAAI,CAAC,MAAO,QAAO;CACnB,MAAM,QAAQ,MAAM,SAAS,QAAQ,aAAa;AAClD,MAAK,MAAM,QAAQ,OAAO;EACzB,MAAM,OAAO,OAAO,MAClB,MACA,EAAE,YAAY,KAAK,MAAM,MACzB,EAAE,0BAA0B,KAAK,MAAM,MACtC,KAAK,MAAM,eACV,EAAE,cAAc,KAAK,MAAM,cAC3B,EAAE,4BAA4B,KAAK,MAAM,YAC5C;AACD,MAAI,KAAM,QAAO;GAAE;GAAM;GAAM;;AAEhC,QAAO,MAAM,WAAW,IAAI;EAAE,MAAM;EAAO,MAAM;EAAW,GAAG;;;;;;;;;AC/EhE,eAAe,gCACd,KACA,SACA,kBACsE;AACtE,KAAI,QAAQ,cAAc,SAAS;EAClC,MAAM,MAAM,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GAC3D,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAoB,OAAO;IAAkB,CAAC;GAC/D,CAAC;AACF,MAAI,IAAK,QAAO;GAAE,cAAc;GAAgB,aAAa,IAAI;GAAI;;CAGtE,MAAM,OAAO,MAAM,IAAI,QAAQ,QAAQ,QAAc;EACpD,OAAO;EACP,OAAO,CAAC;GAAE,OAAO;GAAoB,OAAO;GAAkB,CAAC;EAC/D,CAAC;AACF,KAAI,KAAM,QAAO;EAAE,cAAc;EAAQ,aAAa,KAAK;EAAI;AAE/D,QAAO;;AAGR,eAAsB,2BACrB,KACA,SACA,OACC;AACD,KAAI;EACH,MAAM,SAAS,QAAQ;EACvB,MAAM,kBAAkB,MAAM,KAAK;AACnC,MAAI,gBAAgB,SAAS,WAAW,CAAC,QAAQ,cAAc,QAC9D;EAED,MAAM,eAAe,MAAM,OAAO,cAAc,SAC/C,gBAAgB,aAChB;EACD,MAAM,WAAW,MAAM,gBAAgB,SAAS,aAAa,MAAM,KAAK;AACxE,MAAI,CAAC,UAAU;AACd,OAAI,QAAQ,OAAO,KAClB,wCAAwC,aAAa,GAAG,0CACxD;AACD;;EAGD,MAAM,EAAE,MAAM,kBAAkB,SAAS;AACzC,MAAI,MAAM;GACT,MAAM,eAAe,qBAAqB,IAAI,iBAAiB,SAAS;GACxE,MAAM,cACL,iBAAiB,uBAAuB,aAAa;GACtD,MAAM,EAAE,mBAAmB;GAC3B,MAAM,QAAQ,gBACb,aAAa,MAAM,MACnB,kBACA,KAAK,YACL;AACD,OAAI,eAAe,gBAAgB;IAClC,MAAM,QACL,aAAa,eAAe,aAAa,YACtC;KACA,4BAAY,IAAI,KAAK,aAAa,cAAc,IAAK;KACrD,0BAAU,IAAI,KAAK,aAAa,YAAY,IAAK;KACjD,GACA,EAAE;IAEN,IAAI,iBAAiB,MAAM,IAAI,QAAQ,QAAQ,OAAqB;KACnE,OAAO;KACP,QAAQ;MACP,GAAG;MACH,MAAM,KAAK,KAAK,aAAa;MAC7B,QAAQ,aAAa;MACrB,2BAAW,IAAI,MAAM;MACrB,6BAAa,IAAI,KAAK,iBAAiB,uBAAuB,IAAK;MACnE,2BAAW,IAAI,KAAK,iBAAiB,qBAAqB,IAAK;MAC/D,sBAAsB,gBAAgB;MACtC,mBAAmB,aAAa;MAChC,UAAU,aAAa,4BACpB,IAAI,KAAK,aAAa,YAAY,IAAK,GACvC;MACH,YAAY,aAAa,8BACtB,IAAI,KAAK,aAAa,cAAc,IAAK,GACzC;MACH,SAAS,aAAa,2BACnB,IAAI,KAAK,aAAa,WAAW,IAAK,GACtC;MACI;MACP,iBAAiB,iBAAiB,MAAM,WAAW;MACnD;KACD,OAAO,CACN;MACC,OAAO;MACP,OAAO;MACP,CACD;KACD,CAAC;AAEF,QAAI,MAAM,cAAc,KAAK,WAAW,aACvC,OAAM,KAAK,UAAU,aAAa,eAA+B;AAGlE,QAAI,CAAC,eACJ,kBAAiB,MAAM,IAAI,QAAQ,QAAQ,QAAsB;KAChE,OAAO;KACP,OAAO,CACN;MACC,OAAO;MACP,OAAO;MACP,CACD;KACD,CAAC;AAEH,UAAM,QAAQ,cAAc,yBAC3B;KACC;KACA,cAAc;KACd,oBAAoB;KACpB;KACA,EACD,IACA;AACD;;;UAGM,GAAQ;AAChB,MAAI,QAAQ,OAAO,MAAM,iCAAiC,EAAE,UAAU;;;AAIxE,eAAsB,sBACrB,KACA,SACA,OACC;AACD,KAAI;AACH,MAAI,CAAC,QAAQ,cAAc,QAC1B;EAGD,MAAM,sBAAsB,MAAM,KAAK;EACvC,MAAM,mBAAmB,oBAAoB,UAAU,UAAU;AACjE,MAAI,CAAC,kBAAkB;AACtB,OAAI,QAAQ,OAAO,KAClB,2FACA;AACD;;EAID,MAAM,EAAE,mBAAmB,qBAAqB,IAC/C,oBAAoB,SACpB;EACD,MAAM,uBACL,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GAC/C,OAAO;GACP,OAAO,iBACJ,CAAC;IAAE,OAAO;IAAM,OAAO;IAAgB,CAAC,GACxC,CAAC;IAAE,OAAO;IAAwB,OAAO,oBAAoB;IAAI,CAAC;GACrE,CAAC;AACH,MAAI,sBAAsB;AACzB,OAAI,QAAQ,OAAO,KAClB,gEAAgE,qBAAqB,GAAG,sBACxF;AACD;;EAID,MAAM,YAAY,MAAM,gCACvB,KACA,SACA,iBACA;AACD,MAAI,CAAC,WAAW;AACf,OAAI,QAAQ,OAAO,KAClB,gFAAgF,mBAChF;AACD;;EAED,MAAM,EAAE,aAAa,iBAAiB;EAEtC,MAAM,WAAW,MAAM,gBACtB,SACA,oBAAoB,MAAM,KAC1B;AACD,MAAI,CAAC,UAAU;AACd,OAAI,QAAQ,OAAO,KAClB,wCAAwC,oBAAoB,GAAG,0CAC/D;AACD;;EAGD,MAAM,EAAE,MAAM,kBAAkB,SAAS;AACzC,MAAI,CAAC,MAAM;AACV,OAAI,QAAQ,OAAO,KAClB,+DAA+D,iBAAiB,MAAM,KACtF;AACD;;EAGD,MAAM,QAAQ,gBACb,oBAAoB,MAAM,MAC1B,kBACA,KAAK,YACL;EACD,MAAM,8BAAc,IAAI,KAAK,iBAAiB,uBAAuB,IAAK;EAC1E,MAAM,4BAAY,IAAI,KAAK,iBAAiB,qBAAqB,IAAK;EAEtE,MAAM,QACL,oBAAoB,eAAe,oBAAoB,YACpD;GACA,4BAAY,IAAI,KAAK,oBAAoB,cAAc,IAAK;GAC5D,0BAAU,IAAI,KAAK,oBAAoB,YAAY,IAAK;GACxD,GACA,EAAE;EAGN,MAAM,kBAAkB,MAAM,IAAI,QAAQ,QAAQ,OAAqB;GACtE,OAAO;GACP,MAAM;IACL,GAAG;IACH,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,QAAQ,GAAG,EAAE;IAC9C;IACA;IACA,sBAAsB,oBAAoB;IAC1C,QAAQ,oBAAoB;IAC5B,MAAM,KAAK,KAAK,aAAa;IAC7B;IACA;IACA;IACA,iBAAiB,iBAAiB,MAAM,WAAW;IACnD;GACD,CAAC;AAEF,MAAI,QAAQ,OAAO,KAClB,wCAAwC,oBAAoB,GAAG,OAAO,aAAa,GAAG,YAAY,iBAClG;AAED,QAAM,QAAQ,aAAa,wBAAwB;GAClD;GACA,cAAc;GACd,oBAAoB;GACpB;GACA,CAAC;UACM,OAAY;AACpB,MAAI,QAAQ,OAAO,MAAM,iCAAiC,QAAQ;;;AAIpE,eAAsB,sBACrB,KACA,SACA,OACC;AACD,KAAI;AACH,MAAI,CAAC,QAAQ,cAAc,QAC1B;EAED,MAAM,sBAAsB,MAAM,KAAK;EACvC,MAAM,WAAW,MAAM,gBACtB,SACA,oBAAoB,MAAM,KAC1B;AACD,MAAI,CAAC,UAAU;AACd,OAAI,QAAQ,OAAO,KAClB,wCAAwC,oBAAoB,GAAG,0CAC/D;AACD;;EAGD,MAAM,EAAE,MAAM,kBAAkB,SAAS;EAEzC,MAAM,EAAE,mBAAmB,qBAAqB,IAC/C,oBAAoB,SACpB;EACD,MAAM,aAAa,oBAAoB,UAAU,UAAU;EAC3D,IAAI,eAAe,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GAClE,OAAO;GACP,OAAO,iBACJ,CAAC;IAAE,OAAO;IAAM,OAAO;IAAgB,CAAC,GACxC,CAAC;IAAE,OAAO;IAAwB,OAAO,oBAAoB;IAAI,CAAC;GACrE,CAAC;AACF,MAAI,CAAC,cAAc;GAClB,MAAM,OAAO,MAAM,IAAI,QAAQ,QAAQ,SAAuB;IAC7D,OAAO;IACP,OAAO,CAAC;KAAE,OAAO;KAAoB,OAAO;KAAY,CAAC;IACzD,CAAC;AACF,OAAI,KAAK,SAAS,GAAG;IACpB,MAAM,YAAY,KAAK,MAAM,QAC5B,mBAAmB,IAAI,CACvB;AACD,QAAI,CAAC,WAAW;AACf,SAAI,QAAQ,OAAO,KAClB,sEAAsE,WAAW,sCACjF;AACD;;AAED,mBAAe;SAEf,gBAAe,KAAK;;EAItB,MAAM,QAAQ,OACX,gBACA,oBAAoB,MAAM,MAC1B,kBACA,KAAK,YACL,GACA,iBAAiB;EAEpB,MAAM,QACL,oBAAoB,eAAe,oBAAoB,YACpD;GACA,4BAAY,IAAI,KAAK,oBAAoB,cAAc,IAAK;GAC5D,0BAAU,IAAI,KAAK,oBAAoB,YAAY,IAAK;GACxD,GACA,EAAE;EAEN,MAAM,sBAAsB,MAAM,IAAI,QAAQ,QAAQ,OAAqB;GAC1E,OAAO;GACP,QAAQ;IACP,GAAG;IACH,GAAI,OACD;KACA,MAAM,KAAK,KAAK,aAAa;KAC7B,QAAQ,KAAK;KACb,GACA,EAAE;IACL,2BAAW,IAAI,MAAM;IACrB,QAAQ,oBAAoB;IAC5B,6BAAa,IAAI,KAAK,iBAAiB,uBAAuB,IAAK;IACnE,2BAAW,IAAI,KAAK,iBAAiB,qBAAqB,IAAK;IAC/D,mBAAmB,oBAAoB;IACvC,UAAU,oBAAoB,4BAC3B,IAAI,KAAK,oBAAoB,YAAY,IAAK,GAC9C;IACH,YAAY,oBAAoB,8BAC7B,IAAI,KAAK,oBAAoB,cAAc,IAAK,GAChD;IACH,SAAS,oBAAoB,2BAC1B,IAAI,KAAK,oBAAoB,WAAW,IAAK,GAC7C;IACH;IACA,sBAAsB,oBAAoB;IAC1C,iBAAiB,iBAAiB,MAAM,WAAW;IACnD,kBAAkB,oBAAoB,WACnC,OAAO,oBAAoB,aAAa,WACvC,oBAAoB,WACpB,oBAAoB,SAAS,KAC9B;IACH;GACD,OAAO,CACN;IACC,OAAO;IACP,OAAO,aAAa;IACpB,CACD;GACD,CAAC;AAKF,MAHC,oBAAoB,WAAW,YAC/B,sBAAsB,oBAAoB,IAC1C,CAAC,gBAAgB,aAAa,CAE9B,OAAM,QAAQ,aAAa,uBAAuB;GACjD;GACA,qBACC,oBAAoB,wBAAwB;GAC7C,oBAAoB;GACpB;GACA,CAAC;AAEH,QAAM,QAAQ,aAAa,uBAAuB;GACjD;GACA,cAAc,uBAAuB;GACrC,CAAC;AACF,MAAI,MAAM;AACT,OACC,oBAAoB,WAAW,YAC/B,aAAa,WAAW,cACxB,KAAK,WAAW,WAEhB,OAAM,KAAK,UAAU,WAAW,EAAE,cAAc,EAAE,IAAI;AAEvD,OACC,oBAAoB,WAAW,wBAC/B,aAAa,WAAW,cACxB,KAAK,WAAW,eAEhB,OAAM,KAAK,UAAU,eAAe,cAAc,IAAI;;UAGhD,OAAY;AACpB,MAAI,QAAQ,OAAO,MAAM,iCAAiC,QAAQ;;;AAIpE,eAAsB,sBACrB,KACA,SACA,OACC;AACD,KAAI,CAAC,QAAQ,cAAc,QAC1B;AAED,KAAI;EACH,MAAM,sBAAsB,MAAM,KAAK;EACvC,MAAM,iBAAiB,oBAAoB;EAC3C,MAAM,eAAe,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GACpE,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO;IACP,CACD;GACD,CAAC;AACF,MAAI,cAAc;GACjB,MAAM,QACL,oBAAoB,eAAe,oBAAoB,YACpD;IACA,4BAAY,IAAI,KAAK,oBAAoB,cAAc,IAAK;IAC5D,0BAAU,IAAI,KAAK,oBAAoB,YAAY,IAAK;IACxD,GACA,EAAE;AACN,SAAM,IAAI,QAAQ,QAAQ,OAAO;IAChC,OAAO;IACP,OAAO,CACN;KACC,OAAO;KACP,OAAO,aAAa;KACpB,CACD;IACD,QAAQ;KACP,GAAG;KACH,QAAQ;KACR,2BAAW,IAAI,MAAM;KACrB,mBAAmB,oBAAoB;KACvC,UAAU,oBAAoB,4BAC3B,IAAI,KAAK,oBAAoB,YAAY,IAAK,GAC9C;KACH,YAAY,oBAAoB,8BAC7B,IAAI,KAAK,oBAAoB,cAAc,IAAK,GAChD;KACH,SAAS,oBAAoB,2BAC1B,IAAI,KAAK,oBAAoB,WAAW,IAAK,GAC7C;KACH,kBAAkB;KAClB;IACD,CAAC;AACF,SAAM,QAAQ,aAAa,wBAAwB;IAClD;IACA,oBAAoB;IACpB;IACA,CAAC;QAEF,KAAI,QAAQ,OAAO,KAClB,oEAAoE,iBACpE;UAEM,OAAY;AACpB,MAAI,QAAQ,OAAO,MAAM,iCAAiC,QAAQ;;;;;;ACjdpE,MAAa,0BAA0B,qBACtC,EACC,KAAK,CAAC,kBAAkB,EACxB,EACD,OAAO,QAAQ;AAEd,QAAO,EACN,SAFe,IAAI,QAAQ,SAG3B;EAEF;AAED,MAAa,uBACZ,qBACA,WAEA,qBAAqB,OAAO,QAAQ;CACnC,MAAM,aAAa,IAAI,QAAQ;AAC/B,KAAI,CAAC,WACJ,OAAMA,WAAS,KAAK,gBAAgB,mBAAmB,aAAa;CAGrE,MAAM,eACL,IAAI,MAAM,gBAAgB,IAAI,OAAO;CACtC,MAAM,sBAAsB,IAAI,MAAM,eAAe,IAAI,OAAO;AAEhE,KAAI,iBAAiB,gBAAgB;AAEpC,MAAI,CAAC,oBAAoB,oBAAoB;AAC5C,OAAI,QAAQ,OAAO,MAClB,oGACA;AACD,SAAMA,WAAS,KACd,eACA,mBAAmB,6BACnB;;EAGF,MAAM,cACL,uBAAuB,WAAW,QAAQ;AAC3C,MAAI,CAAC,YACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,mCACnB;AAWF,MAAI,CATiB,MAAM,oBAAoB,mBAC9C;GACC,MAAM,WAAW;GACjB,SAAS,WAAW;GACpB;GACA;GACA,EACD,IACA,CAEA,OAAMA,WAAS,KAAK,gBAAgB,mBAAmB,aAAa;AAErE;;AAID,KAAI,CAAC,oBACJ;AAID,KAAI,wBAAwB,WAAW,KAAK,GAC3C;AAGD,KAAI,CAAC,oBAAoB,oBAAoB;AAC5C,MAAI,QAAQ,OAAO,MAClB,8IACA;AACD,QAAMA,WAAS,KACd,eACA,mBAAmB,yBACnB;;AAWF,KAAI,CATiB,MAAM,oBAAoB,mBAC9C;EACC,MAAM,WAAW;EACjB,SAAS,WAAW;EACpB,aAAa;EACb;EACA,EACD,IACA,CAEA,OAAMA,WAAS,KAAK,gBAAgB,mBAAmB,aAAa;EAEpE;;;;;;;;AC9DH,SAAS,OAAO,KAA6B,KAAa;AACzD,KAAI,6BAA6B,KAAK,IAAI,CACzC,QAAO;AAER,QAAO,GAAG,IAAI,QAAQ,QAAQ,UAC7B,IAAI,WAAW,IAAI,GAAG,MAAM,IAAI;;;;;;AAQlC,eAAe,4BACd,cACA,WAC8B;AAC9B,KAAI,CAAC,UAAW,QAAO;AAMvB,SALe,MAAM,aAAa,OAAO,KAAK;EAC7C,aAAa,CAAC,UAAU;EACxB,QAAQ;EACR,OAAO;EACP,CAAC,EACY,KAAK,IAAI;;;;;;;;AASxB,SAAS,eACR,YACA,cACA,SACS;CACT,MAAM,EAAE,MAAM,YAAY;AAG1B,MAFa,gBAAgB,YAEhB,gBAAgB;AAC5B,MAAI,CAAC,QAAQ,cAAc,QAC1B,OAAMC,WAAS,KACd,eACA,mBAAmB,sCACnB;AAGF,MAAI,CAAC,QAAQ,qBACZ,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;AAEF,SAAO,QAAQ;;AAGhB,QAAO,KAAK;;AAGb,MAAM,gCAAgC,EAAE,OAAO;CAI9C,MAAM,EAAE,QAAQ,CAAC,KAAK,EACrB,aAAa,mDACb,CAAC;CAIF,QAAQ,EACN,SAAS,CACT,KAAK,EACL,aAAa,kDACb,CAAC,CACD,UAAU;CAMZ,aAAa,EACX,QAAQ,CACR,KAAK,EACL,aAAa,sDACb,CAAC,CACD,UAAU;CAKZ,gBAAgB,EACd,QAAQ,CACR,KAAK,EACL,aACC,uEACD,CAAC,CACD,UAAU;CAMZ,cAAc,EACZ,KAAK,CAAC,QAAQ,eAAe,CAAC,CAC9B,KAAK,EACL,aACC,wEACD,CAAC,CACD,UAAU;CAIZ,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;CAIlD,OAAO,EACL,QAAQ,CACR,KAAK,EACL,aAAa,wDACb,CAAC,CACD,UAAU;CAKZ,QAAQ,EACN,QAA4C,iBAAiB;AAC7D,SAAO,OAAO,iBAAiB;GAC9B,CACD,KAAK,EACL,aACC,sHACD,CAAC,CACD,UAAU;CAIZ,YAAY,EACV,QAAQ,CACR,KAAK,EACL,aACC,oGACD,CAAC,CACD,QAAQ,IAAI;CAId,WAAW,EACT,QAAQ,CACR,KAAK,EACL,aACC,wIACD,CAAC,CACD,QAAQ,IAAI;CAId,WAAW,EACT,QAAQ,CACR,KAAK,EACL,aACC,0IACD,CAAC,CACD,UAAU;CAIZ,qBAAqB,EACnB,SAAS,CACT,KAAK,EACL,aACC,sGACD,CAAC,CACD,QAAQ,MAAM;CAIhB,iBAAiB,EACf,SAAS,CACT,KAAK,EACL,aAAa,4DACb,CAAC,CACD,QAAQ,MAAM;CAChB,CAAC;;;;;;;;;;;;;;;;AAiBF,MAAa,uBAAuB,YAA2B;CAC9D,MAAM,SAAS,QAAQ;CACvB,MAAM,sBAAsB,QAAQ;AAEpC,QAAO,mBACN,yBACA;EACC,QAAQ;EACR,MAAM;EACN,UAAU,EACT,SAAS,EACR,aAAa,uBACb,EACD;EACD,KAAK;GACJ;GACA,oBAAoB,qBAAqB,uBAAuB;GAChE,aAAa,MAAM;AAClB,WAAO,CAAC,EAAE,KAAK,YAAsB,EAAE,KAAK,UAAoB;KAC/D;GACF;EACD,EACD,OAAO,QAAQ;EACd,MAAM,EAAE,MAAM,YAAY,IAAI,QAAQ;EACtC,MAAM,eAAe,IAAI,KAAK,gBAAgB;EAC9C,MAAM,cACL,IAAI,KAAK,eACT,eAAe,IAAI,QAAQ,SAAS,cAAc,QAAQ;AAE3D,MAAI,CAAC,KAAK,iBAAiB,oBAAoB,yBAC9C,OAAMA,WAAS,KACd,eACA,mBAAmB,4BACnB;EAGF,MAAM,OAAO,MAAM,cAAc,SAAS,IAAI,KAAK,KAAK;AACxD,MAAI,CAAC,KACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,4BACnB;EAKF,MAAM,uBAAuB,IAAI,KAAK,iBACnC,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GAChD,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO,IAAI,KAAK;IAChB,CACD;GACD,CAAC,GACD;AACH,MAAI,IAAI,KAAK,kBAAkB,CAAC,qBAC/B,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;AAEF,MACC,IAAI,KAAK,kBACT,wBACA,qBAAqB,gBAAgB,YAErC,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;EAIF,IAAI;AACJ,MAAI,iBAAiB,gBAAgB;AAEpC,gBAAa,sBAAsB;AACnC,OAAI,CAAC,YAAY;IAChB,MAAM,MAAM,MAAM,IAAI,QAAQ,QAAQ,QAEpC;KACD,OAAO;KACP,OAAO,CACN;MACC,OAAO;MACP,OAAO;MACP,CACD;KACD,CAAC;AACF,QAAI,CAAC,IACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;AAEF,iBAAa,IAAI;AAGjB,QAAI,CAAC,WACJ,KAAI;KAEH,IAAI;AACJ,SAAI;AAKH,wBAJe,MAAM,OAAO,UAAU,OAAO;OAC5C,OAAO,aAAa,iBAAiB,KAAK,eAAe,MAAM,IAAI,GAAG;OACtE,OAAO;OACP,CAAC,EACsB,KAAK;aACtB;AAEP,UAAI,QAAQ,OAAO,KAClB,iEACA;AACD,iBAAW,MAAM,YAAY,OAAO,UAAU,KAAK,EAClD,OAAO,KACP,CAAC,CACD,KACC,SAAS,WACR,iBAAiB,KAAK,oBACjB,IAAI,IACT;AACD,wBAAiB;AACjB;;;AAKH,SAAI,CAAC,gBAAgB;MAEpB,IAAI,oBACH,EAAE;AACH,UAAI,QAAQ,cAAc,wBACzB,qBACC,MAAM,QAAQ,aAAa,wBAC1B,KACA,IACA;MAMH,MAAM,iBAAiB,KACtB;OACC,MAAM,IAAI;OACV,UAAU,iBAAiB,IAC1B;QACC,gBAAgB,IAAI;QACpB,cAAc;QACd,EACD,IAAI,KAAK,SACT;OACD,EACD,kBACA;AACD,uBAAiB,MAAM,OAAO,UAAU,OAAO,eAAe;AAG9D,YAAM,QAAQ,cAAc,mBAC3B;OACC;OACA,cAAc;QACb,GAAG;QACH,kBAAkB,eAAe;QACjC;OACD,EACD,IACA;;AAGF,WAAM,IAAI,QAAQ,QAAQ,OAAO;MAChC,OAAO;MACP,QAAQ,EACP,kBAAkB,eAAe,IACjC;MACD,OAAO,CACN;OACC,OAAO;OACP,OAAO,IAAI;OACX,CACD;MACD,CAAC;AAEF,kBAAa,eAAe;aACpB,GAAQ;AAChB,SAAI,QAAQ,OAAO,MAAM,EAAE;AAC3B,WAAMA,WAAS,KACd,eACA,mBAAmB,0BACnB;;;SAIE;AAEN,gBACC,sBAAsB,oBAAoB,KAAK;AAChD,OAAI,CAAC,WACJ,KAAI;IAEH,IAAI;AACJ,QAAI;AAKH,uBAJe,MAAM,OAAO,UAAU,OAAO;MAC5C,OAAO,UAAU,wBAAwB,KAAK,MAAM,CAAC,mBAAmB,iBAAiB,KAAK,aAAa;MAC3G,OAAO;MACP,CAAC,EACsB,KAAK;YACtB;AAEP,SAAI,QAAQ,OAAO,KAClB,iEACA;AACD,gBAAW,MAAM,YAAY,OAAO,UAAU,KAAK;MAClD,OAAO,KAAK;MACZ,OAAO;MACP,CAAC,CACD,KACC,SAAS,WAAW,iBAAiB,KAAK,kBAC1C,gBACC;AACD,uBAAiB;AACjB;;;AAKH,QAAI,CAAC,eACJ,kBAAiB,MAAM,OAAO,UAAU,OAAO;KAC9C,OAAO,KAAK;KACZ,MAAM,KAAK;KACX,UAAU,iBAAiB,IAC1B;MACC,QAAQ,KAAK;MACb,cAAc;MACd,EACD,IAAI,KAAK,SACT;KACD,CAAC;AAIH,UAAM,IAAI,QAAQ,QAAQ,OAAO;KAChC,OAAO;KACP,QAAQ,EACP,kBAAkB,eAAe,IACjC;KACD,OAAO,CACN;MACC,OAAO;MACP,OAAO,KAAK;MACZ,CACD;KACD,CAAC;AAEF,iBAAa,eAAe;YACpB,GAAQ;AAChB,QAAI,QAAQ,OAAO,MAAM,EAAE;AAC3B,UAAMA,WAAS,KACd,eACA,mBAAmB,0BACnB;;;EAKJ,MAAM,gBAAgB,uBACnB,CAAC,qBAAqB,GACtB,MAAM,IAAI,QAAQ,QAAQ,SAAuB;GACjD,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO;IACP,CACD;GACD,CAAC;EAEJ,MAAM,+BAA+B,cAAc,MAAM,QACxD,mBAAmB,IAAI,CACvB;EAQD,MAAM,sBANsB,MAAM,OAAO,cACvC,KAAK,EACL,UAAU,YACV,CAAC,CACD,MAAM,QAAQ,IAAI,KAAK,QAAQ,QAAQ,mBAAmB,IAAI,CAAC,CAAC,EAEnB,MAAM,QAAQ;AAE5D,OACC,sBAAsB,wBACtB,IAAI,KAAK,eAET,QACC,IAAI,OAAO,sBAAsB,wBACjC,IAAI,OAAO,IAAI,KAAK;AAItB,OAAI,8BAA8B,qBACjC,QAAO,IAAI,OAAO,6BAA6B;AAEhD,UAAO;IACN;EAMF,MAAM,YAHe,qBAClB,MAAM,gBAAgB,SAAS,mBAAmB,MAAM,KAAK,GAC7D,SAC4B;EAC/B,MAAM,4BAA4B,UAAU,MAAM;EAGlD,MAAM,yBAAyB,cAAc,MAC3C,QAAQ,IAAI,WAAW,aACxB;EAED,MAAM,UAAU,IAAI,KAAK,SACtB,KAAK,wBACL,KAAK;EACR,MAAM,YAAY,IAAI,KAAK,SACxB,KAAK,0BACL,KAAK;EACR,MAAM,kBAAkB,YACrB,MAAM,4BAA4B,QAAQ,UAAU,GACpD;EAEH,MAAM,eAAe,WAAW;AAChC,MAAI,CAAC,aACJ,OAAM,IAAI,MAAM,eAAe,EAC9B,SAAS,4CACT,CAAC;EAKH,MAAM,qBAAqB,CAAC,EAC3B,KAAK,eAAe,iBAAiB;EAEtC,IAAI,cAAc;AAClB,MAAI,mBACH,eAAc,MAAM,IAAI,QAAQ,QAAQ,MAAM;GAC7C,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAkB,OAAO;IAAa,CAAC;GACxD,CAAC;EAGH,MAAM,aAAa,8BAA8B,SAAS,IAAI,KAAK;EACnE,MAAM,cAAc,qBACjB,OACA,8BAA8B,WAAW,IAAI,KAAK,SAAS;EAC9D,MAAM,gBAAgB,8BAA8B;EACpD,MAAM,2BACL,CAAC,8BAA8B,aAC/B,6BAA6B,4BAAY,IAAI,MAAM;EACpD,MAAM,iBACL,sBAAsB,KAAK,gBAAgB,KAAK;AAQjD,MALC,8BAA8B,WAAW,YACzC,cACA,eACA,iBACA,yBAEA,OAAMA,WAAS,KACd,eACA,mBAAmB,wBACnB;AAGF,MAAI,sBAAsB,YAAY;GAErC,IAAI,iBAAiB,MAAM,IAAI,QAAQ,QAAQ,QAAsB;IACpE,OAAO;IACP,OAAO,CACN;KACC,OAAO;KACP,OAAO,mBAAmB;KAC1B,CACD;IACD,CAAC;AAGF,OAAI,CAAC,kBAAkB,8BAA8B;AACpD,UAAM,IAAI,QAAQ,QAAQ,OAAqB;KAC9C,OAAO;KACP,QAAQ;MACP,sBAAsB,mBAAmB;MACzC,2BAAW,IAAI,MAAM;MACrB;KACD,OAAO,CACN;MACC,OAAO;MACP,OAAO,6BAA6B;MACpC,CACD;KACD,CAAC;AACF,qBAAiB;;AAGlB,OAAI,CAAC,SACJ,OAAMA,WAAS,KACd,aACA,mBAAmB,uBACnB;AAKF,OAAI,mBAAmB,UAAU;IAChC,MAAM,EAAE,MAAM,sBACb,MAAM,OAAO,sBAAsB,KAAK,EACvC,UAAU,YACV,CAAC;IACH,MAAM,mBAAmB,kBAAkB,MACzC,OACC,OAAO,EAAE,iBAAiB,WACxB,EAAE,eACF,EAAE,cAAc,QAAQ,mBAAmB,MAC9C,EAAE,WAAW,SACd;AACD,QACC,oBACA,iBAAiB,UAAU,WAAW,uBACrC;AACD,WAAM,OAAO,sBAAsB,QAAQ,iBAAiB,GAAG;AAC/D,SAAI,eACH,OAAM,IAAI,QAAQ,QAAQ,OAAO;MAChC,OAAO;MACP,QAAQ;OACP,kBAAkB;OAClB,2BAAW,IAAI,MAAM;OACrB;MACD,OAAO,CACN;OACC,OAAO;OACP,OAAO,eAAe;OACtB,CACD;MACD,CAAC;;;GAKL,MAAM,UAAU,+BACb,MAAM,cAAc,SAAS,6BAA6B,KAAK,GAC/D;GAKH,MAAM,2BAAW,IAAI,KAGlB;AAEH,OAAI,sBAAsB,KAAK,aAC9B;QACC,SAAS,eACT,QAAQ,gBAAgB,KAAK,YAE7B,UAAS,IAAI,QAAQ,aAAa;KACjC,UAAU,KAAK;KACf,UAAU;KACV,CAAC;;GAOJ,MAAM,gCAAgB,IAAI,KAAqB;AAC/C,QAAK,MAAM,MAAM,SAAS,aAAa,EAAE,CACxC,KAAI,OAAO,GAAG,UAAU,SACvB,eAAc,IAAI,GAAG,QAAQ,cAAc,IAAI,GAAG,MAAM,IAAI,KAAK,EAAE;AAErE,QAAK,MAAM,MAAM,KAAK,aAAa,EAAE,CACpC,KAAI,OAAO,GAAG,UAAU,SACvB,eAAc,IAAI,GAAG,QAAQ,cAAc,IAAI,GAAG,MAAM,IAAI,KAAK,EAAE;AAErE,QAAK,MAAM,CAAC,OAAO,UAAU,cAC5B,KAAI,UAAU,EAAG,eAAc,OAAO,MAAM;GAG7C,IAAI;AACJ,OAAI,IAAI,KAAK,qBAAqB;IAGjC,MAAM,WAAW,MAAM,OAAO,sBAC5B,OAAO,EACP,mBAAmB,mBAAmB,IACtC,CAAC,CACD,MAAM,OAAO,MAAM;AACnB,WAAM,IAAI,MAAM,eAAe;MAC9B,SAAS,EAAE;MACX,MAAM,EAAE;MACR,CAAC;MACD;IAEH,MAAM,eAAe,SAAS,OAAO;AACrC,QAAI,CAAC,aACJ,OAAM,IAAI,MAAM,eAAe,EAC9B,SAAS,uCACT,CAAC;IAGH,MAAM,8BAAc,IAAI,KAAqB;AAC7C,SAAK,MAAM,CAAC,GAAG,MAAM,cACpB,KAAI,IAAI,EAAG,aAAY,IAAI,GAAG,CAAC,EAAE;IAGlC,MAAM,gBAGD,EAAE;AACP,SAAK,MAAM,QAAQ,aAAa,OAAO;KACtC,MAAM,cACL,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAK,MAAM;KAG1D,MAAM,QAAQ,YAAY,IAAI,YAAY,IAAI;AAC9C,SAAI,QAAQ,GAAG;AACd,kBAAY,IAAI,aAAa,QAAQ,EAAE;AACvC;;KAMD,MAAM,cAAc,SAAS,IAAI,YAAY;AAC7C,SAAI,aAAa;AAChB,oBAAc,KAAK;OAClB,OAAO,YAAY;OACnB,UAAU,YAAY,YAAY,KAAK;OACvC,CAAC;AACF;;AAID,SAAI,gBAAgB,2BAA2B;AAC9C,oBAAc,KAAK;OAClB,OAAO;OACP,UAAU,qBAAqB,IAAI,IAAI,KAAK,SAAS;OACrD,CAAC;AACF;;AAID,mBAAc,KAAK;MAClB,OAAO;MACP,UAAU,KAAK;MACf,CAAC;KAEF,MAAM,IAAI,cAAc,IAAI,YAAY;AACxC,SAAI,MAAM,UAAa,IAAI,EAC1B,KAAI,MAAM,EAAG,eAAc,OAAO,YAAY;SACzC,eAAc,IAAI,aAAa,IAAI,EAAE;;AAI5C,SAAK,MAAM,CAAC,OAAO,UAAU,cAC5B,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,IAAK,eAAc,KAAK,EAAE,OAAO,CAAC;AAG9D,UAAM,OAAO,sBACX,OAAO,SAAS,IAAI;KACpB,UAAU,EAAE,QAAQ,uBAAuB;KAC3C,cAAc;KACd,QAAQ,CACP;MACC,OAAO,aAAa,MAAM,KAAK,UAAU;OACxC,OACC,OAAO,KAAK,UAAU,WACnB,KAAK,QACL,KAAK,MAAM;OACf,UAAU,KAAK;OACf,EAAE;MACH,YAAY,aAAa;MACzB,UAAU,aAAa;MACvB,EACD;MACC,OAAO;MACP,YAAY,aAAa;MACzB,oBAAoB;MACpB,CACD;KACD,CAAC,CACD,MAAM,OAAO,MAAM;AACnB,WAAM,IAAI,MAAM,eAAe;MAC9B,SAAS,EAAE;MACX,MAAM,EAAE;MACR,CAAC;MACD;AAGH,QAAI,eACH,OAAM,IAAI,QAAQ,QAAQ,OAAO;KAChC,OAAO;KACP,QAAQ;MACP,kBAAkB,SAAS;MAC3B,2BAAW,IAAI,MAAM;MACrB;KACD,OAAO,CACN;MACC,OAAO;MACP,OAAO,eAAe;MACtB,CACD;KACD,CAAC;AAGH,iBAAa,OAAO,KAAK,IAAI,KAAK,aAAa,IAAI;cACzC,SAAS,OAAO,KAAK,cAAc,OAAO,GAAG;IAKvD,MAAM,8BAAc,IAAI,KAAqB;AAC7C,SAAK,MAAM,CAAC,GAAG,MAAM,cACpB,KAAI,IAAI,EAAG,aAAY,IAAI,GAAG,CAAC,EAAE;IAGlC,MAAM,cAA8C,EAAE;AACtD,SAAK,MAAM,MAAM,mBAAmB,MAAM,MAAM;KAE/C,MAAM,QAAQ,YAAY,IAAI,GAAG,MAAM,GAAG,IAAI;AAC9C,SAAI,QAAQ,GAAG;AACd,kBAAY,IAAI,GAAG,MAAM,IAAI,QAAQ,EAAE;AACvC,kBAAY,KAAK;OAAE,IAAI,GAAG;OAAI,SAAS;OAAM,CAAC;AAC9C;;KAID,MAAM,cAAc,SAAS,IAAI,GAAG,MAAM,GAAG;AAC7C,SAAI,aAAa;AAChB,kBAAY,KAAK;OAChB,IAAI,GAAG;OACP,OAAO,YAAY;OACnB,UAAU,YAAY;OACtB,CAAC;AACF;;AAED,SAAI,GAAG,MAAM,OAAO,2BAA2B;AAC9C,kBAAY,KAAK;OAChB,IAAI,GAAG;OACP,OAAO;OACP,UAAU,qBAAqB,IAAI,IAAI,KAAK,SAAS;OACrD,CAAC;AACF;;KAGD,MAAM,IAAI,cAAc,IAAI,GAAG,MAAM,GAAG;AACxC,SAAI,MAAM,UAAa,IAAI,EAC1B,KAAI,MAAM,EAAG,eAAc,OAAO,GAAG,MAAM,GAAG;SACzC,eAAc,IAAI,GAAG,MAAM,IAAI,IAAI,EAAE;;AAI5C,SAAK,MAAM,CAAC,OAAO,UAAU,cAC5B,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,IAAK,aAAY,KAAK,EAAE,OAAO,CAAC;AAE5D,UAAM,OAAO,cACX,OAAO,mBAAmB,IAAI;KAC9B,OAAO;KACP,oBAAoB;KACpB,CAAC,CACD,MAAM,OAAO,MAAM;AACnB,WAAM,IAAI,MAAM,eAAe;MAC9B,SAAS,EAAE;MACX,MAAM,EAAE;MACR,CAAC;MACD;AAEH,QAAI,eACH,OAAM,IAAI,QAAQ,QAAQ,OAAqB;KAC9C,OAAO;KACP,QAAQ;MACP,MAAM,KAAK,KAAK,aAAa;MAC7B,OAAO;MACP,2BAAW,IAAI,MAAM;MACrB;KACD,OAAO,CAAC;MAAE,OAAO;MAAM,OAAO,eAAe;MAAI,CAAC;KAClD,CAAC;AAGH,iBAAa,OAAO,KAAK,IAAI,KAAK,aAAa,IAAI;SAGnD,EAAC,CAAE,KAAK,cAAe,MAAM,OAAO,cAAc,SAChD,OAAO;IACP,UAAU;IACV,YAAY,OAAO,KAAK,IAAI,KAAK,aAAa,IAAI;IAClD,WAAW;KACV,MAAM;KACN,kBAAkB;MACjB,MAAM;MACN,UAAU,EACT,YAAY,OAAO,KAAK,IAAI,KAAK,aAAa,IAAI,EAClD;MACD;KACD,6BAA6B;MAC5B,cAAc,mBAAmB;MACjC,OAAO,CACN;OACC,IAAI,SAAS;OACb,OAAO;OACP,GAAI,qBACD,EAAE,GACF,EAAE,UAAU,IAAI,KAAK,SAAS,GAAG;OACpC,CACD;MACD;KACD;IACD,CAAC,CACD,MAAM,OAAO,MAAM;AACnB,UAAM,IAAI,MAAM,eAAe;KAC9B,SAAS,EAAE;KACX,MAAM,EAAE;KACR,CAAC;KACD;AAEJ,UAAO,IAAI,KAAK;IACf,KAAK;IACL,UAAU,CAAC,IAAI,KAAK;IACpB,CAAC;;EAGH,IAAI,eACH,gCAAgC;AAEjC,MAAI,0BAA0B,CAAC,6BAe9B,gBAdgB,MAAM,IAAI,QAAQ,QAAQ,OAAqB;GAC9D,OAAO;GACP,QAAQ;IACP,MAAM,KAAK,KAAK,aAAa;IAC7B,OAAO,qBAAqB,cAAc,IAAI,KAAK,SAAS;IAC5D,2BAAW,IAAI,MAAM;IACrB;GACD,OAAO,CACN;IACC,OAAO;IACP,OAAO,uBAAuB;IAC9B,CACD;GACD,CAAC,IAC0C;AAG7C,MAAI,CAAC,aACJ,gBAAe,MAAM,IAAI,QAAQ,QAAQ,OAAqB;GAC7D,OAAO;GACP,MAAM;IACL,MAAM,KAAK,KAAK,aAAa;IAC7B,kBAAkB;IAClB,QAAQ;IACR;IACA,OAAO,qBAAqB,cAAc,IAAI,KAAK,SAAS;IAC5D;GACD,CAAC;AAGH,MAAI,CAAC,cAAc;AAClB,OAAI,QAAQ,OAAO,MAAM,4BAA4B;AACrD,SAAMA,WAAS,KACd,aACA,mBAAmB,uBACnB;;EAGF,MAAM,SAAS,MAAM,oBAAoB,2BACxC;GACC;GACA;GACA;GACA;GACA,EACD,IAAI,SACJ,IACA;EAgBD,MAAM,YACL,EAfwB,MAAM,IAAI,QAAQ,QAAQ,SAClD;GACC,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAe,OAAO;IAAa,CAAC;GACrD,CACD,EACuC,MAAM,MAAM;AAKnD,UADC,CAAC,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW;IAE/C,IAGkB,KAAK,YACrB,EAAE,mBAAmB,KAAK,UAAU,MAAM,GAC1C;EAEJ,MAAM,kBAAkB,MAAM,OAAO,SAAS,SAC5C,OACA;GACC,GAAI,aACD;IACA,UAAU;IACV,iBACC,iBAAiB,SACb,EAAE,SAAS,QAAQ,GACnB;KAAE,MAAM;KAAQ,SAAS;KAAQ;IACtC,GACA,EACA,gBAAgB,KAAK,OACrB;GACH,QAAQ,IAAI,KAAK;GACjB,aAAa,OACZ,KACA,GACC,IAAI,QAAQ,QACZ,oCAAoC,mBACpC,IAAI,KAAK,WAIT,CAAC,0CACF;GACD,YAAY,OAAO,KAAK,IAAI,KAAK,UAAU;GAC3C,YAAY;IAEX,GAAI,CAAC,iBACF,CACA;KACC,OAAO;KACP,UAAU,qBAAqB,IAAI,IAAI,KAAK,SAAS;KACrD,CACD,GACA,EAAE;IAEL,GAAI,qBACD,CAAC;KAAE,OAAO,KAAK;KAAa,UAAU;KAAa,CAAC,GACpD,EAAE;IAEL,GAAI,KAAK,aAAa,EAAE;IACxB;GACD,mBAAmB;IAClB,GAAG;IACH,UAAU,qBAAqB,IAC9B;KACC,QAAQ,KAAK;KACb,gBAAgB,aAAa;KAC7B;KACA,EACD,IAAI,KAAK,UACT,QAAQ,QAAQ,mBAAmB,SACnC;IACD;GACD,MAAM;GACN,qBAAqB;GACrB,GAAG,QAAQ;GAEX,UAAU,qBAAqB,IAC9B;IACC,QAAQ,KAAK;IACb,gBAAgB,aAAa;IAC7B;IACA,EACD,IAAI,KAAK,UACT,QAAQ,QAAQ,SAChB;GACD,EACD,QAAQ,QACR,CACA,MAAM,OAAO,MAAM;AACnB,SAAM,IAAI,MAAM,eAAe;IAC9B,SAAS,EAAE;IACX,MAAM,EAAE;IACR,CAAC;IACD;AACH,SAAO,IAAI,KAAK;GACf,GAAG;GACH,UAAU,CAAC,IAAI,KAAK;GACpB,CAAC;GAEH;;AAGF,MAAM,+BAA+B,EAAE,OAAO;CAC7C,aAAa,EACX,QAAQ,CACR,KAAK,EACL,aAAa,yDACb,CAAC,CACD,UAAU;CACZ,gBAAgB,EACd,QAAQ,CACR,KAAK,EACL,aACC,oEACD,CAAC,CACD,UAAU;CAMZ,cAAc,EACZ,KAAK,CAAC,QAAQ,eAAe,CAAC,CAC9B,KAAK,EACL,aACC,wEACD,CAAC,CACD,UAAU;CACZ,WAAW,EAAE,QAAQ,CAAC,KAAK,EAC1B,aACC,qHACD,CAAC;CAIF,iBAAiB,EACf,SAAS,CACT,KAAK,EACL,aACC,yEACD,CAAC,CACD,QAAQ,MAAM;CAChB,CAAC;;;;;;;;;;;;;;;;AAiBF,MAAa,sBAAsB,YAA2B;CAC7D,MAAM,SAAS,QAAQ;CACvB,MAAM,sBAAsB,QAAQ;AACpC,QAAO,mBACN,wBACA;EACC,QAAQ;EACR,MAAM;EACN,UAAU,EACT,SAAS,EACR,aAAa,sBACb,EACD;EACD,KAAK;GACJ;GACA,oBAAoB,qBAAqB,sBAAsB;GAC/D,aAAa,QAAQ,IAAI,KAAK,UAAU;GACxC;EACD,EACD,OAAO,QAAQ;EACd,MAAM,eAAe,IAAI,KAAK,gBAAgB;EAC9C,MAAM,cACL,IAAI,KAAK,eACT,eAAe,IAAI,QAAQ,SAAS,cAAc,QAAQ;EAE3D,IAAI,eAAe,IAAI,KAAK,iBACzB,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GAChD,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO,IAAI,KAAK;IAChB,CACD;GACD,CAAC,GACD,MAAM,IAAI,QAAQ,QACjB,SAAuB;GACvB,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAe,OAAO;IAAa,CAAC;GACrD,CAAC,CACD,MAAM,SAAS,KAAK,MAAM,QAAQ,mBAAmB,IAAI,CAAC,CAAC;AAC/D,MACC,IAAI,KAAK,kBACT,gBACA,aAAa,gBAAgB,YAE7B,gBAAe;AAGhB,MAAI,CAAC,gBAAgB,CAAC,aAAa,iBAClC,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;EAEF,MAAM,sBAAsB,MAAM,OAAO,cACvC,KAAK,EACL,UAAU,aAAa,kBACvB,CAAC,CACD,MAAM,QAAQ,IAAI,KAAK,QAAQ,QAAQ,mBAAmB,IAAI,CAAC,CAAC;AAClE,MAAI,CAAC,oBAAoB,QAAQ;;;;;AAKhC,SAAM,IAAI,QAAQ,QAAQ,WAAW;IACpC,OAAO;IACP,OAAO,CACN;KACC,OAAO;KACP,OAAO;KACP,CACD;IACD,CAAC;AACF,SAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;;EAEF,MAAM,qBAAqB,oBAAoB,MAC7C,QAAQ,IAAI,OAAO,aAAa,qBACjC;AACD,MAAI,CAAC,mBACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;EAEF,MAAM,EAAE,QAAQ,MAAM,OAAO,cAAc,SACzC,OAAO;GACP,UAAU,aAAa;GACvB,YAAY,OAAO,KAAK,IAAI,MAAM,aAAa,IAAI;GACnD,WAAW;IACV,MAAM;IACN,qBAAqB,EACpB,cAAc,mBAAmB,IACjC;IACD;GACD,CAAC,CACD,MAAM,OAAO,MAAM;AACnB,OAAI,EAAE,SAAS,SAAS,6BAA6B,EAKpD;;;;;QAAI,CAAC,gBAAgB,aAAa,EAAE;KACnC,MAAM,YAAY,MAAM,OAAO,cAAc,SAC5C,mBAAmB,GACnB;AACD,WAAM,IAAI,QAAQ,QAAQ,OAAO;MAChC,OAAO;MACP,QAAQ;OACP,mBAAmB,UAAU;OAC7B,UAAU,UAAU,4BACjB,IAAI,KAAK,UAAU,YAAY,IAAK,GACpC;OACH,YAAY,UAAU,8BACnB,IAAI,KAAK,UAAU,cAAc,IAAK,GACtC;OACH;MACD,OAAO,CACN;OACC,OAAO;OACP,OAAO,aAAa;OACpB,CACD;MACD,CAAC;;;AAGJ,SAAM,IAAI,MAAM,eAAe;IAC9B,SAAS,EAAE;IACX,MAAM,EAAE;IACR,CAAC;IACD;AACH,SAAO,IAAI,KAAK;GACf;GACA,UAAU,CAAC,IAAI,KAAK;GACpB,CAAC;GAEH;;AAGF,MAAM,gCAAgC,EAAE,OAAO;CAC9C,aAAa,EACX,QAAQ,CACR,KAAK,EACL,aAAa,0DACb,CAAC,CACD,UAAU;CACZ,gBAAgB,EACd,QAAQ,CACR,KAAK,EACL,aACC,qEACD,CAAC,CACD,UAAU;CAMZ,cAAc,EACZ,KAAK,CAAC,QAAQ,eAAe,CAAC,CAC9B,KAAK,EACL,aACC,wEACD,CAAC,CACD,UAAU;CACZ,CAAC;AAEF,MAAa,uBAAuB,YAA2B;CAC9D,MAAM,SAAS,QAAQ;CACvB,MAAM,sBAAsB,QAAQ;AACpC,QAAO,mBACN,yBACA;EACC,QAAQ;EACR,MAAM;EACN,UAAU,EACT,SAAS,EACR,aAAa,uBACb,EACD;EACD,KAAK,CACJ,yBACA,oBAAoB,qBAAqB,uBAAuB,CAChE;EACD,EACD,OAAO,QAAQ;EACd,MAAM,eAAe,IAAI,KAAK,gBAAgB;EAC9C,MAAM,cACL,IAAI,KAAK,eACT,eAAe,IAAI,QAAQ,SAAS,cAAc,QAAQ;EAE3D,IAAI,eAAe,IAAI,KAAK,iBACzB,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GAChD,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO,IAAI,KAAK;IAChB,CACD;GACD,CAAC,GACD,MAAM,IAAI,QAAQ,QACjB,SAAuB;GACvB,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO;IACP,CACD;GACD,CAAC,CACD,MAAM,SAAS,KAAK,MAAM,QAAQ,mBAAmB,IAAI,CAAC,CAAC;AAC/D,MACC,IAAI,KAAK,kBACT,gBACA,aAAa,gBAAgB,YAE7B,gBAAe;AAEhB,MAAI,CAAC,gBAAgB,CAAC,aAAa,iBAClC,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;AAEF,MAAI,CAAC,mBAAmB,aAAa,CACpC,OAAMA,WAAS,KACd,eACA,mBAAmB,wBACnB;EAEF,MAAM,mBAAmB,gBAAgB,aAAa;EACtD,MAAM,EAAE,qBAAqB;AAE7B,MAAI,CAAC,oBAAoB,CAAC,iBACzB,OAAMA,WAAS,KACd,eACA,mBAAmB,gCACnB;AAIF,MAAI,kBAAkB;AACrB,OAAI,CAAC,aAAa,qBACjB,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;AAYF,QATiB,MAAM,OAAO,sBAC5B,SAAS,iBAAiB,CAC1B,OAAO,MAAM;AACb,UAAM,IAAI,MAAM,eAAe;KAC9B,SAAS,EAAE;KACX,MAAM,EAAE;KACR,CAAC;KACD,EAEU,WAAW,SACvB,OAAM,OAAO,sBACX,QAAQ,iBAAiB,CACzB,OAAO,MAAM;AACb,UAAM,IAAI,MAAM,eAAe;KAC9B,SAAS,EAAE;KACX,MAAM,EAAE;KACR,CAAC;KACD;AAGJ,SAAM,IAAI,QAAQ,QAAQ,OAAO;IAChC,OAAO;IACP,QAAQ;KACP,kBAAkB;KAClB,2BAAW,IAAI,MAAM;KACrB;IACD,OAAO,CACN;KACC,OAAO;KACP,OAAO,aAAa;KACpB,CACD;IACD,CAAC;GACF,MAAM,cAAc,MAAM,OAAO,cAAc,SAC9C,aAAa,qBACb;AACD,UAAO,IAAI,KAAK,YAAY;;EAI7B,MAAM,qBAAqB,MAAM,OAAO,cACtC,KAAK,EACL,UAAU,aAAa,kBACvB,CAAC,CACD,MAAM,QAAQ,IAAI,KAAK,QAAQ,QAAQ,mBAAmB,IAAI,CAAC,CAAC,GAAG;AACrE,MAAI,CAAC,mBACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;EAKF,MAAM,eAAgD,EAAE;AACxD,MAAI,mBAAmB,UACtB,cAAa,YAAY;WACf,mBAAmB,qBAC7B,cAAa,uBAAuB;EAGrC,MAAM,SAAS,MAAM,OAAO,cAC1B,OAAO,mBAAmB,IAAI,aAAa,CAC3C,OAAO,MAAM;AACb,SAAM,IAAI,MAAM,eAAe;IAC9B,SAAS,EAAE;IACX,MAAM,EAAE;IACR,CAAC;IACD;AAEH,QAAM,IAAI,QAAQ,QAAQ,OAAO;GAChC,OAAO;GACP,QAAQ;IACP,mBAAmB;IACnB,UAAU;IACV,YAAY;IACZ,2BAAW,IAAI,MAAM;IACrB;GACD,OAAO,CACN;IACC,OAAO;IACP,OAAO,aAAa;IACpB,CACD;GACD,CAAC;AAEF,SAAO,IAAI,KAAK,OAAO;GAExB;;AAGF,MAAM,qCAAqC,EAAE,SAC5C,EAAE,OAAO;CACR,aAAa,EACX,QAAQ,CACR,KAAK,EACL,aAAa,uDACb,CAAC,CACD,UAAU;CAMZ,cAAc,EACZ,KAAK,CAAC,QAAQ,eAAe,CAAC,CAC9B,KAAK,EACL,aACC,wEACD,CAAC,CACD,UAAU;CACZ,CAAC,CACF;;;;;;;;;;;;;;;;AAgBD,MAAa,2BAA2B,YAA2B;CAClE,MAAM,sBAAsB,QAAQ;AACpC,QAAO,mBACN,sBACA;EACC,QAAQ;EACR,OAAO;EACP,UAAU,EACT,SAAS,EACR,aAAa,2BACb,EACD;EACD,KAAK,CACJ,yBACA,oBAAoB,qBAAqB,oBAAoB,CAC7D;EACD,EACD,OAAO,QAAQ;EACd,MAAM,eAAe,IAAI,OAAO,gBAAgB;EAChD,MAAM,cACL,IAAI,OAAO,eACX,eAAe,IAAI,QAAQ,SAAS,cAAc,QAAQ;EAE3D,MAAM,gBAAgB,MAAM,IAAI,QAAQ,QAAQ,SAAuB;GACtE,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO;IACP,CACD;GACD,CAAC;AACF,MAAI,CAAC,cAAc,OAClB,QAAO,EAAE;EAEV,MAAM,QAAQ,MAAM,SAAS,QAAQ,aAAa;AAClD,MAAI,CAAC,MACJ,QAAO,EAAE;EAEV,MAAM,OAAO,cACX,KAAK,QAAQ;GACb,MAAM,OAAO,MAAM,MACjB,MAAM,EAAE,KAAK,aAAa,KAAK,IAAI,KAAK,aAAa,CACtD;AACD,UAAO;IACN,GAAG;IACH,QAAQ,MAAM;IACd,SAAS,MAAM;IACf;IACA,CACD,QAAQ,QAAQ,mBAAmB,IAAI,CAAC;AAC1C,SAAO,IAAI,KAAK,KAAK;GAEtB;;AAGF,MAAM,iCAAiC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;AAE/E,MAAa,uBAAuB,YAA2B;CAC9D,MAAM,SAAS,QAAQ;AACvB,QAAO,mBACN,yBACA;EACC,QAAQ;EACR,OAAO;EACP,UAAU,EACT,SAAS,EACR,aAAa,6BACb,EACD;EACD,KAAK,CAAC,aAAa,QAAQ,IAAI,MAAM,YAAY,CAAC;EAClD,EACD,OAAO,QAAQ;EACd,MAAM,cAAc,IAAI,OAAO,eAAe;EAE9C,MAAM,UAAU,MAAM,kBAA+C,IAAI;AACzE,MAAI,CAAC,QACJ,OAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;AAK7C,MAAI,CAAC,IAAI,OAAO,kBACf,OAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;EAM7C,MAAM,kBAAkB,MAAM,OAAO,SAAS,SAC5C,SAAS,IAAI,MAAM,kBAAkB,CACrC,OAAO,UAAU;AACjB,OAAI,QAAQ,OAAO,MAClB,iDACA,MACA;AACD,UAAO;IACN;AACH,MAAI,CAAC,gBACJ,OAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;EAG7C,MAAM,EAAE,mBAAmB,qBAAqB,IAC/C,gBAAgB,SAChB;AACD,MAAI,CAAC,gBAAgB;AACpB,OAAI,QAAQ,OAAO,KAClB,mDAAmD,gBAAgB,KACnE;AACD,SAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;;EAG7C,MAAM,eAAe,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GACpE,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO;IACP,CACD;GACD,CAAC;AACF,MAAI,CAAC,cAAc;AAClB,OAAI,QAAQ,OAAO,KAClB,qDAAqD,iBACrD;AACD,SAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;;AAI7C,MAAI,mBAAmB,aAAa,CACnC,OAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;EAG7C,MAAM,aACL,aAAa,oBAAoB,QAAQ,KAAK;AAC/C,MAAI,CAAC,WACJ,OAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;EAG7C,MAAM,qBAAqB,MAAM,OAAO,cACtC,KAAK;GAAE,UAAU;GAAY,QAAQ;GAAU,CAAC,CAChD,MAAM,QAAQ,IAAI,KAAK,GAAG,CAC1B,OAAO,UAAU;AACjB,OAAI,QAAQ,OAAO,MAClB,2CACA,MACA;AACD,SAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;IAC3C;AACH,MAAI,CAAC,mBACJ,OAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;EAG7C,MAAM,WAAW,MAAM,gBACtB,SACA,mBAAmB,MAAM,KACzB;AACD,MAAI,CAAC,UAAU;AACd,OAAI,QAAQ,OAAO,KAClB,uDAAuD,mBAAmB,KAC1E;AACD,SAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;;EAG7C,MAAM,EAAE,MAAM,kBAAkB,SAAS;AACzC,MAAI,CAAC,MAAM;AACV,OAAI,QAAQ,OAAO,KAClB,4BAA4B,iBAAiB,MAAM,KACnD;AACD,SAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;;EAG7C,MAAM,QACL,gBACC,mBAAmB,MAAM,MACzB,kBACA,KAAK,YACL,IAAI;AAEN,QAAM,IAAI,QAAQ,QAAQ,OAAO;GAChC,OAAO;GACP,QAAQ;IACP,GAAI,mBAAmB,eAAe,mBAAmB,YACtD;KACA,4BAAY,IAAI,KAAK,mBAAmB,cAAc,IAAK;KAC3D,0BAAU,IAAI,KAAK,mBAAmB,YAAY,IAAK;KACvD,GACA,EAAE;IACL,QAAQ,mBAAmB;IAC3B;IACA,MAAM,KAAK,KAAK,aAAa;IAC7B,iBAAiB,iBAAiB,MAAM,WAAW;IACnD,2BAAW,IAAI,KAAK,iBAAiB,qBAAqB,IAAK;IAC/D,6BAAa,IAAI,KAAK,iBAAiB,uBAAuB,IAAK;IACnE,sBAAsB,mBAAmB;IACzC,mBAAmB,mBAAmB;IACtC,UAAU,mBAAmB,4BAC1B,IAAI,KAAK,mBAAmB,YAAY,IAAK,GAC7C;IACH,YAAY,mBAAmB,8BAC5B,IAAI,KAAK,mBAAmB,cAAc,IAAK,GAC/C;IACH;GACD,OAAO,CACN;IACC,OAAO;IACP,OAAO,aAAa;IACpB,CACD;GACD,CAAC;AAEF,QAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;GAE7C;;AAGF,MAAM,gCAAgC,EAAE,OAAO;CAK9C,QAAQ,EACN,QAA4C,iBAAiB;AAC7D,SAAO,OAAO,iBAAiB;GAC9B,CACD,KAAK,EACL,aACC,wJACD,CAAC,CACD,UAAU;CACZ,aAAa,EAAE,QAAQ,CAAC,UAAU;CAMlC,cAAc,EACZ,KAAK,CAAC,QAAQ,eAAe,CAAC,CAC9B,KAAK,EACL,aACC,wEACD,CAAC,CACD,UAAU;CACZ,WAAW,EAAE,QAAQ,CAAC,QAAQ,IAAI;CAIlC,iBAAiB,EACf,SAAS,CACT,KAAK,EACL,aACC,oEACD,CAAC,CACD,QAAQ,MAAM;CAChB,CAAC;AAEF,MAAa,uBAAuB,YAA2B;CAC9D,MAAM,SAAS,QAAQ;CACvB,MAAM,sBAAsB,QAAQ;AACpC,QAAO,mBACN,gCACA;EACC,QAAQ;EACR,MAAM;EACN,UAAU,EACT,SAAS,EACR,aAAa,uBACb,EACD;EACD,KAAK;GACJ;GACA,oBAAoB,qBAAqB,iBAAiB;GAC1D,aAAa,QAAQ,IAAI,KAAK,UAAU;GACxC;EACD,EACD,OAAO,QAAQ;EACd,MAAM,EAAE,SAAS,IAAI,QAAQ;EAC7B,MAAM,eAAe,IAAI,KAAK,gBAAgB;EAC9C,MAAM,cACL,IAAI,KAAK,eACT,eAAe,IAAI,QAAQ,SAAS,cAAc,QAAQ;EAE3D,IAAI;AAEJ,MAAI,iBAAiB,gBAAgB;AAQpC,iBANY,MAAM,IAAI,QAAQ,QAAQ,QAEpC;IACD,OAAO;IACP,OAAO,CAAC;KAAE,OAAO;KAAM,OAAO;KAAa,CAAC;IAC5C,CAAC,GACgB;AAElB,OAAI,CAAC,WAQJ,eANqB,MAAM,IAAI,QAAQ,QACrC,SAAuB;IACvB,OAAO;IACP,OAAO,CAAC;KAAE,OAAO;KAAe,OAAO;KAAa,CAAC;IACrD,CAAC,CACD,MAAM,SAAS,KAAK,MAAM,QAAQ,mBAAmB,IAAI,CAAC,CAAC,GAClC;SAEtB;AAEN,gBAAa,KAAK;AAClB,OAAI,CAAC,WAaJ,eAZqB,MAAM,IAAI,QAAQ,QACrC,SAAuB;IACvB,OAAO;IACP,OAAO,CACN;KACC,OAAO;KACP,OAAO;KACP,CACD;IACD,CAAC,CACD,MAAM,SAAS,KAAK,MAAM,QAAQ,mBAAmB,IAAI,CAAC,CAAC,GAElC;;AAG7B,MAAI,CAAC,WACJ,OAAMA,WAAS,KAAK,aAAa,mBAAmB,mBAAmB;AAGxE,MAAI;GACH,MAAM,EAAE,QAAQ,MAAM,OAAO,cAAc,SAAS,OAAO;IAC1D,QAAQ,IAAI,KAAK;IACjB,UAAU;IACV,YAAY,OAAO,KAAK,IAAI,KAAK,UAAU;IAC3C,CAAC;AAEF,UAAO,IAAI,KAAK;IACf;IACA,UAAU,CAAC,IAAI,KAAK;IACpB,CAAC;WACM,OAAY;AACpB,OAAI,QAAQ,OAAO,MAClB,yCACA,MACA;AACD,SAAMA,WAAS,KACd,yBACA,mBAAmB,gCACnB;;GAGH;;AAGF,MAAa,iBAAiB,YAA2B;CACxD,MAAM,SAAS,QAAQ;AACvB,QAAO,mBACN,mBACA;EACC,QAAQ;EACR,UAAU;GACT,GAAG;GACH,SAAS,EACR,aAAa,uBACb;GACD;EACD,cAAc;EACd,aAAa;EACb,EACD,OAAO,QAAQ;AACd,MAAI,CAAC,IAAI,SAAS,KACjB,OAAMA,WAAS,KACd,eACA,mBAAmB,qBACnB;EAGF,MAAM,MAAM,IAAI,QAAQ,QAAQ,IAAI,mBAAmB;AACvD,MAAI,CAAC,IACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,2BACnB;EAGF,MAAM,gBAAgB,QAAQ;AAC9B,MAAI,CAAC,cACJ,OAAMA,WAAS,KACd,yBACA,mBAAmB,gCACnB;EAGF,MAAM,UAAU,MAAM,IAAI,QAAQ,MAAM;EAExC,IAAI;AACJ,MAAI;AAEH,OAAI,OAAO,OAAO,SAAS,wBAAwB,WAElD,SAAQ,MAAM,OAAO,SAAS,oBAC7B,SACA,KACA,cACA;OAGD,SAAQ,OAAO,SAAS,eAAe,SAAS,KAAK,cAAc;WAE5D,KAAU;AAClB,OAAI,QAAQ,OAAO,MAAM,GAAG,IAAI,UAAU;AAC1C,SAAMA,WAAS,KACd,eACA,mBAAmB,iCACnB;;AAEF,MAAI,CAAC,MACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,iCACnB;AAEF,MAAI;AACH,WAAQ,MAAM,MAAd;IACC,KAAK;AACJ,WAAM,2BAA2B,KAAK,SAAS,MAAM;AACrD,WAAM,QAAQ,UAAU,MAAM;AAC9B;IACD,KAAK;AACJ,WAAM,sBAAsB,KAAK,SAAS,MAAM;AAChD,WAAM,QAAQ,UAAU,MAAM;AAC9B;IACD,KAAK;AACJ,WAAM,sBAAsB,KAAK,SAAS,MAAM;AAChD,WAAM,QAAQ,UAAU,MAAM;AAC9B;IACD,KAAK;AACJ,WAAM,sBAAsB,KAAK,SAAS,MAAM;AAChD,WAAM,QAAQ,UAAU,MAAM;AAC9B;IACD;AACC,WAAM,QAAQ,UAAU,MAAM;AAC9B;;WAEM,GAAQ;AAChB,OAAI,QAAQ,OAAO,MAAM,iCAAiC,EAAE,UAAU;AACtE,SAAMA,WAAS,KACd,eACA,mBAAmB,qBACnB;;AAEF,SAAO,IAAI,KAAK,EAAE,SAAS,MAAM,CAAC;GAEnC;;;;;ACh+DF,MAAa,gBAAgB,EAC5B,cAAc,EACb,QAAQ;CACP,MAAM;EACL,MAAM;EACN,UAAU;EACV;CACD,aAAa;EACZ,MAAM;EACN,UAAU;EACV;CACD,kBAAkB;EACjB,MAAM;EACN,UAAU;EACV;CACD,sBAAsB;EACrB,MAAM;EACN,UAAU;EACV;CACD,QAAQ;EACP,MAAM;EACN,cAAc;EACd;CACD,aAAa;EACZ,MAAM;EACN,UAAU;EACV;CACD,WAAW;EACV,MAAM;EACN,UAAU;EACV;CACD,YAAY;EACX,MAAM;EACN,UAAU;EACV;CACD,UAAU;EACT,MAAM;EACN,UAAU;EACV;CACD,mBAAmB;EAClB,MAAM;EACN,UAAU;EACV,cAAc;EACd;CACD,UAAU;EACT,MAAM;EACN,UAAU;EACV;CACD,YAAY;EACX,MAAM;EACN,UAAU;EACV;CACD,SAAS;EACR,MAAM;EACN,UAAU;EACV;CACD,OAAO;EACN,MAAM;EACN,UAAU;EACV;CACD,iBAAiB;EAChB,MAAM;EACN,UAAU;EACV;CACD,kBAAkB;EACjB,MAAM;EACN,UAAU;EACV;CACD,EACD,EACD;AAED,MAAa,OAAO,EACnB,MAAM,EACL,QAAQ,EACP,kBAAkB;CACjB,MAAM;CACN,UAAU;CACV,EACD,EACD,EACD;AAED,MAAa,eAAe,EAC3B,cAAc,EACb,QAAQ,EACP,kBAAkB;CACjB,MAAM;CACN,UAAU;CACV,EACD,EACD,EACD;AAMD,MAAa,aACZ,YACwB;CACxB,IAAI,aAAuC,EAAE;AAE7C,KAAI,QAAQ,cAAc,QACzB,cAAa;EACZ,GAAG;EACH,GAAG;EACH;KAED,cAAa,EACZ,GAAG,MACH;AAGF,KAAI,QAAQ,cAAc,QACzB,cAAa;EACZ,GAAG;EACH,GAAG;EACH;AAGF,KACC,QAAQ,UACR,CAAC,QAAQ,cAAc,WACvB,kBAAkB,QAAQ,QACzB;EACD,MAAM,EAAE,cAAc,eAAe,GAAG,eAAe,QAAQ;AAC/D,SAAO,YAAY,YAAY,WAAW;;AAG3C,QAAO,YAAY,YAAY,QAAQ,OAAO;;;;;ACrG/C,MAAa,UAAmC,YAAe;CAC9D,MAAM,SAAS,QAAQ;CAEvB,MAAM,wBAAwB;EAC7B,qBAAqB,oBAAoB,QAAQ;EACjD,oBAAoB,mBAAmB,QAAQ;EAC/C,qBAAqB,oBAAoB,QAAQ;EACjD,yBAAyB,wBAAwB,QAAQ;EACzD,qBAAqB,oBAAoB,QAAQ;EACjD,qBAAqB,oBAAoB,QAAQ;EACjD;AAED,QAAO;EACN,IAAI;EACJ,WAAW;GACV,eAAe,cAAc,QAAQ;GACrC,GAAK,QAAQ,cAAc,UACxB,wBACA,EAAE;GAKL;EACD,KAAK,KAAK;AAET,OAAI,QAAQ,cAAc,WAAW,CAAC,QAAQ,cAAc,SAAS;IACpE,MAAM,qBAAqB,UAAwB;AAClD,SAAI,MAAM,MAAM,MAAM,EAAE,YAAY,CACnC,KAAI,OAAO,MACV,6KAEA;;IAGH,MAAM,EAAE,UAAU,QAAQ;AAC1B,QAAI,OAAO,UAAU,WACpB,CAAK,QAAQ,QAAQ,OAAO,CAAC,CAC3B,KAAK,kBAAkB,CACvB,OAAO,MAAW;AAClB,SAAI,OAAO,MACV,wDAAwD,EAAE,UAC1D;MACA;QAEH,mBAAkB,MAAM;;AAI1B,OAAI,QAAQ,cAAc,SAAS;IAClC,MAAM,YAAY,IAAI,UAAU,eAAe;AAC/C,QAAI,CAAC,WAAW;AACf,SAAI,OAAO,MAAM,gCAAgC;AACjD;;IAGD,MAAM,gBAAgB,UAAU,QAAQ,qBAAqB,EAAE;;;;IAK/D,MAAM,uBAAuB,OAAO,SAG9B;KACL,MAAM,EAAE,iBAAiB;AACzB,SAAI,CAAC,cAAc,iBAAkB;AAErC,SAAI;MACH,MAAM,iBAAiB,MAAM,OAAO,UAAU,SAC7C,aAAa,iBACb;AAED,UAAI,eAAe,SAAS;AAC3B,WAAI,OAAO,KACV,mBAAmB,aAAa,iBAAiB,cACjD;AACD;;AAID,UAAI,aAAa,SAAS,eAAe,MAAM;AAC9C,aAAM,OAAO,UAAU,OAAO,aAAa,kBAAkB,EAC5D,MAAM,aAAa,MACnB,CAAC;AACF,WAAI,OAAO,KACV,wCAAwC,eAAe,KAAK,OAAO,aAAa,KAAK,GACrF;;cAEM,GAAQ;AAChB,UAAI,OAAO,MACV,0CAA0C,EAAE,UAC5C;;;;;;IAOH,MAAM,wBAAwB,OAAO,SAG/B;KACL,MAAM,EAAE,iBAAiB;AACzB,SAAI,CAAC,aAAa,iBAAkB;AAEpC,SAAI;MAEH,MAAM,gBAAgB,MAAM,OAAO,cAAc,KAAK;OACrD,UAAU,aAAa;OACvB,QAAQ;OACR,OAAO;OACP,CAAC;AACF,WAAK,MAAM,OAAO,cAAc,KAC/B,KACC,IAAI,WAAW,cACf,IAAI,WAAW,gBACf,IAAI,WAAW,qBAEf,OAAM,SAAS,KACd,eACA,mBAAmB,qCACnB;cAGK,OAAY;AACpB,UAAI,iBAAiB,SACpB,OAAM;AAEP,UAAI,OAAO,MACV,+CAA+C,MAAM,UACrD;AACD,YAAM;;;;;;;IAQR,MAAM,6BAA6B,OAAO,SAEpC;AACL,SACC,CAAC,QAAQ,cAAc,WACvB,CAAC,KAAK,cAAc,iBAEpB;AAGD,SAAI;MACH,MAAM,cAAc,MAAM,IAAI,QAAQ,MAAM;OAC3C,OAAO;OACP,OAAO,CACN;QACC,OAAO;QACP,OAAO,KAAK,aAAa;QACzB,CACD;OACD,CAAC;MAGF,MAAM,aADQ,MAAM,SAAS,QAAQ,aAAa,EAC1B,QAAQ,MAAM,EAAE,YAAY;AACpD,UAAI,UAAU,WAAW,EAAG;MAE5B,MAAM,gBAAgB,IAAI,IACzB,UAAU,KAAK,MAAM,EAAE,KAAK,aAAa,CAAC,CAC1C;MACD,MAAM,QAAQ,MAAM,IAAI,QAAQ,QAAsB;OACrD,OAAO;OACP,OAAO,CACN;QACC,OAAO;QACP,OAAO,KAAK,aAAa;QACzB,CACD;OACD,CAAC;AACF,UACC,CAAC,OAAO,wBACR,CAAC,mBAAmB,MAAM,IAC1B,CAAC,cAAc,IAAI,MAAM,KAAK,CAE9B;MAMD,MAAM,EAAE,gBAHK,UAAU,MACrB,MAAM,EAAE,KAAK,aAAa,KAAK,MAAM,KACtC;MAGD,MAAM,YAAY,MAAM,OAAO,cAAc,SAC5C,MAAM,qBACN;AACD,UAAI,CAAC,mBAAmB,UAAU,CAAE;MAEpC,MAAM,WAAW,UAAU,MAAM,KAAK,MACpC,SAAS,KAAK,MAAM,OAAO,YAC5B;AAGD,UAAI,UAAU,aAAa,YAAa;MAExC,MAAM,QAAQ,WACX,CAAC;OAAE,IAAI,SAAS;OAAI,UAAU;OAAa,CAAC,GAC5C,CAAC;OAAE,OAAO;OAAa,UAAU;OAAa,CAAC;AAElD,YAAM,OAAO,cAAc,OAAO,UAAU,IAAI;OAC/C;OACA,oBAAoB;OACpB,CAAC;AACF,YAAM,IAAI,QAAQ,OAAO;OACxB,OAAO;OACP,QAAQ,EAAE,OAAO,aAAa;OAC9B,OAAO,CAAC;QAAE,OAAO;QAAM,OAAO,MAAM;QAAI,CAAC;OACzC,CAAC;cACM,GAAQ;AAChB,UAAI,OAAO,MAAM,mCAAmC,EAAE,UAAU;;;AAIlE,cAAU,QAAQ,oBAAoB;KACrC,GAAG;KACH,yBAAyB,cAAc,0BACpC,OAAO,SAAS;AAChB,YAAM,cAAc,wBAAyB,KAAK;AAClD,YAAM,qBAAqB,KAAK;SAEhC;KACH,0BAA0B,cAAc,2BACrC,OAAO,SAAS;AAChB,YAAM,cAAc,yBAA0B,KAAK;AACnD,YAAM,sBAAsB,KAAK;SAEjC;KACH,gBAAgB,cAAc,iBAC3B,OAAO,SAAS;AAChB,YAAM,cAAc,eAAgB,KAAK;AACzC,YAAM,2BAA2B,KAAK;SAEtC;KACH,mBAAmB,cAAc,oBAC9B,OAAO,SAAS;AAChB,YAAM,cAAc,kBAAmB,KAAK;AAC5C,YAAM,2BAA2B,KAAK;SAEtC;KACH,uBAAuB,cAAc,wBAClC,OAAO,SAAS;AAChB,YAAM,cAAc,sBAAuB,KAAK;AAChD,YAAM,2BAA2B,KAAK;SAEtC;KACH;;AAGF,UAAO,EACN,SAAS,EACR,eAAe,EACd,MAAM;IACL,QAAQ,EACP,MAAM,MAAM,MAAmC,KAAK;AACnD,SACC,CAAC,OACD,CAAC,QAAQ,0BACT,KAAK,iBAEL;AAGD,SAAI;MAEH,IAAI;AACJ,UAAI;AAKH,yBAJe,MAAM,OAAO,UAAU,OAAO;QAC5C,OAAO,UAAU,wBAAwB,KAAK,MAAM,CAAC,mBAAmB,iBAAiB,KAAK,aAAa;QAC3G,OAAO;QACP,CAAC,EACsB,KAAK;cACtB;AAEP,WAAI,QAAQ,OAAO,KAClB,iEACA;AACD,kBAAW,MAAM,YAAY,OAAO,UAAU,KAAK;QAClD,OAAO,KAAK;QACZ,OAAO;QACP,CAAC,CACD,KACC,SAAS,WACR,iBAAiB,KAAK,kBACjB,gBACL;AACD,yBAAiB;AACjB;;;AAMH,UAAI,gBAAgB;AACnB,aAAM,IAAI,QAAQ,gBAAgB,WAAW,KAAK,IAAI,EACrD,kBAAkB,eAAe,IACjC,CAAC;AACF,aAAM,QAAQ,mBACb;QACC;QACA,MAAM;SACL,GAAG;SACH,kBAAkB,eAAe;SACjC;QACD,EACD,IACA;AACD,WAAI,QAAQ,OAAO,KAClB,mCAAmC,eAAe,GAAG,WAAW,KAAK,KACrE;AACD;;MAID,IAAI,oBACH,EAAE;AACH,UAAI,QAAQ,wBACX,qBAAoB,MAAM,QAAQ,wBACjC,MACA,IACA;MAGF,MAAM,SAAS,KACd;OACC,OAAO,KAAK;OACZ,MAAM,KAAK;OACX,UAAU,iBAAiB,IAC1B;QACC,QAAQ,KAAK;QACb,cAAc;QACd,EACD,mBAAmB,SACnB;OACD,EACD,kBACA;AACD,uBAAiB,MAAM,OAAO,UAAU,OAAO,OAAO;AACtD,YAAM,IAAI,QAAQ,gBAAgB,WAAW,KAAK,IAAI,EACrD,kBAAkB,eAAe,IACjC,CAAC;AACF,YAAM,QAAQ,mBACb;OACC;OACA,MAAM;QACL,GAAG;QACH,kBAAkB,eAAe;QACjC;OACD,EACD,IACA;AACD,UAAI,QAAQ,OAAO,KAClB,+BAA+B,eAAe,GAAG,YAAY,KAAK,KAClE;cACO,GAAQ;AAChB,UAAI,QAAQ,OAAO,MAClB,6CAA6C,EAAE,WAC/C,EACA;;OAGH;IACD,QAAQ,EACP,MAAM,MAAM,MAAmC,KAAK;AACnD,SACC,CAAC,OACD,CAAC,KAAK,iBAEN;AAED,SAAI;MAIH,MAAM,iBAAiB,MAAM,OAAO,UAAU,SAC7C,KAAK,iBACL;AAGD,UAAI,eAAe,SAAS;AAC3B,WAAI,QAAQ,OAAO,KAClB,mBAAmB,KAAK,iBAAiB,mCACzC;AACD;;AAID,UAAI,eAAe,UAAU,KAAK,OAAO;AACxC,aAAM,OAAO,UAAU,OAAO,KAAK,kBAAkB,EACpD,OAAO,KAAK,OACZ,CAAC;AACF,WAAI,QAAQ,OAAO,KAClB,sCAAsC,eAAe,MAAM,MAAM,KAAK,QACtE;;cAEM,GAAQ;AAGhB,UAAI,QAAQ,OAAO,MAClB,4CAA4C,EAAE,WAC9C,EACA;;OAGH;IACD,EACD,EACD,EACD;;EAEF,QAAQ,UAAU,QAAQ;EACjB;EACT,cAAc;EACd"}
1
+ {"version":3,"file":"index.mjs","names":["APIError","APIError"],"sources":["../src/metadata.ts","../src/utils.ts","../src/hooks.ts","../src/middleware.ts","../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { defu } from \"defu\";\nimport type Stripe from \"stripe\";\n\n/**\n * Internal metadata fields for Stripe Customer.\n */\ntype CustomerInternalMetadata =\n\t| { customerType: \"user\"; userId: string }\n\t| { customerType: \"organization\"; organizationId: string };\n\n/**\n * Internal metadata fields for Stripe Subscription/Checkout.\n */\ntype SubscriptionInternalMetadata = {\n\tuserId: string;\n\tsubscriptionId: string;\n\treferenceId: string;\n};\n\n/**\n * Customer metadata - set internal fields and extract typed fields.\n */\nexport const customerMetadata = {\n\t/**\n\t * Internal metadata keys for type-safe access.\n\t */\n\tkeys: {\n\t\tuserId: \"userId\",\n\t\torganizationId: \"organizationId\",\n\t\tcustomerType: \"customerType\",\n\t} as const,\n\n\t/**\n\t * Create metadata with internal fields that cannot be overridden by user metadata.\n\t * Uses `defu` which prioritizes the first argument.\n\t */\n\tset(\n\t\tinternalFields: CustomerInternalMetadata,\n\t\t...userMetadata: (Stripe.Emptyable<Stripe.MetadataParam> | undefined)[]\n\t): Stripe.MetadataParam {\n\t\treturn defu(internalFields, ...userMetadata.filter(Boolean));\n\t},\n\n\t/**\n\t * Extract internal fields from Stripe metadata.\n\t * Provides type-safe access to internal metadata keys.\n\t */\n\tget(metadata: Stripe.Metadata | null | undefined) {\n\t\treturn {\n\t\t\tuserId: metadata?.userId,\n\t\t\torganizationId: metadata?.organizationId,\n\t\t\tcustomerType: metadata?.customerType as\n\t\t\t\t| CustomerInternalMetadata[\"customerType\"]\n\t\t\t\t| undefined,\n\t\t};\n\t},\n};\n\n/**\n * Subscription/Checkout metadata - set internal fields and extract typed fields.\n */\nexport const subscriptionMetadata = {\n\t/**\n\t * Internal metadata keys for type-safe access.\n\t */\n\tkeys: {\n\t\tuserId: \"userId\",\n\t\tsubscriptionId: \"subscriptionId\",\n\t\treferenceId: \"referenceId\",\n\t} as const,\n\n\t/**\n\t * Create metadata with internal fields that cannot be overridden by user metadata.\n\t * Uses `defu` which prioritizes the first argument.\n\t */\n\tset(\n\t\tinternalFields: SubscriptionInternalMetadata,\n\t\t...userMetadata: (Stripe.Emptyable<Stripe.MetadataParam> | undefined)[]\n\t): Stripe.MetadataParam {\n\t\treturn defu(internalFields, ...userMetadata.filter(Boolean));\n\t},\n\n\t/**\n\t * Extract internal fields from Stripe metadata.\n\t * Provides type-safe access to internal metadata keys.\n\t */\n\tget(metadata: Stripe.Metadata | null | undefined) {\n\t\treturn {\n\t\t\tuserId: metadata?.userId,\n\t\t\tsubscriptionId: metadata?.subscriptionId,\n\t\t\treferenceId: metadata?.referenceId,\n\t\t};\n\t},\n};\n","import type Stripe from \"stripe\";\nimport type { StripeOptions, StripePlan, Subscription } from \"./types\";\n\nexport async function getPlans(\n\tsubscriptionOptions: StripeOptions[\"subscription\"],\n) {\n\tif (subscriptionOptions?.enabled) {\n\t\treturn typeof subscriptionOptions.plans === \"function\"\n\t\t\t? await subscriptionOptions.plans()\n\t\t\t: subscriptionOptions.plans;\n\t}\n\tthrow new Error(\"Subscriptions are not enabled in the Stripe options.\");\n}\n\nexport async function getPlanByName(options: StripeOptions, name: string) {\n\treturn await getPlans(options.subscription).then((res) =>\n\t\tres?.find((plan) => plan.name.toLowerCase() === name.toLowerCase()),\n\t);\n}\n\n/**\n * Checks if a subscription is in an available state (active or trialing)\n */\nexport function isActiveOrTrialing(\n\tsub: Subscription | Stripe.Subscription,\n): boolean {\n\treturn sub.status === \"active\" || sub.status === \"trialing\";\n}\n\n/**\n * Check if a subscription is scheduled to be canceled (DB subscription object)\n */\nexport function isPendingCancel(sub: Subscription): boolean {\n\treturn !!(sub.cancelAtPeriodEnd || sub.cancelAt);\n}\n\n/**\n * Check if a Stripe subscription is scheduled to be canceled (Stripe API response)\n */\nexport function isStripePendingCancel(stripeSub: Stripe.Subscription): boolean {\n\treturn !!(stripeSub.cancel_at_period_end || stripeSub.cancel_at);\n}\n\n/**\n * Escapes a value for use in Stripe search queries.\n * Stripe search query uses double quotes for string values,\n * and double quotes within the value need to be escaped with backslash.\n *\n * @see https://docs.stripe.com/search#search-query-language\n */\nexport function escapeStripeSearchValue(value: string): string {\n\treturn value.replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Resolve the quantity for a subscription by checking the seat item first,\n * then falling back to the plan item's quantity.\n */\nexport function resolveQuantity(\n\titems: Stripe.SubscriptionItem[],\n\tplanItem: Stripe.SubscriptionItem,\n\tseatPriceId?: string,\n): number {\n\tif (seatPriceId) {\n\t\tconst seatItem = items.find((item) => item.price.id === seatPriceId);\n\t\tif (seatItem) return seatItem.quantity ?? 1;\n\t}\n\treturn planItem.quantity ?? 1;\n}\n\n/**\n * Resolve the plan-matching subscription item and its plan config\n * from a (possibly multi-item) Stripe subscription.\n *\n * - Iterates items to find one whose price matches a configured plan.\n * - For single-item subscriptions, returns the item even without a plan match.\n */\nexport async function resolvePlanItem(\n\toptions: StripeOptions,\n\titems: Stripe.SubscriptionItem[],\n): Promise<\n\t{ item: Stripe.SubscriptionItem; plan: StripePlan | undefined } | undefined\n> {\n\tconst first = items[0];\n\tif (!first) return undefined;\n\tconst plans = await getPlans(options.subscription);\n\tfor (const item of items) {\n\t\tconst plan = plans?.find(\n\t\t\t(p) =>\n\t\t\t\tp.priceId === item.price.id ||\n\t\t\t\tp.annualDiscountPriceId === item.price.id ||\n\t\t\t\t(item.price.lookup_key &&\n\t\t\t\t\t(p.lookupKey === item.price.lookup_key ||\n\t\t\t\t\t\tp.annualDiscountLookupKey === item.price.lookup_key)),\n\t\t);\n\t\tif (plan) return { item, plan };\n\t}\n\treturn items.length === 1 ? { item: first, plan: undefined } : undefined;\n}\n","import type { GenericEndpointContext } from \"@better-auth/core\";\nimport type { User } from \"@better-auth/core/db\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport type Stripe from \"stripe\";\nimport { subscriptionMetadata } from \"./metadata\";\nimport type { CustomerType, StripeOptions, Subscription } from \"./types\";\nimport {\n\tisActiveOrTrialing,\n\tisPendingCancel,\n\tisStripePendingCancel,\n\tresolvePlanItem,\n\tresolveQuantity,\n} from \"./utils\";\n\n/**\n * Find organization or user by stripeCustomerId.\n * @internal\n */\nasync function findReferenceByStripeCustomerId(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tstripeCustomerId: string,\n): Promise<{ customerType: CustomerType; referenceId: string } | null> {\n\tif (options.organization?.enabled) {\n\t\tconst org = await ctx.context.adapter.findOne<Organization>({\n\t\t\tmodel: \"organization\",\n\t\t\twhere: [{ field: \"stripeCustomerId\", value: stripeCustomerId }],\n\t\t});\n\t\tif (org) return { customerType: \"organization\", referenceId: org.id };\n\t}\n\n\tconst user = await ctx.context.adapter.findOne<User>({\n\t\tmodel: \"user\",\n\t\twhere: [{ field: \"stripeCustomerId\", value: stripeCustomerId }],\n\t});\n\tif (user) return { customerType: \"user\", referenceId: user.id };\n\n\treturn null;\n}\n\nexport async function onCheckoutSessionCompleted(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\ttry {\n\t\tconst client = options.stripeClient;\n\t\tconst checkoutSession = event.data.object as Stripe.Checkout.Session;\n\t\tif (checkoutSession.mode === \"setup\" || !options.subscription?.enabled) {\n\t\t\treturn;\n\t\t}\n\t\tconst subscription = await client.subscriptions.retrieve(\n\t\t\tcheckoutSession.subscription as string,\n\t\t);\n\t\tconst resolved = await resolvePlanItem(options, subscription.items.data);\n\t\tif (!resolved) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscription.id} has no items matching a configured plan`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { item: subscriptionItem, plan } = resolved;\n\t\tif (plan) {\n\t\t\tconst checkoutMeta = subscriptionMetadata.get(checkoutSession?.metadata);\n\t\t\tconst referenceId =\n\t\t\t\tcheckoutSession?.client_reference_id || checkoutMeta.referenceId;\n\t\t\tconst { subscriptionId } = checkoutMeta;\n\t\t\tconst seats = resolveQuantity(\n\t\t\t\tsubscription.items.data,\n\t\t\t\tsubscriptionItem,\n\t\t\t\tplan.seatPriceId,\n\t\t\t);\n\t\t\tif (referenceId && subscriptionId) {\n\t\t\t\tconst trial =\n\t\t\t\t\tsubscription.trial_start && subscription.trial_end\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\ttrialStart: new Date(subscription.trial_start * 1000),\n\t\t\t\t\t\t\t\ttrialEnd: new Date(subscription.trial_end * 1000),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {};\n\n\t\t\t\tlet dbSubscription = await ctx.context.adapter.update<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\t...trial,\n\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\tstatus: subscription.status,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\tperiodStart: new Date(subscriptionItem.current_period_start * 1000),\n\t\t\t\t\t\tperiodEnd: new Date(subscriptionItem.current_period_end * 1000),\n\t\t\t\t\t\tstripeSubscriptionId: checkoutSession.subscription as string,\n\t\t\t\t\t\tcancelAtPeriodEnd: subscription.cancel_at_period_end,\n\t\t\t\t\t\tcancelAt: subscription.cancel_at\n\t\t\t\t\t\t\t? new Date(subscription.cancel_at * 1000)\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tcanceledAt: subscription.canceled_at\n\t\t\t\t\t\t\t? new Date(subscription.canceled_at * 1000)\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tendedAt: subscription.ended_at\n\t\t\t\t\t\t\t? new Date(subscription.ended_at * 1000)\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tseats: seats,\n\t\t\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t\t\t},\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\tif (trial.trialStart && plan.freeTrial?.onTrialStart) {\n\t\t\t\t\tawait plan.freeTrial.onTrialStart(dbSubscription as Subscription);\n\t\t\t\t}\n\n\t\t\t\tif (!dbSubscription) {\n\t\t\t\t\tdbSubscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tawait options.subscription?.onSubscriptionComplete?.(\n\t\t\t\t\t{\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\tsubscription: dbSubscription as Subscription,\n\t\t\t\t\t\tstripeSubscription: subscription,\n\t\t\t\t\t\tplan,\n\t\t\t\t\t},\n\t\t\t\t\tctx,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t} catch (e: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${e.message}`);\n\t}\n}\n\nexport async function onSubscriptionCreated(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\ttry {\n\t\tif (!options.subscription?.enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst subscriptionCreated = event.data.object as Stripe.Subscription;\n\t\tconst stripeCustomerId = subscriptionCreated.customer?.toString();\n\t\tif (!stripeCustomerId) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: customer.subscription.created event received without customer ID`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if subscription already exists in database\n\t\tconst { subscriptionId } = subscriptionMetadata.get(\n\t\t\tsubscriptionCreated.metadata,\n\t\t);\n\t\tconst existingSubscription =\n\t\t\tawait ctx.context.adapter.findOne<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: subscriptionId\n\t\t\t\t\t? [{ field: \"id\", value: subscriptionId }]\n\t\t\t\t\t: [{ field: \"stripeSubscriptionId\", value: subscriptionCreated.id }], // Probably won't match since it's not set yet\n\t\t\t});\n\t\tif (existingSubscription) {\n\t\t\tctx.context.logger.info(\n\t\t\t\t`Stripe webhook: Subscription already exists in database (id: ${existingSubscription.id}), skipping creation`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Find reference\n\t\tconst reference = await findReferenceByStripeCustomerId(\n\t\t\tctx,\n\t\t\toptions,\n\t\t\tstripeCustomerId,\n\t\t);\n\t\tif (!reference) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: No user or organization found with stripeCustomerId: ${stripeCustomerId}`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst { referenceId, customerType } = reference;\n\n\t\tconst resolved = await resolvePlanItem(\n\t\t\toptions,\n\t\t\tsubscriptionCreated.items.data,\n\t\t);\n\t\tif (!resolved) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscriptionCreated.id} has no items matching a configured plan`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { item: subscriptionItem, plan } = resolved;\n\t\tif (!plan) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: No matching plan found for priceId: ${subscriptionItem.price.id}`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst seats = resolveQuantity(\n\t\t\tsubscriptionCreated.items.data,\n\t\t\tsubscriptionItem,\n\t\t\tplan.seatPriceId,\n\t\t);\n\t\tconst periodStart = new Date(subscriptionItem.current_period_start * 1000);\n\t\tconst periodEnd = new Date(subscriptionItem.current_period_end * 1000);\n\n\t\tconst trial =\n\t\t\tsubscriptionCreated.trial_start && subscriptionCreated.trial_end\n\t\t\t\t? {\n\t\t\t\t\t\ttrialStart: new Date(subscriptionCreated.trial_start * 1000),\n\t\t\t\t\t\ttrialEnd: new Date(subscriptionCreated.trial_end * 1000),\n\t\t\t\t\t}\n\t\t\t\t: {};\n\n\t\t// Create the subscription in the database\n\t\tconst newSubscription = await ctx.context.adapter.create<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\tdata: {\n\t\t\t\t...trial,\n\t\t\t\t...(plan.limits ? { limits: plan.limits } : {}),\n\t\t\t\treferenceId,\n\t\t\t\tstripeCustomerId,\n\t\t\t\tstripeSubscriptionId: subscriptionCreated.id,\n\t\t\t\tstatus: subscriptionCreated.status,\n\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\tperiodStart,\n\t\t\t\tperiodEnd,\n\t\t\t\tseats,\n\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t},\n\t\t});\n\n\t\tctx.context.logger.info(\n\t\t\t`Stripe webhook: Created subscription ${subscriptionCreated.id} for ${customerType} ${referenceId} from dashboard`,\n\t\t);\n\n\t\tawait options.subscription.onSubscriptionCreated?.({\n\t\t\tevent,\n\t\t\tsubscription: newSubscription,\n\t\t\tstripeSubscription: subscriptionCreated,\n\t\t\tplan,\n\t\t});\n\t} catch (error: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);\n\t}\n}\n\nexport async function onSubscriptionUpdated(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\ttry {\n\t\tif (!options.subscription?.enabled) {\n\t\t\treturn;\n\t\t}\n\t\tconst subscriptionUpdated = event.data.object as Stripe.Subscription;\n\t\tconst resolved = await resolvePlanItem(\n\t\t\toptions,\n\t\t\tsubscriptionUpdated.items.data,\n\t\t);\n\t\tif (!resolved) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscriptionUpdated.id} has no items matching a configured plan`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { item: subscriptionItem, plan } = resolved;\n\n\t\tconst { subscriptionId } = subscriptionMetadata.get(\n\t\t\tsubscriptionUpdated.metadata,\n\t\t);\n\t\tconst customerId = subscriptionUpdated.customer?.toString();\n\t\tlet subscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\twhere: subscriptionId\n\t\t\t\t? [{ field: \"id\", value: subscriptionId }]\n\t\t\t\t: [{ field: \"stripeSubscriptionId\", value: subscriptionUpdated.id }],\n\t\t});\n\t\tif (!subscription) {\n\t\t\tconst subs = await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [{ field: \"stripeCustomerId\", value: customerId }],\n\t\t\t});\n\t\t\tif (subs.length > 1) {\n\t\t\t\tconst activeSub = subs.find((sub: Subscription) =>\n\t\t\t\t\tisActiveOrTrialing(sub),\n\t\t\t\t);\n\t\t\t\tif (!activeSub) {\n\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t`Stripe webhook error: Multiple subscriptions found for customerId: ${customerId} and no active subscription is found`,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsubscription = activeSub;\n\t\t\t} else {\n\t\t\t\tsubscription = subs[0]!;\n\t\t\t}\n\t\t}\n\n\t\tconst seats = plan\n\t\t\t? resolveQuantity(\n\t\t\t\t\tsubscriptionUpdated.items.data,\n\t\t\t\t\tsubscriptionItem,\n\t\t\t\t\tplan.seatPriceId,\n\t\t\t\t)\n\t\t\t: subscriptionItem.quantity;\n\n\t\tconst trial =\n\t\t\tsubscriptionUpdated.trial_start && subscriptionUpdated.trial_end\n\t\t\t\t? {\n\t\t\t\t\t\ttrialStart: new Date(subscriptionUpdated.trial_start * 1000),\n\t\t\t\t\t\ttrialEnd: new Date(subscriptionUpdated.trial_end * 1000),\n\t\t\t\t\t}\n\t\t\t\t: {};\n\n\t\tconst updatedSubscription = await ctx.context.adapter.update<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\tupdate: {\n\t\t\t\t...trial,\n\t\t\t\t...(plan\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\t\tlimits: plan.limits,\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\tstatus: subscriptionUpdated.status,\n\t\t\t\tperiodStart: new Date(subscriptionItem.current_period_start * 1000),\n\t\t\t\tperiodEnd: new Date(subscriptionItem.current_period_end * 1000),\n\t\t\t\tcancelAtPeriodEnd: subscriptionUpdated.cancel_at_period_end,\n\t\t\t\tcancelAt: subscriptionUpdated.cancel_at\n\t\t\t\t\t? new Date(subscriptionUpdated.cancel_at * 1000)\n\t\t\t\t\t: null,\n\t\t\t\tcanceledAt: subscriptionUpdated.canceled_at\n\t\t\t\t\t? new Date(subscriptionUpdated.canceled_at * 1000)\n\t\t\t\t\t: null,\n\t\t\t\tendedAt: subscriptionUpdated.ended_at\n\t\t\t\t\t? new Date(subscriptionUpdated.ended_at * 1000)\n\t\t\t\t\t: null,\n\t\t\t\tseats,\n\t\t\t\tstripeSubscriptionId: subscriptionUpdated.id,\n\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t\tstripeScheduleId: subscriptionUpdated.schedule\n\t\t\t\t\t? typeof subscriptionUpdated.schedule === \"string\"\n\t\t\t\t\t\t? subscriptionUpdated.schedule\n\t\t\t\t\t\t: subscriptionUpdated.schedule.id\n\t\t\t\t\t: null,\n\t\t\t},\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"id\",\n\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tconst isNewCancellation =\n\t\t\tsubscriptionUpdated.status === \"active\" &&\n\t\t\tisStripePendingCancel(subscriptionUpdated) &&\n\t\t\t!isPendingCancel(subscription);\n\t\tif (isNewCancellation) {\n\t\t\tawait options.subscription.onSubscriptionCancel?.({\n\t\t\t\tsubscription,\n\t\t\t\tcancellationDetails:\n\t\t\t\t\tsubscriptionUpdated.cancellation_details || undefined,\n\t\t\t\tstripeSubscription: subscriptionUpdated,\n\t\t\t\tevent,\n\t\t\t});\n\t\t}\n\t\tawait options.subscription.onSubscriptionUpdate?.({\n\t\t\tevent,\n\t\t\tsubscription: updatedSubscription || subscription,\n\t\t});\n\t\tif (plan) {\n\t\t\tif (\n\t\t\t\tsubscriptionUpdated.status === \"active\" &&\n\t\t\t\tsubscription.status === \"trialing\" &&\n\t\t\t\tplan.freeTrial?.onTrialEnd\n\t\t\t) {\n\t\t\t\tawait plan.freeTrial.onTrialEnd({ subscription }, ctx);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tsubscriptionUpdated.status === \"incomplete_expired\" &&\n\t\t\t\tsubscription.status === \"trialing\" &&\n\t\t\t\tplan.freeTrial?.onTrialExpired\n\t\t\t) {\n\t\t\t\tawait plan.freeTrial.onTrialExpired(subscription, ctx);\n\t\t\t}\n\t\t}\n\t} catch (error: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);\n\t}\n}\n\nexport async function onSubscriptionDeleted(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\tif (!options.subscription?.enabled) {\n\t\treturn;\n\t}\n\ttry {\n\t\tconst subscriptionDeleted = event.data.object as Stripe.Subscription;\n\t\tconst subscriptionId = subscriptionDeleted.id;\n\t\tconst subscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tif (subscription) {\n\t\t\tconst trial =\n\t\t\t\tsubscriptionDeleted.trial_start && subscriptionDeleted.trial_end\n\t\t\t\t\t? {\n\t\t\t\t\t\t\ttrialStart: new Date(subscriptionDeleted.trial_start * 1000),\n\t\t\t\t\t\t\ttrialEnd: new Date(subscriptionDeleted.trial_end * 1000),\n\t\t\t\t\t\t}\n\t\t\t\t\t: {};\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tupdate: {\n\t\t\t\t\t...trial,\n\t\t\t\t\tstatus: \"canceled\",\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\tcancelAtPeriodEnd: subscriptionDeleted.cancel_at_period_end,\n\t\t\t\t\tcancelAt: subscriptionDeleted.cancel_at\n\t\t\t\t\t\t? new Date(subscriptionDeleted.cancel_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tcanceledAt: subscriptionDeleted.canceled_at\n\t\t\t\t\t\t? new Date(subscriptionDeleted.canceled_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tendedAt: subscriptionDeleted.ended_at\n\t\t\t\t\t\t? new Date(subscriptionDeleted.ended_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tstripeScheduleId: null,\n\t\t\t\t},\n\t\t\t});\n\t\t\tawait options.subscription.onSubscriptionDeleted?.({\n\t\t\t\tevent,\n\t\t\t\tstripeSubscription: subscriptionDeleted,\n\t\t\t\tsubscription,\n\t\t\t});\n\t\t} else {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook error: Subscription not found for subscriptionId: ${subscriptionId}`,\n\t\t\t);\n\t\t}\n\t} catch (error: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);\n\t}\n}\n","import { createAuthMiddleware } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport { sessionMiddleware } from \"better-auth/api\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport type {\n\tAuthorizeReferenceAction,\n\tCustomerType,\n\tStripeCtxSession,\n\tSubscriptionOptions,\n} from \"./types\";\n\nexport const stripeSessionMiddleware = createAuthMiddleware(\n\t{\n\t\tuse: [sessionMiddleware],\n\t},\n\tasync (ctx) => {\n\t\tconst session = ctx.context.session as StripeCtxSession;\n\t\treturn {\n\t\t\tsession,\n\t\t};\n\t},\n);\n\nexport const referenceMiddleware = (\n\tsubscriptionOptions: SubscriptionOptions,\n\taction: AuthorizeReferenceAction,\n) =>\n\tcreateAuthMiddleware(async (ctx) => {\n\t\tconst ctxSession = ctx.context.session as StripeCtxSession;\n\t\tif (!ctxSession) {\n\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t}\n\n\t\tconst customerType: CustomerType =\n\t\t\tctx.body?.customerType || ctx.query?.customerType;\n\t\tconst explicitReferenceId = ctx.body?.referenceId || ctx.query?.referenceId;\n\n\t\tif (customerType === \"organization\") {\n\t\t\t// Organization subscriptions always require authorizeReference\n\t\t\tif (!subscriptionOptions.authorizeReference) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t`Organization subscriptions require authorizeReference to be defined in your stripe plugin config.`,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.AUTHORIZE_REFERENCE_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst referenceId =\n\t\t\t\texplicitReferenceId || ctxSession.session.activeOrganizationId;\n\t\t\tif (!referenceId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_REFERENCE_ID_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isAuthorized = await subscriptionOptions.authorizeReference(\n\t\t\t\t{\n\t\t\t\t\tuser: ctxSession.user,\n\t\t\t\t\tsession: ctxSession.session,\n\t\t\t\t\treferenceId,\n\t\t\t\t\taction,\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t);\n\t\t\tif (!isAuthorized) {\n\t\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// User subscriptions - pass if no explicit referenceId\n\t\tif (!explicitReferenceId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass if referenceId is user id\n\t\tif (explicitReferenceId === ctxSession.user.id) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!subscriptionOptions.authorizeReference) {\n\t\t\tctx.context.logger.error(\n\t\t\t\t`Passing referenceId into a subscription action isn't allowed if subscription.authorizeReference isn't defined in your stripe plugin config.`,\n\t\t\t);\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.REFERENCE_ID_NOT_ALLOWED,\n\t\t\t);\n\t\t}\n\t\tconst isAuthorized = await subscriptionOptions.authorizeReference(\n\t\t\t{\n\t\t\t\tuser: ctxSession.user,\n\t\t\t\tsession: ctxSession.session,\n\t\t\t\treferenceId: explicitReferenceId,\n\t\t\t\taction,\n\t\t\t},\n\t\t\tctx,\n\t\t);\n\t\tif (!isAuthorized) {\n\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t}\n\t});\n","import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport type { GenericEndpointContext, User } from \"better-auth\";\nimport { HIDE_METADATA } from \"better-auth\";\nimport { getSessionFromCtx, originCheck } from \"better-auth/api\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport { defu } from \"defu\";\nimport type Stripe from \"stripe\";\nimport type { Stripe as StripeType } from \"stripe\";\nimport * as z from \"zod\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport {\n\tonCheckoutSessionCompleted,\n\tonSubscriptionCreated,\n\tonSubscriptionDeleted,\n\tonSubscriptionUpdated,\n} from \"./hooks\";\nimport { customerMetadata, subscriptionMetadata } from \"./metadata\";\nimport { referenceMiddleware, stripeSessionMiddleware } from \"./middleware\";\nimport type {\n\tCustomerType,\n\tStripeCtxSession,\n\tStripeOptions,\n\tSubscription,\n\tSubscriptionOptions,\n\tWithStripeCustomerId,\n} from \"./types\";\nimport {\n\tescapeStripeSearchValue,\n\tgetPlanByName,\n\tgetPlans,\n\tisActiveOrTrialing,\n\tisPendingCancel,\n\tresolvePlanItem,\n\tresolveQuantity,\n} from \"./utils\";\n\n/**\n * Converts a relative URL to an absolute URL using baseURL.\n * @internal\n */\nfunction getUrl(ctx: GenericEndpointContext, url: string) {\n\tif (/^[a-zA-Z][a-zA-Z0-9+\\-.]*:/.test(url)) {\n\t\treturn url;\n\t}\n\treturn `${ctx.context.options.baseURL}${\n\t\turl.startsWith(\"/\") ? url : `/${url}`\n\t}`;\n}\n\n/**\n * Resolves a Stripe price ID from a lookup key.\n * @internal\n */\nasync function resolvePriceIdFromLookupKey(\n\tstripeClient: Stripe,\n\tlookupKey: string,\n): Promise<string | undefined> {\n\tif (!lookupKey) return undefined;\n\tconst prices = await stripeClient.prices.list({\n\t\tlookup_keys: [lookupKey],\n\t\tactive: true,\n\t\tlimit: 1,\n\t});\n\treturn prices.data[0]?.id;\n}\n\n/**\n * Determines the reference ID based on customer type.\n * - `user` (default): uses userId\n * - `organization`: uses activeOrganizationId from session\n * @internal\n */\nfunction getReferenceId(\n\tctxSession: StripeCtxSession,\n\tcustomerType: CustomerType | undefined,\n\toptions: StripeOptions,\n): string {\n\tconst { user, session } = ctxSession;\n\tconst type = customerType || \"user\";\n\n\tif (type === \"organization\") {\n\t\tif (!options.organization?.enabled) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_SUBSCRIPTION_NOT_ENABLED,\n\t\t\t);\n\t\t}\n\n\t\tif (!session.activeOrganizationId) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t);\n\t\t}\n\t\treturn session.activeOrganizationId;\n\t}\n\n\treturn user.id;\n}\n\nconst upgradeSubscriptionBodySchema = z.object({\n\t/**\n\t * The name of the plan to subscribe\n\t */\n\tplan: z.string().meta({\n\t\tdescription: 'The name of the plan to upgrade to. Eg: \"pro\"',\n\t}),\n\t/**\n\t * If annual plan should be applied.\n\t */\n\tannual: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription: \"Whether to upgrade to an annual plan. Eg: true\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Reference ID for the subscription based on customerType:\n\t * - `user`: defaults to `user.id`\n\t * - `organization`: defaults to `session.activeOrganizationId`\n\t */\n\treferenceId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: 'Reference ID for the subscription. Eg: \"org_123\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The Stripe subscription ID to upgrade.\n\t * If provided and not found, it'll throw an error.\n\t */\n\tsubscriptionId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'The Stripe subscription ID to upgrade. Eg: \"sub_1ABC2DEF3GHI4JKL\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription (requires referenceId)\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Additional metadata to store with the subscription.\n\t */\n\tmetadata: z.record(z.string(), z.any()).optional(),\n\t/**\n\t * Number of seats for subscriptions.\n\t */\n\tseats: z\n\t\t.number()\n\t\t.meta({\n\t\t\tdescription: \"Number of seats to upgrade to (if applicable). Eg: 1\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The IETF language tag of the locale Checkout is displayed in.\n\t * If not provided or set to `auto`, the browser's locale is used.\n\t */\n\tlocale: z\n\t\t.custom<StripeType.Checkout.Session.Locale>((localization) => {\n\t\t\treturn typeof localization === \"string\";\n\t\t})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The locale to display Checkout in. Eg: 'en', 'ko'. If not provided or set to `auto`, the browser's locale is used.\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The URL to which Stripe should send customers when payment or setup is complete.\n\t */\n\tsuccessUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Callback URL to redirect back after successful subscription. Eg: \"https://example.com/success\"',\n\t\t})\n\t\t.default(\"/\"),\n\t/**\n\t * If set, checkout shows a back button and customers will be directed here if they cancel payment.\n\t */\n\tcancelUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'If set, checkout shows a back button and customers will be directed here if they cancel payment. Eg: \"https://example.com/pricing\"',\n\t\t})\n\t\t.default(\"/\"),\n\t/**\n\t * The URL to return to from the Billing Portal (used when upgrading existing subscriptions)\n\t */\n\treturnUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'URL to take customers to when they click on the billing portal’s link to return to your website. Eg: \"https://example.com/dashboard\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Schedule the plan change at the end of the current billing period instead of applying immediately.\n\t */\n\tscheduleAtPeriodEnd: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Schedule the plan change at the end of the current billing period instead of applying immediately.\",\n\t\t})\n\t\t.default(false),\n\t/**\n\t * Disable Redirect\n\t */\n\tdisableRedirect: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription: \"Disable redirect after successful subscription. Eg: true\",\n\t\t})\n\t\t.default(false),\n});\n\n/**\n * ### Endpoint\n *\n * POST `/subscription/upgrade`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.upgradeSubscription`\n *\n * **client:**\n * `authClient.subscription.upgrade`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-upgrade)\n */\nexport const upgradeSubscription = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\n\treturn createAuthEndpoint(\n\t\t\"/subscription/upgrade\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: upgradeSubscriptionBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"upgradeSubscription\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"upgrade-subscription\"),\n\t\t\t\toriginCheck((c) => {\n\t\t\t\t\treturn [c.body.successUrl as string, c.body.cancelUrl as string];\n\t\t\t\t}),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { user, session } = ctx.context.session;\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tif (!user.emailVerified && subscriptionOptions.requireEmailVerification) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst plan = await getPlanByName(options, ctx.body.plan);\n\t\t\tif (!plan) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// If subscriptionId is provided, find that specific subscription.\n\t\t\t// Otherwise, active subscription will be resolved by referenceId later.\n\t\t\tconst subscriptionToUpdate = ctx.body.subscriptionId\n\t\t\t\t? await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\t\tvalue: ctx.body.subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t: null;\n\t\t\tif (ctx.body.subscriptionId && !subscriptionToUpdate) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tctx.body.subscriptionId &&\n\t\t\t\tsubscriptionToUpdate &&\n\t\t\t\tsubscriptionToUpdate.referenceId !== referenceId\n\t\t\t) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Determine customer id\n\t\t\tlet customerId: string | undefined;\n\t\t\tif (customerType === \"organization\") {\n\t\t\t\t// Organization subscription - get customer ID from organization\n\t\t\t\tcustomerId = subscriptionToUpdate?.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\tconst org = await ctx.context.adapter.findOne<\n\t\t\t\t\t\tOrganization & WithStripeCustomerId\n\t\t\t\t\t>({\n\t\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tif (!org) {\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tcustomerId = org.stripeCustomerId;\n\n\t\t\t\t\t// If org doesn't have a customer ID, create one\n\t\t\t\t\tif (!customerId) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Find existing organization customer by organizationId metadata\n\t\t\t\t\t\t\tlet stripeCustomer: Stripe.Customer | undefined;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst result = await client.customers.search({\n\t\t\t\t\t\t\t\t\tquery: `metadata[\"${customerMetadata.keys.organizationId}\"]:\"${org.id}\"`,\n\t\t\t\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tstripeCustomer = result.data[0];\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Search API unavailable in some regions, so fall back to paginated list\n\t\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\t\"Stripe customers.search failed, falling back to customers.list\",\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tfor await (const customer of client.customers.list({\n\t\t\t\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t\t\t\t})) {\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\tcustomer.metadata?.[\n\t\t\t\t\t\t\t\t\t\t\tcustomerMetadata.keys.organizationId\n\t\t\t\t\t\t\t\t\t\t] === org.id\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\tstripeCustomer = customer;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!stripeCustomer) {\n\t\t\t\t\t\t\t\t// Get custom params if provided\n\t\t\t\t\t\t\t\tlet extraCreateParams: Partial<StripeType.CustomerCreateParams> =\n\t\t\t\t\t\t\t\t\t{};\n\t\t\t\t\t\t\t\tif (options.organization?.getCustomerCreateParams) {\n\t\t\t\t\t\t\t\t\textraCreateParams =\n\t\t\t\t\t\t\t\t\t\tawait options.organization.getCustomerCreateParams(\n\t\t\t\t\t\t\t\t\t\t\torg,\n\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Create Stripe customer for organization\n\t\t\t\t\t\t\t\t// Email can be set via getCustomerCreateParams or updated in billing portal\n\t\t\t\t\t\t\t\t// Use defu to merge params (first argument takes priority)\n\t\t\t\t\t\t\t\tconst customerParams = defu(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tname: org.name,\n\t\t\t\t\t\t\t\t\t\tmetadata: customerMetadata.set(\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\torganizationId: org.id,\n\t\t\t\t\t\t\t\t\t\t\t\tcustomerType: \"organization\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\textraCreateParams,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tstripeCustomer = await client.customers.create(customerParams);\n\n\t\t\t\t\t\t\t\t// Call onCustomerCreate callback only for newly created customers\n\t\t\t\t\t\t\t\tawait options.organization?.onCustomerCreate?.(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstripeCustomer,\n\t\t\t\t\t\t\t\t\t\torganization: {\n\t\t\t\t\t\t\t\t\t\t\t...org,\n\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\t\tvalue: org.id,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tcustomerId = stripeCustomer.id;\n\t\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t\tctx.context.logger.error(e);\n\t\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_CUSTOMER,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// User subscription - get customer ID from user\n\t\t\t\tcustomerId =\n\t\t\t\t\tsubscriptionToUpdate?.stripeCustomerId || user.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Find existing user Stripe customer by email\n\t\t\t\t\t\tlet stripeCustomer: Stripe.Customer | undefined;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst result = await client.customers.search({\n\t\t\t\t\t\t\t\tquery: `email:\"${escapeStripeSearchValue(user.email)}\" AND -metadata[\"${customerMetadata.keys.customerType}\"]:\"organization\"`,\n\t\t\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tstripeCustomer = result.data[0];\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Search API unavailable in some regions, so fall back to paginated list\n\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\"Stripe customers.search failed, falling back to customers.list\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tfor await (const customer of client.customers.list({\n\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t\t\t})) {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tcustomer.metadata?.[customerMetadata.keys.customerType] !==\n\t\t\t\t\t\t\t\t\t\"organization\"\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tstripeCustomer = customer;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!stripeCustomer) {\n\t\t\t\t\t\t\tstripeCustomer = await client.customers.create({\n\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\t\t\tmetadata: customerMetadata.set(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\t\t\tcustomerType: \"user\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Update local DB with Stripe customer ID\n\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tcustomerId = stripeCustomer.id;\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.context.logger.error(e);\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_CUSTOMER,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst subscriptions = subscriptionToUpdate\n\t\t\t\t? [subscriptionToUpdate]\n\t\t\t\t: await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\n\t\t\tconst activeOrTrialingSubscription = subscriptions.find((sub) =>\n\t\t\t\tisActiveOrTrialing(sub),\n\t\t\t);\n\n\t\t\tconst activeSubscriptions = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: customerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub)));\n\n\t\t\tconst activeSubscription = activeSubscriptions.find((sub) => {\n\t\t\t\t// If we have a specific subscription to update, match by ID\n\t\t\t\tif (\n\t\t\t\t\tsubscriptionToUpdate?.stripeSubscriptionId ||\n\t\t\t\t\tctx.body.subscriptionId\n\t\t\t\t) {\n\t\t\t\t\treturn (\n\t\t\t\t\t\tsub.id === subscriptionToUpdate?.stripeSubscriptionId ||\n\t\t\t\t\t\tsub.id === ctx.body.subscriptionId\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// Only find subscription for the same referenceId to avoid mixing personal and org subscriptions\n\t\t\t\tif (activeOrTrialingSubscription?.stripeSubscriptionId) {\n\t\t\t\t\treturn sub.id === activeOrTrialingSubscription.stripeSubscriptionId;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\n\t\t\t// Get the current price ID from the active Stripe subscription\n\t\t\tconst resolvedPlan = activeSubscription\n\t\t\t\t? await resolvePlanItem(options, activeSubscription.items.data)\n\t\t\t\t: undefined;\n\t\t\tconst planItem = resolvedPlan?.item;\n\t\t\tconst stripeSubscriptionPriceId = planItem?.price.id;\n\n\t\t\t// Also find any incomplete subscription that we can reuse\n\t\t\tconst incompleteSubscription = subscriptions.find(\n\t\t\t\t(sub) => sub.status === \"incomplete\",\n\t\t\t);\n\n\t\t\tconst priceId = ctx.body.annual\n\t\t\t\t? plan.annualDiscountPriceId\n\t\t\t\t: plan.priceId;\n\t\t\tconst lookupKey = ctx.body.annual\n\t\t\t\t? plan.annualDiscountLookupKey\n\t\t\t\t: plan.lookupKey;\n\t\t\tconst resolvedPriceId = lookupKey\n\t\t\t\t? await resolvePriceIdFromLookupKey(client, lookupKey)\n\t\t\t\t: undefined;\n\n\t\t\tconst priceIdToUse = priceId || resolvedPriceId;\n\t\t\tif (!priceIdToUse) {\n\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Price ID not found for the selected plan\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For org subscriptions with seat-based billing, seats are auto-managed.\n\t\t\t// Quantity = memberCount; use Stripe graduated pricing for free tiers.\n\t\t\tconst isAutoManagedSeats = !!(\n\t\t\t\tplan.seatPriceId && customerType === \"organization\"\n\t\t\t);\n\t\t\tlet memberCount = 0;\n\t\t\tif (isAutoManagedSeats) {\n\t\t\t\tmemberCount = await ctx.context.adapter.count({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [{ field: \"organizationId\", value: referenceId }],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst isSamePlan = activeOrTrialingSubscription?.plan === ctx.body.plan;\n\t\t\tconst isSameSeats = isAutoManagedSeats\n\t\t\t\t? true // seats are auto-managed, don't block upgrade\n\t\t\t\t: activeOrTrialingSubscription?.seats === (ctx.body.seats || 1);\n\t\t\tconst isSamePriceId = stripeSubscriptionPriceId === priceIdToUse;\n\t\t\tconst isSubscriptionStillValid =\n\t\t\t\t!activeOrTrialingSubscription?.periodEnd ||\n\t\t\t\tactiveOrTrialingSubscription.periodEnd > new Date();\n\t\t\tconst isSeatOnlyPlan =\n\t\t\t\tisAutoManagedSeats && plan.seatPriceId === plan.priceId;\n\n\t\t\tconst isAlreadySubscribed =\n\t\t\t\tactiveOrTrialingSubscription?.status === \"active\" &&\n\t\t\t\tisSamePlan &&\n\t\t\t\tisSameSeats &&\n\t\t\t\tisSamePriceId &&\n\t\t\t\tisSubscriptionStillValid;\n\t\t\tif (isAlreadySubscribed) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.ALREADY_SUBSCRIBED_PLAN,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (activeSubscription && customerId) {\n\t\t\t\t// Find the corresponding database subscription for this Stripe subscription\n\t\t\t\tlet dbSubscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\tvalue: activeSubscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\t// If no database record exists for this Stripe subscription, update the existing one\n\t\t\t\tif (!dbSubscription && activeOrTrialingSubscription) {\n\t\t\t\t\tawait ctx.context.adapter.update<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\tstripeSubscriptionId: activeSubscription.id,\n\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: activeOrTrialingSubscription.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tdbSubscription = activeOrTrialingSubscription;\n\t\t\t\t}\n\n\t\t\t\tif (!planItem) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"NOT_FOUND\",\n\t\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Release any existing plugin-created subscription schedule.\n\t\t\t\t// Only check when a schedule is attached to avoid unnecessary API calls.\n\t\t\t\tif (activeSubscription.schedule) {\n\t\t\t\t\tconst { data: existingSchedules } =\n\t\t\t\t\t\tawait client.subscriptionSchedules.list({\n\t\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\t});\n\t\t\t\t\tconst existingSchedule = existingSchedules.find(\n\t\t\t\t\t\t(s) =>\n\t\t\t\t\t\t\t(typeof s.subscription === \"string\"\n\t\t\t\t\t\t\t\t? s.subscription\n\t\t\t\t\t\t\t\t: s.subscription?.id) === activeSubscription.id &&\n\t\t\t\t\t\t\ts.status === \"active\",\n\t\t\t\t\t);\n\t\t\t\t\tif (\n\t\t\t\t\t\texistingSchedule &&\n\t\t\t\t\t\texistingSchedule.metadata?.source === \"@better-auth/stripe\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tawait client.subscriptionSchedules.release(existingSchedule.id);\n\t\t\t\t\t\tif (dbSubscription) {\n\t\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\t\tstripeScheduleId: null,\n\t\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\t\tvalue: dbSubscription.id,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst oldPlan = activeOrTrialingSubscription\n\t\t\t\t\t? await getPlanByName(options, activeOrTrialingSubscription.plan)\n\t\t\t\t\t: undefined;\n\n\t\t\t\t// Build a price replacement map:\n\t\t\t\t// oldPriceId -> { newPrice, quantity? }\n\t\t\t\t// This covers base plan, seat, and line item (usage) price changes.\n\t\t\t\tconst priceMap = new Map<\n\t\t\t\t\tstring,\n\t\t\t\t\t{ newPrice: string; quantity?: number }\n\t\t\t\t>();\n\n\t\t\t\tif (isAutoManagedSeats && plan.seatPriceId) {\n\t\t\t\t\tif (\n\t\t\t\t\t\toldPlan?.seatPriceId &&\n\t\t\t\t\t\toldPlan.seatPriceId !== plan.seatPriceId\n\t\t\t\t\t) {\n\t\t\t\t\t\tpriceMap.set(oldPlan.seatPriceId, {\n\t\t\t\t\t\t\tnewPrice: plan.seatPriceId,\n\t\t\t\t\t\t\tquantity: memberCount,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Multiset diff of line item prices.\n\t\t\t\t// old -> -1, new -> +1.\n\t\t\t\t// delta < 0 = remove, delta > 0 = add.\n\t\t\t\tconst lineItemDelta = new Map<string, number>();\n\t\t\t\tfor (const li of oldPlan?.lineItems ?? []) {\n\t\t\t\t\tif (typeof li.price === \"string\")\n\t\t\t\t\t\tlineItemDelta.set(li.price, (lineItemDelta.get(li.price) ?? 0) - 1);\n\t\t\t\t}\n\t\t\t\tfor (const li of plan.lineItems ?? []) {\n\t\t\t\t\tif (typeof li.price === \"string\")\n\t\t\t\t\t\tlineItemDelta.set(li.price, (lineItemDelta.get(li.price) ?? 0) + 1);\n\t\t\t\t}\n\t\t\t\tfor (const [price, delta] of lineItemDelta) {\n\t\t\t\t\tif (delta === 0) lineItemDelta.delete(price);\n\t\t\t\t}\n\n\t\t\t\tlet upgradeUrl: string;\n\t\t\t\tif (ctx.body.scheduleAtPeriodEnd) {\n\t\t\t\t\t// Deferred change:\n\t\t\t\t\t// schedule at billing period end via Subscription Schedules\n\t\t\t\t\tconst schedule = await client.subscriptionSchedules\n\t\t\t\t\t\t.create({\n\t\t\t\t\t\t\tfrom_subscription: activeSubscription.id,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\tconst currentPhase = schedule.phases[0];\n\t\t\t\t\tif (!currentPhase) {\n\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\tmessage: \"Subscription schedule has no phases\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst removeQuota = new Map<string, number>();\n\t\t\t\t\tfor (const [p, d] of lineItemDelta) {\n\t\t\t\t\t\tif (d < 0) removeQuota.set(p, -d);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst newPhaseItems: Array<{\n\t\t\t\t\t\tprice: string;\n\t\t\t\t\t\tquantity?: number;\n\t\t\t\t\t}> = [];\n\t\t\t\t\tfor (const item of currentPhase.items) {\n\t\t\t\t\t\tconst itemPriceId =\n\t\t\t\t\t\t\ttypeof item.price === \"string\" ? item.price : item.price.id;\n\n\t\t\t\t\t\t// Remove items the new plan no longer needs\n\t\t\t\t\t\tconst quota = removeQuota.get(itemPriceId) ?? 0;\n\t\t\t\t\t\tif (quota > 0) {\n\t\t\t\t\t\t\tremoveQuota.set(itemPriceId, quota - 1);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// priceMap takes priority\n\t\t\t\t\t\t// which handles seat-only plans\n\t\t\t\t\t\t// where base price === seat price\n\t\t\t\t\t\tconst replacement = priceMap.get(itemPriceId);\n\t\t\t\t\t\tif (replacement) {\n\t\t\t\t\t\t\tnewPhaseItems.push({\n\t\t\t\t\t\t\t\tprice: replacement.newPrice,\n\t\t\t\t\t\t\t\tquantity: replacement.quantity ?? item.quantity,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Replace base plan price\n\t\t\t\t\t\tif (itemPriceId === stripeSubscriptionPriceId) {\n\t\t\t\t\t\t\tnewPhaseItems.push({\n\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\tquantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Keep as-is\n\t\t\t\t\t\tnewPhaseItems.push({\n\t\t\t\t\t\t\tprice: itemPriceId,\n\t\t\t\t\t\t\tquantity: item.quantity,\n\t\t\t\t\t\t});\n\t\t\t\t\t\t// Consume positive delta to prevent duplicate addition\n\t\t\t\t\t\tconst d = lineItemDelta.get(itemPriceId);\n\t\t\t\t\t\tif (d !== undefined && d > 0) {\n\t\t\t\t\t\t\tif (d === 1) lineItemDelta.delete(itemPriceId);\n\t\t\t\t\t\t\telse lineItemDelta.set(itemPriceId, d - 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Add line items the new plan introduces\n\t\t\t\t\tfor (const [price, delta] of lineItemDelta) {\n\t\t\t\t\t\tfor (let i = 0; i < delta; i++) newPhaseItems.push({ price });\n\t\t\t\t\t}\n\n\t\t\t\t\tawait client.subscriptionSchedules\n\t\t\t\t\t\t.update(schedule.id, {\n\t\t\t\t\t\t\tmetadata: { source: \"@better-auth/stripe\" },\n\t\t\t\t\t\t\tend_behavior: \"release\",\n\t\t\t\t\t\t\tphases: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\titems: currentPhase.items.map((item) => ({\n\t\t\t\t\t\t\t\t\t\tprice:\n\t\t\t\t\t\t\t\t\t\t\ttypeof item.price === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t? item.price\n\t\t\t\t\t\t\t\t\t\t\t\t: item.price.id,\n\t\t\t\t\t\t\t\t\t\tquantity: item.quantity,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t\tstart_date: currentPhase.start_date,\n\t\t\t\t\t\t\t\t\tend_date: currentPhase.end_date,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\titems: newPhaseItems,\n\t\t\t\t\t\t\t\t\tstart_date: currentPhase.end_date,\n\t\t\t\t\t\t\t\t\tproration_behavior: \"none\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\t// Store schedule ID so clients can detect pending plan changes\n\t\t\t\t\tif (dbSubscription) {\n\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tstripeScheduleId: schedule.id,\n\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\tvalue: dbSubscription.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tupgradeUrl = getUrl(ctx, ctx.body.returnUrl || \"/\");\n\t\t\t\t} else if (priceMap.size > 0 || lineItemDelta.size > 0) {\n\t\t\t\t\t// Immediate change with multi-item updates: use direct API.\n\t\t\t\t\t// Billing Portal supports only 1 item update at a time,\n\t\t\t\t\t// so when multiple prices change between plans we call\n\t\t\t\t\t// subscriptions.update directly.\n\t\t\t\t\tconst removeQuota = new Map<string, number>();\n\t\t\t\t\tfor (const [p, d] of lineItemDelta) {\n\t\t\t\t\t\tif (d < 0) removeQuota.set(p, -d);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst itemUpdates: Array<Record<string, unknown>> = [];\n\t\t\t\t\tfor (const si of activeSubscription.items.data) {\n\t\t\t\t\t\t// Remove items the new plan no longer needs\n\t\t\t\t\t\tconst quota = removeQuota.get(si.price.id) ?? 0;\n\t\t\t\t\t\tif (quota > 0) {\n\t\t\t\t\t\t\tremoveQuota.set(si.price.id, quota - 1);\n\t\t\t\t\t\t\titemUpdates.push({ id: si.id, deleted: true });\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// priceMap takes priority (handles seat-only plans\n\t\t\t\t\t\t// where base price === seat price)\n\t\t\t\t\t\tconst replacement = priceMap.get(si.price.id);\n\t\t\t\t\t\tif (replacement) {\n\t\t\t\t\t\t\titemUpdates.push({\n\t\t\t\t\t\t\t\tid: si.id,\n\t\t\t\t\t\t\t\tprice: replacement.newPrice,\n\t\t\t\t\t\t\t\tquantity: replacement.quantity,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (si.price.id === stripeSubscriptionPriceId) {\n\t\t\t\t\t\t\titemUpdates.push({\n\t\t\t\t\t\t\t\tid: si.id,\n\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\tquantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Consume positive delta to prevent duplicate addition\n\t\t\t\t\t\tconst d = lineItemDelta.get(si.price.id);\n\t\t\t\t\t\tif (d !== undefined && d > 0) {\n\t\t\t\t\t\t\tif (d === 1) lineItemDelta.delete(si.price.id);\n\t\t\t\t\t\t\telse lineItemDelta.set(si.price.id, d - 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Add line items the new plan introduces\n\t\t\t\t\tfor (const [price, delta] of lineItemDelta) {\n\t\t\t\t\t\tfor (let i = 0; i < delta; i++) itemUpdates.push({ price });\n\t\t\t\t\t}\n\t\t\t\t\tawait client.subscriptions\n\t\t\t\t\t\t.update(activeSubscription.id, {\n\t\t\t\t\t\t\titems: itemUpdates,\n\t\t\t\t\t\t\tproration_behavior: \"create_prorations\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\tif (dbSubscription) {\n\t\t\t\t\t\tawait ctx.context.adapter.update<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\t\t\tseats: memberCount,\n\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\twhere: [{ field: \"id\", value: dbSubscription.id }],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tupgradeUrl = getUrl(ctx, ctx.body.returnUrl || \"/\");\n\t\t\t\t} else {\n\t\t\t\t\t// Immediate change via Billing Portal\n\t\t\t\t\t({ url: upgradeUrl } = await client.billingPortal.sessions\n\t\t\t\t\t\t.create({\n\t\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl || \"/\"),\n\t\t\t\t\t\t\tflow_data: {\n\t\t\t\t\t\t\t\ttype: \"subscription_update_confirm\",\n\t\t\t\t\t\t\t\tafter_completion: {\n\t\t\t\t\t\t\t\t\ttype: \"redirect\",\n\t\t\t\t\t\t\t\t\tredirect: {\n\t\t\t\t\t\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl || \"/\"),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tsubscription_update_confirm: {\n\t\t\t\t\t\t\t\t\tsubscription: activeSubscription.id,\n\t\t\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tid: planItem.id,\n\t\t\t\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\t\t\t\t...(isAutoManagedSeats\n\t\t\t\t\t\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t\t\t\t\t\t: { quantity: ctx.body.seats || 1 }),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}));\n\t\t\t\t}\n\t\t\t\treturn ctx.json({\n\t\t\t\t\turl: upgradeUrl,\n\t\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet subscription: Subscription | undefined =\n\t\t\t\tactiveOrTrialingSubscription || incompleteSubscription;\n\n\t\t\tif (incompleteSubscription && !activeOrTrialingSubscription) {\n\t\t\t\tconst updated = await ctx.context.adapter.update<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\tseats: isAutoManagedSeats ? memberCount : ctx.body.seats || 1,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t},\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: incompleteSubscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tsubscription = (updated as Subscription) || incompleteSubscription;\n\t\t\t}\n\n\t\t\tif (!subscription) {\n\t\t\t\tsubscription = await ctx.context.adapter.create<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\tstripeCustomerId: customerId,\n\t\t\t\t\t\tstatus: \"incomplete\",\n\t\t\t\t\t\treferenceId,\n\t\t\t\t\t\tseats: isAutoManagedSeats ? memberCount : ctx.body.seats || 1,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!subscription) {\n\t\t\t\tctx.context.logger.error(\"Subscription ID not found\");\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"NOT_FOUND\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = await subscriptionOptions.getCheckoutSessionParams?.(\n\t\t\t\t{\n\t\t\t\t\tuser,\n\t\t\t\t\tsession,\n\t\t\t\t\tplan,\n\t\t\t\t\tsubscription,\n\t\t\t\t},\n\t\t\t\tctx.request,\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tconst allSubscriptions = await ctx.context.adapter.findMany<Subscription>(\n\t\t\t\t{\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [{ field: \"referenceId\", value: referenceId }],\n\t\t\t\t},\n\t\t\t);\n\t\t\tconst hasEverTrialed = allSubscriptions.some((s) => {\n\t\t\t\t// Check if user has ever had a trial for any plan (not just the same plan)\n\t\t\t\t// This prevents users from getting multiple trials by switching plans\n\t\t\t\tconst hadTrial =\n\t\t\t\t\t!!(s.trialStart || s.trialEnd) || s.status === \"trialing\";\n\t\t\t\treturn hadTrial;\n\t\t\t});\n\n\t\t\tconst freeTrial =\n\t\t\t\t!hasEverTrialed && plan.freeTrial\n\t\t\t\t\t? { trial_period_days: plan.freeTrial.days }\n\t\t\t\t\t: undefined;\n\n\t\t\tconst checkoutSession = await client.checkout.sessions\n\t\t\t\t.create(\n\t\t\t\t\t{\n\t\t\t\t\t\t...(customerId\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\t\t\t\tcustomer_update:\n\t\t\t\t\t\t\t\t\t\tcustomerType !== \"user\"\n\t\t\t\t\t\t\t\t\t\t\t? ({ address: \"auto\" } as const)\n\t\t\t\t\t\t\t\t\t\t\t: ({ name: \"auto\", address: \"auto\" } as const), // The customer name is automatically set only for users\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\tcustomer_email: user.email,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tlocale: ctx.body.locale,\n\t\t\t\t\t\tsuccess_url: getUrl(\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t`${\n\t\t\t\t\t\t\t\tctx.context.baseURL\n\t\t\t\t\t\t\t}/subscription/success?callbackURL=${encodeURIComponent(\n\t\t\t\t\t\t\t\tctx.body.successUrl,\n\t\t\t\t\t\t\t\t// {CHECKOUT_SESSION_ID} is a string literal; do not change it!\n\t\t\t\t\t\t\t\t// the actual Session ID is returned in the query parameter when your customer\n\t\t\t\t\t\t\t\t// is redirected to the success page.\n\t\t\t\t\t\t\t)}&checkoutSessionId={CHECKOUT_SESSION_ID}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tcancel_url: getUrl(ctx, ctx.body.cancelUrl),\n\t\t\t\t\t\tline_items: [\n\t\t\t\t\t\t\t// Base price\n\t\t\t\t\t\t\t...(!isSeatOnlyPlan\n\t\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\t\t\t\tquantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t// Per-seat\n\t\t\t\t\t\t\t...(isAutoManagedSeats\n\t\t\t\t\t\t\t\t? [{ price: plan.seatPriceId, quantity: memberCount }]\n\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t// Additional line items (metered prices, add-ons, etc.)\n\t\t\t\t\t\t\t...(plan.lineItems ?? []),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tsubscription_data: {\n\t\t\t\t\t\t\t...freeTrial,\n\t\t\t\t\t\t\tmetadata: subscriptionMetadata.set(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\t\tsubscriptionId: subscription.id,\n\t\t\t\t\t\t\t\t\treferenceId,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\t\tparams?.params?.subscription_data?.metadata,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmode: \"subscription\",\n\t\t\t\t\t\tclient_reference_id: referenceId,\n\t\t\t\t\t\t...params?.params,\n\t\t\t\t\t\t// metadata should come after spread to protect internal fields\n\t\t\t\t\t\tmetadata: subscriptionMetadata.set(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\tsubscriptionId: subscription.id,\n\t\t\t\t\t\t\t\treferenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\tparams?.params?.metadata,\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\tparams?.options,\n\t\t\t\t)\n\t\t\t\t.catch(async (e) => {\n\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\treturn ctx.json({\n\t\t\t\t...checkoutSession,\n\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t});\n\t\t},\n\t);\n};\n\nconst cancelSubscriptionBodySchema = z.object({\n\treferenceId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: \"Reference id of the subscription to cancel. Eg: '123'\",\n\t\t})\n\t\t.optional(),\n\tsubscriptionId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The Stripe subscription ID to cancel. Eg: 'sub_1ABC2DEF3GHI4JKL'\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n\treturnUrl: z.string().meta({\n\t\tdescription:\n\t\t\t'URL to take customers to when they click on the billing portal\\'s link to return to your website. Eg: \"/account\"',\n\t}),\n\t/**\n\t * Disable Redirect\n\t */\n\tdisableRedirect: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Disable redirect after successful subscription cancellation. Eg: true\",\n\t\t})\n\t\t.default(false),\n});\n\n/**\n * ### Endpoint\n *\n * POST `/subscription/cancel`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.cancelSubscription`\n *\n * **client:**\n * `authClient.subscription.cancel`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-cancel)\n */\nexport const cancelSubscription = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/cancel\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: cancelSubscriptionBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"cancelSubscription\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"cancel-subscription\"),\n\t\t\t\toriginCheck((ctx) => ctx.body.returnUrl),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tlet subscription = ctx.body.subscriptionId\n\t\t\t\t? await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\t\tvalue: ctx.body.subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t: await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [{ field: \"referenceId\", value: referenceId }],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\t\t\tif (\n\t\t\t\tctx.body.subscriptionId &&\n\t\t\t\tsubscription &&\n\t\t\t\tsubscription.referenceId !== referenceId\n\t\t\t) {\n\t\t\t\tsubscription = undefined;\n\t\t\t}\n\n\t\t\tif (!subscription || !subscription.stripeCustomerId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst activeSubscriptions = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub)));\n\t\t\tif (!activeSubscriptions.length) {\n\t\t\t\t/**\n\t\t\t\t * If the subscription is not found, we need to delete the subscription\n\t\t\t\t * from the database. This is a rare case and should not happen.\n\t\t\t\t */\n\t\t\t\tawait ctx.context.adapter.deleteMany({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst activeSubscription = activeSubscriptions.find(\n\t\t\t\t(sub) => sub.id === subscription.stripeSubscriptionId,\n\t\t\t);\n\t\t\tif (!activeSubscription) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst { url } = await client.billingPortal.sessions\n\t\t\t\t.create({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t\treturn_url: getUrl(ctx, ctx.body?.returnUrl || \"/\"),\n\t\t\t\t\tflow_data: {\n\t\t\t\t\t\ttype: \"subscription_cancel\",\n\t\t\t\t\t\tsubscription_cancel: {\n\t\t\t\t\t\t\tsubscription: activeSubscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\t.catch(async (e) => {\n\t\t\t\t\tif (e.message?.includes(\"already set to be canceled\")) {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * in-case we missed the event from stripe, we sync the actual state\n\t\t\t\t\t\t * this is a rare case and should not happen\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (!isPendingCancel(subscription)) {\n\t\t\t\t\t\t\tconst stripeSub = await client.subscriptions.retrieve(\n\t\t\t\t\t\t\t\tactiveSubscription.id,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\t\tcancelAtPeriodEnd: stripeSub.cancel_at_period_end,\n\t\t\t\t\t\t\t\t\tcancelAt: stripeSub.cancel_at\n\t\t\t\t\t\t\t\t\t\t? new Date(stripeSub.cancel_at * 1000)\n\t\t\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t\t\t\tcanceledAt: stripeSub.canceled_at\n\t\t\t\t\t\t\t\t\t\t? new Date(stripeSub.canceled_at * 1000)\n\t\t\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\treturn ctx.json({\n\t\t\t\turl,\n\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t});\n\t\t},\n\t);\n};\n\nconst restoreSubscriptionBodySchema = z.object({\n\treferenceId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: \"Reference id of the subscription to restore. Eg: '123'\",\n\t\t})\n\t\t.optional(),\n\tsubscriptionId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The Stripe subscription ID to restore. Eg: 'sub_1ABC2DEF3GHI4JKL'\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n});\n\nexport const restoreSubscription = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/restore\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: restoreSubscriptionBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"restoreSubscription\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"restore-subscription\"),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tlet subscription = ctx.body.subscriptionId\n\t\t\t\t? await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\t\tvalue: ctx.body.subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t: await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\t\t\tif (\n\t\t\t\tctx.body.subscriptionId &&\n\t\t\t\tsubscription &&\n\t\t\t\tsubscription.referenceId !== referenceId\n\t\t\t) {\n\t\t\t\tsubscription = undefined;\n\t\t\t}\n\t\t\tif (!subscription || !subscription.stripeCustomerId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!isActiveOrTrialing(subscription)) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_ACTIVE,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst hasPendingCancel = isPendingCancel(subscription);\n\t\t\tconst { stripeScheduleId } = subscription;\n\n\t\t\tif (!hasPendingCancel && !stripeScheduleId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_PENDING_CHANGE,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Pending cancel and pending schedule are mutually exclusive in Stripe.\n\t\t\tif (stripeScheduleId) {\n\t\t\t\tif (!subscription.stripeSubscriptionId) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst schedule = await client.subscriptionSchedules\n\t\t\t\t\t.retrieve(stripeScheduleId)\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\n\t\t\t\tif (schedule.status === \"active\") {\n\t\t\t\t\tawait client.subscriptionSchedules\n\t\t\t\t\t\t.release(stripeScheduleId)\n\t\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\tstripeScheduleId: null,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t},\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tconst releasedSub = await client.subscriptions.retrieve(\n\t\t\t\t\tsubscription.stripeSubscriptionId,\n\t\t\t\t);\n\t\t\t\treturn ctx.json(releasedSub);\n\t\t\t}\n\n\t\t\t// Handle pending cancellation\n\t\t\tconst activeSubscription = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub))[0]);\n\t\t\tif (!activeSubscription) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Clear scheduled cancellation based on Stripe subscription state\n\t\t\t// Note: Stripe doesn't accept both `cancel_at` and `cancel_at_period_end` simultaneously\n\t\t\tconst updateParams: Stripe.SubscriptionUpdateParams = {};\n\t\t\tif (activeSubscription.cancel_at) {\n\t\t\t\tupdateParams.cancel_at = \"\";\n\t\t\t} else if (activeSubscription.cancel_at_period_end) {\n\t\t\t\tupdateParams.cancel_at_period_end = false;\n\t\t\t}\n\n\t\t\tconst newSub = await client.subscriptions\n\t\t\t\t.update(activeSubscription.id, updateParams)\n\t\t\t\t.catch((e) => {\n\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\tupdate: {\n\t\t\t\t\tcancelAtPeriodEnd: false,\n\t\t\t\t\tcancelAt: null,\n\t\t\t\t\tcanceledAt: null,\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t},\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\treturn ctx.json(newSub);\n\t\t},\n\t);\n};\n\nconst listActiveSubscriptionsQuerySchema = z.optional(\n\tz.object({\n\t\treferenceId: z\n\t\t\t.string()\n\t\t\t.meta({\n\t\t\t\tdescription: \"Reference id of the subscription to list. Eg: '123'\",\n\t\t\t})\n\t\t\t.optional(),\n\t\t/**\n\t\t * Customer type for the subscription.\n\t\t * - `user`: User owns the subscription (default)\n\t\t * - `organization`: Organization owns the subscription\n\t\t */\n\t\tcustomerType: z\n\t\t\t.enum([\"user\", \"organization\"])\n\t\t\t.meta({\n\t\t\t\tdescription:\n\t\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t\t})\n\t\t\t.optional(),\n\t}),\n);\n/**\n * ### Endpoint\n *\n * GET `/subscription/list`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.listActiveSubscriptions`\n *\n * **client:**\n * `authClient.subscription.list`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-list)\n */\nexport const listActiveSubscriptions = (options: StripeOptions) => {\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/list\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: listActiveSubscriptionsQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"listActiveSubscriptions\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"list-subscription\"),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst customerType = ctx.query?.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.query?.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tconst subscriptions = await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!subscriptions.length) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tconst plans = await getPlans(options.subscription);\n\t\t\tif (!plans) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tconst subs = subscriptions\n\t\t\t\t.map((sub) => {\n\t\t\t\t\tconst plan = plans.find(\n\t\t\t\t\t\t(p) => p.name.toLowerCase() === sub.plan.toLowerCase(),\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...sub,\n\t\t\t\t\t\tlimits: plan?.limits,\n\t\t\t\t\t\tpriceId: plan?.priceId,\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((sub) => isActiveOrTrialing(sub));\n\t\t\treturn ctx.json(subs);\n\t\t},\n\t);\n};\n\nconst subscriptionSuccessQuerySchema = z.record(z.string(), z.any()).optional();\n\nexport const subscriptionSuccess = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/success\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: subscriptionSuccessQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleSubscriptionSuccess\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [originCheck((ctx) => ctx.query.callbackURL)],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst callbackURL = ctx.query?.callbackURL || \"/\";\n\n\t\t\tconst session = await getSessionFromCtx<User & WithStripeCustomerId>(ctx);\n\t\t\tif (!session) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\t// checkoutSessionId is substituted by Stripe from the {CHECKOUT_SESSION_ID}\n\t\t\t// template variable in success_url when redirecting after checkout.\n\t\t\tif (!ctx.query?.checkoutSessionId) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\t// Resolve subscriptionId from Stripe checkout session metadata.\n\t\t\t// The metadata is set server-side when creating the checkout session,\n\t\t\t// so it cannot be tampered with — no ownership check needed.\n\t\t\tconst checkoutSession = await client.checkout.sessions\n\t\t\t\t.retrieve(ctx.query.checkoutSessionId)\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\"Error retrieving checkout session from Stripe\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t\treturn null;\n\t\t\t\t});\n\t\t\tif (!checkoutSession) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst { subscriptionId } = subscriptionMetadata.get(\n\t\t\t\tcheckoutSession.metadata,\n\t\t\t);\n\t\t\tif (!subscriptionId) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`No subscriptionId in checkout session metadata: ${checkoutSession.id}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst subscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!subscription) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`Subscription record not found for subscriptionId: ${subscriptionId}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\t// Already active or trialing, no need to update\n\t\t\tif (isActiveOrTrialing(subscription)) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst customerId =\n\t\t\t\tsubscription.stripeCustomerId || session.user.stripeCustomerId;\n\t\t\tif (!customerId) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst stripeSubscription = await client.subscriptions\n\t\t\t\t.list({ customer: customerId, status: \"active\" })\n\t\t\t\t.then((res) => res.data[0])\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\"Error fetching subscription from Stripe\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t\t});\n\t\t\tif (!stripeSubscription) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst resolved = await resolvePlanItem(\n\t\t\t\toptions,\n\t\t\t\tstripeSubscription.items.data,\n\t\t\t);\n\t\t\tif (!resolved) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`No subscription items found for Stripe subscription ${stripeSubscription.id}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst { item: subscriptionItem, plan } = resolved;\n\t\t\tif (!plan) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`Plan not found for price ${subscriptionItem.price.id}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst seats =\n\t\t\t\tresolveQuantity(\n\t\t\t\t\tstripeSubscription.items.data,\n\t\t\t\t\tsubscriptionItem,\n\t\t\t\t\tplan.seatPriceId,\n\t\t\t\t) || 1;\n\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\tupdate: {\n\t\t\t\t\t...(stripeSubscription.trial_start && stripeSubscription.trial_end\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\ttrialStart: new Date(stripeSubscription.trial_start * 1000),\n\t\t\t\t\t\t\t\ttrialEnd: new Date(stripeSubscription.trial_end * 1000),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t\tstatus: stripeSubscription.status,\n\t\t\t\t\tseats,\n\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t\t\tperiodEnd: new Date(subscriptionItem.current_period_end * 1000),\n\t\t\t\t\tperiodStart: new Date(subscriptionItem.current_period_start * 1000),\n\t\t\t\t\tstripeSubscriptionId: stripeSubscription.id,\n\t\t\t\t\tcancelAtPeriodEnd: stripeSubscription.cancel_at_period_end,\n\t\t\t\t\tcancelAt: stripeSubscription.cancel_at\n\t\t\t\t\t\t? new Date(stripeSubscription.cancel_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tcanceledAt: stripeSubscription.canceled_at\n\t\t\t\t\t\t? new Date(stripeSubscription.canceled_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t},\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t},\n\t);\n};\n\nconst createBillingPortalBodySchema = z.object({\n\t/**\n\t * The IETF language tag of the locale Customer Portal is displayed in.\n\t * If not provided or set to `auto`, the browser's locale is used.\n\t */\n\tlocale: z\n\t\t.custom<StripeType.Checkout.Session.Locale>((localization) => {\n\t\t\treturn typeof localization === \"string\";\n\t\t})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The IETF language tag of the locale Customer Portal is displayed in. Eg: 'en', 'ko'. If not provided or set to `auto`, the browser's locale is used.\",\n\t\t})\n\t\t.optional(),\n\treferenceId: z.string().optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n\treturnUrl: z.string().default(\"/\"),\n\t/**\n\t * Disable Redirect\n\t */\n\tdisableRedirect: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Disable redirect after creating billing portal session. Eg: true\",\n\t\t})\n\t\t.default(false),\n});\n\nexport const createBillingPortal = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/billing-portal\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: createBillingPortalBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"createBillingPortal\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"billing-portal\"),\n\t\t\t\toriginCheck((ctx) => ctx.body.returnUrl),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { user } = ctx.context.session;\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tlet customerId: string | undefined;\n\n\t\t\tif (customerType === \"organization\") {\n\t\t\t\t// Organization billing portal - get customer ID from organization\n\t\t\t\tconst org = await ctx.context.adapter.findOne<\n\t\t\t\t\tOrganization & WithStripeCustomerId\n\t\t\t\t>({\n\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\twhere: [{ field: \"id\", value: referenceId }],\n\t\t\t\t});\n\t\t\t\tcustomerId = org?.stripeCustomerId;\n\n\t\t\t\tif (!customerId) {\n\t\t\t\t\t// Fallback to subscription's stripeCustomerId\n\t\t\t\t\tconst subscription = await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [{ field: \"referenceId\", value: referenceId }],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\t\t\t\t\tcustomerId = subscription?.stripeCustomerId;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// User billing portal\n\t\t\t\tcustomerId = user.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\tconst subscription = await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\n\t\t\t\t\tcustomerId = subscription?.stripeCustomerId;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!customerId) {\n\t\t\t\tthrow APIError.from(\"NOT_FOUND\", STRIPE_ERROR_CODES.CUSTOMER_NOT_FOUND);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { url } = await client.billingPortal.sessions.create({\n\t\t\t\t\tlocale: ctx.body.locale,\n\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl),\n\t\t\t\t});\n\n\t\t\t\treturn ctx.json({\n\t\t\t\t\turl,\n\t\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t\t});\n\t\t\t} catch (error: any) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"Error creating billing portal session\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"INTERNAL_SERVER_ERROR\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_BILLING_PORTAL,\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n};\n\nexport const stripeWebhook = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\treturn createAuthEndpoint(\n\t\t\"/stripe/webhook\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tmetadata: {\n\t\t\t\t...HIDE_METADATA,\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleStripeWebhook\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tcloneRequest: true,\n\t\t\tdisableBody: true, // Don't parse the body\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (!ctx.request?.body) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.INVALID_REQUEST_BODY,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst sig = ctx.request.headers.get(\"stripe-signature\");\n\t\t\tif (!sig) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_SIGNATURE_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst webhookSecret = options.stripeWebhookSecret;\n\t\t\tif (!webhookSecret) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"INTERNAL_SERVER_ERROR\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_WEBHOOK_SECRET_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst payload = await ctx.request.text();\n\n\t\t\tlet event: Stripe.Event;\n\t\t\ttry {\n\t\t\t\t// Support both Stripe v18 (constructEvent) and v19+ (constructEventAsync)\n\t\t\t\tif (typeof client.webhooks.constructEventAsync === \"function\") {\n\t\t\t\t\t// Stripe v19+ - use async method\n\t\t\t\t\tevent = await client.webhooks.constructEventAsync(\n\t\t\t\t\t\tpayload,\n\t\t\t\t\t\tsig,\n\t\t\t\t\t\twebhookSecret,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\t// Stripe v18 - use sync method\n\t\t\t\t\tevent = client.webhooks.constructEvent(payload, sig, webhookSecret);\n\t\t\t\t}\n\t\t\t} catch (err: any) {\n\t\t\t\tctx.context.logger.error(`${err.message}`);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.FAILED_TO_CONSTRUCT_STRIPE_EVENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!event) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.FAILED_TO_CONSTRUCT_STRIPE_EVENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tswitch (event.type) {\n\t\t\t\t\tcase \"checkout.session.completed\":\n\t\t\t\t\t\tawait onCheckoutSessionCompleted(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.created\":\n\t\t\t\t\t\tawait onSubscriptionCreated(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.updated\":\n\t\t\t\t\t\tawait onSubscriptionUpdated(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.deleted\":\n\t\t\t\t\t\tawait onSubscriptionDeleted(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch (e: any) {\n\t\t\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${e.message}`);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_WEBHOOK_ERROR,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ctx.json({ success: true });\n\t\t},\n\t);\n};\n","import type { BetterAuthPluginDBSchema } from \"@better-auth/core/db\";\nimport { mergeSchema } from \"better-auth/db\";\nimport type { StripeOptions } from \"./types\";\n\nexport const subscriptions = {\n\tsubscription: {\n\t\tfields: {\n\t\t\tplan: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\treferenceId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstripeSubscriptionId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstatus: {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdefaultValue: \"incomplete\",\n\t\t\t},\n\t\t\tperiodStart: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tperiodEnd: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\ttrialStart: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\ttrialEnd: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tcancelAtPeriodEnd: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\trequired: false,\n\t\t\t\tdefaultValue: false,\n\t\t\t},\n\t\t\tcancelAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tcanceledAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tendedAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tseats: {\n\t\t\t\ttype: \"number\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tbillingInterval: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstripeScheduleId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\nexport const user = {\n\tuser: {\n\t\tfields: {\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\nexport const organization = {\n\torganization: {\n\t\tfields: {\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\ntype GetSchemaResult<O extends StripeOptions> = typeof user &\n\t(O[\"subscription\"] extends { enabled: true } ? typeof subscriptions : {}) &\n\t(O[\"organization\"] extends { enabled: true } ? typeof organization : {});\n\nexport const getSchema = <O extends StripeOptions>(\n\toptions: O,\n): GetSchemaResult<O> => {\n\tlet baseSchema: BetterAuthPluginDBSchema = {};\n\n\tif (options.subscription?.enabled) {\n\t\tbaseSchema = {\n\t\t\t...subscriptions,\n\t\t\t...user,\n\t\t};\n\t} else {\n\t\tbaseSchema = {\n\t\t\t...user,\n\t\t};\n\t}\n\n\tif (options.organization?.enabled) {\n\t\tbaseSchema = {\n\t\t\t...baseSchema,\n\t\t\t...organization,\n\t\t};\n\t}\n\n\tif (\n\t\toptions.schema &&\n\t\t!options.subscription?.enabled &&\n\t\t\"subscription\" in options.schema\n\t) {\n\t\tconst { subscription: _subscription, ...restSchema } = options.schema;\n\t\treturn mergeSchema(baseSchema, restSchema) as GetSchemaResult<O>;\n\t}\n\n\treturn mergeSchema(baseSchema, options.schema) as GetSchemaResult<O>;\n};\n","import type { BetterAuthPlugin, User } from \"better-auth\";\nimport { APIError } from \"better-auth\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport { defu } from \"defu\";\nimport type Stripe from \"stripe\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport { customerMetadata } from \"./metadata\";\nimport {\n\tcancelSubscription,\n\tcreateBillingPortal,\n\tlistActiveSubscriptions,\n\trestoreSubscription,\n\tstripeWebhook,\n\tsubscriptionSuccess,\n\tupgradeSubscription,\n} from \"./routes\";\nimport { getSchema } from \"./schema\";\nimport type {\n\tStripeOptions,\n\tStripePlan,\n\tSubscription,\n\tWithStripeCustomerId,\n} from \"./types\";\nimport { escapeStripeSearchValue, getPlans, isActiveOrTrialing } from \"./utils\";\n\ndeclare module \"@better-auth/core\" {\n\tinterface BetterAuthPluginRegistry<AuthOptions, Options> {\n\t\tstripe: {\n\t\t\tcreator: typeof stripe;\n\t\t};\n\t}\n}\n\nexport const stripe = <O extends StripeOptions>(options: O) => {\n\tconst client = options.stripeClient;\n\n\tconst subscriptionEndpoints = {\n\t\tupgradeSubscription: upgradeSubscription(options),\n\t\tcancelSubscription: cancelSubscription(options),\n\t\trestoreSubscription: restoreSubscription(options),\n\t\tlistActiveSubscriptions: listActiveSubscriptions(options),\n\t\tsubscriptionSuccess: subscriptionSuccess(options),\n\t\tcreateBillingPortal: createBillingPortal(options),\n\t};\n\n\treturn {\n\t\tid: \"stripe\",\n\t\tendpoints: {\n\t\t\tstripeWebhook: stripeWebhook(options),\n\t\t\t...((options.subscription?.enabled\n\t\t\t\t? subscriptionEndpoints\n\t\t\t\t: {}) as O[\"subscription\"] extends {\n\t\t\t\tenabled: true;\n\t\t\t}\n\t\t\t\t? typeof subscriptionEndpoints\n\t\t\t\t: {}),\n\t\t},\n\t\tinit(ctx) {\n\t\t\t// Validate: seatPriceId requires organization to be enabled\n\t\t\tif (options.subscription?.enabled && !options.organization?.enabled) {\n\t\t\t\tconst warnIfSeatPricing = (plans: StripePlan[]) => {\n\t\t\t\t\tif (plans.some((p) => p.seatPriceId)) {\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t\"seatPriceId is configured on a plan but stripe organization option is not enabled. \" +\n\t\t\t\t\t\t\t\t\"Seat-based billing requires `organization: { enabled: true }` in stripe plugin options.\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst { plans } = options.subscription;\n\t\t\t\tif (typeof plans === \"function\") {\n\t\t\t\t\tvoid Promise.resolve(plans())\n\t\t\t\t\t\t.then(warnIfSeatPricing)\n\t\t\t\t\t\t.catch((e: any) => {\n\t\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t\t`Failed to resolve plans for seat pricing validation: ${e.message}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\twarnIfSeatPricing(plans);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.organization?.enabled) {\n\t\t\t\tconst orgPlugin = ctx.getPlugin(\"organization\");\n\t\t\t\tif (!orgPlugin) {\n\t\t\t\t\tctx.logger.error(`Organization plugin not found`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst existingHooks = orgPlugin.options.organizationHooks ?? {};\n\n\t\t\t\t/**\n\t\t\t\t * Sync organization name to Stripe customer\n\t\t\t\t */\n\t\t\t\tconst afterUpdateStripeOrg = async (data: {\n\t\t\t\t\torganization: (Organization & WithStripeCustomerId) | null;\n\t\t\t\t\tuser: User;\n\t\t\t\t}) => {\n\t\t\t\t\tconst { organization } = data;\n\t\t\t\t\tif (!organization?.stripeCustomerId) return;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst stripeCustomer = await client.customers.retrieve(\n\t\t\t\t\t\t\torganization.stripeCustomerId,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (stripeCustomer.deleted) {\n\t\t\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t\t\t`Stripe customer ${organization.stripeCustomerId} was deleted`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Update Stripe customer if name changed\n\t\t\t\t\t\tif (organization.name !== stripeCustomer.name) {\n\t\t\t\t\t\t\tawait client.customers.update(organization.stripeCustomerId, {\n\t\t\t\t\t\t\t\tname: organization.name,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tctx.logger.info(\n\t\t\t\t\t\t\t\t`Synced organization name to Stripe: \"${stripeCustomer.name}\" → \"${organization.name}\"`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t`Failed to sync organization to Stripe: ${e.message}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t/**\n\t\t\t\t * Block deletion if organization has active subscriptions\n\t\t\t\t */\n\t\t\t\tconst beforeDeleteStripeOrg = async (data: {\n\t\t\t\t\torganization: Organization & WithStripeCustomerId;\n\t\t\t\t\tuser: User;\n\t\t\t\t}) => {\n\t\t\t\t\tconst { organization } = data;\n\t\t\t\t\tif (!organization.stripeCustomerId) return;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Check if organization has any active subscriptions\n\t\t\t\t\t\tconst subscriptions = await client.subscriptions.list({\n\t\t\t\t\t\t\tcustomer: organization.stripeCustomerId,\n\t\t\t\t\t\t\tstatus: \"all\",\n\t\t\t\t\t\t\tlimit: 100, // 1 ~ 100\n\t\t\t\t\t\t});\n\t\t\t\t\t\tfor (const sub of subscriptions.data) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tsub.status !== \"canceled\" &&\n\t\t\t\t\t\t\t\tsub.status !== \"incomplete\" &&\n\t\t\t\t\t\t\t\tsub.status !== \"incomplete_expired\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\tif (error instanceof APIError) {\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t`Failed to check organization subscriptions: ${error.message}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t/**\n\t\t\t\t * Sync seat quantity to Stripe when organization members change.\n\t\t\t\t * quantity = memberCount; Stripe graduated pricing handles free tiers.\n\t\t\t\t */\n\t\t\t\tconst syncSeatsAfterMemberChange = async (data: {\n\t\t\t\t\torganization: Organization & WithStripeCustomerId;\n\t\t\t\t}) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!options.subscription?.enabled ||\n\t\t\t\t\t\t!data.organization?.stripeCustomerId\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst memberCount = await ctx.adapter.count({\n\t\t\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"organizationId\",\n\t\t\t\t\t\t\t\t\tvalue: data.organization.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst plans = await getPlans(options.subscription);\n\t\t\t\t\t\tconst seatPlans = plans.filter((p) => p.seatPriceId);\n\t\t\t\t\t\tif (seatPlans.length === 0) return;\n\n\t\t\t\t\t\tconst seatPlanNames = new Set(\n\t\t\t\t\t\t\tseatPlans.map((p) => p.name.toLowerCase()),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst dbSub = await ctx.adapter.findOne<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\t\tvalue: data.organization.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!dbSub?.stripeSubscriptionId ||\n\t\t\t\t\t\t\t!isActiveOrTrialing(dbSub) ||\n\t\t\t\t\t\t\t!seatPlanNames.has(dbSub.plan)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst plan = seatPlans.find(\n\t\t\t\t\t\t\t(p) => p.name.toLowerCase() === dbSub.plan,\n\t\t\t\t\t\t)!;\n\t\t\t\t\t\tconst { seatPriceId } = plan;\n\n\t\t\t\t\t\tconst stripeSub = await client.subscriptions.retrieve(\n\t\t\t\t\t\t\tdbSub.stripeSubscriptionId,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (!isActiveOrTrialing(stripeSub)) return;\n\n\t\t\t\t\t\tconst seatItem = stripeSub.items.data.find(\n\t\t\t\t\t\t\t(item) => item.price.id === seatPriceId,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Skip if no change needed\n\t\t\t\t\t\tif (seatItem?.quantity === memberCount) return;\n\n\t\t\t\t\t\tconst items = seatItem\n\t\t\t\t\t\t\t? [{ id: seatItem.id, quantity: memberCount }]\n\t\t\t\t\t\t\t: [{ price: seatPriceId, quantity: memberCount }];\n\n\t\t\t\t\t\tawait client.subscriptions.update(stripeSub.id, {\n\t\t\t\t\t\t\titems,\n\t\t\t\t\t\t\tproration_behavior: \"create_prorations\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tawait ctx.adapter.update({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\tupdate: { seats: memberCount },\n\t\t\t\t\t\t\twhere: [{ field: \"id\", value: dbSub.id }],\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.logger.error(`Failed to sync seats to Stripe: ${e.message}`);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\torgPlugin.options.organizationHooks = {\n\t\t\t\t\t...existingHooks,\n\t\t\t\t\tafterUpdateOrganization: existingHooks.afterUpdateOrganization\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterUpdateOrganization!(data);\n\t\t\t\t\t\t\t\tawait afterUpdateStripeOrg(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: afterUpdateStripeOrg,\n\t\t\t\t\tbeforeDeleteOrganization: existingHooks.beforeDeleteOrganization\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.beforeDeleteOrganization!(data);\n\t\t\t\t\t\t\t\tawait beforeDeleteStripeOrg(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: beforeDeleteStripeOrg,\n\t\t\t\t\tafterAddMember: existingHooks.afterAddMember\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterAddMember!(data);\n\t\t\t\t\t\t\t\tawait syncSeatsAfterMemberChange(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: syncSeatsAfterMemberChange,\n\t\t\t\t\tafterRemoveMember: existingHooks.afterRemoveMember\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterRemoveMember!(data);\n\t\t\t\t\t\t\t\tawait syncSeatsAfterMemberChange(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: syncSeatsAfterMemberChange,\n\t\t\t\t\tafterAcceptInvitation: existingHooks.afterAcceptInvitation\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterAcceptInvitation!(data);\n\t\t\t\t\t\t\t\tawait syncSeatsAfterMemberChange(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: syncSeatsAfterMemberChange,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\toptions: {\n\t\t\t\t\tdatabaseHooks: {\n\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\tcreate: {\n\t\t\t\t\t\t\t\tasync after(user: User & WithStripeCustomerId, ctx) {\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t!ctx ||\n\t\t\t\t\t\t\t\t\t\t!options.createCustomerOnSignUp ||\n\t\t\t\t\t\t\t\t\t\tuser.stripeCustomerId // Skip if user already has a Stripe customer ID\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t// Check if user customer already exists in Stripe by email\n\t\t\t\t\t\t\t\t\t\tlet stripeCustomer: Stripe.Customer | undefined;\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tconst result = await client.customers.search({\n\t\t\t\t\t\t\t\t\t\t\t\tquery: `email:\"${escapeStripeSearchValue(user.email)}\" AND -metadata[\"${customerMetadata.keys.customerType}\"]:\"organization\"`,\n\t\t\t\t\t\t\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\tstripeCustomer = result.data[0];\n\t\t\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t\t\t// Search API unavailable in some regions, so fall back to paginated list\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\t\t\t\t\"Stripe customers.search failed, falling back to customers.list\",\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\tfor await (const customer of client.customers.list({\n\t\t\t\t\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t\t\t\t\t\t\t})) {\n\t\t\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\t\t\tcustomer.metadata?.[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcustomerMetadata.keys.customerType\n\t\t\t\t\t\t\t\t\t\t\t\t\t] !== \"organization\"\n\t\t\t\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomer = customer;\n\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// If user customer exists, link it to prevent duplicate creation\n\t\t\t\t\t\t\t\t\t\tif (stripeCustomer) {\n\t\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.updateUser(user.id, {\n\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\tawait options.onCustomerCreate?.(\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomer,\n\t\t\t\t\t\t\t\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t...user,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t\t\t\t\t\t\t\t`Linked existing Stripe customer ${stripeCustomer.id} to user ${user.id}`,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// Create new Stripe customer\n\t\t\t\t\t\t\t\t\t\tlet extraCreateParams: Partial<Stripe.CustomerCreateParams> =\n\t\t\t\t\t\t\t\t\t\t\t{};\n\t\t\t\t\t\t\t\t\t\tif (options.getCustomerCreateParams) {\n\t\t\t\t\t\t\t\t\t\t\textraCreateParams = await options.getCustomerCreateParams(\n\t\t\t\t\t\t\t\t\t\t\t\tuser,\n\t\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tconst params = defu(\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\t\t\t\t\t\t\tmetadata: customerMetadata.set(\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcustomerType: \"user\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\textraCreateParams?.metadata,\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\textraCreateParams,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tstripeCustomer = await client.customers.create(params);\n\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.updateUser(user.id, {\n\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\tawait options.onCustomerCreate?.(\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomer,\n\t\t\t\t\t\t\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t...user,\n\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t\t\t\t\t\t\t`Created new Stripe customer ${stripeCustomer.id} for user ${user.id}`,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\t\t\t\t`Failed to create or link Stripe customer: ${e.message}`,\n\t\t\t\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tasync after(user: User & WithStripeCustomerId, ctx) {\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t!ctx ||\n\t\t\t\t\t\t\t\t\t\t!user.stripeCustomerId // Only proceed if user has a Stripe customer ID\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t// Get the user from the database to check if email actually changed\n\t\t\t\t\t\t\t\t\t\t// The 'user' parameter here is the freshly updated user\n\t\t\t\t\t\t\t\t\t\t// We need to check if the Stripe customer's email matches\n\t\t\t\t\t\t\t\t\t\tconst stripeCustomer = await client.customers.retrieve(\n\t\t\t\t\t\t\t\t\t\t\tuser.stripeCustomerId,\n\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\t\t// Check if customer was deleted\n\t\t\t\t\t\t\t\t\t\tif (stripeCustomer.deleted) {\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\t\t\t\t`Stripe customer ${user.stripeCustomerId} was deleted, cannot update email`,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// If Stripe customer email doesn't match the user's current email, update it\n\t\t\t\t\t\t\t\t\t\tif (stripeCustomer.email !== user.email) {\n\t\t\t\t\t\t\t\t\t\t\tawait client.customers.update(user.stripeCustomerId, {\n\t\t\t\t\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t\t\t\t\t\t\t\t`Updated Stripe customer email from ${stripeCustomer.email} to ${user.email}`,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t\t\t\t\t// Ignore errors - this is a best-effort sync\n\t\t\t\t\t\t\t\t\t\t// Email might have been deleted or Stripe customer might not exist\n\t\t\t\t\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\t\t\t\t`Failed to sync email to Stripe customer: ${e.message}`,\n\t\t\t\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\tschema: getSchema(options),\n\t\toptions: options as NoInfer<O>,\n\t\t$ERROR_CODES: STRIPE_ERROR_CODES,\n\t} satisfies BetterAuthPlugin;\n};\n\nexport type StripePlugin<O extends StripeOptions> = ReturnType<\n\ttypeof stripe<O>\n>;\n\nexport type * from \"./types\";\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/stripe",
3
- "version": "1.5.1-beta.1",
3
+ "version": "1.5.1-beta.2",
4
4
  "description": "Stripe plugin for Better Auth",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -57,14 +57,14 @@
57
57
  "better-call": "1.3.2",
58
58
  "stripe": "^20.4.0",
59
59
  "tsdown": "0.21.0-beta.2",
60
- "@better-auth/core": "1.5.1-beta.1",
61
- "better-auth": "1.5.1-beta.1"
60
+ "@better-auth/core": "1.5.1-beta.2",
61
+ "better-auth": "1.5.1-beta.2"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "better-call": "1.3.2",
65
65
  "stripe": "^18 || ^19 || ^20",
66
- "@better-auth/core": "1.5.1-beta.1",
67
- "better-auth": "1.5.1-beta.1"
66
+ "@better-auth/core": "1.5.1-beta.2",
67
+ "better-auth": "1.5.1-beta.2"
68
68
  },
69
69
  "scripts": {
70
70
  "build": "tsdown",