@better-auth/stripe 1.4.16 → 1.4.18
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/.turbo/turbo-build.log +10 -8
- package/dist/client.d.mts +2 -1
- package/dist/client.mjs +2 -1
- package/dist/client.mjs.map +1 -0
- package/dist/index-BTvn0abC.d.mts +2 -1
- package/dist/index.mjs +77 -51
- package/dist/index.mjs.map +1 -0
- package/package.json +6 -6
- package/src/client.ts +1 -1
- package/src/hooks.ts +10 -5
- package/src/index.ts +10 -6
- package/src/metadata.ts +94 -0
- package/src/routes.ts +83 -95
- package/test/stripe.test.ts +398 -1
- package/tsdown.config.ts +1 -0
- package/CHANGELOG.md +0 -22
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @better-auth/stripe@1.4.
|
|
2
|
+
> @better-auth/stripe@1.4.18 build /home/runner/work/better-auth/better-auth/packages/stripe
|
|
3
3
|
> tsdown
|
|
4
4
|
|
|
5
5
|
[34mℹ[39m tsdown [2mv0.17.2[22m powered by rolldown [2mv1.0.0-beta.53[22m
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
[34mℹ[39m entry: [34msrc/index.ts, src/client.ts[39m
|
|
8
8
|
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
9
9
|
[34mℹ[39m Build start
|
|
10
|
-
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [
|
|
11
|
-
[34mℹ[39m [2mdist/[22m[1mclient.mjs[22m [2m
|
|
12
|
-
[34mℹ[39m [2mdist/[
|
|
13
|
-
[34mℹ[39m [2mdist/[
|
|
14
|
-
[34mℹ[39m [2mdist/[22m[
|
|
15
|
-
[34mℹ[39m
|
|
16
|
-
[
|
|
10
|
+
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [2m 59.18 kB[22m [2m│ gzip: 10.88 kB[22m
|
|
11
|
+
[34mℹ[39m [2mdist/[22m[1mclient.mjs[22m [2m 0.30 kB[22m [2m│ gzip: 0.23 kB[22m
|
|
12
|
+
[34mℹ[39m [2mdist/[22mindex.mjs.map [2m125.22 kB[22m [2m│ gzip: 23.75 kB[22m
|
|
13
|
+
[34mℹ[39m [2mdist/[22mclient.mjs.map [2m 1.10 kB[22m [2m│ gzip: 0.53 kB[22m
|
|
14
|
+
[34mℹ[39m [2mdist/[22m[32m[1mclient.d.mts[22m[39m [2m 0.66 kB[22m [2m│ gzip: 0.37 kB[22m
|
|
15
|
+
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m 0.21 kB[22m [2m│ gzip: 0.14 kB[22m
|
|
16
|
+
[34mℹ[39m [2mdist/[22m[32mindex-BTvn0abC.d.mts[39m [2m 30.38 kB[22m [2m│ gzip: 5.21 kB[22m
|
|
17
|
+
[34mℹ[39m 7 files, total: 217.05 kB
|
|
18
|
+
[32m✔[39m Build complete in [32m22704ms[39m
|
package/dist/client.d.mts
CHANGED
package/dist/client.mjs
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from \"better-auth/client\";\nimport type { stripe } from \"./index\";\n\nexport const stripeClient = <\n\tO extends {\n\t\tsubscription: boolean;\n\t},\n>(\n\toptions?: O | undefined,\n) => {\n\treturn {\n\t\tid: \"stripe-client\",\n\t\t$InferServerPlugin: {} as ReturnType<\n\t\t\ttypeof stripe<\n\t\t\t\tO[\"subscription\"] extends true\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tstripeClient: any;\n\t\t\t\t\t\t\tstripeWebhookSecret: string;\n\t\t\t\t\t\t\tsubscription: {\n\t\t\t\t\t\t\t\tenabled: true;\n\t\t\t\t\t\t\t\tplans: [];\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\t\t\tstripeClient: any;\n\t\t\t\t\t\t\tstripeWebhookSecret: string;\n\t\t\t\t\t\t}\n\t\t\t>\n\t\t>,\n\t\tpathMethods: {\n\t\t\t\"/subscription/billing-portal\": \"POST\",\n\t\t\t\"/subscription/restore\": \"POST\",\n\t\t},\n\t} satisfies BetterAuthClientPlugin;\n};\n"],"mappings":";AAGA,MAAa,gBAKZ,YACI;AACJ,QAAO;EACN,IAAI;EACJ,oBAAoB,EAAE;EAiBtB,aAAa;GACZ,gCAAgC;GAChC,yBAAyB;GACzB;EACD"}
|
|
@@ -942,4 +942,5 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
942
942
|
};
|
|
943
943
|
type StripePlugin<O extends StripeOptions> = ReturnType<typeof stripe<O>>;
|
|
944
944
|
//#endregion
|
|
945
|
-
export { SubscriptionOptions as a, Subscription as i, stripe as n, StripePlan as r, StripePlugin as t };
|
|
945
|
+
export { SubscriptionOptions as a, Subscription as i, stripe as n, StripePlan as r, StripePlugin as t };
|
|
946
|
+
//# sourceMappingURL=index-BTvn0abC.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -31,6 +31,49 @@ const STRIPE_ERROR_CODES = defineErrorCodes({
|
|
|
31
31
|
ORGANIZATION_REFERENCE_ID_REQUIRED: "Reference ID is required. Provide referenceId or set activeOrganizationId in session"
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/metadata.ts
|
|
36
|
+
/**
|
|
37
|
+
* Customer metadata - set internal fields and extract typed fields.
|
|
38
|
+
*/
|
|
39
|
+
const customerMetadata = {
|
|
40
|
+
keys: {
|
|
41
|
+
userId: "userId",
|
|
42
|
+
organizationId: "organizationId",
|
|
43
|
+
customerType: "customerType"
|
|
44
|
+
},
|
|
45
|
+
set(internalFields, ...userMetadata) {
|
|
46
|
+
return defu(internalFields, ...userMetadata.filter(Boolean));
|
|
47
|
+
},
|
|
48
|
+
get(metadata) {
|
|
49
|
+
return {
|
|
50
|
+
userId: metadata?.userId,
|
|
51
|
+
organizationId: metadata?.organizationId,
|
|
52
|
+
customerType: metadata?.customerType
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Subscription/Checkout metadata - set internal fields and extract typed fields.
|
|
58
|
+
*/
|
|
59
|
+
const subscriptionMetadata = {
|
|
60
|
+
keys: {
|
|
61
|
+
userId: "userId",
|
|
62
|
+
subscriptionId: "subscriptionId",
|
|
63
|
+
referenceId: "referenceId"
|
|
64
|
+
},
|
|
65
|
+
set(internalFields, ...userMetadata) {
|
|
66
|
+
return defu(internalFields, ...userMetadata.filter(Boolean));
|
|
67
|
+
},
|
|
68
|
+
get(metadata) {
|
|
69
|
+
return {
|
|
70
|
+
userId: metadata?.userId,
|
|
71
|
+
subscriptionId: metadata?.subscriptionId,
|
|
72
|
+
referenceId: metadata?.referenceId
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
34
77
|
//#endregion
|
|
35
78
|
//#region src/utils.ts
|
|
36
79
|
async function getPlans(subscriptionOptions) {
|
|
@@ -120,8 +163,9 @@ async function onCheckoutSessionCompleted(ctx, options, event) {
|
|
|
120
163
|
const priceLookupKey = subscriptionItem.price.lookup_key;
|
|
121
164
|
const plan = await getPlanByPriceInfo(options, priceId, priceLookupKey);
|
|
122
165
|
if (plan) {
|
|
123
|
-
const
|
|
124
|
-
const
|
|
166
|
+
const checkoutMeta = subscriptionMetadata.get(checkoutSession?.metadata);
|
|
167
|
+
const referenceId = checkoutSession?.client_reference_id || checkoutMeta.referenceId;
|
|
168
|
+
const { subscriptionId } = checkoutMeta;
|
|
125
169
|
const seats = subscriptionItem.quantity;
|
|
126
170
|
if (referenceId && subscriptionId) {
|
|
127
171
|
const trial = subscription.trial_start && subscription.trial_end ? {
|
|
@@ -179,7 +223,7 @@ async function onSubscriptionCreated(ctx, options, event) {
|
|
|
179
223
|
ctx.context.logger.warn(`Stripe webhook warning: customer.subscription.created event received without customer ID`);
|
|
180
224
|
return;
|
|
181
225
|
}
|
|
182
|
-
const subscriptionId = subscriptionCreated.metadata
|
|
226
|
+
const { subscriptionId } = subscriptionMetadata.get(subscriptionCreated.metadata);
|
|
183
227
|
const existingSubscription = await ctx.context.adapter.findOne({
|
|
184
228
|
model: "subscription",
|
|
185
229
|
where: subscriptionId ? [{
|
|
@@ -256,7 +300,7 @@ async function onSubscriptionUpdated(ctx, options, event) {
|
|
|
256
300
|
const priceId = subscriptionItem.price.id;
|
|
257
301
|
const priceLookupKey = subscriptionItem.price.lookup_key;
|
|
258
302
|
const plan = await getPlanByPriceInfo(options, priceId, priceLookupKey);
|
|
259
|
-
const subscriptionId = subscriptionUpdated.metadata
|
|
303
|
+
const { subscriptionId } = subscriptionMetadata.get(subscriptionUpdated.metadata);
|
|
260
304
|
const customerId = subscriptionUpdated.customer?.toString();
|
|
261
305
|
let subscription = await ctx.context.adapter.findOne({
|
|
262
306
|
model: "subscription",
|
|
@@ -493,21 +537,15 @@ const upgradeSubscription = (options) => {
|
|
|
493
537
|
if (!user$1.emailVerified && subscriptionOptions.requireEmailVerification) throw new APIError$1("BAD_REQUEST", { message: STRIPE_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED });
|
|
494
538
|
const plan = await getPlanByName(options, ctx.body.plan);
|
|
495
539
|
if (!plan) throw new APIError$1("BAD_REQUEST", { message: STRIPE_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND });
|
|
496
|
-
|
|
540
|
+
const subscriptionToUpdate = ctx.body.subscriptionId ? await ctx.context.adapter.findOne({
|
|
497
541
|
model: "subscription",
|
|
498
542
|
where: [{
|
|
499
543
|
field: "stripeSubscriptionId",
|
|
500
544
|
value: ctx.body.subscriptionId
|
|
501
545
|
}]
|
|
502
|
-
}) : referenceId ? await ctx.context.adapter.findOne({
|
|
503
|
-
model: "subscription",
|
|
504
|
-
where: [{
|
|
505
|
-
field: "referenceId",
|
|
506
|
-
value: referenceId
|
|
507
|
-
}]
|
|
508
546
|
}) : null;
|
|
509
|
-
if (ctx.body.subscriptionId && subscriptionToUpdate && subscriptionToUpdate.referenceId !== referenceId) subscriptionToUpdate = null;
|
|
510
547
|
if (ctx.body.subscriptionId && !subscriptionToUpdate) throw new APIError$1("BAD_REQUEST", { message: STRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND });
|
|
548
|
+
if (ctx.body.subscriptionId && subscriptionToUpdate && subscriptionToUpdate.referenceId !== referenceId) throw new APIError$1("BAD_REQUEST", { message: STRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND });
|
|
511
549
|
let customerId;
|
|
512
550
|
if (customerType === "organization") {
|
|
513
551
|
customerId = subscriptionToUpdate?.stripeCustomerId;
|
|
@@ -523,7 +561,7 @@ const upgradeSubscription = (options) => {
|
|
|
523
561
|
customerId = org.stripeCustomerId;
|
|
524
562
|
if (!customerId) try {
|
|
525
563
|
let stripeCustomer = (await client.customers.search({
|
|
526
|
-
query: `metadata["organizationId"]:"${org.id}"`,
|
|
564
|
+
query: `metadata["${customerMetadata.keys.organizationId}"]:"${org.id}"`,
|
|
527
565
|
limit: 1
|
|
528
566
|
})).data[0];
|
|
529
567
|
if (!stripeCustomer) {
|
|
@@ -531,11 +569,10 @@ const upgradeSubscription = (options) => {
|
|
|
531
569
|
if (options.organization?.getCustomerCreateParams) extraCreateParams = await options.organization.getCustomerCreateParams(org, ctx);
|
|
532
570
|
const customerParams = defu({
|
|
533
571
|
name: org.name,
|
|
534
|
-
metadata: {
|
|
535
|
-
...ctx.body.metadata,
|
|
572
|
+
metadata: customerMetadata.set({
|
|
536
573
|
organizationId: org.id,
|
|
537
574
|
customerType: "organization"
|
|
538
|
-
}
|
|
575
|
+
}, ctx.body.metadata)
|
|
539
576
|
}, extraCreateParams);
|
|
540
577
|
stripeCustomer = await client.customers.create(customerParams);
|
|
541
578
|
await options.organization?.onCustomerCreate?.({
|
|
@@ -564,17 +601,16 @@ const upgradeSubscription = (options) => {
|
|
|
564
601
|
customerId = subscriptionToUpdate?.stripeCustomerId || user$1.stripeCustomerId;
|
|
565
602
|
if (!customerId) try {
|
|
566
603
|
let stripeCustomer = (await client.customers.search({
|
|
567
|
-
query: `email:"${escapeStripeSearchValue(user$1.email)}" AND -metadata["customerType"]:"organization"`,
|
|
604
|
+
query: `email:"${escapeStripeSearchValue(user$1.email)}" AND -metadata["${customerMetadata.keys.customerType}"]:"organization"`,
|
|
568
605
|
limit: 1
|
|
569
606
|
})).data[0];
|
|
570
607
|
if (!stripeCustomer) stripeCustomer = await client.customers.create({
|
|
571
608
|
email: user$1.email,
|
|
572
609
|
name: user$1.name,
|
|
573
|
-
metadata: {
|
|
574
|
-
...ctx.body.metadata,
|
|
610
|
+
metadata: customerMetadata.set({
|
|
575
611
|
userId: user$1.id,
|
|
576
612
|
customerType: "user"
|
|
577
|
-
}
|
|
613
|
+
}, ctx.body.metadata)
|
|
578
614
|
});
|
|
579
615
|
await ctx.context.adapter.update({
|
|
580
616
|
model: "user",
|
|
@@ -603,8 +639,18 @@ const upgradeSubscription = (options) => {
|
|
|
603
639
|
if (activeOrTrialingSubscription?.stripeSubscriptionId) return sub.id === activeOrTrialingSubscription.stripeSubscriptionId;
|
|
604
640
|
return false;
|
|
605
641
|
});
|
|
642
|
+
const stripeSubscriptionPriceId = activeSubscription?.items.data[0]?.price.id;
|
|
606
643
|
const incompleteSubscription = subscriptions$1.find((sub) => sub.status === "incomplete");
|
|
607
|
-
|
|
644
|
+
const priceId = ctx.body.annual ? plan.annualDiscountPriceId : plan.priceId;
|
|
645
|
+
const lookupKey = ctx.body.annual ? plan.annualDiscountLookupKey : plan.lookupKey;
|
|
646
|
+
const resolvedPriceId = lookupKey ? await resolvePriceIdFromLookupKey(client, lookupKey) : void 0;
|
|
647
|
+
const priceIdToUse = priceId || resolvedPriceId;
|
|
648
|
+
if (!priceIdToUse) throw ctx.error("BAD_REQUEST", { message: "Price ID not found for the selected plan" });
|
|
649
|
+
const isSamePlan = activeOrTrialingSubscription?.plan === ctx.body.plan;
|
|
650
|
+
const isSameSeats = activeOrTrialingSubscription?.seats === (ctx.body.seats || 1);
|
|
651
|
+
const isSamePriceId = stripeSubscriptionPriceId === priceIdToUse;
|
|
652
|
+
const isSubscriptionStillValid = !activeOrTrialingSubscription?.periodEnd || activeOrTrialingSubscription.periodEnd > /* @__PURE__ */ new Date();
|
|
653
|
+
if (activeOrTrialingSubscription?.status === "active" && isSamePlan && isSameSeats && isSamePriceId && isSubscriptionStillValid) throw new APIError$1("BAD_REQUEST", { message: STRIPE_ERROR_CODES.ALREADY_SUBSCRIBED_PLAN });
|
|
608
654
|
if (activeSubscription && customerId) {
|
|
609
655
|
let dbSubscription = await ctx.context.adapter.findOne({
|
|
610
656
|
model: "subscription",
|
|
@@ -627,15 +673,6 @@ const upgradeSubscription = (options) => {
|
|
|
627
673
|
});
|
|
628
674
|
dbSubscription = activeOrTrialingSubscription;
|
|
629
675
|
}
|
|
630
|
-
let priceIdToUse$1 = void 0;
|
|
631
|
-
if (ctx.body.annual) {
|
|
632
|
-
priceIdToUse$1 = plan.annualDiscountPriceId;
|
|
633
|
-
if (!priceIdToUse$1 && plan.annualDiscountLookupKey) priceIdToUse$1 = await resolvePriceIdFromLookupKey(client, plan.annualDiscountLookupKey);
|
|
634
|
-
} else {
|
|
635
|
-
priceIdToUse$1 = plan.priceId;
|
|
636
|
-
if (!priceIdToUse$1 && plan.lookupKey) priceIdToUse$1 = await resolvePriceIdFromLookupKey(client, plan.lookupKey);
|
|
637
|
-
}
|
|
638
|
-
if (!priceIdToUse$1) throw ctx.error("BAD_REQUEST", { message: "Price ID not found for the selected plan" });
|
|
639
676
|
const { url } = await client.billingPortal.sessions.create({
|
|
640
677
|
customer: customerId,
|
|
641
678
|
return_url: getUrl(ctx, ctx.body.returnUrl || "/"),
|
|
@@ -650,7 +687,7 @@ const upgradeSubscription = (options) => {
|
|
|
650
687
|
items: [{
|
|
651
688
|
id: activeSubscription.items.data[0]?.id,
|
|
652
689
|
quantity: ctx.body.seats || 1,
|
|
653
|
-
price: priceIdToUse
|
|
690
|
+
price: priceIdToUse
|
|
654
691
|
}]
|
|
655
692
|
}
|
|
656
693
|
}
|
|
@@ -707,14 +744,6 @@ const upgradeSubscription = (options) => {
|
|
|
707
744
|
})).some((s) => {
|
|
708
745
|
return !!(s.trialStart || s.trialEnd) || s.status === "trialing";
|
|
709
746
|
}) && plan.freeTrial ? { trial_period_days: plan.freeTrial.days } : void 0;
|
|
710
|
-
let priceIdToUse = void 0;
|
|
711
|
-
if (ctx.body.annual) {
|
|
712
|
-
priceIdToUse = plan.annualDiscountPriceId;
|
|
713
|
-
if (!priceIdToUse && plan.annualDiscountLookupKey) priceIdToUse = await resolvePriceIdFromLookupKey(client, plan.annualDiscountLookupKey);
|
|
714
|
-
} else {
|
|
715
|
-
priceIdToUse = plan.priceId;
|
|
716
|
-
if (!priceIdToUse && plan.lookupKey) priceIdToUse = await resolvePriceIdFromLookupKey(client, plan.lookupKey);
|
|
717
|
-
}
|
|
718
747
|
const checkoutSession = await client.checkout.sessions.create({
|
|
719
748
|
...customerId ? {
|
|
720
749
|
customer: customerId,
|
|
@@ -732,24 +761,20 @@ const upgradeSubscription = (options) => {
|
|
|
732
761
|
}],
|
|
733
762
|
subscription_data: {
|
|
734
763
|
...freeTrial,
|
|
735
|
-
metadata: {
|
|
736
|
-
...ctx.body.metadata,
|
|
737
|
-
...params?.params?.subscription_data?.metadata,
|
|
764
|
+
metadata: subscriptionMetadata.set({
|
|
738
765
|
userId: user$1.id,
|
|
739
766
|
subscriptionId: subscription.id,
|
|
740
767
|
referenceId
|
|
741
|
-
}
|
|
768
|
+
}, ctx.body.metadata, params?.params?.subscription_data?.metadata)
|
|
742
769
|
},
|
|
743
770
|
mode: "subscription",
|
|
744
771
|
client_reference_id: referenceId,
|
|
745
772
|
...params?.params,
|
|
746
|
-
metadata: {
|
|
747
|
-
...ctx.body.metadata,
|
|
748
|
-
...params?.params?.metadata,
|
|
773
|
+
metadata: subscriptionMetadata.set({
|
|
749
774
|
userId: user$1.id,
|
|
750
775
|
subscriptionId: subscription.id,
|
|
751
776
|
referenceId
|
|
752
|
-
}
|
|
777
|
+
}, ctx.body.metadata, params?.params?.metadata)
|
|
753
778
|
}, params?.options).catch(async (e) => {
|
|
754
779
|
throw ctx.error("BAD_REQUEST", {
|
|
755
780
|
message: e.message,
|
|
@@ -1400,7 +1425,7 @@ const stripe = (options) => {
|
|
|
1400
1425
|
if (!ctx$1 || !options.createCustomerOnSignUp || user$1.stripeCustomerId) return;
|
|
1401
1426
|
try {
|
|
1402
1427
|
let stripeCustomer = (await client.customers.search({
|
|
1403
|
-
query: `email:"${escapeStripeSearchValue(user$1.email)}" AND -metadata["customerType"]:"organization"`,
|
|
1428
|
+
query: `email:"${escapeStripeSearchValue(user$1.email)}" AND -metadata["${customerMetadata.keys.customerType}"]:"organization"`,
|
|
1404
1429
|
limit: 1
|
|
1405
1430
|
})).data[0];
|
|
1406
1431
|
if (stripeCustomer) {
|
|
@@ -1420,10 +1445,10 @@ const stripe = (options) => {
|
|
|
1420
1445
|
const params = defu({
|
|
1421
1446
|
email: user$1.email,
|
|
1422
1447
|
name: user$1.name,
|
|
1423
|
-
metadata: {
|
|
1448
|
+
metadata: customerMetadata.set({
|
|
1424
1449
|
userId: user$1.id,
|
|
1425
1450
|
customerType: "user"
|
|
1426
|
-
}
|
|
1451
|
+
}, extraCreateParams?.metadata)
|
|
1427
1452
|
}, extraCreateParams);
|
|
1428
1453
|
stripeCustomer = await client.customers.create(params);
|
|
1429
1454
|
await ctx$1.context.internalAdapter.updateUser(user$1.id, { stripeCustomerId: stripeCustomer.id });
|
|
@@ -1464,4 +1489,5 @@ const stripe = (options) => {
|
|
|
1464
1489
|
};
|
|
1465
1490
|
|
|
1466
1491
|
//#endregion
|
|
1467
|
-
export { stripe };
|
|
1492
|
+
export { stripe };
|
|
1493
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["user","e: any","error: any","APIError","customerType: CustomerType","APIError","user","customerId: string | undefined","extraCreateParams: Partial<StripeType.CustomerCreateParams>","e: any","subscriptions","subscription: Subscription | undefined","updateParams: Stripe.SubscriptionUpdateParams","error: any","event: Stripe.Event","err: any","baseSchema: BetterAuthPluginDBSchema","organization","e: any","subscriptions","error: any","ctx","user","extraCreateParams: Partial<Stripe.CustomerCreateParams>"],"sources":["../src/error-codes.ts","../src/metadata.ts","../src/utils.ts","../src/hooks.ts","../src/middleware.ts","../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { defineErrorCodes } from \"@better-auth/core/utils\";\n\nexport const STRIPE_ERROR_CODES = defineErrorCodes({\n\tUNAUTHORIZED: \"Unauthorized access\",\n\tINVALID_REQUEST_BODY: \"Invalid request body\",\n\tSUBSCRIPTION_NOT_FOUND: \"Subscription not found\",\n\tSUBSCRIPTION_PLAN_NOT_FOUND: \"Subscription plan not found\",\n\tALREADY_SUBSCRIBED_PLAN: \"You're already subscribed to this plan\",\n\tREFERENCE_ID_NOT_ALLOWED: \"Reference id is not allowed\",\n\tCUSTOMER_NOT_FOUND: \"Stripe customer not found for this user\",\n\tUNABLE_TO_CREATE_CUSTOMER: \"Unable to create customer\",\n\tUNABLE_TO_CREATE_BILLING_PORTAL: \"Unable to create billing portal session\",\n\tSTRIPE_SIGNATURE_NOT_FOUND: \"Stripe signature not found\",\n\tSTRIPE_WEBHOOK_SECRET_NOT_FOUND: \"Stripe webhook secret not found\",\n\tSTRIPE_WEBHOOK_ERROR: \"Stripe webhook error\",\n\tFAILED_TO_CONSTRUCT_STRIPE_EVENT: \"Failed to construct Stripe event\",\n\tFAILED_TO_FETCH_PLANS: \"Failed to fetch plans\",\n\tEMAIL_VERIFICATION_REQUIRED:\n\t\t\"Email verification is required before you can subscribe to a plan\",\n\tSUBSCRIPTION_NOT_ACTIVE: \"Subscription is not active\",\n\tSUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION:\n\t\t\"Subscription is not scheduled for cancellation\",\n\tORGANIZATION_NOT_FOUND: \"Organization not found\",\n\tORGANIZATION_SUBSCRIPTION_NOT_ENABLED:\n\t\t\"Organization subscription is not enabled\",\n\tORGANIZATION_HAS_ACTIVE_SUBSCRIPTION:\n\t\t\"Cannot delete organization with active subscription\",\n\tORGANIZATION_REFERENCE_ID_REQUIRED:\n\t\t\"Reference ID is required. Provide referenceId or set activeOrganizationId in session\",\n});\n","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, 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 getPlanByPriceInfo(\n\toptions: StripeOptions,\n\tpriceId: string,\n\tpriceLookupKey: string | null,\n) {\n\treturn await getPlans(options.subscription).then((res) =>\n\t\tres?.find(\n\t\t\t(plan) =>\n\t\t\t\tplan.priceId === priceId ||\n\t\t\t\tplan.annualDiscountPriceId === priceId ||\n\t\t\t\t(priceLookupKey &&\n\t\t\t\t\t(plan.lookupKey === priceLookupKey ||\n\t\t\t\t\t\tplan.annualDiscountLookupKey === priceLookupKey)),\n\t\t),\n\t);\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","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\tgetPlanByPriceInfo,\n\tisActiveOrTrialing,\n\tisPendingCancel,\n\tisStripePendingCancel,\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 subscriptionItem = subscription.items.data[0];\n\t\tif (!subscriptionItem) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscription.id} has no items`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst priceId = subscriptionItem.price.id;\n\t\tconst priceLookupKey = subscriptionItem.price.lookup_key;\n\t\tconst plan = await getPlanByPriceInfo(\n\t\t\toptions,\n\t\t\tpriceId as string,\n\t\t\tpriceLookupKey,\n\t\t);\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 = subscriptionItem.quantity;\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\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\t...trial,\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 subscriptionItem = subscriptionCreated.items.data[0];\n\t\tif (!subscriptionItem) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscriptionCreated.id} has no items`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst priceId = subscriptionItem.price.id;\n\t\tconst priceLookupKey = subscriptionItem.price.lookup_key || null;\n\t\tconst plan = await getPlanByPriceInfo(options, priceId, priceLookupKey);\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: ${priceId}`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst seats = subscriptionItem.quantity;\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\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\t...(plan.limits ? { limits: plan.limits } : {}),\n\t\t\t\t...trial,\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 subscriptionItem = subscriptionUpdated.items.data[0];\n\t\tif (!subscriptionItem) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscriptionUpdated.id} has no items`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst priceId = subscriptionItem.price.id;\n\t\tconst priceLookupKey = subscriptionItem.price.lookup_key;\n\t\tconst plan = await getPlanByPriceInfo(options, priceId, priceLookupKey);\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 updatedSubscription = await ctx.context.adapter.update<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\tupdate: {\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: subscriptionItem.quantity,\n\t\t\t\tstripeSubscriptionId: subscriptionUpdated.id,\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\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\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},\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, 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 new APIError(\"UNAUTHORIZED\", {\n\t\t\t\tmessage: STRIPE_ERROR_CODES.UNAUTHORIZED,\n\t\t\t});\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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_ERROR_CODES.ORGANIZATION_SUBSCRIPTION_NOT_ENABLED,\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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\tmessage: STRIPE_ERROR_CODES.UNAUTHORIZED,\n\t\t\t\t});\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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: STRIPE_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 new APIError(\"UNAUTHORIZED\", {\n\t\t\t\tmessage: STRIPE_ERROR_CODES.UNAUTHORIZED,\n\t\t\t});\n\t\t}\n\t});\n","import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport type { GenericEndpointContext, User } from \"better-auth\";\nimport { HIDE_METADATA } from \"better-auth\";\nimport { APIError, 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\tgetPlanByPriceInfo,\n\tgetPlans,\n\tisActiveOrTrialing,\n\tisPendingCancel,\n\tisStripePendingCancel,\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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: STRIPE_ERROR_CODES.ORGANIZATION_SUBSCRIPTION_NOT_ENABLED,\n\t\t\t});\n\t\t}\n\n\t\tif (!session.activeOrganizationId) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: STRIPE_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 * 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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\tmessage: STRIPE_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// First, search for existing organization customer by organizationId\n\t\t\t\t\t\t\tconst existingOrgCustomers = await client.customers.search({\n\t\t\t\t\t\t\t\tquery: `metadata[\"${customerMetadata.keys.organizationId}\"]:\"${org.id}\"`,\n\t\t\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tlet stripeCustomer = existingOrgCustomers.data[0];\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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: STRIPE_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// Try to find existing user Stripe customer by email\n\t\t\t\t\t\tconst existingCustomers = await client.customers.search({\n\t\t\t\t\t\t\tquery: `email:\"${escapeStripeSearchValue(user.email)}\" AND -metadata[\"${customerMetadata.keys.customerType}\"]:\"organization\"`,\n\t\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tlet stripeCustomer = existingCustomers.data[0];\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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\tmessage: STRIPE_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 stripeSubscriptionPriceId =\n\t\t\t\tactiveSubscription?.items.data[0]?.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\tconst isSamePlan = activeOrTrialingSubscription?.plan === ctx.body.plan;\n\t\t\tconst isSameSeats =\n\t\t\t\tactiveOrTrialingSubscription?.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\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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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\tconst { url } = await client.billingPortal.sessions\n\t\t\t\t\t.create({\n\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl || \"/\"),\n\t\t\t\t\t\tflow_data: {\n\t\t\t\t\t\t\ttype: \"subscription_update_confirm\",\n\t\t\t\t\t\t\tafter_completion: {\n\t\t\t\t\t\t\t\ttype: \"redirect\",\n\t\t\t\t\t\t\t\tredirect: {\n\t\t\t\t\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl || \"/\"),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tsubscription_update_confirm: {\n\t\t\t\t\t\t\t\tsubscription: activeSubscription.id,\n\t\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tid: activeSubscription.items.data[0]?.id as string,\n\t\t\t\t\t\t\t\t\t\tquantity: ctx.body.seats || 1,\n\t\t\t\t\t\t\t\t\t\tprice: priceIdToUse,\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\t.catch(async (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\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}\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: 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: 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 new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: STRIPE_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)}&subscriptionId=${encodeURIComponent(subscription.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{\n\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\tquantity: ctx.body.seats || 1,\n\t\t\t\t\t\t\t},\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 cancelSubscriptionCallbackQuerySchema = z\n\t.record(z.string(), z.any())\n\t.optional();\n\nexport const cancelSubscriptionCallback = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/cancel/callback\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: cancelSubscriptionCallbackQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"cancelSubscriptionCallback\",\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\tif (!ctx.query || !ctx.query.callbackURL || !ctx.query.subscriptionId) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, ctx.query?.callbackURL || \"/\"));\n\t\t\t}\n\t\t\tconst session = await getSessionFromCtx<User & WithStripeCustomerId>(ctx);\n\t\t\tif (!session) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, ctx.query?.callbackURL || \"/\"));\n\t\t\t}\n\t\t\tconst { user } = session;\n\t\t\tconst { callbackURL, subscriptionId } = ctx.query;\n\n\t\t\tif (user?.stripeCustomerId) {\n\t\t\t\ttry {\n\t\t\t\t\tconst subscription = 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\tif (\n\t\t\t\t\t\t!subscription ||\n\t\t\t\t\t\tsubscription.status === \"canceled\" ||\n\t\t\t\t\t\tisPendingCancel(subscription)\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t\t\t}\n\n\t\t\t\t\tconst stripeSubscription = await client.subscriptions.list({\n\t\t\t\t\t\tcustomer: user.stripeCustomerId,\n\t\t\t\t\t\tstatus: \"active\",\n\t\t\t\t\t});\n\t\t\t\t\tconst currentSubscription = stripeSubscription.data.find(\n\t\t\t\t\t\t(sub) => sub.id === subscription.stripeSubscriptionId,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst isNewCancellation =\n\t\t\t\t\t\tcurrentSubscription &&\n\t\t\t\t\t\tisStripePendingCancel(currentSubscription) &&\n\t\t\t\t\t\t!isPendingCancel(subscription);\n\t\t\t\t\tif (isNewCancellation) {\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\tstatus: currentSubscription?.status,\n\t\t\t\t\t\t\t\tcancelAtPeriodEnd:\n\t\t\t\t\t\t\t\t\tcurrentSubscription?.cancel_at_period_end || false,\n\t\t\t\t\t\t\t\tcancelAt: currentSubscription?.cancel_at\n\t\t\t\t\t\t\t\t\t? new Date(currentSubscription.cancel_at * 1000)\n\t\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t\t\tcanceledAt: currentSubscription?.canceled_at\n\t\t\t\t\t\t\t\t\t? new Date(currentSubscription.canceled_at * 1000)\n\t\t\t\t\t\t\t\t\t: null,\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: subscription.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\tawait subscriptionOptions.onSubscriptionCancel?.({\n\t\t\t\t\t\t\tsubscription,\n\t\t\t\t\t\t\tcancellationDetails: currentSubscription.cancellation_details,\n\t\t\t\t\t\t\tstripeSubscription: currentSubscription,\n\t\t\t\t\t\t\tevent: undefined,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\"Error checking subscription status from Stripe\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\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 ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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(\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\tctx.context.baseURL\n\t\t\t\t\t\t}/subscription/cancel/callback?callbackURL=${encodeURIComponent(\n\t\t\t\t\t\t\tctx.body?.returnUrl || \"/\",\n\t\t\t\t\t\t)}&subscriptionId=${encodeURIComponent(subscription.id)}`,\n\t\t\t\t\t),\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 ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_ERROR_CODES.SUBSCRIPTION_NOT_ACTIVE,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (!isPendingCancel(subscription)) {\n\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION,\n\t\t\t\t});\n\t\t\t}\n\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 ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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\tif (!ctx.query || !ctx.query.callbackURL || !ctx.query.subscriptionId) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, ctx.query?.callbackURL || \"/\"));\n\t\t\t}\n\t\t\tconst { callbackURL, subscriptionId } = ctx.query;\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, ctx.query?.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 subscriptionItem = stripeSubscription.items.data[0];\n\t\t\tif (!subscriptionItem) {\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 plan = await getPlanByPriceInfo(\n\t\t\t\toptions,\n\t\t\t\tsubscriptionItem.price.id,\n\t\t\t\tsubscriptionItem.price.lookup_key,\n\t\t\t);\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\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\tupdate: {\n\t\t\t\t\tstatus: stripeSubscription.status,\n\t\t\t\t\tseats: subscriptionItem.quantity || 1,\n\t\t\t\t\tplan: plan.name.toLowerCase(),\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\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},\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 new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: STRIPE_ERROR_CODES.CUSTOMER_NOT_FOUND,\n\t\t\t\t});\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 new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\t\tmessage: STRIPE_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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\t\tmessage: STRIPE_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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: STRIPE_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},\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 {\n\tOrganization,\n\tOrganizationOptions,\n\tOrganizationPlugin,\n} 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\tcancelSubscriptionCallback,\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\tSubscriptionOptions,\n\tWithStripeCustomerId,\n} from \"./types\";\nimport { escapeStripeSearchValue } from \"./utils\";\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\tcancelSubscriptionCallback: cancelSubscriptionCallback(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\tif (options.organization?.enabled) {\n\t\t\t\tconst orgPlugin =\n\t\t\t\t\tctx.getPlugin<OrganizationPlugin<OrganizationOptions>>(\n\t\t\t\t\t\t\"organization\",\n\t\t\t\t\t);\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 new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\t\tmessage:\n\t\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\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};\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\tconst existingCustomers = await client.customers.search({\n\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\tlimit: 1,\n\t\t\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\t\t\tlet stripeCustomer = existingCustomers.data[0];\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 { Subscription, SubscriptionOptions, StripePlan };\n"],"mappings":";;;;;;;;;AAEA,MAAa,qBAAqB,iBAAiB;CAClD,cAAc;CACd,sBAAsB;CACtB,wBAAwB;CACxB,6BAA6B;CAC7B,yBAAyB;CACzB,0BAA0B;CAC1B,oBAAoB;CACpB,2BAA2B;CAC3B,iCAAiC;CACjC,4BAA4B;CAC5B,iCAAiC;CACjC,sBAAsB;CACtB,kCAAkC;CAClC,uBAAuB;CACvB,6BACC;CACD,yBAAyB;CACzB,6CACC;CACD,wBAAwB;CACxB,uCACC;CACD,sCACC;CACD,oCACC;CACD,CAAC;;;;;;;ACPF,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,mBACrB,SACA,SACA,gBACC;AACD,QAAO,MAAM,SAAS,QAAQ,aAAa,CAAC,MAAM,QACjD,KAAK,MACH,SACA,KAAK,YAAY,WACjB,KAAK,0BAA0B,WAC9B,mBACC,KAAK,cAAc,kBACnB,KAAK,4BAA4B,gBACpC,CACD;;AAGF,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;;;;;;;;;ACnDlC,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,MAAMA,SAAO,MAAM,IAAI,QAAQ,QAAQ,QAAc;EACpD,OAAO;EACP,OAAO,CAAC;GAAE,OAAO;GAAoB,OAAO;GAAkB,CAAC;EAC/D,CAAC;AACF,KAAIA,OAAM,QAAO;EAAE,cAAc;EAAQ,aAAaA,OAAK;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,mBAAmB,aAAa,MAAM,KAAK;AACjD,MAAI,CAAC,kBAAkB;AACtB,OAAI,QAAQ,OAAO,KAClB,wCAAwC,aAAa,GAAG,eACxD;AACD;;EAGD,MAAM,UAAU,iBAAiB,MAAM;EACvC,MAAM,iBAAiB,iBAAiB,MAAM;EAC9C,MAAM,OAAO,MAAM,mBAClB,SACA,SACA,eACA;AACD,MAAI,MAAM;GACT,MAAM,eAAe,qBAAqB,IAAI,iBAAiB,SAAS;GACxE,MAAM,cACL,iBAAiB,uBAAuB,aAAa;GACtD,MAAM,EAAE,mBAAmB;GAC3B,MAAM,QAAQ,iBAAiB;AAC/B,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,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,GAAG;MACH;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;;;UAGMC,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,mBAAmB,oBAAoB,MAAM,KAAK;AACxD,MAAI,CAAC,kBAAkB;AACtB,OAAI,QAAQ,OAAO,KAClB,wCAAwC,oBAAoB,GAAG,eAC/D;AACD;;EAGD,MAAM,UAAU,iBAAiB,MAAM;EAEvC,MAAM,OAAO,MAAM,mBAAmB,SAAS,SADxB,iBAAiB,MAAM,cAAc,KACW;AACvE,MAAI,CAAC,MAAM;AACV,OAAI,QAAQ,OAAO,KAClB,+DAA+D,UAC/D;AACD;;EAGD,MAAM,QAAQ,iBAAiB;EAC/B,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;IACA;IACA,sBAAsB,oBAAoB;IAC1C,QAAQ,oBAAoB;IAC5B,MAAM,KAAK,KAAK,aAAa;IAC7B;IACA;IACA;IACA,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,QAAQ,GAAG,EAAE;IAC9C,GAAG;IACH;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;UACMC,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,mBAAmB,oBAAoB,MAAM,KAAK;AACxD,MAAI,CAAC,kBAAkB;AACtB,OAAI,QAAQ,OAAO,KAClB,wCAAwC,oBAAoB,GAAG,eAC/D;AACD;;EAGD,MAAM,UAAU,iBAAiB,MAAM;EACvC,MAAM,iBAAiB,iBAAiB,MAAM;EAC9C,MAAM,OAAO,MAAM,mBAAmB,SAAS,SAAS,eAAe;EAEvE,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,sBAAsB,MAAM,IAAI,QAAQ,QAAQ,OAAqB;GAC1E,OAAO;GACP,QAAQ;IACP,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,OAAO,iBAAiB;IACxB,sBAAsB,oBAAoB;IAC1C;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;;UAGhDA,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;AACjB,SAAM,IAAI,QAAQ,QAAQ,OAAO;IAChC,OAAO;IACP,OAAO,CACN;KACC,OAAO;KACP,OAAO,aAAa;KACpB,CACD;IACD,QAAQ;KACP,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;IACD,CAAC;AACF,SAAM,QAAQ,aAAa,wBAAwB;IAClD;IACA,oBAAoB;IACpB;IACA,CAAC;QAEF,KAAI,QAAQ,OAAO,KAClB,oEAAoE,iBACpE;UAEMA,OAAY;AACpB,MAAI,QAAQ,OAAO,MAAM,iCAAiC,QAAQ;;;;;;AC3apE,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,OAAM,IAAIC,WAAS,gBAAgB,EAClC,SAAS,mBAAmB,cAC5B,CAAC;CAGH,MAAMC,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,SAAM,IAAID,WAAS,eAAe,EACjC,SAAS,mBAAmB,uCAC5B,CAAC;;EAGH,MAAM,cACL,uBAAuB,WAAW,QAAQ;AAC3C,MAAI,CAAC,YACJ,OAAM,IAAIA,WAAS,eAAe,EACjC,SAAS,mBAAmB,oCAC5B,CAAC;AAWH,MAAI,CATiB,MAAM,oBAAoB,mBAC9C;GACC,MAAM,WAAW;GACjB,SAAS,WAAW;GACpB;GACA;GACA,EACD,IACA,CAEA,OAAM,IAAIA,WAAS,gBAAgB,EAClC,SAAS,mBAAmB,cAC5B,CAAC;AAEH;;AAID,KAAI,CAAC,oBACJ;AAID,KAAI,wBAAwB,WAAW,KAAK,GAC3C;AAGD,KAAI,CAAC,oBAAoB,oBAAoB;AAC5C,MAAI,QAAQ,OAAO,MAClB,8IACA;AACD,QAAM,IAAIA,WAAS,eAAe,EACjC,SAAS,mBAAmB,0BAC5B,CAAC;;AAWH,KAAI,CATiB,MAAM,oBAAoB,mBAC9C;EACC,MAAM,WAAW;EACjB,SAAS,WAAW;EACpB,aAAa;EACb;EACA,EACD,IACA,CAEA,OAAM,IAAIA,WAAS,gBAAgB,EAClC,SAAS,mBAAmB,cAC5B,CAAC;EAEF;;;;;;;;ACjEH,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,cAAM,YAAY;AAG1B,MAFa,gBAAgB,YAEhB,gBAAgB;AAC5B,MAAI,CAAC,QAAQ,cAAc,QAC1B,OAAM,IAAIE,WAAS,eAAe,EACjC,SAAS,mBAAmB,uCAC5B,CAAC;AAGH,MAAI,CAAC,QAAQ,qBACZ,OAAM,IAAIA,WAAS,eAAe,EACjC,SAAS,mBAAmB,wBAC5B,CAAC;AAEH,SAAO,QAAQ;;AAGhB,QAAOC,OAAK;;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,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,cAAM,YAAY,IAAI,QAAQ;EACtC,MAAM,eAAe,IAAI,KAAK,gBAAgB;EAC9C,MAAM,cACL,IAAI,KAAK,eACT,eAAe,IAAI,QAAQ,SAAS,cAAc,QAAQ;AAE3D,MAAI,CAACA,OAAK,iBAAiB,oBAAoB,yBAC9C,OAAM,IAAID,WAAS,eAAe,EACjC,SAAS,mBAAmB,6BAC5B,CAAC;EAGH,MAAM,OAAO,MAAM,cAAc,SAAS,IAAI,KAAK,KAAK;AACxD,MAAI,CAAC,KACJ,OAAM,IAAIA,WAAS,eAAe,EACjC,SAAS,mBAAmB,6BAC5B,CAAC;EAKH,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,OAAM,IAAIA,WAAS,eAAe,EACjC,SAAS,mBAAmB,wBAC5B,CAAC;AAEH,MACC,IAAI,KAAK,kBACT,wBACA,qBAAqB,gBAAgB,YAErC,OAAM,IAAIA,WAAS,eAAe,EACjC,SAAS,mBAAmB,wBAC5B,CAAC;EAIH,IAAIE;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,OAAM,IAAIF,WAAS,eAAe,EACjC,SAAS,mBAAmB,wBAC5B,CAAC;AAEH,iBAAa,IAAI;AAGjB,QAAI,CAAC,WACJ,KAAI;KAOH,IAAI,kBALyB,MAAM,OAAO,UAAU,OAAO;MAC1D,OAAO,aAAa,iBAAiB,KAAK,eAAe,MAAM,IAAI,GAAG;MACtE,OAAO;MACP,CAAC,EAEwC,KAAK;AAE/C,SAAI,CAAC,gBAAgB;MAEpB,IAAIG,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;aACpBC,GAAQ;AAChB,SAAI,QAAQ,OAAO,MAAM,EAAE;AAC3B,WAAM,IAAIJ,WAAS,eAAe,EACjC,SAAS,mBAAmB,2BAC5B,CAAC;;;SAIC;AAEN,gBACC,sBAAsB,oBAAoBC,OAAK;AAChD,OAAI,CAAC,WACJ,KAAI;IAOH,IAAI,kBALsB,MAAM,OAAO,UAAU,OAAO;KACvD,OAAO,UAAU,wBAAwBA,OAAK,MAAM,CAAC,mBAAmB,iBAAiB,KAAK,aAAa;KAC3G,OAAO;KACP,CAAC,EAEqC,KAAK;AAE5C,QAAI,CAAC,eACJ,kBAAiB,MAAM,OAAO,UAAU,OAAO;KAC9C,OAAOA,OAAK;KACZ,MAAMA,OAAK;KACX,UAAU,iBAAiB,IAC1B;MACC,QAAQA,OAAK;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,OAAOA,OAAK;MACZ,CACD;KACD,CAAC;AAEF,iBAAa,eAAe;YACpBG,GAAQ;AAChB,QAAI,QAAQ,OAAO,MAAM,EAAE;AAC3B,UAAM,IAAIJ,WAAS,eAAe,EACjC,SAAS,mBAAmB,2BAC5B,CAAC;;;EAKL,MAAMK,kBAAgB,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+BA,gBAAc,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;EAGF,MAAM,4BACL,oBAAoB,MAAM,KAAK,IAAI,MAAM;EAG1C,MAAM,yBAAyBA,gBAAc,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;EAGH,MAAM,aAAa,8BAA8B,SAAS,IAAI,KAAK;EACnE,MAAM,cACL,8BAA8B,WAAW,IAAI,KAAK,SAAS;EAC5D,MAAM,gBAAgB,8BAA8B;EACpD,MAAM,2BACL,CAAC,8BAA8B,aAC/B,6BAA6B,4BAAY,IAAI,MAAM;AAQpD,MALC,8BAA8B,WAAW,YACzC,cACA,eACA,iBACA,yBAEA,OAAM,IAAIL,WAAS,eAAe,EACjC,SAAS,mBAAmB,yBAC5B,CAAC;AAGH,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;;GAGlB,MAAM,EAAE,QAAQ,MAAM,OAAO,cAAc,SACzC,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,mBAAmB,MAAM,KAAK,IAAI;OACtC,UAAU,IAAI,KAAK,SAAS;OAC5B,OAAO;OACP,CACD;MACD;KACD;IACD,CAAC,CACD,MAAM,OAAO,MAAM;AACnB,UAAM,IAAI,MAAM,eAAe;KAC9B,SAAS,EAAE;KACX,MAAM,EAAE;KACR,CAAC;KACD;AACH,UAAO,IAAI,KAAK;IACf;IACA,UAAU,CAAC,IAAI,KAAK;IACpB,CAAC;;EAGH,IAAIM,eACH,gCAAgC;AAEjC,MAAI,0BAA0B,CAAC,6BAe9B,gBAdgB,MAAM,IAAI,QAAQ,QAAQ,OAAqB;GAC9D,OAAO;GACP,QAAQ;IACP,MAAM,KAAK,KAAK,aAAa;IAC7B,OAAO,IAAI,KAAK,SAAS;IACzB,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,IAAI,KAAK,SAAS;IACzB;GACD,CAAC;AAGH,MAAI,CAAC,cAAc;AAClB,OAAI,QAAQ,OAAO,MAAM,4BAA4B;AACrD,SAAM,IAAIN,WAAS,aAAa,EAC/B,SAAS,mBAAmB,wBAC5B,CAAC;;EAGH,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,gBAAgBC,OAAK,OACrB;GACH,QAAQ,IAAI,KAAK;GACjB,aAAa,OACZ,KACA,GACC,IAAI,QAAQ,QACZ,oCAAoC,mBACpC,IAAI,KAAK,WACT,CAAC,kBAAkB,mBAAmB,aAAa,GAAG,GACvD;GACD,YAAY,OAAO,KAAK,IAAI,KAAK,UAAU;GAC3C,YAAY,CACX;IACC,OAAO;IACP,UAAU,IAAI,KAAK,SAAS;IAC5B,CACD;GACD,mBAAmB;IAClB,GAAG;IACH,UAAU,qBAAqB,IAC9B;KACC,QAAQA,OAAK;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,QAAQA,OAAK;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,wCAAwC,EAC5C,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAC3B,UAAU;AAEZ,MAAa,8BAA8B,YAA2B;CACrE,MAAM,SAAS,QAAQ;CACvB,MAAM,sBAAsB,QAAQ;AACpC,QAAO,mBACN,iCACA;EACC,QAAQ;EACR,OAAO;EACP,UAAU,EACT,SAAS,EACR,aAAa,8BACb,EACD;EACD,KAAK,CAAC,aAAa,QAAQ,IAAI,MAAM,YAAY,CAAC;EAClD,EACD,OAAO,QAAQ;AACd,MAAI,CAAC,IAAI,SAAS,CAAC,IAAI,MAAM,eAAe,CAAC,IAAI,MAAM,eACtD,OAAM,IAAI,SAAS,OAAO,KAAK,IAAI,OAAO,eAAe,IAAI,CAAC;EAE/D,MAAM,UAAU,MAAM,kBAA+C,IAAI;AACzE,MAAI,CAAC,QACJ,OAAM,IAAI,SAAS,OAAO,KAAK,IAAI,OAAO,eAAe,IAAI,CAAC;EAE/D,MAAM,EAAE,iBAAS;EACjB,MAAM,EAAE,aAAa,mBAAmB,IAAI;AAE5C,MAAIA,QAAM,iBACT,KAAI;GACH,MAAM,eAAe,MAAM,IAAI,QAAQ,QAAQ,QAAsB;IACpE,OAAO;IACP,OAAO,CACN;KACC,OAAO;KACP,OAAO;KACP,CACD;IACD,CAAC;AACF,OACC,CAAC,gBACD,aAAa,WAAW,cACxB,gBAAgB,aAAa,CAE7B,OAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;GAO7C,MAAM,uBAJqB,MAAM,OAAO,cAAc,KAAK;IAC1D,UAAUA,OAAK;IACf,QAAQ;IACR,CAAC,EAC6C,KAAK,MAClD,QAAQ,IAAI,OAAO,aAAa,qBACjC;AAMD,OAHC,uBACA,sBAAsB,oBAAoB,IAC1C,CAAC,gBAAgB,aAAa,EACR;AACtB,UAAM,IAAI,QAAQ,QAAQ,OAAO;KAChC,OAAO;KACP,QAAQ;MACP,QAAQ,qBAAqB;MAC7B,mBACC,qBAAqB,wBAAwB;MAC9C,UAAU,qBAAqB,4BAC5B,IAAI,KAAK,oBAAoB,YAAY,IAAK,GAC9C;MACH,YAAY,qBAAqB,8BAC9B,IAAI,KAAK,oBAAoB,cAAc,IAAK,GAChD;MACH;KACD,OAAO,CACN;MACC,OAAO;MACP,OAAO,aAAa;MACpB,CACD;KACD,CAAC;AACF,UAAM,oBAAoB,uBAAuB;KAChD;KACA,qBAAqB,oBAAoB;KACzC,oBAAoB;KACpB,OAAO;KACP,CAAC;;WAEK,OAAO;AACf,OAAI,QAAQ,OAAO,MAClB,kDACA,MACA;;AAGH,QAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;GAE7C;;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,OAAM,IAAI,MAAM,eAAe,EAC9B,SAAS,mBAAmB,wBAC5B,CAAC;EAEH,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,SAAM,IAAI,MAAM,eAAe,EAC9B,SAAS,mBAAmB,wBAC5B,CAAC;;EAEH,MAAM,qBAAqB,oBAAoB,MAC7C,QAAQ,IAAI,OAAO,aAAa,qBACjC;AACD,MAAI,CAAC,mBACJ,OAAM,IAAI,MAAM,eAAe,EAC9B,SAAS,mBAAmB,wBAC5B,CAAC;EAEH,MAAM,EAAE,QAAQ,MAAM,OAAO,cAAc,SACzC,OAAO;GACP,UAAU,aAAa;GACvB,YAAY,OACX,KACA,GACC,IAAI,QAAQ,QACZ,4CAA4C,mBAC5C,IAAI,MAAM,aAAa,IACvB,CAAC,kBAAkB,mBAAmB,aAAa,GAAG,GACvD;GACD,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,OAAM,IAAI,MAAM,eAAe,EAC9B,SAAS,mBAAmB,wBAC5B,CAAC;AAEH,MAAI,CAAC,mBAAmB,aAAa,CACpC,OAAM,IAAI,MAAM,eAAe,EAC9B,SAAS,mBAAmB,yBAC5B,CAAC;AAEH,MAAI,CAAC,gBAAgB,aAAa,CACjC,OAAM,IAAI,MAAM,eAAe,EAC9B,SACC,mBAAmB,6CACpB,CAAC;EAGH,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,OAAM,IAAI,MAAM,eAAe,EAC9B,SAAS,mBAAmB,wBAC5B,CAAC;EAKH,MAAMM,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,MAAMF,kBAAgB,MAAM,IAAI,QAAQ,QAAQ,SAAuB;GACtE,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO;IACP,CACD;GACD,CAAC;AACF,MAAI,CAACA,gBAAc,OAClB,QAAO,EAAE;EAEV,MAAM,QAAQ,MAAM,SAAS,QAAQ,aAAa;AAClD,MAAI,CAAC,MACJ,QAAO,EAAE;EAEV,MAAM,OAAOA,gBACX,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;AACd,MAAI,CAAC,IAAI,SAAS,CAAC,IAAI,MAAM,eAAe,CAAC,IAAI,MAAM,eACtD,OAAM,IAAI,SAAS,OAAO,KAAK,IAAI,OAAO,eAAe,IAAI,CAAC;EAE/D,MAAM,EAAE,aAAa,mBAAmB,IAAI;EAE5C,MAAM,UAAU,MAAM,kBAA+C,IAAI;AACzE,MAAI,CAAC,QACJ,OAAM,IAAI,SAAS,OAAO,KAAK,IAAI,OAAO,eAAe,IAAI,CAAC;EAG/D,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,mBAAmB,mBAAmB,MAAM,KAAK;AACvD,MAAI,CAAC,kBAAkB;AACtB,OAAI,QAAQ,OAAO,KAClB,uDAAuD,mBAAmB,KAC1E;AACD,SAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;;EAG7C,MAAM,OAAO,MAAM,mBAClB,SACA,iBAAiB,MAAM,IACvB,iBAAiB,MAAM,WACvB;AACD,MAAI,CAAC,MAAM;AACV,OAAI,QAAQ,OAAO,KAClB,4BAA4B,iBAAiB,MAAM,KACnD;AACD,SAAM,IAAI,SAAS,OAAO,KAAK,YAAY,CAAC;;AAG7C,QAAM,IAAI,QAAQ,QAAQ,OAAO;GAChC,OAAO;GACP,QAAQ;IACP,QAAQ,mBAAmB;IAC3B,OAAO,iBAAiB,YAAY;IACpC,MAAM,KAAK,KAAK,aAAa;IAC7B,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,GAAI,mBAAmB,eAAe,mBAAmB,YACtD;KACA,4BAAY,IAAI,KAAK,mBAAmB,cAAc,IAAK;KAC3D,0BAAU,IAAI,KAAK,mBAAmB,YAAY,IAAK;KACvD,GACA,EAAE;IACL;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,iBAAS,IAAI,QAAQ;EAC7B,MAAM,eAAe,IAAI,KAAK,gBAAgB;EAC9C,MAAM,cACL,IAAI,KAAK,eACT,eAAe,IAAI,QAAQ,SAAS,cAAc,QAAQ;EAE3D,IAAIH;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,gBAAaD,OAAK;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,OAAM,IAAID,WAAS,aAAa,EAC/B,SAAS,mBAAmB,oBAC5B,CAAC;AAGH,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;WACMQ,OAAY;AACpB,OAAI,QAAQ,OAAO,MAClB,yCACA,MACA;AACD,SAAM,IAAIR,WAAS,yBAAyB,EAC3C,SAAS,mBAAmB,iCAC5B,CAAC;;GAGJ;;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,OAAM,IAAIA,WAAS,eAAe,EACjC,SAAS,mBAAmB,sBAC5B,CAAC;EAGH,MAAM,MAAM,IAAI,QAAQ,QAAQ,IAAI,mBAAmB;AACvD,MAAI,CAAC,IACJ,OAAM,IAAIA,WAAS,eAAe,EACjC,SAAS,mBAAmB,4BAC5B,CAAC;EAGH,MAAM,gBAAgB,QAAQ;AAC9B,MAAI,CAAC,cACJ,OAAM,IAAIA,WAAS,yBAAyB,EAC3C,SAAS,mBAAmB,iCAC5B,CAAC;EAGH,MAAM,UAAU,MAAM,IAAI,QAAQ,MAAM;EAExC,IAAIS;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;WAE5DC,KAAU;AAClB,OAAI,QAAQ,OAAO,MAAM,GAAG,IAAI,UAAU;AAC1C,SAAM,IAAIV,WAAS,eAAe,EACjC,SAAS,mBAAmB,kCAC5B,CAAC;;AAEH,MAAI,CAAC,MACJ,OAAM,IAAIA,WAAS,eAAe,EACjC,SAAS,mBAAmB,kCAC5B,CAAC;AAEH,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;;WAEMI,GAAQ;AAChB,OAAI,QAAQ,OAAO,MAAM,iCAAiC,EAAE,UAAU;AACtE,SAAM,IAAIJ,WAAS,eAAe,EACjC,SAAS,mBAAmB,sBAC5B,CAAC;;AAEH,SAAO,IAAI,KAAK,EAAE,SAAS,MAAM,CAAC;GAEnC;;;;;ACzmDF,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,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,IAAIW,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;;;;;AC/F/C,MAAa,UAAmC,YAAe;CAC9D,MAAM,SAAS,QAAQ;CAEvB,MAAM,wBAAwB;EAC7B,qBAAqB,oBAAoB,QAAQ;EACjD,4BAA4B,2BAA2B,QAAQ;EAC/D,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;AACT,OAAI,QAAQ,cAAc,SAAS;IAClC,MAAM,YACL,IAAI,UACH,eACA;AACF,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,iCAAiB;AACzB,SAAI,CAACC,gBAAc,iBAAkB;AAErC,SAAI;MACH,MAAM,iBAAiB,MAAM,OAAO,UAAU,SAC7CA,eAAa,iBACb;AAED,UAAI,eAAe,SAAS;AAC3B,WAAI,OAAO,KACV,mBAAmBA,eAAa,iBAAiB,cACjD;AACD;;AAID,UAAIA,eAAa,SAAS,eAAe,MAAM;AAC9C,aAAM,OAAO,UAAU,OAAOA,eAAa,kBAAkB,EAC5D,MAAMA,eAAa,MACnB,CAAC;AACF,WAAI,OAAO,KACV,wCAAwC,eAAe,KAAK,OAAOA,eAAa,KAAK,GACrF;;cAEMC,GAAQ;AAChB,UAAI,OAAO,MACV,0CAA0C,EAAE,UAC5C;;;;;;IAOH,MAAM,wBAAwB,OAAO,SAG/B;KACL,MAAM,EAAE,iCAAiB;AACzB,SAAI,CAACD,eAAa,iBAAkB;AAEpC,SAAI;MAEH,MAAME,kBAAgB,MAAM,OAAO,cAAc,KAAK;OACrD,UAAUF,eAAa;OACvB,QAAQ;OACR,OAAO;OACP,CAAC;AACF,WAAK,MAAM,OAAOE,gBAAc,KAC/B,KACC,IAAI,WAAW,cACf,IAAI,WAAW,gBACf,IAAI,WAAW,qBAEf,OAAM,IAAI,SAAS,eAAe,EACjC,SACC,mBAAmB,sCACpB,CAAC;cAGIC,OAAY;AACpB,UAAI,iBAAiB,SACpB,OAAM;AAEP,UAAI,OAAO,MACV,+CAA+C,MAAM,UACrD;AACD,YAAM;;;AAIR,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;;AAGF,UAAO,EACN,SAAS,EACR,eAAe,EACd,MAAM;IACL,QAAQ,EACP,MAAM,MAAM,QAAmC,OAAK;AACnD,SACC,CAACC,SACD,CAAC,QAAQ,0BACTC,OAAK,iBAEL;AAGD,SAAI;MAOH,IAAI,kBALsB,MAAM,OAAO,UAAU,OAAO;OACvD,OAAO,UAAU,wBAAwBA,OAAK,MAAM,CAAC,mBAAmB,iBAAiB,KAAK,aAAa;OAC3G,OAAO;OACP,CAAC,EAEqC,KAAK;AAG5C,UAAI,gBAAgB;AACnB,aAAMD,MAAI,QAAQ,gBAAgB,WAAWC,OAAK,IAAI,EACrD,kBAAkB,eAAe,IACjC,CAAC;AACF,aAAM,QAAQ,mBACb;QACC;QACA,MAAM;SACL,GAAGA;SACH,kBAAkB,eAAe;SACjC;QACD,EACDD,MACA;AACD,aAAI,QAAQ,OAAO,KAClB,mCAAmC,eAAe,GAAG,WAAWC,OAAK,KACrE;AACD;;MAID,IAAIC,oBACH,EAAE;AACH,UAAI,QAAQ,wBACX,qBAAoB,MAAM,QAAQ,wBACjCD,QACAD,MACA;MAGF,MAAM,SAAS,KACd;OACC,OAAOC,OAAK;OACZ,MAAMA,OAAK;OACX,UAAU,iBAAiB,IAC1B;QACC,QAAQA,OAAK;QACb,cAAc;QACd,EACD,mBAAmB,SACnB;OACD,EACD,kBACA;AACD,uBAAiB,MAAM,OAAO,UAAU,OAAO,OAAO;AACtD,YAAMD,MAAI,QAAQ,gBAAgB,WAAWC,OAAK,IAAI,EACrD,kBAAkB,eAAe,IACjC,CAAC;AACF,YAAM,QAAQ,mBACb;OACC;OACA,MAAM;QACL,GAAGA;QACH,kBAAkB,eAAe;QACjC;OACD,EACDD,MACA;AACD,YAAI,QAAQ,OAAO,KAClB,+BAA+B,eAAe,GAAG,YAAYC,OAAK,KAClE;cACOJ,GAAQ;AAChB,YAAI,QAAQ,OAAO,MAClB,6CAA6C,EAAE,WAC/C,EACA;;OAGH;IACD,QAAQ,EACP,MAAM,MAAM,QAAmC,OAAK;AACnD,SACC,CAACG,SACD,CAACC,OAAK,iBAEN;AAED,SAAI;MAIH,MAAM,iBAAiB,MAAM,OAAO,UAAU,SAC7CA,OAAK,iBACL;AAGD,UAAI,eAAe,SAAS;AAC3B,aAAI,QAAQ,OAAO,KAClB,mBAAmBA,OAAK,iBAAiB,mCACzC;AACD;;AAID,UAAI,eAAe,UAAUA,OAAK,OAAO;AACxC,aAAM,OAAO,UAAU,OAAOA,OAAK,kBAAkB,EACpD,OAAOA,OAAK,OACZ,CAAC;AACF,aAAI,QAAQ,OAAO,KAClB,sCAAsC,eAAe,MAAM,MAAMA,OAAK,QACtE;;cAEMJ,GAAQ;AAGhB,YAAI,QAAQ,OAAO,MAClB,4CAA4C,EAAE,WAC9C,EACA;;OAGH;IACD,EACD,EACD,EACD;;EAEF,QAAQ,UAAU,QAAQ;EACjB;EACT,cAAc;EACd"}
|