@better-auth/stripe 1.5.0-beta.11 → 1.5.0-beta.13
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 +11 -11
- package/dist/client.d.mts +46 -44
- package/dist/client.mjs +1 -1
- package/dist/{error-codes-CMowBCzF.mjs → error-codes-CHMyMR5v.mjs} +2 -1
- package/dist/{error-codes-CMowBCzF.mjs.map → error-codes-CHMyMR5v.mjs.map} +1 -1
- package/dist/{index-DRCIVNqh.d.mts → index-BELqjMd2.d.mts} +37 -36
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/src/error-codes.ts +2 -0
- package/src/index.ts +1 -1
- package/src/middleware.ts +1 -1
- package/src/routes.ts +1 -1
- package/test/stripe-organization.test.ts +2 -2
- package/test/stripe.test.ts +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @better-auth/stripe@1.5.0-beta.
|
|
2
|
+
> @better-auth/stripe@1.5.0-beta.13 build /home/runner/work/better-auth/better-auth/packages/stripe
|
|
3
3
|
> tsdown
|
|
4
4
|
|
|
5
5
|
[34mℹ[39m tsdown [2mv0.20.1[22m powered by rolldown [2mv1.0.0-rc.1[22m
|
|
@@ -7,16 +7,16 @@
|
|
|
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 [2m 57.
|
|
11
|
-
[33m[PLUGIN_TIMINGS] Warning:[0m Your build spent significant time in plugin `rolldown-plugin-dts:generate`. See https://rolldown.rs/options/checks#plugintimings for more details.
|
|
10
|
+
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [2m 57.13 kB[22m [2m│ gzip: 10.39 kB[22m
|
|
12
11
|
[34mℹ[39m [2mdist/[22m[1mclient.mjs[22m [2m 0.43 kB[22m [2m│ gzip: 0.29 kB[22m
|
|
13
|
-
|
|
14
|
-
[34mℹ[39m [2mdist/[
|
|
15
|
-
[34mℹ[39m [2mdist/[22merror-codes-
|
|
16
|
-
[34mℹ[39m [2mdist/[22merror-codes-CMowBCzF.mjs [2m 1.67 kB[22m [2m│ gzip: 0.78 kB[22m
|
|
12
|
+
[34mℹ[39m [2mdist/[22mindex.mjs.map [2m122.17 kB[22m [2m│ gzip: 22.93 kB[22m
|
|
13
|
+
[34mℹ[39m [2mdist/[22merror-codes-CHMyMR5v.mjs.map [2m 2.23 kB[22m [2m│ gzip: 1.01 kB[22m
|
|
14
|
+
[34mℹ[39m [2mdist/[22merror-codes-CHMyMR5v.mjs [2m 1.78 kB[22m [2m│ gzip: 0.82 kB[22m
|
|
17
15
|
[34mℹ[39m [2mdist/[22mclient.mjs.map [2m 1.25 kB[22m [2m│ gzip: 0.59 kB[22m
|
|
18
|
-
[34mℹ[39m [2mdist/[22m[32m[1mclient.d.mts[22m[39m [2m 4.
|
|
16
|
+
[34mℹ[39m [2mdist/[22m[32m[1mclient.d.mts[22m[39m [2m 4.61 kB[22m [2m│ gzip: 0.90 kB[22m
|
|
19
17
|
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m 0.21 kB[22m [2m│ gzip: 0.14 kB[22m
|
|
20
|
-
[34mℹ[39m [2mdist/[22m[32mindex-
|
|
21
|
-
[34mℹ[39m 9 files, total: 220.
|
|
22
|
-
[
|
|
18
|
+
[34mℹ[39m [2mdist/[22m[32mindex-BELqjMd2.d.mts[39m [2m 30.82 kB[22m [2m│ gzip: 5.13 kB[22m
|
|
19
|
+
[34mℹ[39m 9 files, total: 220.63 kB
|
|
20
|
+
[33m[PLUGIN_TIMINGS] Warning:[0m Your build spent significant time in plugin `rolldown-plugin-dts:generate`. See https://rolldown.rs/options/checks#plugintimings for more details.
|
|
21
|
+
|
|
22
|
+
[32m✔[39m Build complete in [32m35719ms[39m
|
package/dist/client.d.mts
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
import { n as stripe } from "./index-
|
|
2
|
-
import * as
|
|
1
|
+
import { n as stripe } from "./index-BELqjMd2.mjs";
|
|
2
|
+
import * as better_auth51 from "better-auth";
|
|
3
3
|
|
|
4
4
|
//#region src/error-codes.d.ts
|
|
5
5
|
declare const STRIPE_ERROR_CODES: {
|
|
6
|
-
UNAUTHORIZED:
|
|
7
|
-
INVALID_REQUEST_BODY:
|
|
8
|
-
SUBSCRIPTION_NOT_FOUND:
|
|
9
|
-
SUBSCRIPTION_PLAN_NOT_FOUND:
|
|
10
|
-
ALREADY_SUBSCRIBED_PLAN:
|
|
11
|
-
REFERENCE_ID_NOT_ALLOWED:
|
|
12
|
-
CUSTOMER_NOT_FOUND:
|
|
13
|
-
UNABLE_TO_CREATE_CUSTOMER:
|
|
14
|
-
UNABLE_TO_CREATE_BILLING_PORTAL:
|
|
15
|
-
STRIPE_SIGNATURE_NOT_FOUND:
|
|
16
|
-
STRIPE_WEBHOOK_SECRET_NOT_FOUND:
|
|
17
|
-
STRIPE_WEBHOOK_ERROR:
|
|
18
|
-
FAILED_TO_CONSTRUCT_STRIPE_EVENT:
|
|
19
|
-
FAILED_TO_FETCH_PLANS:
|
|
20
|
-
EMAIL_VERIFICATION_REQUIRED:
|
|
21
|
-
SUBSCRIPTION_NOT_ACTIVE:
|
|
22
|
-
SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION:
|
|
23
|
-
ORGANIZATION_NOT_FOUND:
|
|
24
|
-
ORGANIZATION_SUBSCRIPTION_NOT_ENABLED:
|
|
25
|
-
|
|
26
|
-
|
|
6
|
+
UNAUTHORIZED: better_auth51.RawError<"UNAUTHORIZED">;
|
|
7
|
+
INVALID_REQUEST_BODY: better_auth51.RawError<"INVALID_REQUEST_BODY">;
|
|
8
|
+
SUBSCRIPTION_NOT_FOUND: better_auth51.RawError<"SUBSCRIPTION_NOT_FOUND">;
|
|
9
|
+
SUBSCRIPTION_PLAN_NOT_FOUND: better_auth51.RawError<"SUBSCRIPTION_PLAN_NOT_FOUND">;
|
|
10
|
+
ALREADY_SUBSCRIBED_PLAN: better_auth51.RawError<"ALREADY_SUBSCRIBED_PLAN">;
|
|
11
|
+
REFERENCE_ID_NOT_ALLOWED: better_auth51.RawError<"REFERENCE_ID_NOT_ALLOWED">;
|
|
12
|
+
CUSTOMER_NOT_FOUND: better_auth51.RawError<"CUSTOMER_NOT_FOUND">;
|
|
13
|
+
UNABLE_TO_CREATE_CUSTOMER: better_auth51.RawError<"UNABLE_TO_CREATE_CUSTOMER">;
|
|
14
|
+
UNABLE_TO_CREATE_BILLING_PORTAL: better_auth51.RawError<"UNABLE_TO_CREATE_BILLING_PORTAL">;
|
|
15
|
+
STRIPE_SIGNATURE_NOT_FOUND: better_auth51.RawError<"STRIPE_SIGNATURE_NOT_FOUND">;
|
|
16
|
+
STRIPE_WEBHOOK_SECRET_NOT_FOUND: better_auth51.RawError<"STRIPE_WEBHOOK_SECRET_NOT_FOUND">;
|
|
17
|
+
STRIPE_WEBHOOK_ERROR: better_auth51.RawError<"STRIPE_WEBHOOK_ERROR">;
|
|
18
|
+
FAILED_TO_CONSTRUCT_STRIPE_EVENT: better_auth51.RawError<"FAILED_TO_CONSTRUCT_STRIPE_EVENT">;
|
|
19
|
+
FAILED_TO_FETCH_PLANS: better_auth51.RawError<"FAILED_TO_FETCH_PLANS">;
|
|
20
|
+
EMAIL_VERIFICATION_REQUIRED: better_auth51.RawError<"EMAIL_VERIFICATION_REQUIRED">;
|
|
21
|
+
SUBSCRIPTION_NOT_ACTIVE: better_auth51.RawError<"SUBSCRIPTION_NOT_ACTIVE">;
|
|
22
|
+
SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION: better_auth51.RawError<"SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION">;
|
|
23
|
+
ORGANIZATION_NOT_FOUND: better_auth51.RawError<"ORGANIZATION_NOT_FOUND">;
|
|
24
|
+
ORGANIZATION_SUBSCRIPTION_NOT_ENABLED: better_auth51.RawError<"ORGANIZATION_SUBSCRIPTION_NOT_ENABLED">;
|
|
25
|
+
AUTHORIZE_REFERENCE_REQUIRED: better_auth51.RawError<"AUTHORIZE_REFERENCE_REQUIRED">;
|
|
26
|
+
ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION: better_auth51.RawError<"ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION">;
|
|
27
|
+
ORGANIZATION_REFERENCE_ID_REQUIRED: better_auth51.RawError<"ORGANIZATION_REFERENCE_ID_REQUIRED">;
|
|
27
28
|
};
|
|
28
29
|
//#endregion
|
|
29
30
|
//#region src/client.d.ts
|
|
@@ -47,27 +48,28 @@ declare const stripeClient: <O extends {
|
|
|
47
48
|
"/subscription/restore": "POST";
|
|
48
49
|
};
|
|
49
50
|
$ERROR_CODES: {
|
|
50
|
-
UNAUTHORIZED:
|
|
51
|
-
INVALID_REQUEST_BODY:
|
|
52
|
-
SUBSCRIPTION_NOT_FOUND:
|
|
53
|
-
SUBSCRIPTION_PLAN_NOT_FOUND:
|
|
54
|
-
ALREADY_SUBSCRIBED_PLAN:
|
|
55
|
-
REFERENCE_ID_NOT_ALLOWED:
|
|
56
|
-
CUSTOMER_NOT_FOUND:
|
|
57
|
-
UNABLE_TO_CREATE_CUSTOMER:
|
|
58
|
-
UNABLE_TO_CREATE_BILLING_PORTAL:
|
|
59
|
-
STRIPE_SIGNATURE_NOT_FOUND:
|
|
60
|
-
STRIPE_WEBHOOK_SECRET_NOT_FOUND:
|
|
61
|
-
STRIPE_WEBHOOK_ERROR:
|
|
62
|
-
FAILED_TO_CONSTRUCT_STRIPE_EVENT:
|
|
63
|
-
FAILED_TO_FETCH_PLANS:
|
|
64
|
-
EMAIL_VERIFICATION_REQUIRED:
|
|
65
|
-
SUBSCRIPTION_NOT_ACTIVE:
|
|
66
|
-
SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION:
|
|
67
|
-
ORGANIZATION_NOT_FOUND:
|
|
68
|
-
ORGANIZATION_SUBSCRIPTION_NOT_ENABLED:
|
|
69
|
-
|
|
70
|
-
|
|
51
|
+
UNAUTHORIZED: better_auth51.RawError<"UNAUTHORIZED">;
|
|
52
|
+
INVALID_REQUEST_BODY: better_auth51.RawError<"INVALID_REQUEST_BODY">;
|
|
53
|
+
SUBSCRIPTION_NOT_FOUND: better_auth51.RawError<"SUBSCRIPTION_NOT_FOUND">;
|
|
54
|
+
SUBSCRIPTION_PLAN_NOT_FOUND: better_auth51.RawError<"SUBSCRIPTION_PLAN_NOT_FOUND">;
|
|
55
|
+
ALREADY_SUBSCRIBED_PLAN: better_auth51.RawError<"ALREADY_SUBSCRIBED_PLAN">;
|
|
56
|
+
REFERENCE_ID_NOT_ALLOWED: better_auth51.RawError<"REFERENCE_ID_NOT_ALLOWED">;
|
|
57
|
+
CUSTOMER_NOT_FOUND: better_auth51.RawError<"CUSTOMER_NOT_FOUND">;
|
|
58
|
+
UNABLE_TO_CREATE_CUSTOMER: better_auth51.RawError<"UNABLE_TO_CREATE_CUSTOMER">;
|
|
59
|
+
UNABLE_TO_CREATE_BILLING_PORTAL: better_auth51.RawError<"UNABLE_TO_CREATE_BILLING_PORTAL">;
|
|
60
|
+
STRIPE_SIGNATURE_NOT_FOUND: better_auth51.RawError<"STRIPE_SIGNATURE_NOT_FOUND">;
|
|
61
|
+
STRIPE_WEBHOOK_SECRET_NOT_FOUND: better_auth51.RawError<"STRIPE_WEBHOOK_SECRET_NOT_FOUND">;
|
|
62
|
+
STRIPE_WEBHOOK_ERROR: better_auth51.RawError<"STRIPE_WEBHOOK_ERROR">;
|
|
63
|
+
FAILED_TO_CONSTRUCT_STRIPE_EVENT: better_auth51.RawError<"FAILED_TO_CONSTRUCT_STRIPE_EVENT">;
|
|
64
|
+
FAILED_TO_FETCH_PLANS: better_auth51.RawError<"FAILED_TO_FETCH_PLANS">;
|
|
65
|
+
EMAIL_VERIFICATION_REQUIRED: better_auth51.RawError<"EMAIL_VERIFICATION_REQUIRED">;
|
|
66
|
+
SUBSCRIPTION_NOT_ACTIVE: better_auth51.RawError<"SUBSCRIPTION_NOT_ACTIVE">;
|
|
67
|
+
SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION: better_auth51.RawError<"SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION">;
|
|
68
|
+
ORGANIZATION_NOT_FOUND: better_auth51.RawError<"ORGANIZATION_NOT_FOUND">;
|
|
69
|
+
ORGANIZATION_SUBSCRIPTION_NOT_ENABLED: better_auth51.RawError<"ORGANIZATION_SUBSCRIPTION_NOT_ENABLED">;
|
|
70
|
+
AUTHORIZE_REFERENCE_REQUIRED: better_auth51.RawError<"AUTHORIZE_REFERENCE_REQUIRED">;
|
|
71
|
+
ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION: better_auth51.RawError<"ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION">;
|
|
72
|
+
ORGANIZATION_REFERENCE_ID_REQUIRED: better_auth51.RawError<"ORGANIZATION_REFERENCE_ID_REQUIRED">;
|
|
71
73
|
};
|
|
72
74
|
};
|
|
73
75
|
//#endregion
|
package/dist/client.mjs
CHANGED
|
@@ -21,10 +21,11 @@ const STRIPE_ERROR_CODES = defineErrorCodes({
|
|
|
21
21
|
SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION: "Subscription is not scheduled for cancellation",
|
|
22
22
|
ORGANIZATION_NOT_FOUND: "Organization not found",
|
|
23
23
|
ORGANIZATION_SUBSCRIPTION_NOT_ENABLED: "Organization subscription is not enabled",
|
|
24
|
+
AUTHORIZE_REFERENCE_REQUIRED: "Organization subscriptions require authorizeReference callback to be configured",
|
|
24
25
|
ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION: "Cannot delete organization with active subscription",
|
|
25
26
|
ORGANIZATION_REFERENCE_ID_REQUIRED: "Reference ID is required. Provide referenceId or set activeOrganizationId in session"
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
//#endregion
|
|
29
30
|
export { STRIPE_ERROR_CODES as t };
|
|
30
|
-
//# sourceMappingURL=error-codes-
|
|
31
|
+
//# sourceMappingURL=error-codes-CHMyMR5v.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-codes-
|
|
1
|
+
{"version":3,"file":"error-codes-CHMyMR5v.mjs","names":[],"sources":["../src/error-codes.ts"],"sourcesContent":["import { defineErrorCodes } from \"@better-auth/core/utils/error-codes\";\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\tAUTHORIZE_REFERENCE_REQUIRED:\n\t\t\"Organization subscriptions require authorizeReference callback to be configured\",\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"],"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,8BACC;CACD,sCACC;CACD,oCACC;CACD,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as better_auth21 from "better-auth";
|
|
2
2
|
import { GenericEndpointContext, InferOptionSchema, Session, User } from "better-auth";
|
|
3
3
|
import * as better_call0 from "better-call";
|
|
4
4
|
import * as zod0 from "zod";
|
|
@@ -488,7 +488,7 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
488
488
|
cancelUrl: zod0.ZodDefault<zod0.ZodString>;
|
|
489
489
|
returnUrl: zod0.ZodOptional<zod0.ZodString>;
|
|
490
490
|
disableRedirect: zod0.ZodDefault<zod0.ZodBoolean>;
|
|
491
|
-
},
|
|
491
|
+
}, better_auth21.$strip>;
|
|
492
492
|
metadata: {
|
|
493
493
|
openapi: {
|
|
494
494
|
operationId: string;
|
|
@@ -513,7 +513,7 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
513
513
|
updatedAt: Date;
|
|
514
514
|
email: string;
|
|
515
515
|
emailVerified: boolean;
|
|
516
|
-
name
|
|
516
|
+
name: string;
|
|
517
517
|
image?: string | null | undefined;
|
|
518
518
|
};
|
|
519
519
|
};
|
|
@@ -624,7 +624,7 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
624
624
|
}>>;
|
|
625
625
|
returnUrl: zod0.ZodString;
|
|
626
626
|
disableRedirect: zod0.ZodDefault<zod0.ZodBoolean>;
|
|
627
|
-
},
|
|
627
|
+
}, better_auth21.$strip>;
|
|
628
628
|
metadata: {
|
|
629
629
|
openapi: {
|
|
630
630
|
operationId: string;
|
|
@@ -649,7 +649,7 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
649
649
|
updatedAt: Date;
|
|
650
650
|
email: string;
|
|
651
651
|
emailVerified: boolean;
|
|
652
|
-
name
|
|
652
|
+
name: string;
|
|
653
653
|
image?: string | null | undefined;
|
|
654
654
|
};
|
|
655
655
|
};
|
|
@@ -670,7 +670,7 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
670
670
|
user: "user";
|
|
671
671
|
organization: "organization";
|
|
672
672
|
}>>;
|
|
673
|
-
},
|
|
673
|
+
}, better_auth21.$strip>;
|
|
674
674
|
metadata: {
|
|
675
675
|
openapi: {
|
|
676
676
|
operationId: string;
|
|
@@ -695,7 +695,7 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
695
695
|
updatedAt: Date;
|
|
696
696
|
email: string;
|
|
697
697
|
emailVerified: boolean;
|
|
698
|
-
name
|
|
698
|
+
name: string;
|
|
699
699
|
image?: string | null | undefined;
|
|
700
700
|
};
|
|
701
701
|
};
|
|
@@ -712,7 +712,7 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
712
712
|
user: "user";
|
|
713
713
|
organization: "organization";
|
|
714
714
|
}>>;
|
|
715
|
-
},
|
|
715
|
+
}, better_auth21.$strip>>;
|
|
716
716
|
metadata: {
|
|
717
717
|
openapi: {
|
|
718
718
|
operationId: string;
|
|
@@ -737,7 +737,7 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
737
737
|
updatedAt: Date;
|
|
738
738
|
email: string;
|
|
739
739
|
emailVerified: boolean;
|
|
740
|
-
name
|
|
740
|
+
name: string;
|
|
741
741
|
image?: string | null | undefined;
|
|
742
742
|
};
|
|
743
743
|
};
|
|
@@ -786,7 +786,7 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
786
786
|
}>>;
|
|
787
787
|
returnUrl: zod0.ZodDefault<zod0.ZodString>;
|
|
788
788
|
disableRedirect: zod0.ZodDefault<zod0.ZodBoolean>;
|
|
789
|
-
},
|
|
789
|
+
}, better_auth21.$strip>;
|
|
790
790
|
metadata: {
|
|
791
791
|
openapi: {
|
|
792
792
|
operationId: string;
|
|
@@ -811,7 +811,7 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
811
811
|
updatedAt: Date;
|
|
812
812
|
email: string;
|
|
813
813
|
emailVerified: boolean;
|
|
814
|
-
name
|
|
814
|
+
name: string;
|
|
815
815
|
image?: string | null | undefined;
|
|
816
816
|
};
|
|
817
817
|
};
|
|
@@ -824,15 +824,15 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
824
824
|
redirect: boolean;
|
|
825
825
|
}>;
|
|
826
826
|
} : {});
|
|
827
|
-
init(ctx:
|
|
827
|
+
init(ctx: better_auth21.AuthContext): {
|
|
828
828
|
options: {
|
|
829
829
|
databaseHooks: {
|
|
830
830
|
user: {
|
|
831
831
|
create: {
|
|
832
|
-
after(user: User & WithStripeCustomerId, ctx:
|
|
832
|
+
after(user: User & WithStripeCustomerId, ctx: better_auth21.GenericEndpointContext | null): Promise<void>;
|
|
833
833
|
};
|
|
834
834
|
update: {
|
|
835
|
-
after(user: User & WithStripeCustomerId, ctx:
|
|
835
|
+
after(user: User & WithStripeCustomerId, ctx: better_auth21.GenericEndpointContext | null): Promise<void>;
|
|
836
836
|
};
|
|
837
837
|
};
|
|
838
838
|
};
|
|
@@ -925,30 +925,31 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
925
925
|
} : {});
|
|
926
926
|
options: NoInfer<O>;
|
|
927
927
|
$ERROR_CODES: {
|
|
928
|
-
UNAUTHORIZED:
|
|
929
|
-
INVALID_REQUEST_BODY:
|
|
930
|
-
SUBSCRIPTION_NOT_FOUND:
|
|
931
|
-
SUBSCRIPTION_PLAN_NOT_FOUND:
|
|
932
|
-
ALREADY_SUBSCRIBED_PLAN:
|
|
933
|
-
REFERENCE_ID_NOT_ALLOWED:
|
|
934
|
-
CUSTOMER_NOT_FOUND:
|
|
935
|
-
UNABLE_TO_CREATE_CUSTOMER:
|
|
936
|
-
UNABLE_TO_CREATE_BILLING_PORTAL:
|
|
937
|
-
STRIPE_SIGNATURE_NOT_FOUND:
|
|
938
|
-
STRIPE_WEBHOOK_SECRET_NOT_FOUND:
|
|
939
|
-
STRIPE_WEBHOOK_ERROR:
|
|
940
|
-
FAILED_TO_CONSTRUCT_STRIPE_EVENT:
|
|
941
|
-
FAILED_TO_FETCH_PLANS:
|
|
942
|
-
EMAIL_VERIFICATION_REQUIRED:
|
|
943
|
-
SUBSCRIPTION_NOT_ACTIVE:
|
|
944
|
-
SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION:
|
|
945
|
-
ORGANIZATION_NOT_FOUND:
|
|
946
|
-
ORGANIZATION_SUBSCRIPTION_NOT_ENABLED:
|
|
947
|
-
|
|
948
|
-
|
|
928
|
+
UNAUTHORIZED: better_auth21.RawError<"UNAUTHORIZED">;
|
|
929
|
+
INVALID_REQUEST_BODY: better_auth21.RawError<"INVALID_REQUEST_BODY">;
|
|
930
|
+
SUBSCRIPTION_NOT_FOUND: better_auth21.RawError<"SUBSCRIPTION_NOT_FOUND">;
|
|
931
|
+
SUBSCRIPTION_PLAN_NOT_FOUND: better_auth21.RawError<"SUBSCRIPTION_PLAN_NOT_FOUND">;
|
|
932
|
+
ALREADY_SUBSCRIBED_PLAN: better_auth21.RawError<"ALREADY_SUBSCRIBED_PLAN">;
|
|
933
|
+
REFERENCE_ID_NOT_ALLOWED: better_auth21.RawError<"REFERENCE_ID_NOT_ALLOWED">;
|
|
934
|
+
CUSTOMER_NOT_FOUND: better_auth21.RawError<"CUSTOMER_NOT_FOUND">;
|
|
935
|
+
UNABLE_TO_CREATE_CUSTOMER: better_auth21.RawError<"UNABLE_TO_CREATE_CUSTOMER">;
|
|
936
|
+
UNABLE_TO_CREATE_BILLING_PORTAL: better_auth21.RawError<"UNABLE_TO_CREATE_BILLING_PORTAL">;
|
|
937
|
+
STRIPE_SIGNATURE_NOT_FOUND: better_auth21.RawError<"STRIPE_SIGNATURE_NOT_FOUND">;
|
|
938
|
+
STRIPE_WEBHOOK_SECRET_NOT_FOUND: better_auth21.RawError<"STRIPE_WEBHOOK_SECRET_NOT_FOUND">;
|
|
939
|
+
STRIPE_WEBHOOK_ERROR: better_auth21.RawError<"STRIPE_WEBHOOK_ERROR">;
|
|
940
|
+
FAILED_TO_CONSTRUCT_STRIPE_EVENT: better_auth21.RawError<"FAILED_TO_CONSTRUCT_STRIPE_EVENT">;
|
|
941
|
+
FAILED_TO_FETCH_PLANS: better_auth21.RawError<"FAILED_TO_FETCH_PLANS">;
|
|
942
|
+
EMAIL_VERIFICATION_REQUIRED: better_auth21.RawError<"EMAIL_VERIFICATION_REQUIRED">;
|
|
943
|
+
SUBSCRIPTION_NOT_ACTIVE: better_auth21.RawError<"SUBSCRIPTION_NOT_ACTIVE">;
|
|
944
|
+
SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION: better_auth21.RawError<"SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION">;
|
|
945
|
+
ORGANIZATION_NOT_FOUND: better_auth21.RawError<"ORGANIZATION_NOT_FOUND">;
|
|
946
|
+
ORGANIZATION_SUBSCRIPTION_NOT_ENABLED: better_auth21.RawError<"ORGANIZATION_SUBSCRIPTION_NOT_ENABLED">;
|
|
947
|
+
AUTHORIZE_REFERENCE_REQUIRED: better_auth21.RawError<"AUTHORIZE_REFERENCE_REQUIRED">;
|
|
948
|
+
ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION: better_auth21.RawError<"ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION">;
|
|
949
|
+
ORGANIZATION_REFERENCE_ID_REQUIRED: better_auth21.RawError<"ORGANIZATION_REFERENCE_ID_REQUIRED">;
|
|
949
950
|
};
|
|
950
951
|
};
|
|
951
952
|
type StripePlugin<O extends StripeOptions> = ReturnType<typeof stripe<O>>;
|
|
952
953
|
//#endregion
|
|
953
954
|
export { SubscriptionOptions as a, Subscription as i, stripe as n, StripePlan as r, StripePlugin as t };
|
|
954
|
-
//# sourceMappingURL=index-
|
|
955
|
+
//# sourceMappingURL=index-BELqjMd2.d.mts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as SubscriptionOptions, i as Subscription, n as stripe, r as StripePlan, t as StripePlugin } from "./index-
|
|
1
|
+
import { a as SubscriptionOptions, i as Subscription, n as stripe, r as StripePlan, t as StripePlugin } from "./index-BELqjMd2.mjs";
|
|
2
2
|
export { StripePlan, StripePlugin, Subscription, SubscriptionOptions, stripe };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as STRIPE_ERROR_CODES } from "./error-codes-
|
|
1
|
+
import { t as STRIPE_ERROR_CODES } from "./error-codes-CHMyMR5v.mjs";
|
|
2
2
|
import { APIError, HIDE_METADATA } from "better-auth";
|
|
3
3
|
import { defu } from "defu";
|
|
4
4
|
import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
|
|
@@ -397,7 +397,7 @@ const referenceMiddleware = (subscriptionOptions, action) => createAuthMiddlewar
|
|
|
397
397
|
if (customerType === "organization") {
|
|
398
398
|
if (!subscriptionOptions.authorizeReference) {
|
|
399
399
|
ctx.context.logger.error(`Organization subscriptions require authorizeReference to be defined in your stripe plugin config.`);
|
|
400
|
-
throw APIError$1.from("BAD_REQUEST", STRIPE_ERROR_CODES.
|
|
400
|
+
throw APIError$1.from("BAD_REQUEST", STRIPE_ERROR_CODES.AUTHORIZE_REFERENCE_REQUIRED);
|
|
401
401
|
}
|
|
402
402
|
const referenceId = explicitReferenceId || ctxSession.session.activeOrganizationId;
|
|
403
403
|
if (!referenceId) throw APIError$1.from("BAD_REQUEST", STRIPE_ERROR_CODES.ORGANIZATION_REFERENCE_ID_REQUIRED);
|
|
@@ -581,7 +581,7 @@ const upgradeSubscription = (options) => {
|
|
|
581
581
|
})).data[0];
|
|
582
582
|
if (!stripeCustomer) stripeCustomer = await client.customers.create({
|
|
583
583
|
email: user.email,
|
|
584
|
-
name: user.name
|
|
584
|
+
name: user.name,
|
|
585
585
|
metadata: customerMetadata.set({
|
|
586
586
|
userId: user.id,
|
|
587
587
|
customerType: "user"
|
|
@@ -1419,7 +1419,7 @@ const stripe = (options) => {
|
|
|
1419
1419
|
if (options.getCustomerCreateParams) extraCreateParams = await options.getCustomerCreateParams(user, ctx);
|
|
1420
1420
|
const params = defu({
|
|
1421
1421
|
email: user.email,
|
|
1422
|
-
name: user.name
|
|
1422
|
+
name: user.name,
|
|
1423
1423
|
metadata: customerMetadata.set({
|
|
1424
1424
|
userId: user.id,
|
|
1425
1425
|
customerType: "user"
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["APIError","APIError"],"sources":["../src/metadata.ts","../src/utils.ts","../src/hooks.ts","../src/middleware.ts","../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { defu } from \"defu\";\nimport type Stripe from \"stripe\";\n\n/**\n * Internal metadata fields for Stripe Customer.\n */\ntype CustomerInternalMetadata =\n\t| { customerType: \"user\"; userId: string }\n\t| { customerType: \"organization\"; organizationId: string };\n\n/**\n * Internal metadata fields for Stripe Subscription/Checkout.\n */\ntype SubscriptionInternalMetadata = {\n\tuserId: string;\n\tsubscriptionId: string;\n\treferenceId: string;\n};\n\n/**\n * Customer metadata - set internal fields and extract typed fields.\n */\nexport const customerMetadata = {\n\t/**\n\t * Internal metadata keys for type-safe access.\n\t */\n\tkeys: {\n\t\tuserId: \"userId\",\n\t\torganizationId: \"organizationId\",\n\t\tcustomerType: \"customerType\",\n\t} as const,\n\n\t/**\n\t * Create metadata with internal fields that cannot be overridden by user metadata.\n\t * Uses `defu` which prioritizes the first argument.\n\t */\n\tset(\n\t\tinternalFields: CustomerInternalMetadata,\n\t\t...userMetadata: (Stripe.Emptyable<Stripe.MetadataParam> | undefined)[]\n\t): Stripe.MetadataParam {\n\t\treturn defu(internalFields, ...userMetadata.filter(Boolean));\n\t},\n\n\t/**\n\t * Extract internal fields from Stripe metadata.\n\t * Provides type-safe access to internal metadata keys.\n\t */\n\tget(metadata: Stripe.Metadata | null | undefined) {\n\t\treturn {\n\t\t\tuserId: metadata?.userId,\n\t\t\torganizationId: metadata?.organizationId,\n\t\t\tcustomerType: metadata?.customerType as\n\t\t\t\t| CustomerInternalMetadata[\"customerType\"]\n\t\t\t\t| undefined,\n\t\t};\n\t},\n};\n\n/**\n * Subscription/Checkout metadata - set internal fields and extract typed fields.\n */\nexport const subscriptionMetadata = {\n\t/**\n\t * Internal metadata keys for type-safe access.\n\t */\n\tkeys: {\n\t\tuserId: \"userId\",\n\t\tsubscriptionId: \"subscriptionId\",\n\t\treferenceId: \"referenceId\",\n\t} as const,\n\n\t/**\n\t * Create metadata with internal fields that cannot be overridden by user metadata.\n\t * Uses `defu` which prioritizes the first argument.\n\t */\n\tset(\n\t\tinternalFields: SubscriptionInternalMetadata,\n\t\t...userMetadata: (Stripe.Emptyable<Stripe.MetadataParam> | undefined)[]\n\t): Stripe.MetadataParam {\n\t\treturn defu(internalFields, ...userMetadata.filter(Boolean));\n\t},\n\n\t/**\n\t * Extract internal fields from Stripe metadata.\n\t * Provides type-safe access to internal metadata keys.\n\t */\n\tget(metadata: Stripe.Metadata | null | undefined) {\n\t\treturn {\n\t\t\tuserId: metadata?.userId,\n\t\t\tsubscriptionId: metadata?.subscriptionId,\n\t\t\treferenceId: metadata?.referenceId,\n\t\t};\n\t},\n};\n","import type Stripe from \"stripe\";\nimport type { StripeOptions, 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 } from \"@better-auth/core/error\";\nimport { sessionMiddleware } from \"better-auth/api\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport type {\n\tAuthorizeReferenceAction,\n\tCustomerType,\n\tStripeCtxSession,\n\tSubscriptionOptions,\n} from \"./types\";\n\nexport const stripeSessionMiddleware = createAuthMiddleware(\n\t{\n\t\tuse: [sessionMiddleware],\n\t},\n\tasync (ctx) => {\n\t\tconst session = ctx.context.session as StripeCtxSession;\n\t\treturn {\n\t\t\tsession,\n\t\t};\n\t},\n);\n\nexport const referenceMiddleware = (\n\tsubscriptionOptions: SubscriptionOptions,\n\taction: AuthorizeReferenceAction,\n) =>\n\tcreateAuthMiddleware(async (ctx) => {\n\t\tconst ctxSession = ctx.context.session as StripeCtxSession;\n\t\tif (!ctxSession) {\n\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t}\n\n\t\tconst customerType: CustomerType =\n\t\t\tctx.body?.customerType || ctx.query?.customerType;\n\t\tconst explicitReferenceId = ctx.body?.referenceId || ctx.query?.referenceId;\n\n\t\tif (customerType === \"organization\") {\n\t\t\t// Organization subscriptions always require authorizeReference\n\t\t\tif (!subscriptionOptions.authorizeReference) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t`Organization subscriptions require authorizeReference to be defined in your stripe plugin config.`,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.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 APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_REFERENCE_ID_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isAuthorized = await subscriptionOptions.authorizeReference(\n\t\t\t\t{\n\t\t\t\t\tuser: ctxSession.user,\n\t\t\t\t\tsession: ctxSession.session,\n\t\t\t\t\treferenceId,\n\t\t\t\t\taction,\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t);\n\t\t\tif (!isAuthorized) {\n\t\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// User subscriptions - pass if no explicit referenceId\n\t\tif (!explicitReferenceId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass if referenceId is user id\n\t\tif (explicitReferenceId === ctxSession.user.id) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!subscriptionOptions.authorizeReference) {\n\t\t\tctx.context.logger.error(\n\t\t\t\t`Passing referenceId into a subscription action isn't allowed if subscription.authorizeReference isn't defined in your stripe plugin config.`,\n\t\t\t);\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.REFERENCE_ID_NOT_ALLOWED,\n\t\t\t);\n\t\t}\n\t\tconst isAuthorized = await subscriptionOptions.authorizeReference(\n\t\t\t{\n\t\t\t\tuser: ctxSession.user,\n\t\t\t\tsession: ctxSession.session,\n\t\t\t\treferenceId: explicitReferenceId,\n\t\t\t\taction,\n\t\t\t},\n\t\t\tctx,\n\t\t);\n\t\tif (!isAuthorized) {\n\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t}\n\t});\n","import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport type { GenericEndpointContext, User } from \"better-auth\";\nimport { HIDE_METADATA } from \"better-auth\";\nimport { getSessionFromCtx, originCheck } from \"better-auth/api\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport { defu } from \"defu\";\nimport type Stripe from \"stripe\";\nimport type { Stripe as StripeType } from \"stripe\";\nimport * as z from \"zod/v4\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport {\n\tonCheckoutSessionCompleted,\n\tonSubscriptionCreated,\n\tonSubscriptionDeleted,\n\tonSubscriptionUpdated,\n} from \"./hooks\";\nimport { customerMetadata, subscriptionMetadata } from \"./metadata\";\nimport { referenceMiddleware, stripeSessionMiddleware } from \"./middleware\";\nimport type {\n\tCustomerType,\n\tStripeCtxSession,\n\tStripeOptions,\n\tSubscription,\n\tSubscriptionOptions,\n\tWithStripeCustomerId,\n} from \"./types\";\nimport {\n\tescapeStripeSearchValue,\n\tgetPlanByName,\n\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 APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_SUBSCRIPTION_NOT_ENABLED,\n\t\t\t);\n\t\t}\n\n\t\tif (!session.activeOrganizationId) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t);\n\t\t}\n\t\treturn session.activeOrganizationId;\n\t}\n\n\treturn user.id;\n}\n\nconst upgradeSubscriptionBodySchema = z.object({\n\t/**\n\t * The name of the plan to subscribe\n\t */\n\tplan: z.string().meta({\n\t\tdescription: 'The name of the plan to upgrade to. Eg: \"pro\"',\n\t}),\n\t/**\n\t * If annual plan should be applied.\n\t */\n\tannual: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription: \"Whether to upgrade to an annual plan. Eg: true\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Reference ID for the subscription based on customerType:\n\t * - `user`: defaults to `user.id`\n\t * - `organization`: defaults to `session.activeOrganizationId`\n\t */\n\treferenceId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: 'Reference ID for the subscription. Eg: \"org_123\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The Stripe subscription ID to upgrade.\n\t * If provided and not found, it'll throw an error.\n\t */\n\tsubscriptionId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'The Stripe subscription ID to upgrade. Eg: \"sub_1ABC2DEF3GHI4JKL\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription (requires referenceId)\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Additional metadata to store with the subscription.\n\t */\n\tmetadata: z.record(z.string(), z.any()).optional(),\n\t/**\n\t * Number of seats for subscriptions.\n\t */\n\tseats: z\n\t\t.number()\n\t\t.meta({\n\t\t\tdescription: \"Number of seats to upgrade to (if applicable). Eg: 1\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The IETF language tag of the locale Checkout is displayed in.\n\t * If not provided or set to `auto`, the browser's locale is used.\n\t */\n\tlocale: z\n\t\t.custom<StripeType.Checkout.Session.Locale>((localization) => {\n\t\t\treturn typeof localization === \"string\";\n\t\t})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The locale to display Checkout in. Eg: 'en', 'ko'. If not provided or set to `auto`, the browser's locale is used.\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The URL to which Stripe should send customers when payment or setup is complete.\n\t */\n\tsuccessUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Callback URL to redirect back after successful subscription. Eg: \"https://example.com/success\"',\n\t\t})\n\t\t.default(\"/\"),\n\t/**\n\t * If set, checkout shows a back button and customers will be directed here if they cancel payment.\n\t */\n\tcancelUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'If set, checkout shows a back button and customers will be directed here if they cancel payment. Eg: \"https://example.com/pricing\"',\n\t\t})\n\t\t.default(\"/\"),\n\t/**\n\t * The URL to return to from the Billing Portal (used when upgrading existing subscriptions)\n\t */\n\treturnUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'URL to take customers to when they click on the billing portal’s link to return to your website. Eg: \"https://example.com/dashboard\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Disable Redirect\n\t */\n\tdisableRedirect: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription: \"Disable redirect after successful subscription. Eg: true\",\n\t\t})\n\t\t.default(false),\n});\n\n/**\n * ### Endpoint\n *\n * POST `/subscription/upgrade`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.upgradeSubscription`\n *\n * **client:**\n * `authClient.subscription.upgrade`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-upgrade)\n */\nexport const upgradeSubscription = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\n\treturn createAuthEndpoint(\n\t\t\"/subscription/upgrade\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: upgradeSubscriptionBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"upgradeSubscription\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"upgrade-subscription\"),\n\t\t\t\toriginCheck((c) => {\n\t\t\t\t\treturn [c.body.successUrl as string, c.body.cancelUrl as string];\n\t\t\t\t}),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { user, session } = ctx.context.session;\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tif (!user.emailVerified && subscriptionOptions.requireEmailVerification) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst plan = await getPlanByName(options, ctx.body.plan);\n\t\t\tif (!plan) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// If subscriptionId is provided, find that specific subscription.\n\t\t\t// Otherwise, active subscription will be resolved by referenceId later.\n\t\t\tconst subscriptionToUpdate = ctx.body.subscriptionId\n\t\t\t\t? await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\t\tvalue: ctx.body.subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t: null;\n\t\t\tif (ctx.body.subscriptionId && !subscriptionToUpdate) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tctx.body.subscriptionId &&\n\t\t\t\tsubscriptionToUpdate &&\n\t\t\t\tsubscriptionToUpdate.referenceId !== referenceId\n\t\t\t) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Determine customer id\n\t\t\tlet customerId: string | undefined;\n\t\t\tif (customerType === \"organization\") {\n\t\t\t\t// Organization subscription - get customer ID from organization\n\t\t\t\tcustomerId = subscriptionToUpdate?.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\tconst org = await ctx.context.adapter.findOne<\n\t\t\t\t\t\tOrganization & WithStripeCustomerId\n\t\t\t\t\t>({\n\t\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tif (!org) {\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tcustomerId = org.stripeCustomerId;\n\n\t\t\t\t\t// If org doesn't have a customer ID, create one\n\t\t\t\t\tif (!customerId) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// 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 APIError.from(\n\t\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_CUSTOMER,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// User subscription - get customer ID from user\n\t\t\t\tcustomerId =\n\t\t\t\t\tsubscriptionToUpdate?.stripeCustomerId || user.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// 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 ?? undefined,\n\t\t\t\t\t\t\t\tmetadata: customerMetadata.set(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\t\t\tcustomerType: \"user\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Update local DB with Stripe customer ID\n\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tcustomerId = stripeCustomer.id;\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.context.logger.error(e);\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_CUSTOMER,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst subscriptions = subscriptionToUpdate\n\t\t\t\t? [subscriptionToUpdate]\n\t\t\t\t: await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\n\t\t\tconst activeOrTrialingSubscription = subscriptions.find((sub) =>\n\t\t\t\tisActiveOrTrialing(sub),\n\t\t\t);\n\n\t\t\tconst activeSubscriptions = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: customerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub)));\n\n\t\t\tconst activeSubscription = activeSubscriptions.find((sub) => {\n\t\t\t\t// If we have a specific subscription to update, match by ID\n\t\t\t\tif (\n\t\t\t\t\tsubscriptionToUpdate?.stripeSubscriptionId ||\n\t\t\t\t\tctx.body.subscriptionId\n\t\t\t\t) {\n\t\t\t\t\treturn (\n\t\t\t\t\t\tsub.id === subscriptionToUpdate?.stripeSubscriptionId ||\n\t\t\t\t\t\tsub.id === ctx.body.subscriptionId\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// Only find subscription for the same referenceId to avoid mixing personal and org subscriptions\n\t\t\t\tif (activeOrTrialingSubscription?.stripeSubscriptionId) {\n\t\t\t\t\treturn sub.id === activeOrTrialingSubscription.stripeSubscriptionId;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\n\t\t\t// Get the current price ID from the active Stripe subscription\n\t\t\tconst 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 APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.ALREADY_SUBSCRIBED_PLAN,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (activeSubscription && customerId) {\n\t\t\t\t// Find the corresponding database subscription for this Stripe subscription\n\t\t\t\tlet dbSubscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\tvalue: activeSubscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\t// If no database record exists for this Stripe subscription, update the existing one\n\t\t\t\tif (!dbSubscription && activeOrTrialingSubscription) {\n\t\t\t\t\tawait ctx.context.adapter.update<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\tstripeSubscriptionId: activeSubscription.id,\n\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: activeOrTrialingSubscription.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tdbSubscription = activeOrTrialingSubscription;\n\t\t\t\t}\n\n\t\t\t\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 APIError.from(\n\t\t\t\t\t\"NOT_FOUND\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = await subscriptionOptions.getCheckoutSessionParams?.(\n\t\t\t\t{\n\t\t\t\t\tuser,\n\t\t\t\t\tsession,\n\t\t\t\t\tplan,\n\t\t\t\t\tsubscription,\n\t\t\t\t},\n\t\t\t\tctx.request,\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tconst allSubscriptions = await ctx.context.adapter.findMany<Subscription>(\n\t\t\t\t{\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [{ field: \"referenceId\", value: referenceId }],\n\t\t\t\t},\n\t\t\t);\n\t\t\tconst hasEverTrialed = allSubscriptions.some((s) => {\n\t\t\t\t// Check if user has ever had a trial for any plan (not just the same plan)\n\t\t\t\t// This prevents users from getting multiple trials by switching plans\n\t\t\t\tconst hadTrial =\n\t\t\t\t\t!!(s.trialStart || s.trialEnd) || s.status === \"trialing\";\n\t\t\t\treturn hadTrial;\n\t\t\t});\n\n\t\t\tconst freeTrial =\n\t\t\t\t!hasEverTrialed && plan.freeTrial\n\t\t\t\t\t? { trial_period_days: plan.freeTrial.days }\n\t\t\t\t\t: undefined;\n\n\t\t\tconst checkoutSession = await client.checkout.sessions\n\t\t\t\t.create(\n\t\t\t\t\t{\n\t\t\t\t\t\t...(customerId\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\t\t\t\tcustomer_update:\n\t\t\t\t\t\t\t\t\t\tcustomerType !== \"user\"\n\t\t\t\t\t\t\t\t\t\t\t? ({ address: \"auto\" } as const)\n\t\t\t\t\t\t\t\t\t\t\t: ({ name: \"auto\", address: \"auto\" } as const), // The customer name is automatically set only for users\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\tcustomer_email: user.email,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tlocale: ctx.body.locale,\n\t\t\t\t\t\tsuccess_url: getUrl(\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t`${\n\t\t\t\t\t\t\t\tctx.context.baseURL\n\t\t\t\t\t\t\t}/subscription/success?callbackURL=${encodeURIComponent(\n\t\t\t\t\t\t\t\tctx.body.successUrl,\n\t\t\t\t\t\t\t)}&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 APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst activeSubscriptions = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub)));\n\t\t\tif (!activeSubscriptions.length) {\n\t\t\t\t/**\n\t\t\t\t * If the subscription is not found, we need to delete the subscription\n\t\t\t\t * from the database. This is a rare case and should not happen.\n\t\t\t\t */\n\t\t\t\tawait ctx.context.adapter.deleteMany({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst activeSubscription = activeSubscriptions.find(\n\t\t\t\t(sub) => sub.id === subscription.stripeSubscriptionId,\n\t\t\t);\n\t\t\tif (!activeSubscription) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst { url } = await client.billingPortal.sessions\n\t\t\t\t.create({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t\treturn_url: getUrl(\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 APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!isActiveOrTrialing(subscription)) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_ACTIVE,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!isPendingCancel(subscription)) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_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 APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Clear scheduled cancellation based on Stripe subscription state\n\t\t\t// Note: Stripe doesn't accept both `cancel_at` and `cancel_at_period_end` simultaneously\n\t\t\tconst updateParams: Stripe.SubscriptionUpdateParams = {};\n\t\t\tif (activeSubscription.cancel_at) {\n\t\t\t\tupdateParams.cancel_at = \"\";\n\t\t\t} else if (activeSubscription.cancel_at_period_end) {\n\t\t\t\tupdateParams.cancel_at_period_end = false;\n\t\t\t}\n\n\t\t\tconst newSub = await client.subscriptions\n\t\t\t\t.update(activeSubscription.id, updateParams)\n\t\t\t\t.catch((e) => {\n\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\tupdate: {\n\t\t\t\t\tcancelAtPeriodEnd: false,\n\t\t\t\t\tcancelAt: null,\n\t\t\t\t\tcanceledAt: null,\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t},\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\treturn ctx.json(newSub);\n\t\t},\n\t);\n};\n\nconst listActiveSubscriptionsQuerySchema = z.optional(\n\tz.object({\n\t\treferenceId: z\n\t\t\t.string()\n\t\t\t.meta({\n\t\t\t\tdescription: \"Reference id of the subscription to list. Eg: '123'\",\n\t\t\t})\n\t\t\t.optional(),\n\t\t/**\n\t\t * Customer type for the subscription.\n\t\t * - `user`: User owns the subscription (default)\n\t\t * - `organization`: Organization owns the subscription\n\t\t */\n\t\tcustomerType: z\n\t\t\t.enum([\"user\", \"organization\"])\n\t\t\t.meta({\n\t\t\t\tdescription:\n\t\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t\t})\n\t\t\t.optional(),\n\t}),\n);\n/**\n * ### Endpoint\n *\n * GET `/subscription/list`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.listActiveSubscriptions`\n *\n * **client:**\n * `authClient.subscription.list`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-list)\n */\nexport const listActiveSubscriptions = (options: StripeOptions) => {\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/list\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: listActiveSubscriptionsQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"listActiveSubscriptions\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"list-subscription\"),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst customerType = ctx.query?.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.query?.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tconst subscriptions = await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!subscriptions.length) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tconst plans = await getPlans(options.subscription);\n\t\t\tif (!plans) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tconst subs = subscriptions\n\t\t\t\t.map((sub) => {\n\t\t\t\t\tconst plan = plans.find(\n\t\t\t\t\t\t(p) => p.name.toLowerCase() === sub.plan.toLowerCase(),\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...sub,\n\t\t\t\t\t\tlimits: plan?.limits,\n\t\t\t\t\t\tpriceId: plan?.priceId,\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((sub) => isActiveOrTrialing(sub));\n\t\t\treturn ctx.json(subs);\n\t\t},\n\t);\n};\n\nconst subscriptionSuccessQuerySchema = z.record(z.string(), z.any()).optional();\n\nexport const subscriptionSuccess = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/success\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: subscriptionSuccessQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleSubscriptionSuccess\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [originCheck((ctx) => ctx.query.callbackURL)],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\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 APIError.from(\"NOT_FOUND\", STRIPE_ERROR_CODES.CUSTOMER_NOT_FOUND);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { url } = await client.billingPortal.sessions.create({\n\t\t\t\t\tlocale: ctx.body.locale,\n\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl),\n\t\t\t\t});\n\n\t\t\t\treturn ctx.json({\n\t\t\t\t\turl,\n\t\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t\t});\n\t\t\t} catch (error: any) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"Error creating billing portal session\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"INTERNAL_SERVER_ERROR\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_BILLING_PORTAL,\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n};\n\nexport const stripeWebhook = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\treturn createAuthEndpoint(\n\t\t\"/stripe/webhook\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tmetadata: {\n\t\t\t\t...HIDE_METADATA,\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleStripeWebhook\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tcloneRequest: true,\n\t\t\tdisableBody: true, // Don't parse the body\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (!ctx.request?.body) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.INVALID_REQUEST_BODY,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst sig = ctx.request.headers.get(\"stripe-signature\");\n\t\t\tif (!sig) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_SIGNATURE_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst webhookSecret = options.stripeWebhookSecret;\n\t\t\tif (!webhookSecret) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"INTERNAL_SERVER_ERROR\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_WEBHOOK_SECRET_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst payload = await ctx.request.text();\n\n\t\t\tlet event: Stripe.Event;\n\t\t\ttry {\n\t\t\t\t// Support both Stripe v18 (constructEvent) and v19+ (constructEventAsync)\n\t\t\t\tif (typeof client.webhooks.constructEventAsync === \"function\") {\n\t\t\t\t\t// Stripe v19+ - use async method\n\t\t\t\t\tevent = await client.webhooks.constructEventAsync(\n\t\t\t\t\t\tpayload,\n\t\t\t\t\t\tsig,\n\t\t\t\t\t\twebhookSecret,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\t// Stripe v18 - use sync method\n\t\t\t\t\tevent = client.webhooks.constructEvent(payload, sig, webhookSecret);\n\t\t\t\t}\n\t\t\t} catch (err: any) {\n\t\t\t\tctx.context.logger.error(`${err.message}`);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.FAILED_TO_CONSTRUCT_STRIPE_EVENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!event) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.FAILED_TO_CONSTRUCT_STRIPE_EVENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tswitch (event.type) {\n\t\t\t\t\tcase \"checkout.session.completed\":\n\t\t\t\t\t\tawait onCheckoutSessionCompleted(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.created\":\n\t\t\t\t\t\tawait onSubscriptionCreated(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.updated\":\n\t\t\t\t\t\tawait onSubscriptionUpdated(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.deleted\":\n\t\t\t\t\t\tawait onSubscriptionDeleted(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch (e: any) {\n\t\t\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${e.message}`);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_WEBHOOK_ERROR,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ctx.json({ success: true });\n\t\t},\n\t);\n};\n","import type { BetterAuthPluginDBSchema } from \"@better-auth/core/db\";\nimport { mergeSchema } from \"better-auth/db\";\nimport type { StripeOptions } from \"./types\";\n\nexport const subscriptions = {\n\tsubscription: {\n\t\tfields: {\n\t\t\tplan: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\treferenceId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstripeSubscriptionId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstatus: {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdefaultValue: \"incomplete\",\n\t\t\t},\n\t\t\tperiodStart: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tperiodEnd: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\ttrialStart: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\ttrialEnd: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tcancelAtPeriodEnd: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\trequired: false,\n\t\t\t\tdefaultValue: false,\n\t\t\t},\n\t\t\tcancelAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tcanceledAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tendedAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tseats: {\n\t\t\t\ttype: \"number\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\nexport const user = {\n\tuser: {\n\t\tfields: {\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\nexport const organization = {\n\torganization: {\n\t\tfields: {\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\ntype GetSchemaResult<O extends StripeOptions> = typeof user &\n\t(O[\"subscription\"] extends { enabled: true } ? typeof subscriptions : {}) &\n\t(O[\"organization\"] extends { enabled: true } ? typeof organization : {});\n\nexport const getSchema = <O extends StripeOptions>(\n\toptions: O,\n): GetSchemaResult<O> => {\n\tlet baseSchema: BetterAuthPluginDBSchema = {};\n\n\tif (options.subscription?.enabled) {\n\t\tbaseSchema = {\n\t\t\t...subscriptions,\n\t\t\t...user,\n\t\t};\n\t} else {\n\t\tbaseSchema = {\n\t\t\t...user,\n\t\t};\n\t}\n\n\tif (options.organization?.enabled) {\n\t\tbaseSchema = {\n\t\t\t...baseSchema,\n\t\t\t...organization,\n\t\t};\n\t}\n\n\tif (\n\t\toptions.schema &&\n\t\t!options.subscription?.enabled &&\n\t\t\"subscription\" in options.schema\n\t) {\n\t\tconst { subscription: _subscription, ...restSchema } = options.schema;\n\t\treturn mergeSchema(baseSchema, restSchema) as GetSchemaResult<O>;\n\t}\n\n\treturn mergeSchema(baseSchema, options.schema) as GetSchemaResult<O>;\n};\n","import type { BetterAuthPlugin, User } from \"better-auth\";\nimport { APIError } from \"better-auth\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport { defu } from \"defu\";\nimport type Stripe from \"stripe\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport { customerMetadata } from \"./metadata\";\nimport {\n\tcancelSubscription,\n\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\ndeclare module \"@better-auth/core\" {\n\tinterface BetterAuthPluginRegistry<AuthOptions, Options> {\n\t\tstripe: {\n\t\t\tcreator: typeof stripe;\n\t\t};\n\t}\n}\n\nexport const stripe = <O extends StripeOptions>(options: O) => {\n\tconst client = options.stripeClient;\n\n\tconst subscriptionEndpoints = {\n\t\tupgradeSubscription: upgradeSubscription(options),\n\t\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 = ctx.getPlugin(\"organization\");\n\t\t\t\tif (!orgPlugin) {\n\t\t\t\t\tctx.logger.error(`Organization plugin not found`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst existingHooks = orgPlugin.options.organizationHooks ?? {};\n\n\t\t\t\t/**\n\t\t\t\t * Sync organization name to Stripe customer\n\t\t\t\t */\n\t\t\t\tconst afterUpdateStripeOrg = async (data: {\n\t\t\t\t\torganization: (Organization & WithStripeCustomerId) | null;\n\t\t\t\t\tuser: User;\n\t\t\t\t}) => {\n\t\t\t\t\tconst { organization } = data;\n\t\t\t\t\tif (!organization?.stripeCustomerId) return;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst stripeCustomer = await client.customers.retrieve(\n\t\t\t\t\t\t\torganization.stripeCustomerId,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (stripeCustomer.deleted) {\n\t\t\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t\t\t`Stripe customer ${organization.stripeCustomerId} was deleted`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Update Stripe customer if name changed\n\t\t\t\t\t\tif (organization.name !== stripeCustomer.name) {\n\t\t\t\t\t\t\tawait client.customers.update(organization.stripeCustomerId, {\n\t\t\t\t\t\t\t\tname: organization.name,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tctx.logger.info(\n\t\t\t\t\t\t\t\t`Synced organization name to Stripe: \"${stripeCustomer.name}\" → \"${organization.name}\"`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t`Failed to sync organization to Stripe: ${e.message}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t/**\n\t\t\t\t * Block deletion if organization has active subscriptions\n\t\t\t\t */\n\t\t\t\tconst beforeDeleteStripeOrg = async (data: {\n\t\t\t\t\torganization: Organization & WithStripeCustomerId;\n\t\t\t\t\tuser: User;\n\t\t\t\t}) => {\n\t\t\t\t\tconst { organization } = data;\n\t\t\t\t\tif (!organization.stripeCustomerId) return;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Check if organization has any active subscriptions\n\t\t\t\t\t\tconst subscriptions = await client.subscriptions.list({\n\t\t\t\t\t\t\tcustomer: organization.stripeCustomerId,\n\t\t\t\t\t\t\tstatus: \"all\",\n\t\t\t\t\t\t\tlimit: 100, // 1 ~ 100\n\t\t\t\t\t\t});\n\t\t\t\t\t\tfor (const sub of subscriptions.data) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tsub.status !== \"canceled\" &&\n\t\t\t\t\t\t\t\tsub.status !== \"incomplete\" &&\n\t\t\t\t\t\t\t\tsub.status !== \"incomplete_expired\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\tif (error instanceof APIError) {\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t`Failed to check organization subscriptions: ${error.message}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\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 ?? undefined,\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":";;;;;;;;;;;;;AAsBA,MAAa,mBAAmB;CAI/B,MAAM;EACL,QAAQ;EACR,gBAAgB;EAChB,cAAc;EACd;CAMD,IACC,gBACA,GAAG,cACoB;AACvB,SAAO,KAAK,gBAAgB,GAAG,aAAa,OAAO,QAAQ,CAAC;;CAO7D,IAAI,UAA8C;AACjD,SAAO;GACN,QAAQ,UAAU;GAClB,gBAAgB,UAAU;GAC1B,cAAc,UAAU;GAGxB;;CAEF;;;;AAKD,MAAa,uBAAuB;CAInC,MAAM;EACL,QAAQ;EACR,gBAAgB;EAChB,aAAa;EACb;CAMD,IACC,gBACA,GAAG,cACoB;AACvB,SAAO,KAAK,gBAAgB,GAAG,aAAa,OAAO,QAAQ,CAAC;;CAO7D,IAAI,UAA8C;AACjD,SAAO;GACN,QAAQ,UAAU;GAClB,gBAAgB,UAAU;GAC1B,aAAa,UAAU;GACvB;;CAEF;;;;AC1FD,eAAsB,SACrB,qBACC;AACD,KAAI,qBAAqB,QACxB,QAAO,OAAO,oBAAoB,UAAU,aACzC,MAAM,oBAAoB,OAAO,GACjC,oBAAoB;AAExB,OAAM,IAAI,MAAM,uDAAuD;;AAGxE,eAAsB,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,MAAM,OAAO,MAAM,IAAI,QAAQ,QAAQ,QAAc;EACpD,OAAO;EACP,OAAO,CAAC;GAAE,OAAO;GAAoB,OAAO;GAAkB,CAAC;EAC/D,CAAC;AACF,KAAI,KAAM,QAAO;EAAE,cAAc;EAAQ,aAAa,KAAK;EAAI;AAE/D,QAAO;;AAGR,eAAsB,2BACrB,KACA,SACA,OACC;AACD,KAAI;EACH,MAAM,SAAS,QAAQ;EACvB,MAAM,kBAAkB,MAAM,KAAK;AACnC,MAAI,gBAAgB,SAAS,WAAW,CAAC,QAAQ,cAAc,QAC9D;EAED,MAAM,eAAe,MAAM,OAAO,cAAc,SAC/C,gBAAgB,aAChB;EACD,MAAM,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;;;UAGM,GAAQ;AAChB,MAAI,QAAQ,OAAO,MAAM,iCAAiC,EAAE,UAAU;;;AAIxE,eAAsB,sBACrB,KACA,SACA,OACC;AACD,KAAI;AACH,MAAI,CAAC,QAAQ,cAAc,QAC1B;EAGD,MAAM,sBAAsB,MAAM,KAAK;EACvC,MAAM,mBAAmB,oBAAoB,UAAU,UAAU;AACjE,MAAI,CAAC,kBAAkB;AACtB,OAAI,QAAQ,OAAO,KAClB,2FACA;AACD;;EAID,MAAM,EAAE,mBAAmB,qBAAqB,IAC/C,oBAAoB,SACpB;EACD,MAAM,uBACL,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GAC/C,OAAO;GACP,OAAO,iBACJ,CAAC;IAAE,OAAO;IAAM,OAAO;IAAgB,CAAC,GACxC,CAAC;IAAE,OAAO;IAAwB,OAAO,oBAAoB;IAAI,CAAC;GACrE,CAAC;AACH,MAAI,sBAAsB;AACzB,OAAI,QAAQ,OAAO,KAClB,gEAAgE,qBAAqB,GAAG,sBACxF;AACD;;EAID,MAAM,YAAY,MAAM,gCACvB,KACA,SACA,iBACA;AACD,MAAI,CAAC,WAAW;AACf,OAAI,QAAQ,OAAO,KAClB,gFAAgF,mBAChF;AACD;;EAED,MAAM,EAAE,aAAa,iBAAiB;EAEtC,MAAM,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;UACM,OAAY;AACpB,MAAI,QAAQ,OAAO,MAAM,iCAAiC,QAAQ;;;AAIpE,eAAsB,sBACrB,KACA,SACA,OACC;AACD,KAAI;AACH,MAAI,CAAC,QAAQ,cAAc,QAC1B;EAED,MAAM,sBAAsB,MAAM,KAAK;EACvC,MAAM,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;;UAGhD,OAAY;AACpB,MAAI,QAAQ,OAAO,MAAM,iCAAiC,QAAQ;;;AAIpE,eAAsB,sBACrB,KACA,SACA,OACC;AACD,KAAI,CAAC,QAAQ,cAAc,QAC1B;AAED,KAAI;EACH,MAAM,sBAAsB,MAAM,KAAK;EACvC,MAAM,iBAAiB,oBAAoB;EAC3C,MAAM,eAAe,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GACpE,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO;IACP,CACD;GACD,CAAC;AACF,MAAI,cAAc;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;UAEM,OAAY;AACpB,MAAI,QAAQ,OAAO,MAAM,iCAAiC,QAAQ;;;;;;AC1apE,MAAa,0BAA0B,qBACtC,EACC,KAAK,CAAC,kBAAkB,EACxB,EACD,OAAO,QAAQ;AAEd,QAAO,EACN,SAFe,IAAI,QAAQ,SAG3B;EAEF;AAED,MAAa,uBACZ,qBACA,WAEA,qBAAqB,OAAO,QAAQ;CACnC,MAAM,aAAa,IAAI,QAAQ;AAC/B,KAAI,CAAC,WACJ,OAAMA,WAAS,KAAK,gBAAgB,mBAAmB,aAAa;CAGrE,MAAM,eACL,IAAI,MAAM,gBAAgB,IAAI,OAAO;CACtC,MAAM,sBAAsB,IAAI,MAAM,eAAe,IAAI,OAAO;AAEhE,KAAI,iBAAiB,gBAAgB;AAEpC,MAAI,CAAC,oBAAoB,oBAAoB;AAC5C,OAAI,QAAQ,OAAO,MAClB,oGACA;AACD,SAAMA,WAAS,KACd,eACA,mBAAmB,sCACnB;;EAGF,MAAM,cACL,uBAAuB,WAAW,QAAQ;AAC3C,MAAI,CAAC,YACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,mCACnB;AAWF,MAAI,CATiB,MAAM,oBAAoB,mBAC9C;GACC,MAAM,WAAW;GACjB,SAAS,WAAW;GACpB;GACA;GACA,EACD,IACA,CAEA,OAAMA,WAAS,KAAK,gBAAgB,mBAAmB,aAAa;AAErE;;AAID,KAAI,CAAC,oBACJ;AAID,KAAI,wBAAwB,WAAW,KAAK,GAC3C;AAGD,KAAI,CAAC,oBAAoB,oBAAoB;AAC5C,MAAI,QAAQ,OAAO,MAClB,8IACA;AACD,QAAMA,WAAS,KACd,eACA,mBAAmB,yBACnB;;AAWF,KAAI,CATiB,MAAM,oBAAoB,mBAC9C;EACC,MAAM,WAAW;EACjB,SAAS,WAAW;EACpB,aAAa;EACb;EACA,EACD,IACA,CAEA,OAAMA,WAAS,KAAK,gBAAgB,mBAAmB,aAAa;EAEpE;;;;;;;;AC9DH,SAAS,OAAO,KAA6B,KAAa;AACzD,KAAI,6BAA6B,KAAK,IAAI,CACzC,QAAO;AAER,QAAO,GAAG,IAAI,QAAQ,QAAQ,UAC7B,IAAI,WAAW,IAAI,GAAG,MAAM,IAAI;;;;;;AAQlC,eAAe,4BACd,cACA,WAC8B;AAC9B,KAAI,CAAC,UAAW,QAAO;AAMvB,SALe,MAAM,aAAa,OAAO,KAAK;EAC7C,aAAa,CAAC,UAAU;EACxB,QAAQ;EACR,OAAO;EACP,CAAC,EACY,KAAK,IAAI;;;;;;;;AASxB,SAAS,eACR,YACA,cACA,SACS;CACT,MAAM,EAAE,MAAM,YAAY;AAG1B,MAFa,gBAAgB,YAEhB,gBAAgB;AAC5B,MAAI,CAAC,QAAQ,cAAc,QAC1B,OAAMC,WAAS,KACd,eACA,mBAAmB,sCACnB;AAGF,MAAI,CAAC,QAAQ,qBACZ,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;AAEF,SAAO,QAAQ;;AAGhB,QAAO,KAAK;;AAGb,MAAM,gCAAgC,EAAE,OAAO;CAI9C,MAAM,EAAE,QAAQ,CAAC,KAAK,EACrB,aAAa,mDACb,CAAC;CAIF,QAAQ,EACN,SAAS,CACT,KAAK,EACL,aAAa,kDACb,CAAC,CACD,UAAU;CAMZ,aAAa,EACX,QAAQ,CACR,KAAK,EACL,aAAa,sDACb,CAAC,CACD,UAAU;CAKZ,gBAAgB,EACd,QAAQ,CACR,KAAK,EACL,aACC,uEACD,CAAC,CACD,UAAU;CAMZ,cAAc,EACZ,KAAK,CAAC,QAAQ,eAAe,CAAC,CAC9B,KAAK,EACL,aACC,wEACD,CAAC,CACD,UAAU;CAIZ,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;CAIlD,OAAO,EACL,QAAQ,CACR,KAAK,EACL,aAAa,wDACb,CAAC,CACD,UAAU;CAKZ,QAAQ,EACN,QAA4C,iBAAiB;AAC7D,SAAO,OAAO,iBAAiB;GAC9B,CACD,KAAK,EACL,aACC,sHACD,CAAC,CACD,UAAU;CAIZ,YAAY,EACV,QAAQ,CACR,KAAK,EACL,aACC,oGACD,CAAC,CACD,QAAQ,IAAI;CAId,WAAW,EACT,QAAQ,CACR,KAAK,EACL,aACC,wIACD,CAAC,CACD,QAAQ,IAAI;CAId,WAAW,EACT,QAAQ,CACR,KAAK,EACL,aACC,0IACD,CAAC,CACD,UAAU;CAIZ,iBAAiB,EACf,SAAS,CACT,KAAK,EACL,aAAa,4DACb,CAAC,CACD,QAAQ,MAAM;CAChB,CAAC;;;;;;;;;;;;;;;;AAiBF,MAAa,uBAAuB,YAA2B;CAC9D,MAAM,SAAS,QAAQ;CACvB,MAAM,sBAAsB,QAAQ;AAEpC,QAAO,mBACN,yBACA;EACC,QAAQ;EACR,MAAM;EACN,UAAU,EACT,SAAS,EACR,aAAa,uBACb,EACD;EACD,KAAK;GACJ;GACA,oBAAoB,qBAAqB,uBAAuB;GAChE,aAAa,MAAM;AAClB,WAAO,CAAC,EAAE,KAAK,YAAsB,EAAE,KAAK,UAAoB;KAC/D;GACF;EACD,EACD,OAAO,QAAQ;EACd,MAAM,EAAE,MAAM,YAAY,IAAI,QAAQ;EACtC,MAAM,eAAe,IAAI,KAAK,gBAAgB;EAC9C,MAAM,cACL,IAAI,KAAK,eACT,eAAe,IAAI,QAAQ,SAAS,cAAc,QAAQ;AAE3D,MAAI,CAAC,KAAK,iBAAiB,oBAAoB,yBAC9C,OAAMA,WAAS,KACd,eACA,mBAAmB,4BACnB;EAGF,MAAM,OAAO,MAAM,cAAc,SAAS,IAAI,KAAK,KAAK;AACxD,MAAI,CAAC,KACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,4BACnB;EAKF,MAAM,uBAAuB,IAAI,KAAK,iBACnC,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GAChD,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO,IAAI,KAAK;IAChB,CACD;GACD,CAAC,GACD;AACH,MAAI,IAAI,KAAK,kBAAkB,CAAC,qBAC/B,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;AAEF,MACC,IAAI,KAAK,kBACT,wBACA,qBAAqB,gBAAgB,YAErC,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;EAIF,IAAI;AACJ,MAAI,iBAAiB,gBAAgB;AAEpC,gBAAa,sBAAsB;AACnC,OAAI,CAAC,YAAY;IAChB,MAAM,MAAM,MAAM,IAAI,QAAQ,QAAQ,QAEpC;KACD,OAAO;KACP,OAAO,CACN;MACC,OAAO;MACP,OAAO;MACP,CACD;KACD,CAAC;AACF,QAAI,CAAC,IACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;AAEF,iBAAa,IAAI;AAGjB,QAAI,CAAC,WACJ,KAAI;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,IAAI,oBACH,EAAE;AACH,UAAI,QAAQ,cAAc,wBACzB,qBACC,MAAM,QAAQ,aAAa,wBAC1B,KACA,IACA;MAMH,MAAM,iBAAiB,KACtB;OACC,MAAM,IAAI;OACV,UAAU,iBAAiB,IAC1B;QACC,gBAAgB,IAAI;QACpB,cAAc;QACd,EACD,IAAI,KAAK,SACT;OACD,EACD,kBACA;AACD,uBAAiB,MAAM,OAAO,UAAU,OAAO,eAAe;AAG9D,YAAM,QAAQ,cAAc,mBAC3B;OACC;OACA,cAAc;QACb,GAAG;QACH,kBAAkB,eAAe;QACjC;OACD,EACD,IACA;;AAGF,WAAM,IAAI,QAAQ,QAAQ,OAAO;MAChC,OAAO;MACP,QAAQ,EACP,kBAAkB,eAAe,IACjC;MACD,OAAO,CACN;OACC,OAAO;OACP,OAAO,IAAI;OACX,CACD;MACD,CAAC;AAEF,kBAAa,eAAe;aACpB,GAAQ;AAChB,SAAI,QAAQ,OAAO,MAAM,EAAE;AAC3B,WAAMA,WAAS,KACd,eACA,mBAAmB,0BACnB;;;SAIE;AAEN,gBACC,sBAAsB,oBAAoB,KAAK;AAChD,OAAI,CAAC,WACJ,KAAI;IAOH,IAAI,kBALsB,MAAM,OAAO,UAAU,OAAO;KACvD,OAAO,UAAU,wBAAwB,KAAK,MAAM,CAAC,mBAAmB,iBAAiB,KAAK,aAAa;KAC3G,OAAO;KACP,CAAC,EAEqC,KAAK;AAE5C,QAAI,CAAC,eACJ,kBAAiB,MAAM,OAAO,UAAU,OAAO;KAC9C,OAAO,KAAK;KACZ,MAAM,KAAK,QAAQ;KACnB,UAAU,iBAAiB,IAC1B;MACC,QAAQ,KAAK;MACb,cAAc;MACd,EACD,IAAI,KAAK,SACT;KACD,CAAC;AAIH,UAAM,IAAI,QAAQ,QAAQ,OAAO;KAChC,OAAO;KACP,QAAQ,EACP,kBAAkB,eAAe,IACjC;KACD,OAAO,CACN;MACC,OAAO;MACP,OAAO,KAAK;MACZ,CACD;KACD,CAAC;AAEF,iBAAa,eAAe;YACpB,GAAQ;AAChB,QAAI,QAAQ,OAAO,MAAM,EAAE;AAC3B,UAAMA,WAAS,KACd,eACA,mBAAmB,0BACnB;;;EAKJ,MAAM,gBAAgB,uBACnB,CAAC,qBAAqB,GACtB,MAAM,IAAI,QAAQ,QAAQ,SAAuB;GACjD,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO;IACP,CACD;GACD,CAAC;EAEJ,MAAM,+BAA+B,cAAc,MAAM,QACxD,mBAAmB,IAAI,CACvB;EAQD,MAAM,sBANsB,MAAM,OAAO,cACvC,KAAK,EACL,UAAU,YACV,CAAC,CACD,MAAM,QAAQ,IAAI,KAAK,QAAQ,QAAQ,mBAAmB,IAAI,CAAC,CAAC,EAEnB,MAAM,QAAQ;AAE5D,OACC,sBAAsB,wBACtB,IAAI,KAAK,eAET,QACC,IAAI,OAAO,sBAAsB,wBACjC,IAAI,OAAO,IAAI,KAAK;AAItB,OAAI,8BAA8B,qBACjC,QAAO,IAAI,OAAO,6BAA6B;AAEhD,UAAO;IACN;EAGF,MAAM,4BACL,oBAAoB,MAAM,KAAK,IAAI,MAAM;EAG1C,MAAM,yBAAyB,cAAc,MAC3C,QAAQ,IAAI,WAAW,aACxB;EAED,MAAM,UAAU,IAAI,KAAK,SACtB,KAAK,wBACL,KAAK;EACR,MAAM,YAAY,IAAI,KAAK,SACxB,KAAK,0BACL,KAAK;EACR,MAAM,kBAAkB,YACrB,MAAM,4BAA4B,QAAQ,UAAU,GACpD;EAEH,MAAM,eAAe,WAAW;AAChC,MAAI,CAAC,aACJ,OAAM,IAAI,MAAM,eAAe,EAC9B,SAAS,4CACT,CAAC;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,OAAMA,WAAS,KACd,eACA,mBAAmB,wBACnB;AAGF,MAAI,sBAAsB,YAAY;GAErC,IAAI,iBAAiB,MAAM,IAAI,QAAQ,QAAQ,QAAsB;IACpE,OAAO;IACP,OAAO,CACN;KACC,OAAO;KACP,OAAO,mBAAmB;KAC1B,CACD;IACD,CAAC;AAGF,OAAI,CAAC,kBAAkB,8BAA8B;AACpD,UAAM,IAAI,QAAQ,QAAQ,OAAqB;KAC9C,OAAO;KACP,QAAQ;MACP,sBAAsB,mBAAmB;MACzC,2BAAW,IAAI,MAAM;MACrB;KACD,OAAO,CACN;MACC,OAAO;MACP,OAAO,6BAA6B;MACpC,CACD;KACD,CAAC;AACF,qBAAiB;;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,IAAI,eACH,gCAAgC;AAEjC,MAAI,0BAA0B,CAAC,6BAe9B,gBAdgB,MAAM,IAAI,QAAQ,QAAQ,OAAqB;GAC9D,OAAO;GACP,QAAQ;IACP,MAAM,KAAK,KAAK,aAAa;IAC7B,OAAO,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,SAAMA,WAAS,KACd,aACA,mBAAmB,uBACnB;;EAGF,MAAM,SAAS,MAAM,oBAAoB,2BACxC;GACC;GACA;GACA;GACA;GACA,EACD,IAAI,SACJ,IACA;EAgBD,MAAM,YACL,EAfwB,MAAM,IAAI,QAAQ,QAAQ,SAClD;GACC,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAe,OAAO;IAAa,CAAC;GACrD,CACD,EACuC,MAAM,MAAM;AAKnD,UADC,CAAC,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW;IAE/C,IAGkB,KAAK,YACrB,EAAE,mBAAmB,KAAK,UAAU,MAAM,GAC1C;EAEJ,MAAM,kBAAkB,MAAM,OAAO,SAAS,SAC5C,OACA;GACC,GAAI,aACD;IACA,UAAU;IACV,iBACC,iBAAiB,SACb,EAAE,SAAS,QAAQ,GACnB;KAAE,MAAM;KAAQ,SAAS;KAAQ;IACtC,GACA,EACA,gBAAgB,KAAK,OACrB;GACH,QAAQ,IAAI,KAAK;GACjB,aAAa,OACZ,KACA,GACC,IAAI,QAAQ,QACZ,oCAAoC,mBACpC,IAAI,KAAK,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,QAAQ,KAAK;KACb,gBAAgB,aAAa;KAC7B;KACA,EACD,IAAI,KAAK,UACT,QAAQ,QAAQ,mBAAmB,SACnC;IACD;GACD,MAAM;GACN,qBAAqB;GACrB,GAAG,QAAQ;GAEX,UAAU,qBAAqB,IAC9B;IACC,QAAQ,KAAK;IACb,gBAAgB,aAAa;IAC7B;IACA,EACD,IAAI,KAAK,UACT,QAAQ,QAAQ,SAChB;GACD,EACD,QAAQ,QACR,CACA,MAAM,OAAO,MAAM;AACnB,SAAM,IAAI,MAAM,eAAe;IAC9B,SAAS,EAAE;IACX,MAAM,EAAE;IACR,CAAC;IACD;AACH,SAAO,IAAI,KAAK;GACf,GAAG;GACH,UAAU,CAAC,IAAI,KAAK;GACpB,CAAC;GAEH;;AAGF,MAAM,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,SAAS;EACjB,MAAM,EAAE,aAAa,mBAAmB,IAAI;AAE5C,MAAI,MAAM,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,UAAU,KAAK;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,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;EAEF,MAAM,sBAAsB,MAAM,OAAO,cACvC,KAAK,EACL,UAAU,aAAa,kBACvB,CAAC,CACD,MAAM,QAAQ,IAAI,KAAK,QAAQ,QAAQ,mBAAmB,IAAI,CAAC,CAAC;AAClE,MAAI,CAAC,oBAAoB,QAAQ;;;;;AAKhC,SAAM,IAAI,QAAQ,QAAQ,WAAW;IACpC,OAAO;IACP,OAAO,CACN;KACC,OAAO;KACP,OAAO;KACP,CACD;IACD,CAAC;AACF,SAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;;EAEF,MAAM,qBAAqB,oBAAoB,MAC7C,QAAQ,IAAI,OAAO,aAAa,qBACjC;AACD,MAAI,CAAC,mBACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;EAEF,MAAM,EAAE,QAAQ,MAAM,OAAO,cAAc,SACzC,OAAO;GACP,UAAU,aAAa;GACvB,YAAY,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,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;AAEF,MAAI,CAAC,mBAAmB,aAAa,CACpC,OAAMA,WAAS,KACd,eACA,mBAAmB,wBACnB;AAEF,MAAI,CAAC,gBAAgB,aAAa,CACjC,OAAMA,WAAS,KACd,eACA,mBAAmB,4CACnB;EAGF,MAAM,qBAAqB,MAAM,OAAO,cACtC,KAAK,EACL,UAAU,aAAa,kBACvB,CAAC,CACD,MAAM,QAAQ,IAAI,KAAK,QAAQ,QAAQ,mBAAmB,IAAI,CAAC,CAAC,GAAG;AACrE,MAAI,CAAC,mBACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,uBACnB;EAKF,MAAM,eAAgD,EAAE;AACxD,MAAI,mBAAmB,UACtB,cAAa,YAAY;WACf,mBAAmB,qBAC7B,cAAa,uBAAuB;EAGrC,MAAM,SAAS,MAAM,OAAO,cAC1B,OAAO,mBAAmB,IAAI,aAAa,CAC3C,OAAO,MAAM;AACb,SAAM,IAAI,MAAM,eAAe;IAC9B,SAAS,EAAE;IACX,MAAM,EAAE;IACR,CAAC;IACD;AAEH,QAAM,IAAI,QAAQ,QAAQ,OAAO;GAChC,OAAO;GACP,QAAQ;IACP,mBAAmB;IACnB,UAAU;IACV,YAAY;IACZ,2BAAW,IAAI,MAAM;IACrB;GACD,OAAO,CACN;IACC,OAAO;IACP,OAAO,aAAa;IACpB,CACD;GACD,CAAC;AAEF,SAAO,IAAI,KAAK,OAAO;GAExB;;AAGF,MAAM,qCAAqC,EAAE,SAC5C,EAAE,OAAO;CACR,aAAa,EACX,QAAQ,CACR,KAAK,EACL,aAAa,uDACb,CAAC,CACD,UAAU;CAMZ,cAAc,EACZ,KAAK,CAAC,QAAQ,eAAe,CAAC,CAC9B,KAAK,EACL,aACC,wEACD,CAAC,CACD,UAAU;CACZ,CAAC,CACF;;;;;;;;;;;;;;;;AAgBD,MAAa,2BAA2B,YAA2B;CAClE,MAAM,sBAAsB,QAAQ;AACpC,QAAO,mBACN,sBACA;EACC,QAAQ;EACR,OAAO;EACP,UAAU,EACT,SAAS,EACR,aAAa,2BACb,EACD;EACD,KAAK,CACJ,yBACA,oBAAoB,qBAAqB,oBAAoB,CAC7D;EACD,EACD,OAAO,QAAQ;EACd,MAAM,eAAe,IAAI,OAAO,gBAAgB;EAChD,MAAM,cACL,IAAI,OAAO,eACX,eAAe,IAAI,QAAQ,SAAS,cAAc,QAAQ;EAE3D,MAAM,gBAAgB,MAAM,IAAI,QAAQ,QAAQ,SAAuB;GACtE,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO;IACP,CACD;GACD,CAAC;AACF,MAAI,CAAC,cAAc,OAClB,QAAO,EAAE;EAEV,MAAM,QAAQ,MAAM,SAAS,QAAQ,aAAa;AAClD,MAAI,CAAC,MACJ,QAAO,EAAE;EAEV,MAAM,OAAO,cACX,KAAK,QAAQ;GACb,MAAM,OAAO,MAAM,MACjB,MAAM,EAAE,KAAK,aAAa,KAAK,IAAI,KAAK,aAAa,CACtD;AACD,UAAO;IACN,GAAG;IACH,QAAQ,MAAM;IACd,SAAS,MAAM;IACf;IACA,CACD,QAAQ,QAAQ,mBAAmB,IAAI,CAAC;AAC1C,SAAO,IAAI,KAAK,KAAK;GAEtB;;AAGF,MAAM,iCAAiC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;AAE/E,MAAa,uBAAuB,YAA2B;CAC9D,MAAM,SAAS,QAAQ;AACvB,QAAO,mBACN,yBACA;EACC,QAAQ;EACR,OAAO;EACP,UAAU,EACT,SAAS,EACR,aAAa,6BACb,EACD;EACD,KAAK,CAAC,aAAa,QAAQ,IAAI,MAAM,YAAY,CAAC;EAClD,EACD,OAAO,QAAQ;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,SAAS,IAAI,QAAQ;EAC7B,MAAM,eAAe,IAAI,KAAK,gBAAgB;EAC9C,MAAM,cACL,IAAI,KAAK,eACT,eAAe,IAAI,QAAQ,SAAS,cAAc,QAAQ;EAE3D,IAAI;AAEJ,MAAI,iBAAiB,gBAAgB;AAQpC,iBANY,MAAM,IAAI,QAAQ,QAAQ,QAEpC;IACD,OAAO;IACP,OAAO,CAAC;KAAE,OAAO;KAAM,OAAO;KAAa,CAAC;IAC5C,CAAC,GACgB;AAElB,OAAI,CAAC,WAQJ,eANqB,MAAM,IAAI,QAAQ,QACrC,SAAuB;IACvB,OAAO;IACP,OAAO,CAAC;KAAE,OAAO;KAAe,OAAO;KAAa,CAAC;IACrD,CAAC,CACD,MAAM,SAAS,KAAK,MAAM,QAAQ,mBAAmB,IAAI,CAAC,CAAC,GAClC;SAEtB;AAEN,gBAAa,KAAK;AAClB,OAAI,CAAC,WAaJ,eAZqB,MAAM,IAAI,QAAQ,QACrC,SAAuB;IACvB,OAAO;IACP,OAAO,CACN;KACC,OAAO;KACP,OAAO;KACP,CACD;IACD,CAAC,CACD,MAAM,SAAS,KAAK,MAAM,QAAQ,mBAAmB,IAAI,CAAC,CAAC,GAElC;;AAG7B,MAAI,CAAC,WACJ,OAAMA,WAAS,KAAK,aAAa,mBAAmB,mBAAmB;AAGxE,MAAI;GACH,MAAM,EAAE,QAAQ,MAAM,OAAO,cAAc,SAAS,OAAO;IAC1D,QAAQ,IAAI,KAAK;IACjB,UAAU;IACV,YAAY,OAAO,KAAK,IAAI,KAAK,UAAU;IAC3C,CAAC;AAEF,UAAO,IAAI,KAAK;IACf;IACA,UAAU,CAAC,IAAI,KAAK;IACpB,CAAC;WACM,OAAY;AACpB,OAAI,QAAQ,OAAO,MAClB,yCACA,MACA;AACD,SAAMA,WAAS,KACd,yBACA,mBAAmB,gCACnB;;GAGH;;AAGF,MAAa,iBAAiB,YAA2B;CACxD,MAAM,SAAS,QAAQ;AACvB,QAAO,mBACN,mBACA;EACC,QAAQ;EACR,UAAU;GACT,GAAG;GACH,SAAS,EACR,aAAa,uBACb;GACD;EACD,cAAc;EACd,aAAa;EACb,EACD,OAAO,QAAQ;AACd,MAAI,CAAC,IAAI,SAAS,KACjB,OAAMA,WAAS,KACd,eACA,mBAAmB,qBACnB;EAGF,MAAM,MAAM,IAAI,QAAQ,QAAQ,IAAI,mBAAmB;AACvD,MAAI,CAAC,IACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,2BACnB;EAGF,MAAM,gBAAgB,QAAQ;AAC9B,MAAI,CAAC,cACJ,OAAMA,WAAS,KACd,yBACA,mBAAmB,gCACnB;EAGF,MAAM,UAAU,MAAM,IAAI,QAAQ,MAAM;EAExC,IAAI;AACJ,MAAI;AAEH,OAAI,OAAO,OAAO,SAAS,wBAAwB,WAElD,SAAQ,MAAM,OAAO,SAAS,oBAC7B,SACA,KACA,cACA;OAGD,SAAQ,OAAO,SAAS,eAAe,SAAS,KAAK,cAAc;WAE5D,KAAU;AAClB,OAAI,QAAQ,OAAO,MAAM,GAAG,IAAI,UAAU;AAC1C,SAAMA,WAAS,KACd,eACA,mBAAmB,iCACnB;;AAEF,MAAI,CAAC,MACJ,OAAMA,WAAS,KACd,eACA,mBAAmB,iCACnB;AAEF,MAAI;AACH,WAAQ,MAAM,MAAd;IACC,KAAK;AACJ,WAAM,2BAA2B,KAAK,SAAS,MAAM;AACrD,WAAM,QAAQ,UAAU,MAAM;AAC9B;IACD,KAAK;AACJ,WAAM,sBAAsB,KAAK,SAAS,MAAM;AAChD,WAAM,QAAQ,UAAU,MAAM;AAC9B;IACD,KAAK;AACJ,WAAM,sBAAsB,KAAK,SAAS,MAAM;AAChD,WAAM,QAAQ,UAAU,MAAM;AAC9B;IACD,KAAK;AACJ,WAAM,sBAAsB,KAAK,SAAS,MAAM;AAChD,WAAM,QAAQ,UAAU,MAAM;AAC9B;IACD;AACC,WAAM,QAAQ,UAAU,MAAM;AAC9B;;WAEM,GAAQ;AAChB,OAAI,QAAQ,OAAO,MAAM,iCAAiC,EAAE,UAAU;AACtE,SAAMA,WAAS,KACd,eACA,mBAAmB,qBACnB;;AAEF,SAAO,IAAI,KAAK,EAAE,SAAS,MAAM,CAAC;GAEnC;;;;;AChoDF,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,IAAI,aAAuC,EAAE;AAE7C,KAAI,QAAQ,cAAc,QACzB,cAAa;EACZ,GAAG;EACH,GAAG;EACH;KAED,cAAa,EACZ,GAAG,MACH;AAGF,KAAI,QAAQ,cAAc,QACzB,cAAa;EACZ,GAAG;EACH,GAAG;EACH;AAGF,KACC,QAAQ,UACR,CAAC,QAAQ,cAAc,WACvB,kBAAkB,QAAQ,QACzB;EACD,MAAM,EAAE,cAAc,eAAe,GAAG,eAAe,QAAQ;AAC/D,SAAO,YAAY,YAAY,WAAW;;AAG3C,QAAO,YAAY,YAAY,QAAQ,OAAO;;;;;AC3F/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,YAAY,IAAI,UAAU,eAAe;AAC/C,QAAI,CAAC,WAAW;AACf,SAAI,OAAO,MAAM,gCAAgC;AACjD;;IAGD,MAAM,gBAAgB,UAAU,QAAQ,qBAAqB,EAAE;;;;IAK/D,MAAM,uBAAuB,OAAO,SAG9B;KACL,MAAM,EAAE,iBAAiB;AACzB,SAAI,CAAC,cAAc,iBAAkB;AAErC,SAAI;MACH,MAAM,iBAAiB,MAAM,OAAO,UAAU,SAC7C,aAAa,iBACb;AAED,UAAI,eAAe,SAAS;AAC3B,WAAI,OAAO,KACV,mBAAmB,aAAa,iBAAiB,cACjD;AACD;;AAID,UAAI,aAAa,SAAS,eAAe,MAAM;AAC9C,aAAM,OAAO,UAAU,OAAO,aAAa,kBAAkB,EAC5D,MAAM,aAAa,MACnB,CAAC;AACF,WAAI,OAAO,KACV,wCAAwC,eAAe,KAAK,OAAO,aAAa,KAAK,GACrF;;cAEM,GAAQ;AAChB,UAAI,OAAO,MACV,0CAA0C,EAAE,UAC5C;;;;;;IAOH,MAAM,wBAAwB,OAAO,SAG/B;KACL,MAAM,EAAE,iBAAiB;AACzB,SAAI,CAAC,aAAa,iBAAkB;AAEpC,SAAI;MAEH,MAAM,gBAAgB,MAAM,OAAO,cAAc,KAAK;OACrD,UAAU,aAAa;OACvB,QAAQ;OACR,OAAO;OACP,CAAC;AACF,WAAK,MAAM,OAAO,cAAc,KAC/B,KACC,IAAI,WAAW,cACf,IAAI,WAAW,gBACf,IAAI,WAAW,qBAEf,OAAM,SAAS,KACd,eACA,mBAAmB,qCACnB;cAGK,OAAY;AACpB,UAAI,iBAAiB,SACpB,OAAM;AAEP,UAAI,OAAO,MACV,+CAA+C,MAAM,UACrD;AACD,YAAM;;;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,MAAmC,KAAK;AACnD,SACC,CAAC,OACD,CAAC,QAAQ,0BACT,KAAK,iBAEL;AAGD,SAAI;MAOH,IAAI,kBALsB,MAAM,OAAO,UAAU,OAAO;OACvD,OAAO,UAAU,wBAAwB,KAAK,MAAM,CAAC,mBAAmB,iBAAiB,KAAK,aAAa;OAC3G,OAAO;OACP,CAAC,EAEqC,KAAK;AAG5C,UAAI,gBAAgB;AACnB,aAAM,IAAI,QAAQ,gBAAgB,WAAW,KAAK,IAAI,EACrD,kBAAkB,eAAe,IACjC,CAAC;AACF,aAAM,QAAQ,mBACb;QACC;QACA,MAAM;SACL,GAAG;SACH,kBAAkB,eAAe;SACjC;QACD,EACD,IACA;AACD,WAAI,QAAQ,OAAO,KAClB,mCAAmC,eAAe,GAAG,WAAW,KAAK,KACrE;AACD;;MAID,IAAI,oBACH,EAAE;AACH,UAAI,QAAQ,wBACX,qBAAoB,MAAM,QAAQ,wBACjC,MACA,IACA;MAGF,MAAM,SAAS,KACd;OACC,OAAO,KAAK;OACZ,MAAM,KAAK,QAAQ;OACnB,UAAU,iBAAiB,IAC1B;QACC,QAAQ,KAAK;QACb,cAAc;QACd,EACD,mBAAmB,SACnB;OACD,EACD,kBACA;AACD,uBAAiB,MAAM,OAAO,UAAU,OAAO,OAAO;AACtD,YAAM,IAAI,QAAQ,gBAAgB,WAAW,KAAK,IAAI,EACrD,kBAAkB,eAAe,IACjC,CAAC;AACF,YAAM,QAAQ,mBACb;OACC;OACA,MAAM;QACL,GAAG;QACH,kBAAkB,eAAe;QACjC;OACD,EACD,IACA;AACD,UAAI,QAAQ,OAAO,KAClB,+BAA+B,eAAe,GAAG,YAAY,KAAK,KAClE;cACO,GAAQ;AAChB,UAAI,QAAQ,OAAO,MAClB,6CAA6C,EAAE,WAC/C,EACA;;OAGH;IACD,QAAQ,EACP,MAAM,MAAM,MAAmC,KAAK;AACnD,SACC,CAAC,OACD,CAAC,KAAK,iBAEN;AAED,SAAI;MAIH,MAAM,iBAAiB,MAAM,OAAO,UAAU,SAC7C,KAAK,iBACL;AAGD,UAAI,eAAe,SAAS;AAC3B,WAAI,QAAQ,OAAO,KAClB,mBAAmB,KAAK,iBAAiB,mCACzC;AACD;;AAID,UAAI,eAAe,UAAU,KAAK,OAAO;AACxC,aAAM,OAAO,UAAU,OAAO,KAAK,kBAAkB,EACpD,OAAO,KAAK,OACZ,CAAC;AACF,WAAI,QAAQ,OAAO,KAClB,sCAAsC,eAAe,MAAM,MAAM,KAAK,QACtE;;cAEM,GAAQ;AAGhB,UAAI,QAAQ,OAAO,MAClB,4CAA4C,EAAE,WAC9C,EACA;;OAGH;IACD,EACD,EACD,EACD;;EAEF,QAAQ,UAAU,QAAQ;EACjB;EACT,cAAc;EACd"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["APIError","APIError"],"sources":["../src/metadata.ts","../src/utils.ts","../src/hooks.ts","../src/middleware.ts","../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { defu } from \"defu\";\nimport type Stripe from \"stripe\";\n\n/**\n * Internal metadata fields for Stripe Customer.\n */\ntype CustomerInternalMetadata =\n\t| { customerType: \"user\"; userId: string }\n\t| { customerType: \"organization\"; organizationId: string };\n\n/**\n * Internal metadata fields for Stripe Subscription/Checkout.\n */\ntype SubscriptionInternalMetadata = {\n\tuserId: string;\n\tsubscriptionId: string;\n\treferenceId: string;\n};\n\n/**\n * Customer metadata - set internal fields and extract typed fields.\n */\nexport const customerMetadata = {\n\t/**\n\t * Internal metadata keys for type-safe access.\n\t */\n\tkeys: {\n\t\tuserId: \"userId\",\n\t\torganizationId: \"organizationId\",\n\t\tcustomerType: \"customerType\",\n\t} as const,\n\n\t/**\n\t * Create metadata with internal fields that cannot be overridden by user metadata.\n\t * Uses `defu` which prioritizes the first argument.\n\t */\n\tset(\n\t\tinternalFields: CustomerInternalMetadata,\n\t\t...userMetadata: (Stripe.Emptyable<Stripe.MetadataParam> | undefined)[]\n\t): Stripe.MetadataParam {\n\t\treturn defu(internalFields, ...userMetadata.filter(Boolean));\n\t},\n\n\t/**\n\t * Extract internal fields from Stripe metadata.\n\t * Provides type-safe access to internal metadata keys.\n\t */\n\tget(metadata: Stripe.Metadata | null | undefined) {\n\t\treturn {\n\t\t\tuserId: metadata?.userId,\n\t\t\torganizationId: metadata?.organizationId,\n\t\t\tcustomerType: metadata?.customerType as\n\t\t\t\t| CustomerInternalMetadata[\"customerType\"]\n\t\t\t\t| undefined,\n\t\t};\n\t},\n};\n\n/**\n * Subscription/Checkout metadata - set internal fields and extract typed fields.\n */\nexport const subscriptionMetadata = {\n\t/**\n\t * Internal metadata keys for type-safe access.\n\t */\n\tkeys: {\n\t\tuserId: \"userId\",\n\t\tsubscriptionId: \"subscriptionId\",\n\t\treferenceId: \"referenceId\",\n\t} as const,\n\n\t/**\n\t * Create metadata with internal fields that cannot be overridden by user metadata.\n\t * Uses `defu` which prioritizes the first argument.\n\t */\n\tset(\n\t\tinternalFields: SubscriptionInternalMetadata,\n\t\t...userMetadata: (Stripe.Emptyable<Stripe.MetadataParam> | undefined)[]\n\t): Stripe.MetadataParam {\n\t\treturn defu(internalFields, ...userMetadata.filter(Boolean));\n\t},\n\n\t/**\n\t * Extract internal fields from Stripe metadata.\n\t * Provides type-safe access to internal metadata keys.\n\t */\n\tget(metadata: Stripe.Metadata | null | undefined) {\n\t\treturn {\n\t\t\tuserId: metadata?.userId,\n\t\t\tsubscriptionId: metadata?.subscriptionId,\n\t\t\treferenceId: metadata?.referenceId,\n\t\t};\n\t},\n};\n","import type Stripe from \"stripe\";\nimport type { StripeOptions, 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 } from \"@better-auth/core/error\";\nimport { sessionMiddleware } from \"better-auth/api\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport type {\n\tAuthorizeReferenceAction,\n\tCustomerType,\n\tStripeCtxSession,\n\tSubscriptionOptions,\n} from \"./types\";\n\nexport const stripeSessionMiddleware = createAuthMiddleware(\n\t{\n\t\tuse: [sessionMiddleware],\n\t},\n\tasync (ctx) => {\n\t\tconst session = ctx.context.session as StripeCtxSession;\n\t\treturn {\n\t\t\tsession,\n\t\t};\n\t},\n);\n\nexport const referenceMiddleware = (\n\tsubscriptionOptions: SubscriptionOptions,\n\taction: AuthorizeReferenceAction,\n) =>\n\tcreateAuthMiddleware(async (ctx) => {\n\t\tconst ctxSession = ctx.context.session as StripeCtxSession;\n\t\tif (!ctxSession) {\n\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t}\n\n\t\tconst customerType: CustomerType =\n\t\t\tctx.body?.customerType || ctx.query?.customerType;\n\t\tconst explicitReferenceId = ctx.body?.referenceId || ctx.query?.referenceId;\n\n\t\tif (customerType === \"organization\") {\n\t\t\t// Organization subscriptions always require authorizeReference\n\t\t\tif (!subscriptionOptions.authorizeReference) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t`Organization subscriptions require authorizeReference to be defined in your stripe plugin config.`,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.AUTHORIZE_REFERENCE_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst referenceId =\n\t\t\t\texplicitReferenceId || ctxSession.session.activeOrganizationId;\n\t\t\tif (!referenceId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_REFERENCE_ID_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isAuthorized = await subscriptionOptions.authorizeReference(\n\t\t\t\t{\n\t\t\t\t\tuser: ctxSession.user,\n\t\t\t\t\tsession: ctxSession.session,\n\t\t\t\t\treferenceId,\n\t\t\t\t\taction,\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t);\n\t\t\tif (!isAuthorized) {\n\t\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// User subscriptions - pass if no explicit referenceId\n\t\tif (!explicitReferenceId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass if referenceId is user id\n\t\tif (explicitReferenceId === ctxSession.user.id) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!subscriptionOptions.authorizeReference) {\n\t\t\tctx.context.logger.error(\n\t\t\t\t`Passing referenceId into a subscription action isn't allowed if subscription.authorizeReference isn't defined in your stripe plugin config.`,\n\t\t\t);\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.REFERENCE_ID_NOT_ALLOWED,\n\t\t\t);\n\t\t}\n\t\tconst isAuthorized = await subscriptionOptions.authorizeReference(\n\t\t\t{\n\t\t\t\tuser: ctxSession.user,\n\t\t\t\tsession: ctxSession.session,\n\t\t\t\treferenceId: explicitReferenceId,\n\t\t\t\taction,\n\t\t\t},\n\t\t\tctx,\n\t\t);\n\t\tif (!isAuthorized) {\n\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t}\n\t});\n","import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport type { GenericEndpointContext, User } from \"better-auth\";\nimport { HIDE_METADATA } from \"better-auth\";\nimport { getSessionFromCtx, originCheck } from \"better-auth/api\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport { defu } from \"defu\";\nimport type Stripe from \"stripe\";\nimport type { Stripe as StripeType } from \"stripe\";\nimport * as z from \"zod/v4\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport {\n\tonCheckoutSessionCompleted,\n\tonSubscriptionCreated,\n\tonSubscriptionDeleted,\n\tonSubscriptionUpdated,\n} from \"./hooks\";\nimport { customerMetadata, subscriptionMetadata } from \"./metadata\";\nimport { referenceMiddleware, stripeSessionMiddleware } from \"./middleware\";\nimport type {\n\tCustomerType,\n\tStripeCtxSession,\n\tStripeOptions,\n\tSubscription,\n\tSubscriptionOptions,\n\tWithStripeCustomerId,\n} from \"./types\";\nimport {\n\tescapeStripeSearchValue,\n\tgetPlanByName,\n\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 APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_SUBSCRIPTION_NOT_ENABLED,\n\t\t\t);\n\t\t}\n\n\t\tif (!session.activeOrganizationId) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t);\n\t\t}\n\t\treturn session.activeOrganizationId;\n\t}\n\n\treturn user.id;\n}\n\nconst upgradeSubscriptionBodySchema = z.object({\n\t/**\n\t * The name of the plan to subscribe\n\t */\n\tplan: z.string().meta({\n\t\tdescription: 'The name of the plan to upgrade to. Eg: \"pro\"',\n\t}),\n\t/**\n\t * If annual plan should be applied.\n\t */\n\tannual: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription: \"Whether to upgrade to an annual plan. Eg: true\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Reference ID for the subscription based on customerType:\n\t * - `user`: defaults to `user.id`\n\t * - `organization`: defaults to `session.activeOrganizationId`\n\t */\n\treferenceId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: 'Reference ID for the subscription. Eg: \"org_123\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The Stripe subscription ID to upgrade.\n\t * If provided and not found, it'll throw an error.\n\t */\n\tsubscriptionId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'The Stripe subscription ID to upgrade. Eg: \"sub_1ABC2DEF3GHI4JKL\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription (requires referenceId)\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Additional metadata to store with the subscription.\n\t */\n\tmetadata: z.record(z.string(), z.any()).optional(),\n\t/**\n\t * Number of seats for subscriptions.\n\t */\n\tseats: z\n\t\t.number()\n\t\t.meta({\n\t\t\tdescription: \"Number of seats to upgrade to (if applicable). Eg: 1\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The IETF language tag of the locale Checkout is displayed in.\n\t * If not provided or set to `auto`, the browser's locale is used.\n\t */\n\tlocale: z\n\t\t.custom<StripeType.Checkout.Session.Locale>((localization) => {\n\t\t\treturn typeof localization === \"string\";\n\t\t})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The locale to display Checkout in. Eg: 'en', 'ko'. If not provided or set to `auto`, the browser's locale is used.\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The URL to which Stripe should send customers when payment or setup is complete.\n\t */\n\tsuccessUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Callback URL to redirect back after successful subscription. Eg: \"https://example.com/success\"',\n\t\t})\n\t\t.default(\"/\"),\n\t/**\n\t * If set, checkout shows a back button and customers will be directed here if they cancel payment.\n\t */\n\tcancelUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'If set, checkout shows a back button and customers will be directed here if they cancel payment. Eg: \"https://example.com/pricing\"',\n\t\t})\n\t\t.default(\"/\"),\n\t/**\n\t * The URL to return to from the Billing Portal (used when upgrading existing subscriptions)\n\t */\n\treturnUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'URL to take customers to when they click on the billing portal’s link to return to your website. Eg: \"https://example.com/dashboard\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Disable Redirect\n\t */\n\tdisableRedirect: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription: \"Disable redirect after successful subscription. Eg: true\",\n\t\t})\n\t\t.default(false),\n});\n\n/**\n * ### Endpoint\n *\n * POST `/subscription/upgrade`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.upgradeSubscription`\n *\n * **client:**\n * `authClient.subscription.upgrade`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-upgrade)\n */\nexport const upgradeSubscription = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\n\treturn createAuthEndpoint(\n\t\t\"/subscription/upgrade\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: upgradeSubscriptionBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"upgradeSubscription\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"upgrade-subscription\"),\n\t\t\t\toriginCheck((c) => {\n\t\t\t\t\treturn [c.body.successUrl as string, c.body.cancelUrl as string];\n\t\t\t\t}),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { user, session } = ctx.context.session;\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tif (!user.emailVerified && subscriptionOptions.requireEmailVerification) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst plan = await getPlanByName(options, ctx.body.plan);\n\t\t\tif (!plan) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// If subscriptionId is provided, find that specific subscription.\n\t\t\t// Otherwise, active subscription will be resolved by referenceId later.\n\t\t\tconst subscriptionToUpdate = ctx.body.subscriptionId\n\t\t\t\t? await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\t\tvalue: ctx.body.subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t: null;\n\t\t\tif (ctx.body.subscriptionId && !subscriptionToUpdate) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tctx.body.subscriptionId &&\n\t\t\t\tsubscriptionToUpdate &&\n\t\t\t\tsubscriptionToUpdate.referenceId !== referenceId\n\t\t\t) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Determine customer id\n\t\t\tlet customerId: string | undefined;\n\t\t\tif (customerType === \"organization\") {\n\t\t\t\t// Organization subscription - get customer ID from organization\n\t\t\t\tcustomerId = subscriptionToUpdate?.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\tconst org = await ctx.context.adapter.findOne<\n\t\t\t\t\t\tOrganization & WithStripeCustomerId\n\t\t\t\t\t>({\n\t\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tif (!org) {\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tcustomerId = org.stripeCustomerId;\n\n\t\t\t\t\t// If org doesn't have a customer ID, create one\n\t\t\t\t\tif (!customerId) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// 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 APIError.from(\n\t\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_CUSTOMER,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// User subscription - get customer ID from user\n\t\t\t\tcustomerId =\n\t\t\t\t\tsubscriptionToUpdate?.stripeCustomerId || user.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// 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 APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_CUSTOMER,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst subscriptions = subscriptionToUpdate\n\t\t\t\t? [subscriptionToUpdate]\n\t\t\t\t: await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\n\t\t\tconst activeOrTrialingSubscription = subscriptions.find((sub) =>\n\t\t\t\tisActiveOrTrialing(sub),\n\t\t\t);\n\n\t\t\tconst activeSubscriptions = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: customerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub)));\n\n\t\t\tconst activeSubscription = activeSubscriptions.find((sub) => {\n\t\t\t\t// If we have a specific subscription to update, match by ID\n\t\t\t\tif (\n\t\t\t\t\tsubscriptionToUpdate?.stripeSubscriptionId ||\n\t\t\t\t\tctx.body.subscriptionId\n\t\t\t\t) {\n\t\t\t\t\treturn (\n\t\t\t\t\t\tsub.id === subscriptionToUpdate?.stripeSubscriptionId ||\n\t\t\t\t\t\tsub.id === ctx.body.subscriptionId\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// Only find subscription for the same referenceId to avoid mixing personal and org subscriptions\n\t\t\t\tif (activeOrTrialingSubscription?.stripeSubscriptionId) {\n\t\t\t\t\treturn sub.id === activeOrTrialingSubscription.stripeSubscriptionId;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\n\t\t\t// Get the current price ID from the active Stripe subscription\n\t\t\tconst 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 APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.ALREADY_SUBSCRIBED_PLAN,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (activeSubscription && customerId) {\n\t\t\t\t// Find the corresponding database subscription for this Stripe subscription\n\t\t\t\tlet dbSubscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\tvalue: activeSubscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\t// If no database record exists for this Stripe subscription, update the existing one\n\t\t\t\tif (!dbSubscription && activeOrTrialingSubscription) {\n\t\t\t\t\tawait ctx.context.adapter.update<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\tstripeSubscriptionId: activeSubscription.id,\n\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: activeOrTrialingSubscription.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tdbSubscription = activeOrTrialingSubscription;\n\t\t\t\t}\n\n\t\t\t\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 APIError.from(\n\t\t\t\t\t\"NOT_FOUND\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = await subscriptionOptions.getCheckoutSessionParams?.(\n\t\t\t\t{\n\t\t\t\t\tuser,\n\t\t\t\t\tsession,\n\t\t\t\t\tplan,\n\t\t\t\t\tsubscription,\n\t\t\t\t},\n\t\t\t\tctx.request,\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tconst allSubscriptions = await ctx.context.adapter.findMany<Subscription>(\n\t\t\t\t{\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [{ field: \"referenceId\", value: referenceId }],\n\t\t\t\t},\n\t\t\t);\n\t\t\tconst hasEverTrialed = allSubscriptions.some((s) => {\n\t\t\t\t// Check if user has ever had a trial for any plan (not just the same plan)\n\t\t\t\t// This prevents users from getting multiple trials by switching plans\n\t\t\t\tconst hadTrial =\n\t\t\t\t\t!!(s.trialStart || s.trialEnd) || s.status === \"trialing\";\n\t\t\t\treturn hadTrial;\n\t\t\t});\n\n\t\t\tconst freeTrial =\n\t\t\t\t!hasEverTrialed && plan.freeTrial\n\t\t\t\t\t? { trial_period_days: plan.freeTrial.days }\n\t\t\t\t\t: undefined;\n\n\t\t\tconst checkoutSession = await client.checkout.sessions\n\t\t\t\t.create(\n\t\t\t\t\t{\n\t\t\t\t\t\t...(customerId\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\t\t\t\tcustomer_update:\n\t\t\t\t\t\t\t\t\t\tcustomerType !== \"user\"\n\t\t\t\t\t\t\t\t\t\t\t? ({ address: \"auto\" } as const)\n\t\t\t\t\t\t\t\t\t\t\t: ({ name: \"auto\", address: \"auto\" } as const), // The customer name is automatically set only for users\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\tcustomer_email: user.email,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tlocale: ctx.body.locale,\n\t\t\t\t\t\tsuccess_url: getUrl(\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t`${\n\t\t\t\t\t\t\t\tctx.context.baseURL\n\t\t\t\t\t\t\t}/subscription/success?callbackURL=${encodeURIComponent(\n\t\t\t\t\t\t\t\tctx.body.successUrl,\n\t\t\t\t\t\t\t)}&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 APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst activeSubscriptions = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub)));\n\t\t\tif (!activeSubscriptions.length) {\n\t\t\t\t/**\n\t\t\t\t * If the subscription is not found, we need to delete the subscription\n\t\t\t\t * from the database. This is a rare case and should not happen.\n\t\t\t\t */\n\t\t\t\tawait ctx.context.adapter.deleteMany({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst activeSubscription = activeSubscriptions.find(\n\t\t\t\t(sub) => sub.id === subscription.stripeSubscriptionId,\n\t\t\t);\n\t\t\tif (!activeSubscription) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst { url } = await client.billingPortal.sessions\n\t\t\t\t.create({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t\treturn_url: getUrl(\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 APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!isActiveOrTrialing(subscription)) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_ACTIVE,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!isPendingCancel(subscription)) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_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 APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Clear scheduled cancellation based on Stripe subscription state\n\t\t\t// Note: Stripe doesn't accept both `cancel_at` and `cancel_at_period_end` simultaneously\n\t\t\tconst updateParams: Stripe.SubscriptionUpdateParams = {};\n\t\t\tif (activeSubscription.cancel_at) {\n\t\t\t\tupdateParams.cancel_at = \"\";\n\t\t\t} else if (activeSubscription.cancel_at_period_end) {\n\t\t\t\tupdateParams.cancel_at_period_end = false;\n\t\t\t}\n\n\t\t\tconst newSub = await client.subscriptions\n\t\t\t\t.update(activeSubscription.id, updateParams)\n\t\t\t\t.catch((e) => {\n\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\tupdate: {\n\t\t\t\t\tcancelAtPeriodEnd: false,\n\t\t\t\t\tcancelAt: null,\n\t\t\t\t\tcanceledAt: null,\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t},\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\treturn ctx.json(newSub);\n\t\t},\n\t);\n};\n\nconst listActiveSubscriptionsQuerySchema = z.optional(\n\tz.object({\n\t\treferenceId: z\n\t\t\t.string()\n\t\t\t.meta({\n\t\t\t\tdescription: \"Reference id of the subscription to list. Eg: '123'\",\n\t\t\t})\n\t\t\t.optional(),\n\t\t/**\n\t\t * Customer type for the subscription.\n\t\t * - `user`: User owns the subscription (default)\n\t\t * - `organization`: Organization owns the subscription\n\t\t */\n\t\tcustomerType: z\n\t\t\t.enum([\"user\", \"organization\"])\n\t\t\t.meta({\n\t\t\t\tdescription:\n\t\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t\t})\n\t\t\t.optional(),\n\t}),\n);\n/**\n * ### Endpoint\n *\n * GET `/subscription/list`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.listActiveSubscriptions`\n *\n * **client:**\n * `authClient.subscription.list`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-list)\n */\nexport const listActiveSubscriptions = (options: StripeOptions) => {\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/list\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: listActiveSubscriptionsQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"listActiveSubscriptions\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"list-subscription\"),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst customerType = ctx.query?.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.query?.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tconst subscriptions = await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!subscriptions.length) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tconst plans = await getPlans(options.subscription);\n\t\t\tif (!plans) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tconst subs = subscriptions\n\t\t\t\t.map((sub) => {\n\t\t\t\t\tconst plan = plans.find(\n\t\t\t\t\t\t(p) => p.name.toLowerCase() === sub.plan.toLowerCase(),\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...sub,\n\t\t\t\t\t\tlimits: plan?.limits,\n\t\t\t\t\t\tpriceId: plan?.priceId,\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((sub) => isActiveOrTrialing(sub));\n\t\t\treturn ctx.json(subs);\n\t\t},\n\t);\n};\n\nconst subscriptionSuccessQuerySchema = z.record(z.string(), z.any()).optional();\n\nexport const subscriptionSuccess = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/success\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: subscriptionSuccessQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleSubscriptionSuccess\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [originCheck((ctx) => ctx.query.callbackURL)],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\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 APIError.from(\"NOT_FOUND\", STRIPE_ERROR_CODES.CUSTOMER_NOT_FOUND);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { url } = await client.billingPortal.sessions.create({\n\t\t\t\t\tlocale: ctx.body.locale,\n\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl),\n\t\t\t\t});\n\n\t\t\t\treturn ctx.json({\n\t\t\t\t\turl,\n\t\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t\t});\n\t\t\t} catch (error: any) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"Error creating billing portal session\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"INTERNAL_SERVER_ERROR\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_BILLING_PORTAL,\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n};\n\nexport const stripeWebhook = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\treturn createAuthEndpoint(\n\t\t\"/stripe/webhook\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tmetadata: {\n\t\t\t\t...HIDE_METADATA,\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleStripeWebhook\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tcloneRequest: true,\n\t\t\tdisableBody: true, // Don't parse the body\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (!ctx.request?.body) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.INVALID_REQUEST_BODY,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst sig = ctx.request.headers.get(\"stripe-signature\");\n\t\t\tif (!sig) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_SIGNATURE_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst webhookSecret = options.stripeWebhookSecret;\n\t\t\tif (!webhookSecret) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"INTERNAL_SERVER_ERROR\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_WEBHOOK_SECRET_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst payload = await ctx.request.text();\n\n\t\t\tlet event: Stripe.Event;\n\t\t\ttry {\n\t\t\t\t// Support both Stripe v18 (constructEvent) and v19+ (constructEventAsync)\n\t\t\t\tif (typeof client.webhooks.constructEventAsync === \"function\") {\n\t\t\t\t\t// Stripe v19+ - use async method\n\t\t\t\t\tevent = await client.webhooks.constructEventAsync(\n\t\t\t\t\t\tpayload,\n\t\t\t\t\t\tsig,\n\t\t\t\t\t\twebhookSecret,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\t// Stripe v18 - use sync method\n\t\t\t\t\tevent = client.webhooks.constructEvent(payload, sig, webhookSecret);\n\t\t\t\t}\n\t\t\t} catch (err: any) {\n\t\t\t\tctx.context.logger.error(`${err.message}`);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.FAILED_TO_CONSTRUCT_STRIPE_EVENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!event) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.FAILED_TO_CONSTRUCT_STRIPE_EVENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tswitch (event.type) {\n\t\t\t\t\tcase \"checkout.session.completed\":\n\t\t\t\t\t\tawait onCheckoutSessionCompleted(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.created\":\n\t\t\t\t\t\tawait onSubscriptionCreated(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.updated\":\n\t\t\t\t\t\tawait onSubscriptionUpdated(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.deleted\":\n\t\t\t\t\t\tawait onSubscriptionDeleted(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch (e: any) {\n\t\t\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${e.message}`);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_WEBHOOK_ERROR,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ctx.json({ success: true });\n\t\t},\n\t);\n};\n","import type { BetterAuthPluginDBSchema } from \"@better-auth/core/db\";\nimport { mergeSchema } from \"better-auth/db\";\nimport type { StripeOptions } from \"./types\";\n\nexport const subscriptions = {\n\tsubscription: {\n\t\tfields: {\n\t\t\tplan: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\treferenceId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstripeSubscriptionId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstatus: {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdefaultValue: \"incomplete\",\n\t\t\t},\n\t\t\tperiodStart: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tperiodEnd: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\ttrialStart: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\ttrialEnd: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tcancelAtPeriodEnd: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\trequired: false,\n\t\t\t\tdefaultValue: false,\n\t\t\t},\n\t\t\tcancelAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tcanceledAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tendedAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tseats: {\n\t\t\t\ttype: \"number\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\nexport const user = {\n\tuser: {\n\t\tfields: {\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\nexport const organization = {\n\torganization: {\n\t\tfields: {\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\ntype GetSchemaResult<O extends StripeOptions> = typeof user &\n\t(O[\"subscription\"] extends { enabled: true } ? typeof subscriptions : {}) &\n\t(O[\"organization\"] extends { enabled: true } ? typeof organization : {});\n\nexport const getSchema = <O extends StripeOptions>(\n\toptions: O,\n): GetSchemaResult<O> => {\n\tlet baseSchema: BetterAuthPluginDBSchema = {};\n\n\tif (options.subscription?.enabled) {\n\t\tbaseSchema = {\n\t\t\t...subscriptions,\n\t\t\t...user,\n\t\t};\n\t} else {\n\t\tbaseSchema = {\n\t\t\t...user,\n\t\t};\n\t}\n\n\tif (options.organization?.enabled) {\n\t\tbaseSchema = {\n\t\t\t...baseSchema,\n\t\t\t...organization,\n\t\t};\n\t}\n\n\tif (\n\t\toptions.schema &&\n\t\t!options.subscription?.enabled &&\n\t\t\"subscription\" in options.schema\n\t) {\n\t\tconst { subscription: _subscription, ...restSchema } = options.schema;\n\t\treturn mergeSchema(baseSchema, restSchema) as GetSchemaResult<O>;\n\t}\n\n\treturn mergeSchema(baseSchema, options.schema) as GetSchemaResult<O>;\n};\n","import type { BetterAuthPlugin, User } from \"better-auth\";\nimport { APIError } from \"better-auth\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport { defu } from \"defu\";\nimport type Stripe from \"stripe\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport { customerMetadata } from \"./metadata\";\nimport {\n\tcancelSubscription,\n\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\ndeclare module \"@better-auth/core\" {\n\tinterface BetterAuthPluginRegistry<AuthOptions, Options> {\n\t\tstripe: {\n\t\t\tcreator: typeof stripe;\n\t\t};\n\t}\n}\n\nexport const stripe = <O extends StripeOptions>(options: O) => {\n\tconst client = options.stripeClient;\n\n\tconst subscriptionEndpoints = {\n\t\tupgradeSubscription: upgradeSubscription(options),\n\t\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 = ctx.getPlugin(\"organization\");\n\t\t\t\tif (!orgPlugin) {\n\t\t\t\t\tctx.logger.error(`Organization plugin not found`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst existingHooks = orgPlugin.options.organizationHooks ?? {};\n\n\t\t\t\t/**\n\t\t\t\t * Sync organization name to Stripe customer\n\t\t\t\t */\n\t\t\t\tconst afterUpdateStripeOrg = async (data: {\n\t\t\t\t\torganization: (Organization & WithStripeCustomerId) | null;\n\t\t\t\t\tuser: User;\n\t\t\t\t}) => {\n\t\t\t\t\tconst { organization } = data;\n\t\t\t\t\tif (!organization?.stripeCustomerId) return;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst stripeCustomer = await client.customers.retrieve(\n\t\t\t\t\t\t\torganization.stripeCustomerId,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (stripeCustomer.deleted) {\n\t\t\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t\t\t`Stripe customer ${organization.stripeCustomerId} was deleted`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Update Stripe customer if name changed\n\t\t\t\t\t\tif (organization.name !== stripeCustomer.name) {\n\t\t\t\t\t\t\tawait client.customers.update(organization.stripeCustomerId, {\n\t\t\t\t\t\t\t\tname: organization.name,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tctx.logger.info(\n\t\t\t\t\t\t\t\t`Synced organization name to Stripe: \"${stripeCustomer.name}\" → \"${organization.name}\"`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t`Failed to sync organization to Stripe: ${e.message}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t/**\n\t\t\t\t * Block deletion if organization has active subscriptions\n\t\t\t\t */\n\t\t\t\tconst beforeDeleteStripeOrg = async (data: {\n\t\t\t\t\torganization: Organization & WithStripeCustomerId;\n\t\t\t\t\tuser: User;\n\t\t\t\t}) => {\n\t\t\t\t\tconst { organization } = data;\n\t\t\t\t\tif (!organization.stripeCustomerId) return;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Check if organization has any active subscriptions\n\t\t\t\t\t\tconst subscriptions = await client.subscriptions.list({\n\t\t\t\t\t\t\tcustomer: organization.stripeCustomerId,\n\t\t\t\t\t\t\tstatus: \"all\",\n\t\t\t\t\t\t\tlimit: 100, // 1 ~ 100\n\t\t\t\t\t\t});\n\t\t\t\t\t\tfor (const sub of subscriptions.data) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tsub.status !== \"canceled\" &&\n\t\t\t\t\t\t\t\tsub.status !== \"incomplete\" &&\n\t\t\t\t\t\t\t\tsub.status !== \"incomplete_expired\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\tif (error instanceof APIError) {\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t`Failed to check organization subscriptions: ${error.message}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\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":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/stripe",
|
|
3
3
|
"author": "Bereket Engida",
|
|
4
|
-
"version": "1.5.0-beta.
|
|
4
|
+
"version": "1.5.0-beta.13",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.mts",
|
|
@@ -49,17 +49,17 @@
|
|
|
49
49
|
"zod": "^4.3.6"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"better-call": "1.2.
|
|
52
|
+
"better-call": "1.2.1",
|
|
53
53
|
"stripe": "^18 || ^19 || ^20",
|
|
54
|
-
"@better-auth/core": "1.5.0-beta.
|
|
55
|
-
"better-auth": "1.5.0-beta.
|
|
54
|
+
"@better-auth/core": "1.5.0-beta.13",
|
|
55
|
+
"better-auth": "1.5.0-beta.13"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"better-call": "1.2.
|
|
58
|
+
"better-call": "1.2.1",
|
|
59
59
|
"stripe": "^20.2.0",
|
|
60
60
|
"tsdown": "^0.20.1",
|
|
61
|
-
"@better-auth/core": "1.5.0-beta.
|
|
62
|
-
"better-auth": "1.5.0-beta.
|
|
61
|
+
"@better-auth/core": "1.5.0-beta.13",
|
|
62
|
+
"better-auth": "1.5.0-beta.13"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
|
65
65
|
"test": "vitest",
|
package/src/error-codes.ts
CHANGED
|
@@ -23,6 +23,8 @@ export const STRIPE_ERROR_CODES = defineErrorCodes({
|
|
|
23
23
|
ORGANIZATION_NOT_FOUND: "Organization not found",
|
|
24
24
|
ORGANIZATION_SUBSCRIPTION_NOT_ENABLED:
|
|
25
25
|
"Organization subscription is not enabled",
|
|
26
|
+
AUTHORIZE_REFERENCE_REQUIRED:
|
|
27
|
+
"Organization subscriptions require authorizeReference callback to be configured",
|
|
26
28
|
ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION:
|
|
27
29
|
"Cannot delete organization with active subscription",
|
|
28
30
|
ORGANIZATION_REFERENCE_ID_REQUIRED:
|
package/src/index.ts
CHANGED
package/src/middleware.ts
CHANGED
package/src/routes.ts
CHANGED
|
@@ -424,7 +424,7 @@ export const upgradeSubscription = (options: StripeOptions) => {
|
|
|
424
424
|
if (!stripeCustomer) {
|
|
425
425
|
stripeCustomer = await client.customers.create({
|
|
426
426
|
email: user.email,
|
|
427
|
-
name: user.name
|
|
427
|
+
name: user.name,
|
|
428
428
|
metadata: customerMetadata.set(
|
|
429
429
|
{
|
|
430
430
|
userId: user.id,
|
|
@@ -832,7 +832,7 @@ describe("stripe - organization customer", () => {
|
|
|
832
832
|
expect(cancelRes.error?.code).toBe("UNAUTHORIZED");
|
|
833
833
|
});
|
|
834
834
|
|
|
835
|
-
it("should reject organization subscription when
|
|
835
|
+
it("should reject organization subscription when authorizeReference is not configured", async () => {
|
|
836
836
|
const stripeOptionsWithoutOrg: StripeOptions = {
|
|
837
837
|
...baseOrgStripeOptions,
|
|
838
838
|
organization: undefined, // Disable organization support
|
|
@@ -878,7 +878,7 @@ describe("stripe - organization customer", () => {
|
|
|
878
878
|
fetchOptions: { headers },
|
|
879
879
|
});
|
|
880
880
|
|
|
881
|
-
expect(res.error?.code).toBe("
|
|
881
|
+
expect(res.error?.code).toBe("AUTHORIZE_REFERENCE_REQUIRED");
|
|
882
882
|
});
|
|
883
883
|
|
|
884
884
|
it("should keep user and organization subscriptions separate", async () => {
|
package/test/stripe.test.ts
CHANGED
|
@@ -4962,7 +4962,7 @@ describe("stripe", () => {
|
|
|
4962
4962
|
fetchOptions: { headers },
|
|
4963
4963
|
});
|
|
4964
4964
|
|
|
4965
|
-
expect(res.error?.code).toBe("
|
|
4965
|
+
expect(res.error?.code).toBe("AUTHORIZE_REFERENCE_REQUIRED");
|
|
4966
4966
|
});
|
|
4967
4967
|
|
|
4968
4968
|
it("should reject when no referenceId or activeOrganizationId", async () => {
|